两个相似形状之间的OpenCV形状匹配

时间:2019-04-05 06:24:13

标签: python opencv image-processing image-recognition opencv-contour

我正在尝试将稍微不规则的形状与形状数据库匹配。例如,在这里我要匹配的轮廓:

enter image description here

有关更多信息,这是HDMI连接器的轮廓,以轮廓表示。有点粗糙,因为这是在握住HDMI的同时用手机拍摄的。

这是我的连接器数据库:

HDMI:enter image description here

DVI:enter image description here

5PinDIN:enter image description here

DB25:enter image description here

这些轮廓更加清晰,因为这些轮廓是从互联网的连接器图像中收集的。

对于我尝试过的:

cv2.matchShapes()

由于这些都是轮廓,因此我尝试使用matchShapes()方法直接比较它们,但结果不佳。不规则轮廓与我的数据库之间的相似之处是:

HDMI:0.90

DB25:0.84

5针DIN:0.5

DVI:0.21

由于轮廓越相似,匹配结果越接近0,则算法完全失败。我通过更改第三个参数尝试了其他匹配方法,但仍然没有成功。

ORB:

与SIFT类似,我尝试了关键点匹配。在数据库中找到不同匹配项之间的平均距离(找到匹配项的前15%之后):

mean([m.distance for m in matches])

距离出现为:

五针DIN:7.6

DB25:11.7

DVI:12.1

HDMI:19.6

由于将圆形归类为最像我的轮廓的形状,因此也失败了。

以下是实际HDMI插槽与我的示例HDMI插槽的ORB中的匹配关键点,以获取更多信息: enter image description here

