首页 > 解决方案 > 使用 numpy (Python 3) 生成软圆形遮罩

问题描述

我需要生成代表人眼感受野的圆形蒙版。主要问题是我不确定如何计算部分在圆外的像素覆盖的区域百分比。例如,考虑以下用于生成圆形掩码的简单代码:

def circle(size=19, r=7, x_offset=0, y_offset=0):
    x0 = y0 = size // 2
    x0 += x_offset
    y0 += y_offset
    y, x = np.ogrid[:size, :size]
    y = y[::-1]

    return ((x - x0)**2 + (y-y0)**2)<= r**2

生成这个圆圈:

在此处输入图像描述

如您所见,输出是二进制的,如果圆圈覆盖至少 50% 的区域,则其权重设置为 1,如果覆盖率小于 50%,则设置为 0。这是一个叠加层,显示得更清楚:

在此处输入图像描述

生成看起来更平滑的圆的另一种常用方法是使用 2D 高斯:

def gaussian2d(size=19, sigma = 3, amplitude=1, x_offset=0, y_offset=0):
    x = np.arange(0, size, 1, float)
    y = x[:,np.newaxis][::-1]
    x0 = y0 = size // 2
    x0 += x_offset
    y0 += y_offset

    return amplitude*np.exp(-(((x-x0)**2 /(2*sigma**2)) + ((y-y0)**2 /(2*sigma**2))))

产生如下所示的输出:

在此处输入图像描述

这解决了二进制输出的问题(权重设置为 1 或 0),但问题是即使对于 100% 被圆圈覆盖的像素,权重也会开始下降,这是不正确的。

理想情况下,面具应该看起来像这样(这是手绘的):

在此处输入图像描述

被圆圈100%覆盖的像素设置为1,部分覆盖的像素设置为圆圈覆盖的百分比面积,例如:

在此处输入图像描述

效率(在运行时和内存使用方面)并不是那么重要,我只需要一种方法来计算像素面积的百分比,即被圆圈覆盖。准确性也不是很重要,1% 的误差应该没问题。

标签: python-3.xnumpygeometryarea

解决方案


一个盒子和一个圆圈可以重叠也可以不重叠。如果四个角在圆圈内,那么这就是你的“面积百分比”目标的 100%。

所以第一步是计算从角到圆心的四个距离。

di = sqrt((xi-xc)^2 + (yi-yc)^2)

如果di都大于或等于圆的半径R,则框在圆外。同样,如果所有角都验证,di <= R则单元格完全在圆圈内。

其余的情况在圆圈内有一到三个角。
让我们看看这个“外面的一个角落”案例:

在此处输入图像描述

该点是计算点的坐标P,Q

因为您知道 AB 是水平的,所以您可以xQ通过使用坐标来获得y坐标(A 和 B 相同)

xQ= xc + sqrt(R^2-(yA-yc)^2)

其中xc,yc是圆心的坐标。请注意yQ = yA = yB

请注意,您可以获得两个值,具体取决于您使用的 sqrt 的符号。拿那个是xA <= xQ <= xB

同样,使用xA您可以找到xP.

在这一刻你知道所有的坐标。
注意直P,Q。它下面的区域很简单,只是一些三角形。或减去三角形的细胞总面积P,A,Q

直线和圆周之间的面积是一个扇形的面积(你可以浏览互联网找到它)减去一个三角形。
你需要找到这个扇区的角度。为此,我建议使用atan2而不是atan,因为前者给出了 (0, 2pi) 范围内的角度。您必须使用atan2(f1)-atan2(f2)归一化为 (0,pi) 范围的减法。

您可以绘制其他案例(例如三个角)并使用上述方法找到您需要的区域。

例如,您有一个带有两个角的盒子,但不知道圆圈交叉的边。您可以通过以下方式测试一方:

  • 找一面(我们称它为 AB)。如果它是水平的,你知道它的y= yA=yB坐标。
  • 为此找到x 圆圈中的坐标y(两个解决方案,+x,-x)
  • 测试xA < x xB。如果两种解决方案都失败,则圆圈不会越过这一侧。

如果一侧是垂直的,则使用其y坐标找到x一侧。
如果一侧的两个角都在外面,那么您可以避免对其进行测试。

一个简单的近似

与蒙特卡罗方法非常相似

对于那些穿过圆周的单元格(一些但不是所有角落都在外面),您可以将每个角落细分为一个n x n网格,并测试每个子单元格。

测试可以是从子单元的中心(例如其对角线的平均点)到圆心的距离。

如果你指定 area=0 dist > R,否则你指定 area=1。
那么单元格的“分配”区域就是平均值;这是 n^2 个子单元的所有分配区域的总和,除以单元的面积(即 nxn)。

在最坏的情况下,大约n有分配错误区域的子小区。这是n/n^2 = 1/n错误。因此,如果您想以小于 1% 的误差进行计算,则单元格中的网格需要一个 100x100 的网格。


推荐阅读