如何量化两幅图像之间的差异?

时间:2008-10-10 02:39:34

标签: python image-processing background-subtraction image-comparison timelapse

这就是我想做的事情:

我定期用网络摄像头拍照。有点像时间流逝的事情。但是,如果没有真正改变,也就是说,图片几乎看起来相同,我不想存储最新的快照。

我想有一些量化差异的方法,我必须凭经验确定一个阈值。

我在寻求简洁而不是完美。 我正在使用python。

23 个答案:

答案 0 :(得分:243)

答案 1 :(得分:75)

一个简单的解决方案:

将图像编码为 jpeg ,并在 filesize 中查找实质性更改。

我已经实现了类似于视频缩略图的功能,并且具有很多成功和可扩展性。

答案 2 :(得分:54)

您可以使用PIL中的功能比较两个图像。

import Image
import ImageChops

im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")

diff = ImageChops.difference(im2, im1)

diff对象是这样的图像,其中每个像素是从第一图像中减去第二图像中的该像素的颜色值的结果。使用差异图像,您可以做几件事。最简单的是diff.getbbox()函数。它将告诉您包含两个图像之间所有更改的最小矩形。

您可以使用PIL中的函数实现此处提到的其他内容的近似值。

答案 3 :(得分:19)

两种流行且相对简单的方法是:(a)已经建议的欧几里德距离,或(b)归一化的互相关。与简单的互相关相比,归一化的互相关对于光照变化往往更加稳健。维基百科给出了normalized cross-correlation的公式。也存在更复杂的方法,但它们需要更多的工作。

使用类似numpy的语法,

dist_euclidean = sqrt(sum((i1 - i2)^2)) / i1.size

dist_manhattan = sum(abs(i1 - i2)) / i1.size

dist_ncc = sum( (i1 - mean(i1)) * (i2 - mean(i2)) ) / (
  (i1.size - 1) * stdev(i1) * stdev(i2) )

假设i1i2是2D灰度图像数组。

答案 4 :(得分:13)

尝试一件微不足道的事情:

将两个图像重新采样为小缩略图(例如64 x 64),并将缩略图逐个像素地与特定阈值进行比较。如果原始图像几乎相同,则重新采样的缩略图将非常相似或甚至完全相同。该方法处理可能发生的噪声,尤其是在低光场景中。如果你去灰度可能会更好。

答案 5 :(得分:7)

我具体解决了如何计算它们是否“足够不同”的问题。我假设你可以弄清楚如何逐个减去像素。

首先,我会拍摄一堆 nothing 更改的图像,并找出因捕捉的变化,成像系统中的噪点,JPEG压缩工件而导致的任何像素变化的最大量,以及照明的瞬间变化。也许你会发现即使没有任何动作也可以预期有1或2位差异。

然后,对于“真实”测试,您需要这样的标准:

  • 如果P像素相差不超过E。

所以,也许,如果E = 0.02,P = 1000,那将意味着(大约)如果任何单个像素变化超过~5个单位(假设8位图像),或者如果它将是“不同的”超过1000个像素都有任何错误。

这主要是为了快速识别足够接近不需要进一步检查的图像的良好“分类”技术。然而,“失败”的图像可能更多地是更精细/昂贵的技术,例如,如果相机抖动,或者对照明变化更加稳健,则不会产生误报。

我运行一个开源项目OpenImageIO,其中包含一个名为“idiff”的实用程序,它将差异与这样的阈值进行比较(实际上更精细)。即使您不想使用此软件,您也可以查看源代码以了解我们是如何使用它的。它在商业上得到了很大的应用,并开发了这种阈值技术,以便我们可以拥有一个用于渲染和图像处理软件的测试套件,其中“参考图像”可能与平台到平台之间存在细微差别,或者我们对其进行了小幅调整。算法,所以我们想要一个“容忍范围内的匹配”操作。

答案 6 :(得分:5)

给出的大部分答案都不会涉及照明等级。

