从没有库的图像中裁剪多边形形状,除了 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() 会是什么样子,我会这样做:

  1. 对于图像中的每个像素,检查它是否在多边形内
  2. 如果不在内部,将该像素的 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 文章