2009-11-30 119 views
39

我试图比较图像,以找出彼此是否不同。首先,我试图对RGB值进行Pearson校正,除非图片位移小一点,否则这种方法的效果也不错。所以如果一个图像100%完全相同,但有一个被移动一点,我会得到一个不好的相关值。图像比较算法

任何建议更好的算法?

顺便说一句,我说的是比较imgages千...

编辑: 这里是我的照片的例子(微观):

IM1:

enter image description here

im2:

enter image description here

IM3:

enter image description here

IM 1和IM是相同的,但一点点移动/板缺,IM3,应当确认为完全地不同...

编辑: 问题是用Peter Hansen的建议解决!工作得很好!感谢所有的答案!一些结果可以在这里找到 http://labtools.ipk-gatersleben.de/image%20comparison/image%20comparision.pdf

+1

如果您对哪种类型的照片更具特色,以及它们可以以何种方式变化(比例,旋转,照明......),那么给出一个好的答案和解决方案会容易得多。 – 2009-11-30 11:49:50

+0

这里有很多这样的问题。 http://stackoverflow.com/questions/336067/detecting-if-two-images-are-visually-identical http://stackoverflow.com/questions/189943/how-can-i-quantify-difference-between-two -images http://stackoverflow.com/questions/336067/detecting-if-two-images-are-visually-identical这也是关于显微镜,也是:http://stackoverflow.com/questions/967436/checking-image-功能对齐 – endolith 2009-12-01 19:14:45

+0

除了这些优秀的答案 - 通常最好比较真实世界中的HSV空间图像,而不是RGB。 – 2009-12-07 15:20:15

回答

35

A similar question在一年前被问到,并且有很多回复,其中包括一个关于像素化图像的内容,我至少会提出一个资格预审步骤(因为它会很快排除非相似的图像) 。

还有一些还有更多参考和良好答案的更早的问题的链接。

下面是一个使用Scipy的一些想法的实现,使用上面的三个图像(分别保存为im1.jpg,im2.jpg,im3.jpg)。最终的输出结果显示im1与其本身相比,作为基线,然后将每个图像与其他图像进行比较。

>>> import scipy as sp 
>>> from scipy.misc import imread 
>>> from scipy.signal.signaltools import correlate2d as c2d 
>>> 
>>> def get(i): 
...  # get JPG image as Scipy array, RGB (3 layer) 
...  data = imread('im%s.jpg' % i) 
...  # convert to grey-scale using W3C luminance calc 
...  data = sp.inner(data, [299, 587, 114])/1000.0 
...  # normalize per http://en.wikipedia.org/wiki/Cross-correlation 
...  return (data - data.mean())/data.std() 
... 
>>> im1 = get(1) 
>>> im2 = get(2) 
>>> im3 = get(3) 
>>> im1.shape 
(105, 401) 
>>> im2.shape 
(109, 373) 
>>> im3.shape 
(121, 457) 
>>> c11 = c2d(im1, im1, mode='same') # baseline 
>>> c12 = c2d(im1, im2, mode='same') 
>>> c13 = c2d(im1, im3, mode='same') 
>>> c23 = c2d(im2, im3, mode='same') 
>>> c11.max(), c12.max(), c13.max(), c23.max() 
(42105.00000000259, 39898.103896795357, 16482.883608327804, 15873.465425120798) 

所以比较注意与自己给出了一个得分42105,IM2与IM1相比已经不远了这一点,但相比IM3与任何其他的下一半的值给出清楚,IM1。您必须尝试其他图片才能了解其效果如何,以及如何改进效果。

运行时间很长......在我的机器上运行几分钟。我会尝试一些预过滤,以避免浪费时间比较非常不相似的图像,可能与回应其他问题时提及的“比较jpg文件大小”技巧或像素化。事实上,你有不同大小的图像使事情复杂化,但是你没有提供足够的关于屠宰的可能性的信息,所以很难给出具体的答案。

+0

感谢您的执行!我正在试运行。如果它是成功的,我可以先考虑过滤。比较大小听起来不错,我也可以尝试调整大小... – snowflake 2009-12-01 08:54:33

+2

这看起来像这种方法很好!我的积极控制得到了明确的结果! 随着图片大小调整到50%,我获得了很多速度。 非常感谢! – snowflake 2009-12-01 16:52:04

+0

