python-3.x - 使用 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% 的误差应该没问题。
解决方案
一个盒子和一个圆圈可以重叠也可以不重叠。如果四个角在圆圈内,那么这就是你的“面积百分比”目标的 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 的网格。
推荐阅读
- r - for 循环迭代字符值
- javascript - discord.js-commando TypeError: this.fetchUser 不是一个函数
- java - 通过代理服务器连接到数据库休眠、JDBC
- python - 在 pandas 中创建 n 个行的副本
- python-3.x - 如果将 Firebase 广告和分析添加到 buildozer.spec 并请求广告,Kivy Buildozer APK 会崩溃
- c# - 在 EF 上查询虚拟列表属性
- linux - Linux At Now 命令调用带有属性的 Bash 脚本
- sql-server - 如何修复 - 无法加载 DLL 'SqlServerSpatial110.dll' 错误
- jenkins - 如何在 Linux 的从节点中运行 Jenkins 作业时指定用户
- c++ - 缺少功能描述 (IDE)