Cyo*_*yon 7 python opencv image-processing image-recognition opencv-contour
我正在尝试将稍微不规则的形状与形状数据库相匹配。例如,这里我试图匹配的轮廓:
有关更多信息,这是 HDMI 连接器的轮廓,表示为轮廓。有点粗糙,因为这是在拿着 HDMI 的情况下用手机拍摄的。
这是我的连接器数据库:
这些更清晰,因为这些是从互联网连接器图像中收集的轮廓。
对于我尝试过的:
cv2.matchShapes()
由于这些都只是轮廓,我尝试直接使用 matchShapes() 方法进行比较,但未能产生良好的结果。不规则轮廓和我的数据库之间的相似之处是:
HDMI:0.90
DB25:0.84
5 针 DIN:0.5
DVI:0.21
由于轮廓越相似,匹配结果越接近 0,算法完全失败。我通过更改第三个参数尝试了其他匹配方法,但仍然不成功。
球体:
与SIFT类似,我尝试了关键点匹配。平均我数据库中不同匹配项之间的距离(在找到前 15% 的匹配项之后):
mean([m.distance for m in matches])
Run Code Online (Sandbox Code Playgroud)
距离如下:
五针DIN:7.6
DB25:11.7
DVI:12.1
HDMI:19.6
由于这将圆形归类为最像我的轮廓的形状,因此这也失败了。
以下是实际 HDMI 插槽与我的示例 HDMI 插槽的 ORB 匹配的关键点,以获取更多信息:

我应该尝试任何想法/其他算法吗?或者 CNN 是我唯一的选择(我宁愿避免,因为我没有适当数量的数据)。
此答案基于 ZdaR 在此处的回答/sf/answers/3887102831/。我尝试了一些变体,希望cv2.matchShapes()通过在预处理中加入更多来使用单一的辨别标准 ( )。
我喜欢标准化(裁剪和调整大小)的想法。但是在缩小图像后,由于像素分辨率低,其原本封闭的轮廓可能会被分解为多个不连续的部分。的结果cv2.matchShapes()不可靠。通过比较整个调整大小的图像,我得到以下结果。它说圆圈是最相似的。不好!
通过填充形状,我们考虑了面积。结果看起来更好,但 DVI 仍然优于 HDMI,因为它具有更相似的高度或高度/宽度比。我们想忽略这一点。
通过将所有尺寸调整为相同的尺寸,我们消除了尺寸中的一些比例。(300, 300) 在这里效果很好。
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))
Run Code Online (Sandbox Code Playgroud)
可以执行多个步骤以获得更好的结果。并且不需要 CNN 或一些复杂的特征匹配,让我们尝试使用非常基本的方法来解决这个问题。
这可以通过密切裁剪输入轮廓然后将所有图像调整为相同的高度或宽度来完成。我会在这里选择宽度,比如说 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
Run Code Online (Sandbox Code Playgroud)
此代码片段将返回一个固定宽度为 300 的裁剪良好的轮廓。将此方法应用于所有数据库图像和输入查询图像。
由于我们已经将输入图像归一化为 300 像素,我们可以拒绝所有高度不接近归一化图像高度的候选者。这将排除 5PinDIN。
现在您可以尝试使用最大重叠对结果进行排序,您cv2.contourArea()可以获得轮廓区域并对所有剩余的候选对象进行排序以获得最接近的可能匹配。