不客气。我也尝试过使用FFT,考虑到你对图像的描述(“相同”,但可能是移位和/或剪切),我认为如果仅使用幅度结果,FFT可能会表现良好。这次我将图像剪裁到最小公共尺寸,对每个图像执行scipy.fftpack.fft2和abs()以获得f1,f2,f3,并且使用(f1-x)**对基线图像进行了归一化比较2/f1/x其中x是f1,f2,f3等,结果分别为0,143,211。对于im1的亮度调整和版本化,我得到了146和1189个结果。运行时间只有1.2s ... – 2009-12-01 17:34:55

6

如果你的问题是关于移位像素,也许你应该比较频率变换。

的FFT应该OK(numpy has an implementation for 2D matrices),但我总是听到小波是这类任务的更好的^ _^

关于性能,如果所有的图像的大小相同,如果我记得很清楚,FFTW软件包为每个FFT输入大小创建了一个专门的函数,所以你可以获得一个很好的性能提升,重用相同的代码......我不知道numpy是否基于FFTW,但如果它不是可以试着在那里调查一下。

在这里你有一个原型...你可以玩一点点,看看哪个阈值适合你的图像。

import Image 
import numpy 
import sys 

def main(): 
    img1 = Image.open(sys.argv[1]) 
    img2 = Image.open(sys.argv[2]) 

    if img1.size != img2.size or img1.getbands() != img2.getbands(): 
     return -1 

    s = 0 
    for band_index, band in enumerate(img1.getbands()): 
     m1 = numpy.fft.fft2(numpy.array([p[band_index] for p in img1.getdata()]).reshape(*img1.size)) 
     m2 = numpy.fft.fft2(numpy.array([p[band_index] for p in img2.getdata()]).reshape(*img2.size)) 
     s += numpy.sum(numpy.abs(m1-m2)) 
    print s 

if __name__ == "__main__": 
    sys.exit(main()) 

另一种进行的方式可能是模糊图像,然后从两幅图像中减去像素值。如果差值不为零,则可以将每个方向上的一个图像移位1 px并再次进行比较,如果差值低于上一步骤,则可以沿梯度方向重复移动,然后减去直到差值低于某个阈值或再次增加。如果模糊内核的半径大于图像的移位,那么这应该起作用。

此外,您可以尝试使用摄影工作流程中常用的一些工具来混合多个展览或进行全景拍摄,如Pano Tools

+0

这种方法的问题是(自然)照片通常具有非常相似的频率内容。所以单单一个FFT很可能不会很好地工作。我会假设对于很多其他主题领域来说也是如此。 – 2009-11-30 11:42:12

+1

@kigurai我对图像进行了一些与自然内容相同的测试(两种不同的核爆炸,他们很容易找到,在谷歌图片中搜索“测试”)和另一张伦敦眼图片,他们得分41583454和45014233在我的快速和肮脏的FFT测试中...其中一个爆炸向右移动了3个像素(填充白色)得分仅为8749886(减少了4倍),而15像素移位仍然为17325409(减少了2倍)。当然,图像的分数为0.所以,尽管您有异议,但平凡的FFT似乎是一种很好的比较方式。 – fortran 2009-11-30 13:25:02

+0

我在三个例子上试过你的代码,它看起来相当有前途!我现在开始跑得太快了! 非常感谢! – snowflake 2009-11-30 16:17:58

0

我想你可以做这样的事情:

  • 估计垂直/水平的参考图像VS的比较图像位移。一个简单的SAD(绝对差之和)与运动矢量将做到。

  • 转变比较像据此

  • 计算Pearson相关你试图做

移测量并不困难。

  • 在比较图像中取一个区域(比如约32x32)。
  • 将它沿水平方向移动x个像素,并在垂直方向移动y个像素。
  • 计算SAD(绝对差之和)w.r.t.原始图像
  • 在小范围内做此,x和y的几个值(-10,+ 10)
  • 找到地方,差最小
  • 挑选一个值作为移位运动矢量

注:

如果SAD来了非常高的X和Y,那么你反正可以认为图像是非常不同的,并移测量的所有值是没有必要的。

+0

围绕着更复杂的运动估计(“Shift测量”)算法。如果您检查的区域选择不当,您的方法将失败(本质上不是2D)。 – 2009-11-30 11:38:38

+0

