两个相似形状之间的 OpenCV 形状匹配
OpenCV shape matching between two similar shapes
我正在尝试将略微不规则的形状与形状数据库相匹配。例如,这里是我要匹配的轮廓:
有关详细信息,这是 HDMI 连接器的轮廓,以等高线表示。有点粗糙,因为这是用 phone 拿着 HDMI 拍摄的。
这是我的连接器数据库:
HDMI:
DVI:
5拼丁:
DB25:
这些更清晰,因为这些是从互联网上的连接器图像中收集的轮廓。
对于我所尝试的:
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 插槽的匹配要点,以获取更多信息:
是否有任何我应该尝试的 ideas/other 算法?或者 CNN 是我唯一的选择(我宁愿避免,因为我没有适当的数据量)。
可以执行多个步骤以获得更好的结果。并且不需要 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
此代码片段将 return 裁剪得很好的固定宽度为 300 的轮廓。将此方法应用于所有数据库图像和输入查询图像。
2。简单地使用输入归一化图像的高度进行过滤。
由于我们已将输入图像标准化为 300 像素,因此我们可以拒绝所有高度不接近标准化图像高度的候选者。这将排除 5PinDIN。
3。比较面积
现在您可以尝试使用最大重叠对结果进行排序,您可以cv2.contourArea()
获取轮廓区域并对所有剩余的候选对象进行排序以获得最接近的匹配。
此答案基于 ZdaR 在此处的回答 。我尝试了一些变体,希望通过在 pre-processing.
中加入更多来使用单一的识别标准 (cv2.matchShapes()
)
1。比较图像而不是轮廓
我喜欢规范化的想法(裁剪和调整大小)。但在缩小图像后,由于像素分辨率低,其原本封闭的轮廓可能会被打断成多个不相连的部分。 cv2.matchShapes()
的结果不可靠。通过比较整个调整大小的图像,我得到以下结果。它说圆是最相似的。不好!
2。填充形状
通过填充形状,我们将面积考虑在内。结果看起来更好,但 DVI 仍然优于 HDMI,因为它具有更相似的高度或 Height/Width 比率。我们想忽略它。
3。将每张图片调整为相同大小
通过将所有尺寸调整为相同尺寸,我们消除了一些尺寸比例。 (300, 300) 在这里效果很好。
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))
这组图像的简短答案是使用 OpenCV matchShapes 方法 I2 并用更小的 "eps." 重新编码方法 matchShapes double eps = 1.e-20;
已经足够小了。
我是一名高中机器人团队导师,我认为 OpenCV matchShapes 正是我们改善机器人视觉所需要的(缩放、平移和旋转不变且易于学生在现有 OpenCV 代码中使用)。我在研究的几个小时内看到了这篇文章,这太可怕了!鉴于这些结果,matchShapes 如何对我们有用?我对这些糟糕的结果感到难以置信。
我编写了自己的 matchShapes(在 Java 中——这就是学生们想要使用的)以查看更改 eps 的效果是什么(这个小值显然可以保护 log10 函数不为零并防止将它们称为完美匹配存在巨大差异 - 与实际情况相反;我找不到价值的基础)。我将 matchShapes eps 从 OpenCV 编号 1.e-5 更改为 1.e-20 并获得了良好的结果,但过程仍然令人不安。
给定正确答案我们可以扭曲一个过程来得到它,这很美妙但很可怕。所附图片包含胡矩比较的所有 3 种方法,方法 2 和 3 做得很好。
我的过程是保存上面的图像,转换为二进制 1 通道,膨胀 1,腐蚀 1,findCountours,matchShapes with 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
我继续我天真的研究(统计学背景不多)并找到了各种其他方法来进行标准化和比较。我无法弄清楚 Pearson 相关系数和其他协方差方法的细节,也许它们不合适。我又测试了两种归一化方法和另一种匹配方法
OpenCV 使用 Log10 函数对其所有三个匹配计算进行归一化。
我尝试用与每对最大值 max(Ai,Bi) 的比率对每对 Hu 矩进行归一化,并尝试将每对归一化为向量长度 1(除以平方和的 sqrt) .
在使用余弦 theta 方法计算 7 维 Hu 矩向量之间的角度之前,以及在计算类似于 OpenCV 方法 I2 的元素对差异之和之前,我使用了这两个新的归一化。
我的四种新组合效果很好,但除了值的范围更小而且仍然排序相同之外,除了具有 "corrected" eps 的 openCV I2 之外没有任何贡献。
请注意 I3 方法不是对称的 - 交换 matchShapes 参数顺序会改变结果。对于这组图像,将 "UNKNOWN" 的时刻作为第一个参数,并将已知形状列表作为第二个参数进行比较以获得最佳结果。反过来将结果更改为 "wrong" 答案!
我尝试的第7种匹配方式只是巧合胡矩数- 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 中的这一弱点。]
我正在尝试将略微不规则的形状与形状数据库相匹配。例如,这里是我要匹配的轮廓:
有关详细信息,这是 HDMI 连接器的轮廓,以等高线表示。有点粗糙,因为这是用 phone 拿着 HDMI 拍摄的。
这是我的连接器数据库:
HDMI:
DVI:
5拼丁:
DB25:
这些更清晰,因为这些是从互联网上的连接器图像中收集的轮廓。
对于我所尝试的:
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 插槽的匹配要点,以获取更多信息:
是否有任何我应该尝试的 ideas/other 算法?或者 CNN 是我唯一的选择(我宁愿避免,因为我没有适当的数据量)。
可以执行多个步骤以获得更好的结果。并且不需要 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
此代码片段将 return 裁剪得很好的固定宽度为 300 的轮廓。将此方法应用于所有数据库图像和输入查询图像。
2。简单地使用输入归一化图像的高度进行过滤。
由于我们已将输入图像标准化为 300 像素,因此我们可以拒绝所有高度不接近标准化图像高度的候选者。这将排除 5PinDIN。
3。比较面积
现在您可以尝试使用最大重叠对结果进行排序,您可以cv2.contourArea()
获取轮廓区域并对所有剩余的候选对象进行排序以获得最接近的匹配。
此答案基于 ZdaR 在此处的回答
cv2.matchShapes()
)
1。比较图像而不是轮廓
我喜欢规范化的想法(裁剪和调整大小)。但在缩小图像后,由于像素分辨率低,其原本封闭的轮廓可能会被打断成多个不相连的部分。 cv2.matchShapes()
的结果不可靠。通过比较整个调整大小的图像,我得到以下结果。它说圆是最相似的。不好!
2。填充形状
通过填充形状,我们将面积考虑在内。结果看起来更好,但 DVI 仍然优于 HDMI,因为它具有更相似的高度或 Height/Width 比率。我们想忽略它。
3。将每张图片调整为相同大小
通过将所有尺寸调整为相同尺寸,我们消除了一些尺寸比例。 (300, 300) 在这里效果很好。
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))
这组图像的简短答案是使用 OpenCV matchShapes 方法 I2 并用更小的 "eps." 重新编码方法 matchShapes double eps = 1.e-20;
已经足够小了。
我是一名高中机器人团队导师,我认为 OpenCV matchShapes 正是我们改善机器人视觉所需要的(缩放、平移和旋转不变且易于学生在现有 OpenCV 代码中使用)。我在研究的几个小时内看到了这篇文章,这太可怕了!鉴于这些结果,matchShapes 如何对我们有用?我对这些糟糕的结果感到难以置信。
我编写了自己的 matchShapes(在 Java 中——这就是学生们想要使用的)以查看更改 eps 的效果是什么(这个小值显然可以保护 log10 函数不为零并防止将它们称为完美匹配存在巨大差异 - 与实际情况相反;我找不到价值的基础)。我将 matchShapes eps 从 OpenCV 编号 1.e-5 更改为 1.e-20 并获得了良好的结果,但过程仍然令人不安。
给定正确答案我们可以扭曲一个过程来得到它,这很美妙但很可怕。所附图片包含胡矩比较的所有 3 种方法,方法 2 和 3 做得很好。
我的过程是保存上面的图像,转换为二进制 1 通道,膨胀 1,腐蚀 1,findCountours,matchShapes with 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
我继续我天真的研究(统计学背景不多)并找到了各种其他方法来进行标准化和比较。我无法弄清楚 Pearson 相关系数和其他协方差方法的细节,也许它们不合适。我又测试了两种归一化方法和另一种匹配方法
OpenCV 使用 Log10 函数对其所有三个匹配计算进行归一化。
我尝试用与每对最大值 max(Ai,Bi) 的比率对每对 Hu 矩进行归一化,并尝试将每对归一化为向量长度 1(除以平方和的 sqrt) .
在使用余弦 theta 方法计算 7 维 Hu 矩向量之间的角度之前,以及在计算类似于 OpenCV 方法 I2 的元素对差异之和之前,我使用了这两个新的归一化。
我的四种新组合效果很好,但除了值的范围更小而且仍然排序相同之外,除了具有 "corrected" eps 的 openCV I2 之外没有任何贡献。
请注意 I3 方法不是对称的 - 交换 matchShapes 参数顺序会改变结果。对于这组图像,将 "UNKNOWN" 的时刻作为第一个参数,并将已知形状列表作为第二个参数进行比较以获得最佳结果。反过来将结果更改为 "wrong" 答案!
我尝试的第7种匹配方式只是巧合胡矩数- 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 中的这一弱点。]