如何在Python中根据像素标签获取物体边界框?

3

假设我有一张图像的场景解析地图,其中每个像素都指示该像素属于哪个对象。现在我想获得每个对象的边界框,在python中如何实现? 举个具体的例子,假设我有一个场景解析地图像这样:

0 0 0 0 0 0 0
0 1 1 0 0 0 0
1 1 1 1 0 0 0
0 0 1 1 1 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 0 0 0 0 0
1 1 1 1 1 0 0
1 0 0 0 1 0 0
1 0 0 0 1 0 0
1 1 1 1 1 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0

实际上,在我的任务中,只需要知道这个对象的宽度和高度就足够了。

基本想法是搜索场景解析图中的四条边,从上、下、左、右四个方向来找。但是在图像中可能有很多小物体,这种方法并不高效。

第二种方法是计算所有非零元素的坐标,并找到最大/最小的x/y值,然后使用这些x和y计算重量和高度。

是否有其他更有效的方法呢?谢谢。


请展示一下你的尝试。 - Julien
Added.@JulienBernu - Demonedge
你需要做的听起来非常类似于图形填充,因此你可以使用执行此操作的算法作为起点(双关语)。 - martineau
2个回答

10

如果您正在处理图像,可以使用scipy的ndimage库。

如果图片中只有一个对象,则可以使用scipy.ndimage.measurements.find_objects(http://docs.scipy.org/doc/scipy-0.16.1/reference/generated/scipy.ndimage.measurements.find_objects.html)获取测量值:

import numpy as np
from scipy import ndimage
a = np.array([[0, 0, 0, 0, 0, 0, 0],
              [0, 1, 1, 0, 0, 0, 0],
              [1, 1, 1, 1, 0, 0, 0],
              [0, 0, 1, 1, 1, 0, 0],
              [0, 0, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 0, 0],
              [0, 0, 0, 0, 0, 0, 0]])

# Find the location of all objects
objs = ndimage.find_objects(a)

# Get the height and width
height = int(objs[0][0].stop - objs[0][0].start)
width = int(objs[0][1].stop - objs[0][1].start)
如果图像中有许多物体,则首先必须为每个物体进行标记,然后获取测量值:
import numpy as np
from scipy import ndimage
a = np.array([[0, 0, 0, 0, 0, 0, 0],
              [0, 1, 1, 0, 0, 0, 0],
              [1, 1, 1, 1, 0, 0, 0],
              [0, 0, 1, 1, 1, 0, 0],
              [0, 0, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 0, 0],
              [0, 0, 1, 1, 1, 0, 0]])  # Second object here
# Label objects
labeled_image, num_features = ndimage.label(a)
# Find the location of all objects
objs = ndimage.find_objects(labeled_image)
# Get the height and width
measurements = []
for ob in objs:
    measurements.append((int(ob[0].stop - ob[0].start), int(ob[1].stop - ob[1].start)))

如果您查看ndimage.measurements,您可以获取更多的测量值:重心、面积...


3

使用numpy:

import numpy as np

ind = np.nonzero(arr.any(axis=0))[0] # indices of non empty columns 
width = ind[-1] - ind[0] + 1
ind = np.nonzero(arr.any(axis=1))[0] # indices of non empty rows
height = ind[-1] - ind[0] + 1

稍微解释一下:

arr.any(axis=0) 会返回一个布尔数组,告诉你每一列是否为空(False)或不为空(True)。np.nonzero(arr.any(axis=0))[0] 然后从该数组中提取非零(即True)索引。ind[0] 是该数组的第一个元素,因此是最左边的非空列,而 ind[-1] 是最后一个元素,因此是最右边的非空列。然后将宽度相减,加上1(根据您是否包括边框而定),这就是宽度。 高度的计算方式与宽度类似,但是在另一个轴上。


我不知道,也许是因为强制使用 numpy,因为用 Python 完全可以做到。但是 OP 没有展示任何代码,所以我怀疑他没有编写任何代码。一些高声望的人不喜欢在 OP 没有编写代码之前为其编写代码。 - Jean-François Fabre
OP要求效率,因此事先禁止使用numpy并因此而被downvote对我来说似乎很愚蠢。如果是第二个原因,我不是通灵者,所以希望被告知而不是期望我读他们的心思...另外,我看到很多类似的问题都是由高声望的人自己回答的... - Julien
谢谢你的回答,我不知道为什么会被投票否决,但这正是我所需的。 - Demonedge
@Julien Bernu:在一些企业系统中,可能没有numpy,并且无法安装它。我怀疑这个downvote与此有关。我认为你没有充分解释你的解决方案。好吧,有些人有声望可以花费downvoting工作答案,对他们来说很好。我更喜欢评论并询问它是如何工作的。+1(但我希望得到一些解释:)) - Jean-François Fabre
@Jean-FrançoisFabre,问清楚从来不会害死任何人... 这是你需要的 ;) - Julien

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