简体   繁体   English

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

[英]OpenCV shape matching between two similar shapes

I'm trying to match a slightly irregular shape to a database of shapes.我正在尝试将稍微不规则的形状与形状数据库相匹配。 For example, here the contour I'm trying to match:例如,这里我试图匹配的轮廓:

在此处输入图片说明

For more information, this is an outline of an HDMI connector, represented as a contour.有关更多信息,这是 HDMI 连接器的轮廓,表示为轮廓。 It is slightly rough as this was taken with a phone while holding the HDMI.有点粗糙,因为这是在拿着 HDMI 的情况下用手机拍摄的。

This is my database of connectors:这是我的连接器数据库:

HDMI: HDMI: 在此处输入图片说明

DVI:视频: 在此处输入图片说明

5PinDIN: 5 引脚: 在此处输入图片说明

DB25: DB25: 在此处输入图片说明

These are a lot clearer as these are contours gathered from connector images from the internet.这些更清晰,因为这些是从互联网连接器图像中收集的轮廓。

For what I have tried:对于我尝试过的:

cv2.matchShapes() cv2.matchShapes()

Since these are all just contours, I tried directly comparing them using the matchShapes() method, and it failed to produce good results.由于这些都只是轮廓,我尝试使用matchShapes()方法直接比较它们,并没有产生好的结果。 The similarities between the irregular contour, and my database was:不规则轮廓和我的数据库之间的相似之处是:

HDMI: 0.90 HDMI:0.90

DB25: 0.84 DB25:0.84

5 Pin DIN: 0.5 5 针 DIN:0.5

DVI: 0.21 DVI:0.21

Since contours are more similar the closer to 0 the match result is, the algorithm completely failed.由于轮廓越相似,匹配结果越接近 0,算法完全失败。 I tried the other methods of matching by changing the third parameter and was still unsuccessful.我通过更改第三个参数尝试了其他匹配方法,但仍然不成功。

ORB:球体:

Being similar to SIFT, I tried keypoint matching.与SIFT类似,我尝试了关键点匹配。 Averaging the distance between the different matches in my database (after finding the top 15% of matches):平均我数据库中不同匹配项之间的距离(在找到前 15% 的匹配项之后):

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

The distances came up as:距离如下:

Five Pin DIN: 7.6五针DIN:7.6

DB25: 11.7 DB25:11.7

DVI: 12.1 DVI:12.1

HDMI: 19.6 HDMI:19.6

As this classified a circle as the shape most like my contour, this has failed as well.由于这将圆形归类为最像我的轮廓的形状,因此这也失败了。

Here are the matching key points from ORB of the actual HDMI slot vs my example HDMI slot for more information:以下是实际 HDMI 插槽与我的示例 HDMI 插槽的 ORB 匹配的关键点,以获取更多信息: 在此处输入图片说明

Are there any ideas/other algorithms I should try?我应该尝试任何想法/其他算法吗? Or is a CNN my only choice (which I would rather avoid as I don't have the appropriate amount of data).或者 CNN 是我唯一的选择(我宁愿避免,因为我没有适当数量的数据)。

This answer is based on ZdaR's answer here https://stackoverflow.com/a/55530040/1787145 .此答案基于 ZdaR 在此处的答案https://stackoverflow.com/a/55530040/1787145 I have tried some variations in hope of using a single discerning criterion ( cv2.matchShapes() ) by incorporating more in the pre-processing.我尝试了一些变体,希望通过在预处理中加入更多来使用单一的辨别标准( cv2.matchShapes() )。

1. Compare images instead of contours 1.比较图像而不是轮廓

I like the idea of normalization (crop and resize).我喜欢标准化(裁剪和调整大小)的想法。 But after shrinking an image, its originally closed contour might be broken into multiple disconnected parts, due to the low resolution of pixels.但是在缩小图像后,由于像素分辨率低,其原本封闭的轮廓可能会被分解为多个不连续的部分。 The result of cv2.matchShapes() is unreliable. cv2.matchShapes()的结果不可靠。 By comparing whole resized images, I get following results.通过比较整个调整大小的图像,我得到以下结果。 It says the circle is the most similar.它说圆圈是最相似的。 Not good!不好!

在此处输入图片说明

2. Fill the shape 2.填充形状

By filling the shape, we take area into consideration.通过填充形状,我们考虑了面积。 The result looks better, but DVI still beats HDMI for having a more similar height or Height/Width ratio.结果看起来更好,但 DVI 仍然优于 HDMI,因为它具有更相似的高度或高度/宽度比。 We want to ignore that.我们想忽略这一点。

在此处输入图片说明

3. Resize every image to the same size 3.将每个图像调整为相同的大小

By resizing all to the same size, we eliminate some ratio in the dimensions.通过将所有尺寸调整为相同的尺寸,我们消除了尺寸中的一些比例。 (300, 300) works well here. (300, 300) 在这里效果很好。

在此处输入图片说明

4. Code 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))

