从没有库的图像中裁剪多边形形状,除了 numpy
Crop a polygon shape from an image without libraries except numpy
我有一个分数列表,比方说 5 分。我想从图像中裁剪这个多边形覆盖的区域。在这里,红色区域是点,我想从黑色背景中裁剪到白色区域。
我可以用 cv2.fillConvexPoly() 函数做到这一点,但我会 运行 GPU 上的这段代码,所以我不能使用 cv2。我只想用 numpy 数组来做到这一点。我有点的 X 和 Y 坐标及其绘制边的顺序。如果不使用 PIL 或 opencv 等库,我无法实现代码,因此任何建议都会有所帮助。
我认为您无法通过仅使用 python 获得比 cv2
更优化的方法。但是如果你想知道 python + NumPy 实现 cv2.fillConvexPoly()
会是什么样子,我会这样做:
- 对于图像中的每个像素,检查它是否在多边形内
- 如果不在内部,将该像素的 alpha 值更改为
0
(假设图像具有 alpha 通道。或者您可以将该像素设为黑色)
为了知道一个像素是否在多边形内,您可以使用 Winding Number Algorithm / Nonzero-rule ,它指出:
For any point inside the polygon the winding number would be non-zero.
Therefore it is also known as the nonzero-rule algorithm.
并且:
For a given curve C and a given point P: construct a ray (a straight
line) heading out from P in any direction towards infinity. Find all
the intersections of C with this ray. Score up the winding number as
follows: for every clockwise intersection (the curve passing through
the ray from left to right, as viewed from P) subtract 1; for every
counter-clockwise intersection (curve passing from right to left, as
viewed from P) add 1. If the total winding number is zero, P is
outside C; otherwise, it is inside.
在我的方法中,我不会添加或减去 1
,而是将其视为转数,这意味着 如果 sum
光线之间的所有角度都是360
,这意味着该点在多边形
内
import numpy as np
def _angle_between_three_points(A, B, C):
a, b, c = np.array(A), np.array(B), np.array(C)
ba = a - b
bc = c - b
cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
angle = np.arccos(cosine_angle) # in radians
return np.degrees(angle) # in degrees
def _get_edges_from_points(points):
edges = []
dist = lambda p1, p2: np.hypot(p2[0] - p1[0], p2[1] - p1[1])
_p = points.copy()
for i, p in enumerate(points):
_p.pop(0)
try:
next_point = sorted(map(lambda pn: (pn, dist(p, pn)), _p), key=lambda x: x[1])[0][0]
except IndexError:
next_point = points[0]
edges.append((p, next_point))
return edges
def is_point_inside(point, polygon):
point = [point[0], point[1]]
angles = map(lambda edge: _angle_between_three_points(edge[0], point, edge[1]), _get_edges_from_points(polygon))
return sum(angles) == 360
现在您可以将 is_point_inside()
应用于每个像素。
注意:值得一读 this Medium 的 Towards Data Science 文章
我有一个分数列表,比方说 5 分。我想从图像中裁剪这个多边形覆盖的区域。在这里,红色区域是点,我想从黑色背景中裁剪到白色区域。
我可以用 cv2.fillConvexPoly() 函数做到这一点,但我会 运行 GPU 上的这段代码,所以我不能使用 cv2。我只想用 numpy 数组来做到这一点。我有点的 X 和 Y 坐标及其绘制边的顺序。如果不使用 PIL 或 opencv 等库,我无法实现代码,因此任何建议都会有所帮助。
我认为您无法通过仅使用 python 获得比 cv2
更优化的方法。但是如果你想知道 python + NumPy 实现 cv2.fillConvexPoly()
会是什么样子,我会这样做:
- 对于图像中的每个像素,检查它是否在多边形内
- 如果不在内部,将该像素的 alpha 值更改为
0
(假设图像具有 alpha 通道。或者您可以将该像素设为黑色)
为了知道一个像素是否在多边形内,您可以使用 Winding Number Algorithm / Nonzero-rule ,它指出:
For any point inside the polygon the winding number would be non-zero. Therefore it is also known as the nonzero-rule algorithm.
并且:
For a given curve C and a given point P: construct a ray (a straight line) heading out from P in any direction towards infinity. Find all the intersections of C with this ray. Score up the winding number as follows: for every clockwise intersection (the curve passing through the ray from left to right, as viewed from P) subtract 1; for every counter-clockwise intersection (curve passing from right to left, as viewed from P) add 1. If the total winding number is zero, P is outside C; otherwise, it is inside.
在我的方法中,我不会添加或减去 1
,而是将其视为转数,这意味着 如果 sum
光线之间的所有角度都是360
,这意味着该点在多边形
import numpy as np
def _angle_between_three_points(A, B, C):
a, b, c = np.array(A), np.array(B), np.array(C)
ba = a - b
bc = c - b
cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
angle = np.arccos(cosine_angle) # in radians
return np.degrees(angle) # in degrees
def _get_edges_from_points(points):
edges = []
dist = lambda p1, p2: np.hypot(p2[0] - p1[0], p2[1] - p1[1])
_p = points.copy()
for i, p in enumerate(points):
_p.pop(0)
try:
next_point = sorted(map(lambda pn: (pn, dist(p, pn)), _p), key=lambda x: x[1])[0][0]
except IndexError:
next_point = points[0]
edges.append((p, next_point))
return edges
def is_point_inside(point, polygon):
point = [point[0], point[1]]
angles = map(lambda edge: _angle_between_three_points(edge[0], point, edge[1]), _get_edges_from_points(polygon))
return sum(angles) == 360
现在您可以将 is_point_inside()
应用于每个像素。
注意:值得一读 this Medium 的 Towards Data Science 文章