47

我正在尝试将图像相互比较以了解它们是否不同。首先,我尝试对 RGB 值进行 Pearson 相关,除非图片稍微偏移,否则效果也很好。因此,如果一个有 100% 相同的图像但有一个有点移动,我会得到一个不好的相关值。

对更好的算法有什么建议吗?

顺便说一句,我说的是比较数千张图片......

编辑:这是我的图片示例(显微):

IM1:

在此处输入图像描述

IM2:

在此处输入图像描述

IM3:

在此处输入图像描述

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

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

4

9 回答 9

38

一年前有人问了一个类似的问题,并得到了很多回应,包括一个关于像素化图像的问题,我建议这至少是一个资格预审步骤(因为它会很快排除非常不相似的图像)。

那里还有更早的问题的链接,这些问题有更多的参考资料和好的答案。

这是一个使用 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 文件大小”技巧,或者像素化。您拥有不同大小的图像这一事实使事情变得复杂,但是您没有提供足够的信息来说明人们可能期望的屠宰程度,因此很难给出一个考虑到这一点的具体答案。

于 2009-11-30T18:51:30.603 回答
13

我已经通过图像直方图比较来做到这一点。我的基本算法是这样的:

  1. 将图像拆分为红色、绿色和蓝色
  2. 为红色、绿色和蓝色通道创建标准化直方图,并将它们连接成一个向量(r0...rn, g0...gn, b0...bn),其中 n 是“桶”的数量,256 应该足够了
  3. 从另一个图像的直方图中减去这个直方图并计算距离

这是一些带有numpy和的代码pil

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,它们越发散,距离越大。

对我来说,它在照片上效果很好,但在文本和徽标等图形上却失败了。

于 2009-11-30T11:27:37.520 回答
6

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

FFT 应该没问题(numpy 有一个二维矩阵的实现),但我总是听说小波更适合这种任务^_^

关于性能,如果所有图像的大小相同,如果我没记错的话,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 并再次比较,如果差异低于上一步,则可以重复在梯度方向移动并减去,直到差异低于某个阈值或再次增加。如果模糊内核的半径大于图像的位移,那应该可以工作。

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

于 2009-11-30T11:14:10.060 回答
6

您确实需要更好地指定问题,但是,查看这 5 张图像,生物体似乎都以相同的方式定向。如果总是这样,您可以尝试在两幅图像之间进行归一化互相关,并将峰值作为您的相似度。我不知道 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)

这不会像所写的那样工作,因为图像的大小不同,并且输出根本没有加权或标准化。

输出峰值的位置表示两幅图像之间的偏移量,峰值的大小表示相似度。应该有一种方法对其进行加权/标准化,以便您可以分辨出好的匹配和差的匹配之间的区别。

这不是我想要的答案,因为我还没有弄清楚如何对其进行规范化,但是如果我弄清楚了,我会更新它,它会给你一个想法来研究。

于 2009-12-04T19:05:27.003 回答
2

我很久以前就学过一些图像处理课程,记得在匹配时,我通常从使图像灰度化开始,然后锐化图像的边缘,这样你就只能看到边缘了。然后,您(软件)可以移动和减去图像,直到差异最小。

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

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

我不能像很久以前那样真正显示代码,我在那门课程中使用了 Khoros/Cantata。

于 2009-11-30T15:12:42.933 回答
1

首先,相关性是一种非常占用 CPU 资源且不准确的相似性度量。如果单个像素之间存在差异,为什么不直接求平方和?

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

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

于 2009-11-30T12:27:13.220 回答
0

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

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

  • 相应地移动比较图像

  • 计算你试图做的皮尔逊相关性

位移测量并不困难。

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

笔记:

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

于 2009-11-30T11:21:26.820 回答
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 的脚本对我来说太棒了!

于 2017-04-30T16:38:21.113 回答
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),因为它们当然会在调整大小时改变颜色分布。

此外,请考虑交换图像会改变结果,因为第二张图像可能会被下采样而不是上采样(毕竟,裁剪可能更适合您的情况而不是重新缩放。)

于 2019-05-03T08:32:04.377 回答