There are multiple steps which can be performed to get better results.可以执行多个步骤以获得更好的结果。 And there is no need of a CNN or some complex feature matching, lets try to solve this using very basic approached.并且不需要 CNN 或一些复杂的特征匹配,让我们尝试使用非常基本的方法来解决这个问题。

1. Normalize query image and database images as well. 1. 也规范化查询图像和数据库图像。

This can be done by closely cropping the input contour and then resize all the images either to same height or width.这可以通过密切裁剪输入轮廓然后将所有图像调整为相同的高度或宽度来完成。 I will chose width here, let's say 300px.我会在这里选择宽度,比如说 300px。 Let's define a utility method for this:让我们为此定义一个实用方法:

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

This code snippet would return a nicely cropped contour with a fixed width of 300. Apply this method to all the database images and input query image as well.此代码片段将返回一个固定宽度为 300 的裁剪良好的轮廓。将此方法应用于所有数据库图像和输入查询图像。

2. Filter simply using the height of input normalized image. 2. 简单地使用输入归一化图像的高度进行过滤。

Since we have normalized the input image to 300 px we can reject all the candidates whose height is not close to the normalized image height.由于我们已经将输入图像归一化为 300 像素,我们可以拒绝所有高度不接近归一化图像高度的候选者。 This will rule out 5PinDIN.这将排除 5PinDIN。

3. Compare area 3. 比较区域

Now you can try sorting the results with max overlap, you can cv2.contourArea() to get the contour area and sort all the remaining candidates to get the closest possible match.现在您可以尝试使用最大重叠对结果进行排序,您可以使用cv2.contourArea()来获取轮廓区域并对所有剩余的候选对象进行排序以获得最接近的可能匹配。

The short answer for this set of images is use OpenCV matchShapes method I2 and re-code the method matchShapes with a smaller "eps."这组图像的简短答案是使用 OpenCV matchShapes 方法 I2 并使用较小的“eps”重新编码 matchShapes 方法。 double eps = 1.e-20; is more than small enough.足够小。

I'm a high school robotics team mentor and I thought the OpenCV matchShapes was just what we needed to improve the robot's vision (scale, translation and rotation invariant and easy for the students to use in existing OpenCV code).我是一名高中机器人团队导师,我认为 OpenCV matchShapes 正是我们改进机器人视觉所需要的(缩放、平移和旋转不变,并且易于学生在现有 OpenCV 代码中使用)。 I came across this article a couple of hours into my research and this was horrifying!我在研究几个小时后看到了这篇文章,这太可怕了! How could matchShapes ever work for us given these results?鉴于这些结果,matchShapes 如何为我们工作? I was incredulous about these poor results.我对这些糟糕的结果表示怀疑。