在进行比较之前,我首先将图像标准化为标准光照水平。

答案 7 :(得分:4)

另一种测量两幅图像之间相似性的简单方法:

import sys
from skimage.measure import compare_ssim
from skimage.transform import resize
from scipy.ndimage import imread

# get two images - resize both to 1024 x 1024
img_a = resize(imread(sys.argv[1]), (2**10, 2**10))
img_b = resize(imread(sys.argv[2]), (2**10, 2**10))

# score: {-1:1} measure of the structural similarity between the images
score, diff = compare_ssim(img_a, img_b, full=True)
print(score)

如果其他人对比较图像相似度的更强大方法感兴趣,我将tutorial和网络app放在一起,用于使用Tensorflow测量和可视化相似图像。

答案 8 :(得分:4)

我在工作中遇到了类似的问题,我正在重写我们的图像转换端点,我想检查新版本是否与旧版本产生相同或几乎相同的输出。所以我写了这个:

https://github.com/nicolashahn/diffimg

对相同大小的图像进行操作,并以每像素级别测量每个通道的值差异:R,G,B(,A),取这些通道的平均差异,然后取平均值所有像素的差异,并返回一个比率。

例如,对于10x10的白色像素图像,并且相同的图像但是一个像素已变为红色,该像素的差异为1/3或0.33 ...(RGB 0,0,0对255, 0,0)和所有其他像素为0.总共100个像素,0.33 ... / 100 =图像差异〜0.33%。

我相信这对于OP的项目非常有用(我意识到这是一个非常古老的帖子,但是发布给未来的StackOverflowers,他们也希望在python中比较图像)。

答案 9 :(得分:3)

您是否看到过Algorithm for finding similar images个问题?看看是否有建议。

我建议对你的帧进行小波变换(我用Haar变换写了一个C扩展名);然后,比较两张图片中最大(按比例)小波因子的指标,你应该得到一个数值相似近似。

答案 10 :(得分:2)

如果现在回复已经太迟了,我很抱歉,但是因为我已经做了类似的事情,我认为我可以以某种方式做出贡献。

也许使用OpenCV,您可以使用模板匹配。假设你正在使用网络摄像头,如你所说:

  1. 简化图像(可能是阈值处理?)
  2. 应用模板匹配并使用minMaxLoc检查max_val
  3. 提示:max_val(或min_val取决于所使用的方法)将为您提供数字,大数字。要获得百分比差异,请使用与同一图像匹配的模板 - 结果将是您的100%。

    伪代码示例:

    previous_screenshot = ...
    current_screenshot = ...
    
    # simplify both images somehow
    
    # get the 100% corresponding value
    res = matchTemplate(previous_screenshot, previous_screenshot, TM_CCOEFF)
    _, hundred_p_val, _, _ = minMaxLoc(res)
    
    # hundred_p_val is now the 100%
    
    res = matchTemplate(previous_screenshot, current_screenshot, TM_CCOEFF)
    _, max_val, _, _ = minMaxLoc(res)
    
    difference_percentage = max_val / hundred_p_val
    
    # the tolerance is now up to you
    

    希望它有所帮助。

答案 11 :(得分:1)

计算两张图像的Manhattan Distance怎么样?这给你n * n值。然后你可以做一些像行平均值来减少n值和一个函数来获得一个单值。

答案 12 :(得分:1)

Earth movers distance可能正是您所需要的。 实时实现可能 abit 很重。

答案 13 :(得分:1)

我在三脚架上使用相同的相机拍摄的jpg图像有很多运气 (1)大大简化(如从3000像素宽到100像素宽甚至更少) (2)将每个jpg阵列展平成单个矢量 (3)将序列图像与简单相关算法成对相关,得到相关系数 (4)求相关系数得到r平方(即一个图像中的变异部分由下一个变化解释) (5)一般在我的申请中,如果r-square< 0.9,我说这两个图像是不同的,之间发生了一些事情。

