使用numpy/scipy进行形状识别(可能涉及分水岭算法)

15
我的目标是追踪包含许多分离形状的图纸,并将这些形状拆分为单独的图像。图案为黑白。尽管我对numpy、opencv&co不太熟悉,但这是我的当前想法:
  • 扫描黑色像素
  • 发现黑色像素 -> 分水岭
  • 找到分水岭边界(作为多边形路径)
  • 继续搜索,但忽略已经找到边界内的点
我不是很擅长这种事情,有更好的方法吗?
首先我试图找到分水岭结果的矩形边界框(这更或多少是一组例子的拼贴):
from numpy import *
import numpy as np
from scipy import ndimage

np.set_printoptions(threshold=np.nan)

a = np.zeros((512, 512)).astype(np.uint8) #unsigned integer type needed by watershed
y, x = np.ogrid[0:512, 0:512]
m1 = ((y-200)**2 + (x-100)**2 < 30**2)
m2 = ((y-350)**2 + (x-400)**2 < 20**2)
m3 = ((y-260)**2 + (x-200)**2 < 20**2)
a[m1+m2+m3]=1

markers = np.zeros_like(a).astype(int16)
markers[0, 0] = 1
markers[200, 100] = 2
markers[350, 400] = 3
markers[260, 200] = 4

res = ndimage.watershed_ift(a.astype(uint8), markers)
unique(res) 

B = argwhere(res.astype(uint8))
(ystart, xstart), (ystop, xstop) = B.min(0), B.max(0) + 1 
tr = a[ystart:ystop, xstart:xstop]

print tr
一些神奇的事情发生了,当我使用原始数组(a)时,argwhere似乎可以工作,但在分界线(res)之后它只是再次输出完整的数组。
接下来的步骤可能是找到形状周围的多边形路径,但现在先用边界框就好了!
请帮忙!
2个回答

16

@Hooked已经回答了你问题的大部分,但当他回答时我正在写这篇文章,所以我会发布它,希望它仍然有用...

你试图跳过太多的步骤。 你不需要watershed_ift

你可以使用scipy.ndimage.label来区分布尔数组中的不同对象,并使用scipy.ndimage.find_objects找到每个对象的边界框。

让我们把事情分解一下。

import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt

def draw_circle(grid, x0, y0, radius):
    ny, nx = grid.shape
    y, x = np.ogrid[:ny, :nx]
    dist = np.hypot(x - x0, y - y0)
    grid[dist < radius] = True
    return grid

# Generate 3 circles...
a = np.zeros((512, 512), dtype=np.bool)
draw_circle(a, 100, 200, 30)
draw_circle(a, 400, 350, 20)
draw_circle(a, 200, 260, 20)

# Label the objects in the array. 
labels, numobjects = ndimage.label(a)

# Now find their bounding boxes (This will be a tuple of slice objects)
# You can use each one to directly index your data. 
# E.g. a[slices[0]] gives you the original data within the bounding box of the
# first object.
slices = ndimage.find_objects(labels)

#-- Plotting... -------------------------------------
fig, ax = plt.subplots()
ax.imshow(a)
ax.set_title('Original Data')

fig, ax = plt.subplots()
ax.imshow(labels)
ax.set_title('Labeled objects')

fig, axes = plt.subplots(ncols=numobjects)
for ax, sli in zip(axes.flat, slices):
    ax.imshow(labels[sli], vmin=0, vmax=numobjects)
    tpl = 'BBox:\nymin:{0.start}, ymax:{0.stop}\nxmin:{1.start}, xmax:{1.stop}'
    ax.set_title(tpl.format(*sli))
fig.suptitle('Individual Objects')

plt.show()

enter image description here enter image description here enter image description here

希望这能更清楚地说明如何找到对象的边界框。


非常感谢你们两位的回答,我想这就是答案了。如果可以的话,我只有一个新手numpy问题:我不能仅保存边界矩形的区域,因为其他形状会“窥视”。所以我的计划是将图像面积乘以反转标签数组(使当前形状之外的所有内容变黑),然后使用ndimage保存图像区域。你能指点我如何做吗?我知道,一旦我有时间,我会仔细阅读文档! - bpat
1
我认为你只是想要 label == num,其中 numlabel(标记数组)中对象的数量。这样的操作在numpy数组上是向量化的,因此它就是上面的语句。你将得到一个布尔数组,在“对象”内部为 True,在外部为 False - Joe Kington

6

使用scipy中的ndimage库。函数label会对每个在阈值内的像素块进行唯一标记,从而识别出唯一的聚类(形状)。从您定义的a开始:

from scipy import ndimage

image_threshold = .5
label_array, n_features =  ndimage.label(a>image_threshold)

# Plot the resulting shapes
import pylab as plt
plt.subplot(121)
plt.imshow(a)
plt.subplot(122)
plt.imshow(label_array)
plt.show()

enter image description here


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