图像比较算法

时间:2009-11-30 10:57:55

标签: python image image-processing compare computer-vision

我正在尝试将图像相互比较,以确定它们是否不同。首先,我尝试对RGB值进行Pearson相关,除非图像是轻微的位移,否则它的效果也相当不错。因此,如果a具有100%相同的图像,但有一个稍微移动,则会得到错误的相关值。

有关更好算法的任何建议吗?

顺便说一下,我正在谈论比较数千个图片......

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

im1:

enter image description here

im2:

enter image description here

im3:

enter image description here

im1和im2是相同但有点移位/切割,im3应该被认为是完全不同的...

修改 问题通过Peter Hansen的建议解决了!效果很好!感谢所有答案!一些结果可以在这里找到 http://labtools.ipk-gatersleben.de/image%20comparison/image%20comparision.pdf

9 个答案:

答案 0 :(得分:37)

一年前曾问过一个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)

所以请注意,im1与自身相比得分为42105,im2与im1相比并不遥远,但im3与其他任何一个相比都低于该值的一半。您必须尝试使用​​其他图片来了解这可能会有多好以及如何改进它。

我的机器运行时间很长......几分钟。我会尝试一些预过滤,以避免浪费时间比较非常不同的图像,可能与回答另一个问题时提到的“比较jpg文件大小”技巧,或与像素化。你有不同大小的图像这一事实使事情变得复杂,但你没有提供足够的信息来说明人们可能期望的屠杀程度,因此很难给出一个考虑到这一点的具体答案。

答案 1 :(得分:13)

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

  1. 将图像分割为红色,绿色和蓝色
  2. 为红色,绿色和蓝色通道创建标准化直方图,并将它们连接成一个向量(r0...rn, g0...gn, b0...bn),其中n是“桶”的数量,256应该足够
  3. 从另一幅图像的直方图中减去此直方图并计算距离
  4. 这是一些包含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,它们发散的越多,距离就越大。

    对于我而言,它对照片效果很好,但在文字和徽标等图形上却失败了。

答案 2 :(得分:6)

如果您的问题与移位像素有关,那么您应该与频率变换进行比较。