这在我的实现中非常强大且快速(Mathematica 7)

值得玩一下您感兴趣的图像部分并通过将所有图像裁剪到该小区域来进行聚焦,否则将远离相机,但重要的变化将被遗漏。

我不知道如何使用Python,但我确定它也有相关性,不是吗?

答案 14 :(得分:1)

你可以计算两个图像的直方图,然后计算Bhattacharyya Coefficient,这是一个非常快速的算法,我用它来检测板球视频中的镜头变化(在C中使用openCV)

答案 15 :(得分:1)

查看isk-daemon如何实施Haar Wavelet。您可以使用它的imgdb C ++代码来计算动态图像之间的差异:

  

isk-daemon是一个开源数据库服务器,能够将基于内容的(可视)图像搜索添加到任何与图像相关的网站或软件。

     

该技术允许任何与图像相关的网站或软件的用户在小工具上绘制他们想要找到的图像,并让网站回复最相似的图像,或者只是在每个图像细节页面上请求更多类似的照片。

答案 16 :(得分:1)

我遇到了同样的问题并编写了一个简单的python模块,它使用枕头的ImageChops比较两个相同大小的图像,以创建黑/白差异图像并总结直方图值。

您可以直接获得此分数,或者与完整黑色与白色差异相比获得百分比值。

它还包含一个简单的is_equal函数,可以在图像传递下(并包括)提供模糊阈值。

这种方法并不是很精细,但也许可以用于解决同一问题的其他人。

https://pypi.python.org/pypi/imgcompare/

答案 17 :(得分:1)

import os
from PIL import Image
from PIL import ImageFile
import imagehash

#just use to the size diferent picture
def compare_image(img_file1, img_file2):
    if img_file1 == img_file2:
        return True
    fp1 = open(img_file1, 'rb')
    fp2 = open(img_file2, 'rb')

    img1 = Image.open(fp1)
    img2 = Image.open(fp2)

    ImageFile.LOAD_TRUNCATED_IMAGES = True
    b = img1 == img2

    fp1.close()
    fp2.close()

    return b





#through picturu hash to compare
def get_hash_dict(dir):
    hash_dict = {}
    image_quantity = 0
    for _, _, files in os.walk(dir):
        for i, fileName in enumerate(files):
            with open(dir + fileName, 'rb') as fp:
                hash_dict[dir + fileName] = imagehash.average_hash(Image.open(fp))
                image_quantity += 1

    return hash_dict, image_quantity

def compare_image_with_hash(image_file_name_1, image_file_name_2, max_dif=0):
    """
    max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.
    recommend to use
    """
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    hash_1 = None
    hash_2 = None
    with open(image_file_name_1, 'rb') as fp:
        hash_1 = imagehash.average_hash(Image.open(fp))
    with open(image_file_name_2, 'rb') as fp:
        hash_2 = imagehash.average_hash(Image.open(fp))
    dif = hash_1 - hash_2
    if dif < 0:
        dif = -dif
    if dif <= max_dif:
        return True
    else:
        return False


def compare_image_dir_with_hash(dir_1, dir_2, max_dif=0):
    """
    max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.

    """
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    hash_dict_1, image_quantity_1 = get_hash_dict(dir_1)
    hash_dict_2, image_quantity_2 = get_hash_dict(dir_2)

    if image_quantity_1 > image_quantity_2:
        tmp = image_quantity_1
        image_quantity_1 = image_quantity_2
        image_quantity_2 = tmp

        tmp = hash_dict_1
        hash_dict_1 = hash_dict_2
        hash_dict_2 = tmp

    result_dict = {}

    for k in hash_dict_1.keys():
        result_dict[k] = None

    for dif_i in range(0, max_dif + 1):
        have_none = False

        for k_1 in result_dict.keys():
            if result_dict.get(k_1) is None:
                have_none = True

        if not have_none:
            return result_dict

        for k_1, v_1 in hash_dict_1.items():
            for k_2, v_2 in hash_dict_2.items():
                sub = (v_1 - v_2)
                if sub < 0:
                    sub = -sub
                if sub == dif_i and result_dict.get(k_1) is None:
                    result_dict[k_1] = k_2
                    break
    return result_dict