供参考:“标准”运动估计算法之一是Lucas-Kanade跟踪器。 – 2009-11-30 11:40:05

13

我有一个这样做的图像直方图比较。我的基本算法是这样的:

  1. 双像成红,绿,蓝
  2. 创建红色,绿色和蓝色通道标准化的直方图,并将它们连接成一个矢量​​其中n是“桶”的数量, 256应该是足够
  3. 减去从另一个图像的直方图此直方图和计算距离

这里是一些代码numpypil

r = numpy.asarray(im.convert("RGB", (1,0,0,0, 1,0,0,0, 1,0,0,0))) 
g = numpy.asarray(im.convert("RGB", (0,1,0,0, 0,1,0,0, 0,1,0,0))) 
b = numpy.asarray(im.convert("RGB", (0,0,1,0, 0,0,1,0, 0,0,1,0))) 
hr, h_bins = numpy.histogram(r, bins=256, new=True, normed=True) 
hg, h_bins = numpy.histogram(g, bins=256, new=True, normed=True) 
hb, h_bins = numpy.histogram(b, bins=256, new=True, normed=True) 
hist = numpy.array([hr, hg, hb]).ravel() 

如果你有两个柱状图,你可以得到的距离是这样的:

diff = hist1 - hist2 
distance = numpy.sqrt(numpy.dot(diff, diff)) 

如果两个图像相同,距离为0,他们更发散,距离越远。

它对我的照片效果不错,但在文字和徽标等图形上失败。

+3

只是为了强调:即使两个直方图相等,它并不一定意味着生成它们的两幅图像在结构上相似。它们恰好具有相同的颜色分布。举一个例子,美国和英国的国旗可能会生成类似的直方图。 – 2009-11-30 11:52:47

1

首先,相关性对于相似性而言是一个非常CPU密集而不准确的测量。如果单个像素之间存在差异,为什么不去求平方和?

一个简单的解决方案,如果最大偏移是有限的:生成所有可能的移位图像并找到最匹配的一个。确保仅在所有移位图像中可匹配的像素子集上计算匹配变量(即相关性)。另外,你的最大移动幅度应该比图像的大小要小得多。

如果你想使用一些更先进的图像处理技术,我建议你看看SIFT这是一个非常强大的方法,(理论上来说)可以正确地匹配与平移,旋转和缩放无关的图像中的项目。

2

我早已做了一些图像处理的过程,并记住,匹配时,我通常开始制作图像灰度,然后锐化图像的边缘,以便只看到边缘。你(软件)可以移动和减去图像,直到差异最小。

如果差异大于您设置的阈值,则图像不相同,您可以继续下一步。然后可以分析具有较小阈值的图像。

我的确认为充其量你可以从根本上消除可能的匹配,但需要亲自比较可能的匹配以确定它们是否相等。

我真的不能显示代码,因为它很久以前,我用Khoros /康塔塔的课程。

6

你确实需要更好地指出问题,但是看着这5张图片,这些有机体似乎都以相同的方式定向。如果情况总是如此,您可以尝试在两幅图像之间做一个normalized cross-correlation,并将峰值作为相似度。我不知道在Python归一化互相关函数的,但有一个类似fftconvolve()功能,你可以做的圆形互相关自己:

a = asarray(Image.open('c603225337.jpg').convert('L')) 
b = asarray(Image.open('9b78f22f42.jpg').convert('L')) 
f1 = rfftn(a) 
f2 = rfftn(b) 
g = f1 * f2 
c = irfftn(g) 

,因为图像作为书面这将无法正常工作是不同的大小,并且输出没有加权或归一化。

输出的峰值位置表示两幅图像之间的偏移量,峰值的大小表示相似性。应该有一种方法来对它进行加权/归一化,这样你就可以分辨好匹配和差匹配之间的区别。

这不是我想要的答案,因为我还没有想出如何规范化它,但是如果我找出它,我会更新它,它会给你一个想法调查。

0

为了让进口在我的Ubuntu 16.04正常工作(如2017年4月),我安装Python 2.7版和这些:

sudo apt-get install python-dev 
sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk 
sudo apt-get install python-scipy 
sudo pip install pillow 

然后,我改变雪花进口这些:

import scipy as sp 
from scipy.ndimage import imread 
from scipy.signal.signaltools import correlate2d as c2d 

8年后,Snowflake的脚本为我工作的真棒!