【问题标题】:How do I detect that two images are "the same" even if one has slightly different cropping/ratio?即使一张图像的裁剪/比例略有不同,我如何检测到两张图像是“相同的”?
【发布时间】:2020-05-17 01:20:18
【问题描述】:

我有两张不同的图片:

或 400 像素 为 100 像素

在 100px 宽度 或 400px

如您所见,从人类的角度来看,两者显然是“相同的”。现在我想以编程方式检测它们是否相同。我一直在通过名为 rmagick 的 ruby​​ gem 使用图像魔法,如下所示:

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。如果有帮助,我可以获得更高分辨率的图像(例如双精度)

【问题讨论】:

  • 不确定 RMagick,但 ImageMagick 的 compare 命令行工具有一个 -subimage-search 开关。
  • 这很有趣,这样的命令看起来如何?
  • 我自己没用过,也许这有帮助:stackoverflow.com/q/29062811/477037
  • 谢谢,这是一个很棒的信息。但是我不知道如何从 ruby​​ 中做到这一点......
  • 图片质量那么差吗?如果没有,请分享更大版本的图像,质量更高。

标签: ruby image-processing computer-vision imagemagick rmagick


【解决方案1】:

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

鉴于您提供的两张图片,这里尝试使用 FLANN feature matcher 匹配功能。为了确定这两个图像是否相同,我们可以根据某个预定阈值来确定它,该阈值跟踪通过Distinctive Image Features from Scale-Invariant Keypoints by 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()

【讨论】:

  • 超级有趣的方法,我会试一试,然后回来......
  • @nathancy 是不是在您的示例中,绿点匹配,而蓝点不匹配?好像有太多不匹配的点?
  • @DracoAter 好问题,蓝点代表所有匹配,而我们只绘制通过绿色比率测试的“好匹配”。如果您不使用比率测试,则将绘制所有点,但我们使用比率测试过滤以绘制“更好”的匹配。这样,OP 可以将此测试作为阈值,仅保留最佳匹配的特征。所以所有蓝点都是 SIFT 发现的特征,但我们过滤以保留以绿色绘制的好的特征
  • 谢谢。答案很激烈,很多很棒的答案:-)
【解决方案2】:

由于 ImageMagick 是一个非常古老、先进且功能丰富的工具,因此很难构建一个涵盖大部分功能的界面。尽管它很棒,但 rmagick 并没有(python 进行的许多尝试也没有)接近涵盖所有功能。

我想对于许多用例来说,只需执行命令行方法并从中读取就足够安全且容易得多。在红宝石中,它看起来像这样;

require 'open3'

def check_subimage(large, small)
    stdin, stdout, stderr, wait_thr = Open3.popen3("magick compare -subimage-search -metric RMSE #{large} #{small} temp.jpg")
    result = stderr.gets
    stderr.close
    stdout.close
    return result.split[1][1..-2].to_f < 0.2
end

if check_subimage('a.jpg', 'b.jpg')
    puts "b is a crop of a"
else
    puts "b is not a crop of a"
end

我将介绍重要内容,然后讨论其他注意事项。

该命令使用 magick compare 来检查第二个图像 (small) 是否是第一个 (large) 的子图像。此函数不检查 small 是否严格小于 large(高度和宽度)。我为相似度输入的数字是 0.2(20% 错误),您提供的图像的值约为 0.15。您可能需要对此进行微调!我发现严格子集的图像小于 0.01。

  • 如果您希望在有 90% 重叠但第二张图像有一些额外的东西第一个没有的情况下减少错误(较小的数字),您可以运行一次,然后将第一个大图像裁剪到包含子图像,然后再次运行它,将裁剪后的图像作为“小”图像,将原始“小”图像作为大图像。
  • 如果您真的想要一个漂亮的 Ruby 中面向对象的接口,rmagick 使用 MagicCore API。 This (link to docs) 命令可能是你想要用来实现它的,你可以打开一个 pr 到 rmagick 或者自己打包 cext。
  • 使用 open3 将启动一个线程 (see docs)。关闭 stderrstdout 不是“必要的”,但您应该这样做。
  • 第三个参数的“临时”图像指定了一个文件以输出分析结果。快速浏览一下,我找不到不需要它的方法,但它只是自动覆盖并且可以很好地保存以供调试。对于您的示例,它看起来像这样;

  • 完整输出的格式为 10092.6 (0.154003) @ 0,31。第一个数字是 655535 中的 rmse 值,第二个(我使用的)是标准化百分比。最后两个数字代表小图像开始的原始图像的位置。
  • 由于没有关于图像“相似”程度的客观事实来源,我选择了 RMSE(查看更多度量选项here)。这是一种相当常见的衡量值之间差异的方法。绝对错误计数 (AE) 似乎是个好主意,但似乎某些裁剪软件不能完美地保留像素,因此您可能需要调整 fuzz 并且它不是标准化值,因此您必须比较错误计数图像的大小等等。

