温馨提示:本文翻译自stackoverflow.com,查看原文请点击:ruby - How do I detect that two images are "the same" even if one has slightly different cropping/ratio?
image-processing ruby imagemagick rmagick image-comparison

ruby - 即使一个图像的裁剪/比率略有不同,我如何检测到两个图像“相同”?

发布于 2020-04-03 23:27:38

我有两个不同的图像:

在100px 在此处输入图片说明或400px在此处输入图片说明

宽度为100 在此处输入图片说明像素或400像素在此处输入图片说明

如您所见,从人的角度来看,两者显然是“相同的”。现在,我想以编程方式检测它们是否相同。我一直在通过rmagick像这样的红宝石来使用图像魔术

img1 = Magick::Image.from_blob(File.read("image_1.jpeg")).first
img2 = Magick::Image.from_blob(File.read("image_2.jpeg")).first

if img1.difference(img2).first < 4000.0 # I have found this to be a good threshold, but does not work for cropped images
  puts "they are the same!!!"
end

虽然这对于具有相同比率/裁切的图像效果很好,但是当裁切略有不同并且调整为相同宽度时,这并不是理想的选择。

有没有一种方法可以对不同裁切的图像进行处理?我对一种可以说类似的解决方案很感兴趣:一个图像包含在另一个图像中,并且覆盖了大约90%的位置。

PS。如果有帮助,我可以以更高的分辨率获得图像(例如,双倍)

查看更多

提问者
Niels Kristian
被浏览
89
nathancy 2020-02-05 08:05

您可能需要看一下功能匹配。这个想法是在两个图像中找到特征并匹配它们。此方法通常用于在其他图像中查找模板(例如徽标)。本质上,特征可以描述为人类会在图像中发现有趣的事物,例如角落或开放空间。特征检测技术有很多种,但是我的建议是使用尺度不变特征变换(SIFT)作为特征检测算法。SIFT对图像平移,缩放,旋转不变,对照明变化部分不变,并且对局部几何变形具有鲁棒性。这似乎与您的规格相符,在这些规格中图像的比率可能略有不同。

给定您提供的两个图像,这是尝试使用FLANN功能匹配器来匹配功能要确定两个图像是否相同,我们可以基于某个预定阈值,该阈值跟踪通过比例测试的匹配次数,该匹配测试 David G. Lowe的“ 比例不变关键点从比例不变关键点”进行了描述对测试的简单解释是,比率测试检查匹配项是否模棱两可,应将其删除,您可以将其视为异常删除技术。我们可以计算通过此测试的匹配数,以确定两个图像是否相同。这是功能匹配结果:

Matches: 42

点表示检测到的所有匹配,而绿线表示通过比率测试的“良好匹配”。如果不使用比率测试,则将绘制所有点。这样,您可以将此过滤器用作阈值,以仅保留最匹配的功能。


我用Python实现的,我对Rails不太熟悉。希望这有帮助,祝你好运!

import numpy as np
import cv2

# Load images
image1 = cv2.imread('1.jpg', 0)
image2 = cv2.imread('2.jpg', 0)

# Create the sift object
sift = cv2.xfeatures2d.SIFT_create(700)

# Find keypoints and descriptors directly
kp1, des1 = sift.detectAndCompute(image2, None)
kp2, des2 = sift.detectAndCompute(image1, None)

# FLANN parameters
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary
flann = cv2.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)

# Need to draw only good matches, so create a mask
matchesMask = [[0,0] for i in range(len(matches))]

count = 0
# Ratio test as per Lowe's paper (0.7)
# Modify to change threshold 
for i,(m,n) in enumerate(matches):
    if m.distance < 0.15*n.distance:
        count += 1
        matchesMask[i]=[1,0]

# Draw lines
draw_params = dict(matchColor = (0,255,0),
                   # singlePointColor = (255,0,0),
                   matchesMask = matchesMask,
                   flags = 0)

# Display the matches
result = cv2.drawMatchesKnn(image2,kp1,image1,kp2,matches,None,**draw_params)
print('Matches:', count)
cv2.imshow('result', result)
cv2.waitKey()