def main():
    print(compare_image('image1\\815.jpg', 'image2\\5.jpg'))
    print(compare_image_with_hash('image1\\815.jpg', 'image2\\5.jpg', 6))
    r = compare_image_dir_with_hash('image1\\', image2\\', 10)
    for k in r.keys():
        print(k, r.get(k))


if __name__ == '__main__':
    main()
  • 输出:



    image2 \ 5.jpg image1 \ 815.jpg
    image2 \ 6.jpg image1 \ 819.jpg
    image2 \ 7.jpg image1 \ 900.jpg
    image2 \ 8.jpg image1 \ 998.jpg
    image2 \ 9.jpg image1 \ 1012.jpg

  • 示例图片:

    • 815.jpg
      815.jpg

    • 5.JPG
      5.jpg

答案 18 :(得分:1)

一种更有原则性的方法是使用全局描述符来比较图像,例如GIST或CENTRIST。如here所述,哈希函数也提供了类似的解决方案。

答案 19 :(得分:0)

这是我写的一个函数,它以 2 个图像(文件路径)作为参数,并返回两个图像像素分量之间的平均差异。这对我确定视觉上“相等”的图像非常有效(当它们不 == 相等时)。

(我发现 8 个是确定图像是否基本相同的一个很好的限制。)

(如果不添加预处理,图像必须具有相同的尺寸。)

from PIL import Image

def imagesDifference( imageA, imageB ):
    A = list(Image.open(imageA, r'r').convert(r'RGB').getdata())
    B = list(Image.open(imageB, r'r').convert(r'RGB').getdata())
    if (len(A) != len(B)): return -1
    diff = []
    for i in range(0, len(A)):
        diff += [abs(A[i][0] - B[i][0]), abs(A[i][1] - B[i][1]), abs(A[i][2] - B[i][2])]
    return (sum(diff) / len(diff))

答案 20 :(得分:0)

使用numpy可以通过计算均方误差来实现简单快速的解决方案:

before = np.array(get_picture())
while True:
    now = np.array(get_picture())
    MSE = np.mean((now - before)**2)

    if  MSE > threshold:
        break

    before = now

答案 21 :(得分:0)

有很多指标可以评估两个图像的外观/外观。

在这里我将不讨论任何代码,因为我认为这应该是科学问题,而不是技术问题。

通常,这个问题与人类对图像的感知有关,因此每种算法都对人类视觉系统特征有支持。

经典方法是:

可见差异预测器:一种评估图像保真度(https://www.spiedigitallibrary.org/conference-proceedings-of-spie/1666/0000/Visible-differences-predictor--an-algorithm-for-the-assessment-of/10.1117/12.135952.short?SSO=1)的算法

图像质量评估:从错误可见性到结构相似性(http://www.cns.nyu.edu/pub/lcv/wang03-reprint.pdf

FSIM:用于图像质量评估(https://www4.comp.polyu.edu.hk/~cslzhang/IQA/TIP_IQA_FSIM.pdf)的特征相似性索引

其中,SSIM(图像质量评估:从错误可见性到结构相似性)是最容易计算的,其开销也很小,如另一篇论文《基于梯度相似性的图像质量评估》({{3} }。

还有更多其他方法。如果您有兴趣/真的很在意艺术品,请看一下Google学术搜索并搜索“视觉差异”,“图像质量评估”之类的东西。

答案 22 :(得分:0)

我认为您可以简单地计算两个图像的亮度之间的欧氏距离(即sqrt(差异的平方和,逐个像素)),并且如果它落在某个经验阈值之下则认为它们相等。你最好把它包装成一个C函数。