我应该尝试任何想法/其他算法吗?还是CNN是我唯一的选择(由于我没有足够的数据量,所以我宁愿避免使用它。

3 个答案:

答案 0 :(得分:2)

这组图像的简短答案是使用OpenCV matchShapes方法I2,并使用较小的“ eps”重新编码matchShapes方法。 double eps = 1.e-20;足够小。

我是一名高中机器人团队的导师,我认为OpenCV matchShapes正是我们改善机器人视力所需要的(缩放,平移和旋转不变,并且使学生易于在现有的OpenCV代码中使用)。我在研究中花了两个小时才看到这篇文章,这真是令人震惊!鉴于这些结果,matchShapes如何为我们工作?我对这些糟糕的结果感到怀疑。

我编写了自己的matchShapes(用Java-这是学生想要使用的代码),以查看更改eps的效果(该值很小,显然可以保护log10函数免受零影响,并通过将其称为a来防止BIG差异完美匹配-与实际情况相反;我找不到价值的基础)。我将matchShapes eps从OpenCV的1.e-5更改为1.e-20,并获得了不错的结果,但是过程仍然令人不安。

这是一个奇妙但令人恐惧的事情,只要给出正确的答案,我们就可以扭曲获取它的过程。随附的图片包含了Hu Moment比较的所有3种方法,而方法2和3做得很好。

我的过程是保存上面的图像,转换为二进制1通道,扩张1,侵蚀1,findCountours,matchShapes,eps = 1.e-20。

Method 2,Target HDMI with itself = 0., HDMI=1.15, DVI=11.48, DB25=27.37, DIN=74.82
Method 3,Target HDMI with itself = 0. ,HDMI=0.34, DVI= 0.48, DB25= 2.33, DIN= 3.29

contours and Hu Moment comparisons - matchShapes 3 methods

我继续我的幼稚研究(统计学的小背景),并找到了进行标准化和比较的各种其他方法。我无法弄清楚皮尔森相关系数和其他协方差方法的细节,也许它们不合适。我测试了另外两种归一化方法和另一种匹配方法。

OpenCV使用Log10函数对其所有三个匹配计算进行标准化。

我尝试通过与每对Hu矩之比与每对最大值的比率max(Ai,Bi)归一化,并尝试将每对Hu矩归一化为矢量长度1(除以平方和的平方和)。 / p>

在使用余弦theta方法计算7维Hu矩矢量之间的角度之前以及在计算类似于OpenCV方法I2的元素对差之和之前,我使用了这两个新的归一化。

我的四个新混合液效果很好,但是除了具有“校正” eps的openCV I2之外,没有做任何其他贡献,除了值的范围较小并且仍然订购相同。

请注意,I3方法不是对称的-交换matchShapes参数顺序将更改结果。对于这组图像,将“未知”的矩作为第一个参数,并与已知形状的列表作为第二个参数进行比较以获得最佳结果。另一种方法是将结果更改为“错误”答案!

我尝试的匹配方法的数量7与Hu Moments-7的数量只是偶然的。

7种不同计算的匹配索引的描述

|Id|normalization            |matching index computation       |best value|
|--|-------------------------|---------------------------------|----------|
|I1|OpenCV log               |sum element pair reciprocals diff|0|
|I2|OpenCV log               |sum element pair diff            |0|
|I3|OpenCV log               |maximum fraction to A diff       |0|
|T4|ratio to element pair max|vectors cosine angle             |1|
|T5|unit vector              |vectors cosine angle             |1|
|T6|ratio to element pair max|sum element pair diff            |0|
|T7|unit vector              |sum element pair diff            |0|

对5张图像中的每张图像进行7种不同计算的匹配指标结果

|               |  I1 |  I2 |  I3 |  T4 |  T5 |  T6 |  T7 |
|---------------|-----|-----|-----|-----|-----|-----|-----|     
|HDMI 0         | 1.13| 1.15| 0.34| 0.93| 0.92| 2.02| 1.72|
|DB25 1         | 1.37|27.37| 2.33| 0.36| 0.32| 5.79| 5.69|
|DVI 2          | 0.36|11.48| 0.48| 0.53| 0.43| 5.06| 5.02|
|DIN5 3         | 1.94|74.82| 3.29| 0.38| 0.34| 6.39| 6.34|
|unknown(HDMI) 4| 0.00| 0.00| 0.00| 1.00| 1.00| 0.00| 0.00|(this image matches itself)

[创建了OpenCV问题16997,以解决matchShapes中的这一弱点。]

答案 1 :(得分:1)

可以执行多个步骤以获得更好的结果。而且不需要CNN或某些复杂的功能匹配,让我们尝试使用非常基本的方法来解决此问题。

1。规范化查询图像和数据库图像。

这可以通过仔细裁剪输入轮廓,然后将所有图像调整为相同的高度或宽度来完成。我将在这里选择宽度,例如300px。让我们为此定义一个实用方法:

def normalize_contour(img):
    im, cnt, _ = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    bounding_rect = cv2.boundingRect(cnt[0])
    img_cropped_bounding_rect = img[bounding_rect[1]:bounding_rect[1] + bounding_rect[3],
                                bounding_rect[0]:bounding_rect[0] + bounding_rect[2]]

    new_height = int((1.0 * img.shape[0])/img.shape[1] * 300.0)
    img_resized = cv2.resize(img_cropped_bounding_rect, (300, new_height))
    return img_resized

此代码段将返回固定宽度为300的裁剪良好的轮廓。将此方法应用于所有数据库图像以及输入的查询图像。

2。只需使用输入归一化图像的高度进行过滤。

由于我们已将输入图像标准化为300像素,因此我们可以拒绝所有高度不接近标准化图像高度的候选对象。这将排除5PinDIN。

3。比较面积

现在,您可以尝试以最大重叠度对结果进行排序,可以cv2.contourArea()来获取轮廓区域,并对所有剩余的候选项进行排序以得到最接近的匹配项。

答案 2 :(得分:1)

此答案基于ZdaR此处https://stackoverflow.com/a/55530040/1787145的答案。我尝试了一些变体,希望通过在预处理过程中加入更多内容来使用单一的区分标准(cv2.matchShapes())。

1。比较图像而不是轮廓

我喜欢规范化的概念(裁剪和调整大小)。但是在缩小图像后,由于像素的分辨率低,其最初关闭的轮廓可能会分成多个断开的部分。 cv2.matchShapes()的结果不可靠。通过比较整个调整大小的图像,我得到以下结果。它说圈子是最相似的。不好!<​​/ p>

enter image description here

2。填充形状

通过填充形状,我们考虑了面积。结果看起来更好,但是DVI仍然比HDMI具有更高的高度或高度/宽度比。我们想忽略那个。

enter image description here

3。将每个图像调整为相同大小

通过将所有尺寸调整为相同大小,我们消除了尺寸上的某些比例。 (300,300)在这里效果很好。

enter image description here

4。代码

def normalize_filled(img):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    im, cnt, _ = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    # fill shape
    cv2.fillPoly(img, pts=cnt, color=(255,255,255))
    bounding_rect = cv2.boundingRect(cnt[0])
    img_cropped_bounding_rect = img[bounding_rect[1]:bounding_rect[1] + bounding_rect[3], bounding_rect[0]:bounding_rect[0] + bounding_rect[2]]
    # resize all to same size
    img_resized = cv2.resize(img_cropped_bounding_rect, (300, 300))
    return img_resized

imgs = [imgQuery, imgHDMI, imgDVI, img5PinDin, imgDB25]
imgs = [normalize_filled(i) for i in imgs]

for i in range(1, 6):
    plt.subplot(2, 3, i), plt.imshow(imgs[i - 1], cmap='gray')
    print(cv2.matchShapes(imgs[0], imgs[i - 1], 1, 0.0))