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

问题描述 投票:2回答:1

我试图将略微不规则的形状与形状数据库相匹配。例如,这里我试图匹配的轮廓:

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插槽的ORB与我的示例HDMI插槽的匹配关键点,以获取更多信息:enter image description here

我应该尝试任何想法/其他算法吗?或者是CNN我唯一的选择(我宁愿避免,因为我没有适当数量的数据)。

python opencv image-processing image-recognition opencv-contour
1个回答
2
投票

可以执行多个步骤以获得更好的结果。并且不需要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()获取轮廓区域并对所有剩余的候选项进行排序以获得最接近的匹配。


1
投票

这个答案基于ZdaR在这里https://stackoverflow.com/a/55530040/1787145的答案。我已经尝试了一些变化,希望通过在预处理中加入更多来使用单一的辨别标准(cv2.matchShapes())。

1. Compare images instead of contours

我喜欢规范化(裁剪和调整大小)的想法。但是,在缩小图像之后,由于像素的分辨率较低,其原始闭合的轮廓可能会被分成多个断开的部分。 cv2.matchShapes()的结果是不可靠的。通过比较整个调整大小的图像,我得到以下结果。它说圆圈是最相似的。不好!

enter image description here

2. Fill the shape

通过填充形状,我们考虑区域。结果看起来更好,但DVI仍然胜过HDMI,因为它具有更高的相似高度或高度/宽度比。我们想忽略这一点。

enter image description here

3. Resize every image to the same size

通过将所有尺寸调整为相同尺寸,我们消除了尺寸中的一些比率。 (300,300)在这里运作良好。

enter image description here

4. Code

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))
© www.soinside.com 2019 - 2024. All rights reserved.