I coded my own matchShapes (in Java - that's what the students wanted to use) to see what is the effect of changing the eps (the small value that apparently protects the log10 function from zero and prevents BIG discrepancies by calling them a perfect match - the opposite of what it really is; I couldn't find the basis of the value).我编写了自己的 matchShapes(在 Java 中 - 这就是学生想要使用的)来查看更改 eps(显然保护 log10 函数为零并通过将它们称为完美匹配来防止大差异的小值)的影响 -与实际情况相反;我找不到价值的基础)。 I changed matchShapes eps to 1.e-20 from the OpenCV number 1.e-5 and got good results but still the process is disconcerting.我将 matchShapes eps 从 OpenCV 编号 1.e-5 更改为 1.e-20 并获得了不错的结果,但过程仍然令人不安。

It's wonderful but scary that given the right answer we can contort a process to get it.给出正确答案,我们可以扭曲一个过程来得到它,这真是太棒了,但也很可怕。 The attached image has all 3 methods of the Hu Moment comparisons and methods 2 and 3 do a pretty good job.所附图片包含 Hu Moment 比较的所有 3 种方法,方法 2 和方法 3 做得很好。

My process was save the images above, convert to binary 1 channel, dilate 1, erode 1, findCountours, matchShapes with eps = 1.e-20.我的过程是保存上面的图像,转换为二进制 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轮廓和胡矩比较 - matchShapes 3 方法

I continued my naive research (little background in statistics) and found various other ways to make normalizations and comparisons.我继续我的天真研究(统计学背景很少),并找到了各种其他方法来进行标准化和比较。 I couldn't figure out the details for the Pearson correlation coefficient and other co-variance methods and maybe they aren't appropriate.我无法弄清楚 Pearson 相关系数和其他协方差方法的细节,也许它们不合适。 I tested two more normalization methods and another matching method.我测试了另外两种标准化方法和另一种匹配方法。

OpenCV normalizes with the Log10 function for all three of its matching computations. OpenCV 使用 Log10 函数对其所有三个匹配计算进行标准化。

I tried normalizing each pair of Hu moments with the ratio to each pair's maximum value max(Ai,Bi) and I tried normalizing each pair to a vector length of 1 (divide by sqrt of the sum of the squares).我尝试将每对 Hu 矩与每对最大值 max(Ai,Bi) 的比率归一化,并尝试将每对归一化为向量长度为​​ 1(除以平方和的 sqrt)。

I used those two new normalizations before computing the angle between the 7-dimension Hu moments vectors using the cosine theta method and before computing the sum of the element pair differences similar to OpenCV method I2.在使用余弦 theta 方法计算 7 维 Hu 矩向量之间的角度之前,以及在计算类似于 OpenCV 方法 I2 的元素对差异的总和之前,我使用了这两个新的归一化。

My four new concoctions worked well but didn't contribute anything beyond the openCV I2 with "corrected" eps except the range of values was smaller and still ordered the same.我的四个新混合物运行良好,但除了具有“校正”eps 的 openCV I2 之外没有任何贡献,只是值的范围更小并且顺序仍然相同。

Notice that the I3 method is not symmetric - swapping the matchShapes argument order changes the results.请注意, I3 方法不是对称的 - 交换 matchShapes 参数顺序会更改结果。 For this set of images put the moments of the "UNKNOWN" as the first argument and compare with the list of known shapes as the second argument for best results.对于这组图像,将“UNKNOWN”的矩作为第一个参数,并与已知形状列表作为第二个参数进行比较以获得最佳结果。 The other way around changes the results to the "wrong" answer!相反,将结果更改为“错误”的答案!

The number 7 of the matching methods I attempted is merely co-incidental to the number of Hu Moments - 7.我尝试的匹配方法中的数字 7 只是与 Hu Moments 的数量 - 7 巧合。

Description of the matching indices for the 7 different computations 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|

Matching indices results for 7 different computations for each of the 5 images 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)

[Created OpenCV issue 16997 to address this weakness in matchShapes.] [创建 OpenCV 问题 16997 以解决 matchShapes 中的此弱点。]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM