4

我正在开发一个应用程序来识别圆形/椭圆形内的线状特征。形状如下所示(此处显示两个):

形状

  • 形状本身可以在圆形和椭圆形之间略有不同。
  • 形状内部最多有 5 条线,它们位于每个形状大致相同的区域。
  • 这些线条的长度、粗细、旋转和曲率可能略有不同。
  • 线条有时会稍微接触/相交。
  • 通常正好有 5 个,但有时可能会完全丢失一行。
  • 我不在乎颜色,黑白阈值很好。

每个对象(超过 100 个)将通过视频单独捕获;捕获是一个手动/物理过程(即我每次都拿着相机)。我可以完全控制相机,因此我可以在每次拍摄时始终如一地定位它。

现在我正在尝试使用 OpenCV 进行识别。我能够修改示例“面部识别”应用程序以使用另一个 Haar 标识符 XML 文件,但这似乎只处理外部圆圈/椭圆的检测。

我有兴趣为每个样本生成一个对象,以描述 5 条内部线以进行进一步处理:

{
    1: { length: 20, avg_thick: 2.3 },
    2: { length: 4, avg_thick: 2.0 },
    3: { length: 9.1, avg_thick: 2.1 },
    4: { length: 2, avg_thick: 1.9 },
    5: { length: 17, avg_thick: 2.1 }
}

这是我第一个涉及图像识别的项目。我应该使用/研究什么算法或程序来实现这一目标?谢谢!

更新:

由于图像是手工拍摄的,它们不是纯黑/白的。尝试应用阈值会使形状内的(假装)线有时会消失。如何改善阈值结果?

4

2 回答 2

5

如果线条近似直线,则使用霍夫变换查找所有线条,使用霍夫变换的圆形版本查找所有圆/椭圆(然后您可以检查是否找到了边界圆/椭圆,以及哪些线条在里面例如)。

如果线条不直:您的意思是“狭窄的细长区域”而不是线条,对吗?:) 你必须骨架化(可能首先是阈值)。有用的教程:“使用 OpenCV-Python 进行骨架化”。由于您还需要宽度(= 从骨架到边缘的距离),请使用skimage.morphology.medial_axis (..., return_distance=True)。您可能还需要一些方法来穿过每个骨架的分支并修剪短分支(抱歉,没有现成的东西已经这样做了)。

Haar 类型的方法根本不起作用,它仅适用于(甚至理论上)具有固定相对位置和形状的特征。您想要某种几何特征提取算法,而不是图像识别。

编辑: python中的示例代码:

import numpy, scipy, scipy.ndimage, skimage.morphology, matplotlib.pyplot

img = scipy.ndimage.imread("test.png")

# quick and dirty threshold
binary_img = img[:,:,0] < 0.1

# skeletonize
skel_binary, skel_distances = skimage.morphology.medial_axis(binary_img, return_distance=True)

# find individual lines
structure_element = scipy.ndimage.generate_binary_structure(2,2)
skel_labels, num_skel_labels = scipy.ndimage.measurements.label(skel_binary, structure=structure_element)

for n in range(1, num_skel_labels + 1):
    # make a binary label for this line
    line = (skel_labels == n)
    # calculate width from skeleton
    mean_width = 2 * numpy.mean( skel_distances[ line ] )
    print "line %d: width %f" % (n, mean_width)
    # you need some way to find the ends of a line
    # perhaps the most distant pair of points?

# show the labels
# the circle is also labelled
# you need some way to check which label is the circle and exclude that
matplotlib.pyplot.imshow(skel_labels)
matplotlib.pyplot.show()

上面程序的输出

这会在您在上面发布的图像上产生合理的结果,并且(检查线条粗细是否有效)在这些图像的放大 10 倍版本上。它不处理相交的线,也许你可以为此做一个图形算法。此外,您确实需要以某种方式排除外圈(它似乎总是 n=1,因为标记发生在左上角,并且它找到的第一个标记区域是圆圈)。

编辑:如何(或是否)阈值是一个有趣的问题。您可以尝试自动阈值化,可能基于 Otsu 的方法,或者基于高斯混合(示例)。我认为您可能会通过某种背景和前景颜色亮度的统计模型以及局部自适应阈值处理获得最佳结果。真的取决于你的图像的性质。

于 2013-01-02T07:04:44.277 回答
1
  1. 骨架化图像并跟踪每一行,然后你就有了每行的数量和长度。
  2. 对于每条线,计算面积(计算原始 BW 图像中黑色像素的数量)然后将其除以长度以找到平均厚度。
于 2013-01-02T06:31:39.513 回答