我有一个 640x480 二进制图像(0s 和 255s)。图像中有一个白色斑点(几乎是圆形),我想找到斑点的质心(它总是凸的)。本质上,我们正在处理的是一个 2D 布尔矩阵。如果可能的话,我希望运行时是线性的或更好的——这可能吗?
到目前为止的两条思路:
- 使用
numpy.where()
功能 - 对每一列和每一行的值求和,然后根据这些数字找到最大值的位置......但是有没有一种快速有效的方法来做到这一点?这可能只是我对 python 比较陌生的一个例子。
我有一个 640x480 二进制图像(0s 和 255s)。图像中有一个白色斑点(几乎是圆形),我想找到斑点的质心(它总是凸的)。本质上,我们正在处理的是一个 2D 布尔矩阵。如果可能的话,我希望运行时是线性的或更好的——这可能吗?
到目前为止的两条思路:
numpy.where()
功能此代码将找到任何形状图像的质心。它会准确地找到它rez = 1
。增加rez
, 增加网格间距,因此可以大大提高搜索速度,但代价是精度明显下降。如果在范围内已知 blob 的大小,则可以将低rez
搜索与高rez
搜索链接起来,从而快速且昂贵地找到答案
import Image
def find_centroid_faster(im, rez):
width, height = im.size
XX, YY, count = 0, 0, 0
for x in xrange(0, width, rez):
for y in xrange(0, height, rez):
if im.getpixel((x, y)) == 255:
XX += x
YY += y
count += 1
return XX/count, YY/count
例如,使用下图:
im = Image.open('blob.png')
print find_centroid(im, 1)
print find_centroid(im, 20)
#output:
(432, 191)
(430, 190)
使用第一个选项计时timeit
(即线性时间,O(n)
)的运行时间为1.7s
,第二个选项为0.005s
。
除非您对尺寸和形状有一些限制,否则您最好O(n)
找到一个确切的答案。但是,您可以牺牲准确性来换取速度。上面的代码是O(n/(rez ** 2))
,这可以是一个巨大的改进。报告结果的准确性为:± rez / 2
,在每个维度上。
更新:
sega_sai
写了一段很好的numpy
代码(见下面的帖子)来找到质心。我已经通过使用切片对其进行了修改以利用网格间距。它以与上述相同的方式运行:
def find_centroid_faster_numpy(im,rez):
h, w = im.size
arr = np.array(im)
arr_rez = arr[::rez,::rez]
ygrid, xgrid = np.mgrid[0:w:rez, 0:h:rez]
xcen, ycen = xgrid[arr_rez == 255].mean(), ygrid[arr_rez == 255].mean()
return xcen, ycen
timeit
以下是这两个函数在一系列值上的图形结果rez
:
它是一个日志图,因此它真正说明了结合这两种方法的优势。
这是我用于测试的图像:
质心坐标是点坐标的算术平均值。如果你想要线性解决方案,只需逐个像素地计算每个坐标的平均值,其中像素为白色,这就是质心。
在一般情况下,你可能没有办法让它比线性更好,但是,如果你的圆形对象比图像小得多,你可以通过首先搜索它来加速它(采样一些随机像素,或者像素网格,如果您知道 blob 足够大),然后使用 BFS 或 DFS 找到所有白点。
这段代码:
import Image, numpy as np
def getBlobCenter(imname):
im = Image.open(imname)
w, h = im.size
arr = np.array(im)
xgrid, ygrid = np.mgrid[0:w, 0:h]
xcen, ycen = xgrid[arr == 255].mean(), ygrid[arr == 255].mean()
return xcen, ycen
fraxel 提供的 640x480 图片需要 10 毫秒
根据你的 blob 的大小,我会说大幅降低图像的分辨率可能会达到你想要的效果。
将其降低到 1/10 分辨率,找到一个白色像素,然后您就可以准确了解在哪里搜索质心。