【讨论】:

  • 卡罗尔那里有一些非常棒的信息。谢谢
  • 很想知道这对您的其他情况有何作用!
  • 感谢超级棒的回答。如果可以的话,我也给了你 100 便士的奖励 :-)
【解决方案3】:

通常模板匹配在这些情况下都有很好的效果。模板匹配是一种用于查找与模板图像(第二张图像)匹配(相似)的图像区域的技术。该算法为源图像(第二个)中的最佳匹配位置打分。

在opencv中使用TM_CCOEFF_NORMED方法,给出0到1之间的分数。如果分数为1,则表示模板图像恰好是一个部分(矩形)源图像,但如果两个图像之间的亮度或透视有一点变化,分数会低于 1。

现在通过考虑相似度分数的阈值,您可以确定它们是否相同。该阈值可以通过对一些样本图像进行反复试验来获得。我试了你的图片,得到了 0.823863 的分数。下面是代码(opencv C++)和两张图片的公共区域,通过匹配得到:

Mat im2 = imread("E:/1/1.jpg", 1);
//Mat im2;// = imread("E:/1/1.jpg", 1);
Mat im1 = imread("E:/1/2.jpg", 1);

//im1(Rect(0, 0, im1.cols - 5, im1.rows - 5)).copyTo(im2);

int result_cols = im1.cols - im2.cols + 1;
int result_rows = im1.rows - im2.rows + 1;

Mat result = Mat::zeros(result_rows, result_cols, CV_32FC1);

matchTemplate(im1, im2, result, TM_CCOEFF_NORMED);

double minVal; double maxVal;
Point minLoc; Point maxLoc;
Point matchLoc;

minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

cout << minVal << " " << maxVal << " " << minLoc << " " << maxLoc << "\n";
matchLoc = maxLoc;

rectangle(im1, matchLoc, Point(matchLoc.x + im2.cols, matchLoc.y + im2.rows), Scalar::all(0), 2, 8, 0);
rectangle(result, matchLoc, Point(matchLoc.x + im2.cols, matchLoc.y + im2.rows), Scalar::all(0), 2, 8, 0);

imshow("1", im1);
imshow("2", result);
waitKey(0);

【讨论】:

  • 感谢超级棒的回答。如果可以的话,我也给了你 100 便士的奖励 :-)
【解决方案4】:

考虑find_similar_region 方法。使用两个图像中较小的一个作为目标图像。为图像和目标图像上的模糊属性尝试各种值。

【讨论】:

    【解决方案5】:

    获取两张图像的直方图并进行比较。这对于裁剪和缩放效果非常好,除非因为这些而发生太大的变化。

    这比当前直接减去图像的方法要好。但是这种方式还是很少的。

    【讨论】:

    • 感谢您的建议,我会看看它。
    • 这不是一个非常有用的答案,因为它没有演示如何实现目标。相当于“谷歌这个词,自己弄明白”。
    • 直方图是人们在图像处理中首先学习的东西之一。如果有人不得不用谷歌搜索,那么我深表歉意。
    猜你喜欢
    • 1970-01-01
    • 2013-08-08
    • 2011-05-04
    • 2018-06-22
    • 1970-01-01
    • 2011-08-09
    • 2020-10-08
    • 2019-03-15
    相关资源
    最近更新 更多