FFT应该没问题(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

答案 3 :(得分: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)

由于图像尺寸不同,输出没有加权或标准化,因此无法正常工作。

输出峰值的位置表示两个图像之间的偏移,峰值的大小表示相似性。应该有一种方法来对它进行加权/标准化,这样你就能分辨出好的比赛和糟糕的比赛之间的区别。

这不是我想要的答案,因为我还没有弄清楚如何将其标准化,但是如果我弄明白的话我会更新它,它会给你一个想法成。

答案 4 :(得分:2)

我很久以前就已经完成了一些图像处理过程,并且记住在匹配时我通常从制作图像灰度开始,然后锐化图像的边缘,这样你才能看到边缘。然后,您(软件)可以移动和减去图像,直到差异最小。

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

我认为最多可以从根本上淡化可能的匹配,但需要亲自比较可能的匹配,以确定它们是否真的相同。

我不能真正展示代码,就像很久以前一样,我使用Khoros / Cantata进行该课程。

答案 5 :(得分:1)

首先,相关性是一种非常耗费CPU的相当不准确的度量。如果单个像素之间存在差异,为什么不去寻找平方和?

一个简单的解决方案,如果最大偏移是有限的:生成所有可能的移位图像并找到最匹配的图像。确保仅在可以在所有移位图像中匹配的像素子集上计算匹配变量(即相关性)。此外,您的最大偏移应远小于图像的大小。

如果你想使用一些更高级的图像处理技术,我建议你看看SIFT这是一个非常强大的方法(理论上无论如何)可以正确匹配图像中的项目,而不依赖于平移,旋转和缩放。< / p>

答案 6 :(得分:0)

我想你可以这样做:

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

  • 相应地移动比较图像

  • 计算你想要做的皮尔逊相关

换档测量并不困难。

  • 在比较图像中取一个区域(比如大约32x32)。
  • 将其在水平方向上移动x像素,在垂直方向上移位y像素。
  • 计算SAD(绝对差值之和)w.r.t.原始图像
  • 在小范围(-10,+ 10)
  • 中为x和y的多个值执行此操作
  • 找出差异最小的地方
  • 选择该值作为移位运动矢量

注意:

如果对于x和y的所有值,SAD变得非常高,那么无论如何你可以假设图像高度不同并且不需要移位测量。

答案 7 :(得分: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

然后我将Snowflake的导入改为:

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

8年后Snowflake的脚本对我有用了多么棒啊!

答案 8 :(得分:0)

我提出了一种基于图像直方图上相似度的Jaccard索引的解决方案。参见:https://en.wikipedia.org/wiki/Jaccard_index#Weighted_Jaccard_similarity_and_distance

您可以计算像素颜色分布的差异。对于翻译来说,这确实是不变的。

from PIL.Image import Image
from typing import List

def jaccard_similarity(im1: Image, im2: Image) -> float:
    """Compute the similarity between two images.
    First, for each image an histogram of the pixels distribution is extracted.
    Then, the similarity between the histograms is compared using the weighted Jaccard index of similarity, defined as:
    Jsimilarity = sum(min(b1_i, b2_i)) / sum(max(b1_i, b2_i)
    where b1_i, and b2_i are the ith histogram bin of images 1 and 2, respectively.

    The two images must have same resolution and number of channels (depth).

    See: https://en.wikipedia.org/wiki/Jaccard_index
    Where it is also called Ruzicka similarity."""

    if im1.size != im2.size:
        raise Exception("Images must have the same size. Found {} and {}".format(im1.size, im2.size))

    n_channels_1 = len(im1.getbands())
    n_channels_2 = len(im2.getbands())
    if n_channels_1 != n_channels_2:
        raise Exception("Images must have the same number of channels. Found {} and {}".format(n_channels_1, n_channels_2))

    assert n_channels_1 == n_channels_2

    sum_mins = 0
    sum_maxs = 0

    hi1 = im1.histogram()  # type: List[int]
    hi2 = im2.histogram()  # type: List[int]

    # Since the two images have the same amount of channels, they must have the same amount of bins in the histogram.
    assert len(hi1) == len(hi2)

    for b1, b2 in zip(hi1, hi2):
        min_b = min(b1, b2)
        sum_mins += min_b
        max_b = max(b1, b2)
        sum_maxs += max_b

    jaccard_index = sum_mins / sum_maxs

    return jaccard_index

关于均方误差,Jaccard索引始终位于[0,1]范围内,因此可以在不同图像尺寸之间进行比较。

然后,您可以比较两个图像,但是在重新缩放为相同大小之后!否则必须以某种方式对像素数进行标准化。我用了这个:

import sys

from skincare.common.utils import jaccard_similarity

import PIL.Image
from PIL.Image import Image

file1 = sys.argv[1]
file2 = sys.argv[2]

im1 = PIL.Image.open(file1)  # type: Image
im2 = PIL.Image.open(file2)  # type: Image

print("Image 1: mode={}, size={}".format(im1.mode, im1.size))
print("Image 2: mode={}, size={}".format(im2.mode, im2.size))

if im1.size != im2.size:
    print("Resizing image 2 to {}".format(im1.size))
    im2 = im2.resize(im1.size, resample=PIL.Image.BILINEAR)

j = jaccard_similarity(im1, im2)
print("Jaccard similarity index = {}".format(j))

对图像进行测试:

$ python CompareTwoImages.py im1.jpg im2.jpg
Image 1: mode=RGB, size=(401, 105)
Image 2: mode=RGB, size=(373, 109)
Resizing image 2 to (401, 105)
Jaccard similarity index = 0.7238955686269157
$ python CompareTwoImages.py im1.jpg im3.jpg 
Image 1: mode=RGB, size=(401, 105)
Image 2: mode=RGB, size=(457, 121)
Resizing image 2 to (401, 105)
Jaccard similarity index = 0.22785529941822316
$ python CompareTwoImages.py im2.jpg im3.jpg 
Image 1: mode=RGB, size=(373, 109)
Image 2: mode=RGB, size=(457, 121)
Resizing image 2 to (373, 109)
Jaccard similarity index = 0.29066426814105445

您还可以考虑尝试使用不同的重采样滤镜(例如NEAREST或LANCZOS),因为它们当然会在调整大小时改变颜色分布。

另外,考虑交换图像会改变结果,因为第二张图像可能会降低采样率而不是升高采样率(毕竟,裁剪可能更适合您的情况,而不是重新缩放)。