在图像中寻找空白区域

11

这个问题有点与语言无关,但我选择使用的工具是numpy数组。

我的操作是通过PIL获取两幅图像的差异:

img = ImageChops.difference(img1, img2)

我想要找到包含两张图片之间变化的矩形区域。当然,可以使用内置的.getbbox()方法,但如果有两个具有变化的区域,它将返回从一个区域到另一个区域的框,而如果每个角落只有1个像素的变化,则会返回整个图像。

例如,考虑以下情况,其中o是非零像素:

______________________
|o            ooo    |
|      oooo   ooo    |
|      o             |
|      o  o          |
|                    |
|     oo       o     |
|    o  o     ooo    |
|     oo     ooooo   |
|             ooo    |
|              o     |
|____________________|

我想获取包含每个非零区域边界框的4x4元组。对于边缘情况,

oooo
o
o  o

结构,我并不担心它是如何处理的 - 无论是分别获取两个部分还是一起获取,因为倒L形状的边界将完全重叠在单个像素的边界上。

我以前从未在图像处理方面做过这样高级的事情,所以我想在真正编写任何代码之前先获得一些输入(如果我已经使用的模块中有现成的方法,我欢迎它们!)。

我的伪代码版本大概是这样的:

for line in image:
   started = False
   for pixel in line:
      if pixel and not started:
         started = True
         save start coords
      elif started and not pixel:
         started = False
         save end coords (x - 1 of course)

这段代码应该会给我一列坐标,但我需要确定这些区域是否连续。我可以用图形搜索(我们在算法课上做了很多DFS和BFS)实现吗?当然,我猜我也可以用之前的循环来实现这个目标?

我不会处理“大”图像 - 它们是从网络摄像头中获取的,我目前最好的摄像头只有640x480的分辨率。最多我可能会处理720p或1080p,但那已经远远超出了我的实际需求。

因此,我的问题是:我走在正确的道路上吗,还是步履蹒跚?更重要的是,有没有内置函数可以避免我重新发明轮子?最后,有没有一些好的资源(教程、论文等)能够帮助我解决这个问题?

谢谢!

3个回答

20

我相信 scipy的ndimage模块 包含了你需要的所有内容...

这里是一个快速示例:

import numpy as np
import scipy as sp
import scipy.ndimage.morphology

# The array you gave above
data = np.array( 
        [
           [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0], 
           [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0], 
           [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
           [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
           [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], 
           [0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0], 
           [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0], 
           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0], 
           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], 
        ])


# Fill holes to make sure we get nice clusters
filled = sp.ndimage.morphology.binary_fill_holes(data)

# Now seperate each group of contigous ones into a distinct value
# This will be an array of values from 1 - num_objects, with zeros
# outside of any contigous object
objects, num_objects = sp.ndimage.label(filled)

# Now return a list of slices around each object
#  (This is effectively the tuple that you wanted)
object_slices =  sp.ndimage.find_objects(objects)

# Just to illustrate using the object_slices
for obj_slice in object_slices:
    print data[obj_slice]

这将输出:

[[1]]
[[1 1 1]
 [1 1 1]]
[[1 1 1 1]
 [1 0 0 0]
 [1 0 0 1]]
[[1]]
[[0 1 1 0]
 [1 0 0 1]
 [0 1 1 0]]
[[0 0 1 0 0]
 [0 1 1 1 0]
 [1 1 1 1 1]
 [0 1 1 1 0]
 [0 0 1 0 0]]

请注意,“object_slices”基本上是您最初要求的内容,如果您需要实际的索引。

编辑:只想指出,尽管它似乎正确处理了边缘情况

[[1 1 1 1]
 [1 0 0 0]
 [1 0 0 1]]

实际上并不是这样的(因此有了额外的单独[[1]])。如果您打印“objects”数组并查看对象3和4,就会发现这一点。

[[1 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 0 0 0 0]
 [0 0 0 0 0 0 3 3 3 3 0 0 0 2 2 2 0 0 0 0]
 [0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 3 0 0 4 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 5 5 0 0 0 0 0 0 0 6 0 0 0 0 0]
 [0 0 0 0 5 5 5 5 0 0 0 0 0 6 6 6 0 0 0 0]
 [0 0 0 0 0 5 5 0 0 0 0 0 6 6 6 6 6 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0]]

希望这可以帮助你!

[1]


哇塞,太完美了!这正是我想要的 - 而且我认为我实际上更喜欢它以那种方式处理边缘情况 - 这样它确实会获取所有像素的框。我希望我可以投多次票! - Wayne Werner
@JoeKington 是最有帮助的程序员之一。 - chimpsarehungry
@chimpsarehungry - 你所说的“最大”,是指覆盖面积最大的那个吗?如果是这样,只需按切片大小的乘积对object_slices列表进行排序。例如,请查看此处的最后几行:https://gist.github.com/joferkington/5074650#file-gistfile1-py - Joe Kington
循环度=4π(面积/周长^2)...我所需要的只是一个周长算法。 - chimpsarehungry
这似乎是有效的:np.sum(mahotas.labeled.bwperim(object, n=8)*object) - chimpsarehungry
显示剩余5条评论

1
您可以在图像中寻找连接的组件,然后确定这些组件的边界框。

我有点困惑 - 这跟我说的我正在做的有什么不同吗? - Wayne Werner

1
一个聚类包(如此)应该能够完成大部分工作(找到相连的像素)。然后,为聚类找到边界框就很容易了。

聚类会如何帮助(尤其是这个软件包)?从我阅读的文档字符串中了解到,它只能找到距离为X的数据,并且似乎没有存储任何X-Y数据。我有什么遗漏吗? - Wayne Werner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接