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

问题:

这是我想做的:
我定期用网络摄像头拍照。排序像时间流逝的东西。但是,如果没有任何事情真的改变,也就是说,这个图片很多looks一样,我不想存储最新的快照。
我想象有一些量化差异的方法,我必须经验性地确定一个阈值。
我正在寻找简单而不是完美。
我在使用python

回答:

大概的概念

选项1:将两个图像加载为数组(scipy.misc.imread),并计算元素(逐像素)差异。计算差异的范数。
选项2:加载两个图像。计算每个特征向量(如直方图)。计算特征向量而不是图像之间的距离。
但是,有一些首先要做出的决定。

问题

你应该首先回答这些问题:

  • 图像是相同的形状和尺寸?
    如果没有,您可能需要调整大小或裁剪。 PIL库将帮助您在Python中执行此操作。
    如果它们采用相同的设置和相同的设备,它们可能是一样的。
  • 图像是否一致?
    如果没有,您可能需要首先运行互相关,首先找到最佳对齐。 SciPy有这样做的功能。
    如果相机和场景仍然存在,则图像可能会很好对齐。
  • 图像的曝光总是一样吗? (亮度/对比度是否一样?)
    如果没有,您可能需要to normalize图像。
    但要小心,在某些情况下,这可能会比错误更好。例如,黑暗背景上的单个明亮像素将使归一化图像非常不同。
  • 颜色信息重要吗?
    如果您想注意到颜色变化,您将具有每个点的颜色值向量,而不是像灰度图像中的标量值。写这样的代码时需要更多的关注。
  • 图像中有明显的边缘吗?他们有可能移动吗?
    如果是,您可以先应用边缘检测算法(例如,使用Sobel或Prewitt变换计算梯度,应用一些阈值),然后将第一个图像上的边缘与第二个图像上的边缘进行比较。
  • 图像中有噪音吗?
    所有传感器都会污染具有一定数量噪音的图像。低成本传感器有更多的噪音。您可能希望在比较图像之前应用一些降噪功能。模糊是最简单(但不是最好的)方法。
  • 你想注意什么样的变化?
    这可能会影响到使用图像之间差异的规范选择。
    考虑使用曼哈顿范数(绝对值的和)或零范数(不等于零的元素的数量)来测量图像已经改变了多少。前者会告诉你图像是多少,后者只会说明像素有多少。

我认为你的图像是对齐的,大小和形状相同,可能会有不同的曝光。为了简单起见,即使它们是彩色(RGB)图像,我也将它们转换成灰度级。
您将需要这些导入:

import sys

from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average

主要功能,读取两个图像,转换为灰度,比较和打印结果:

def main():
    file1, file2 = sys.argv[1:1+2]
    # read images as 2D arrays (convert to grayscale for simplicity)
    img1 = to_grayscale(imread(file1).astype(float))
    img2 = to_grayscale(imread(file2).astype(float))
    # compare
    n_m, n_0 = compare_images(img1, img2)
    print "Manhattan norm:", n_m, "/ per pixel:", n_m/img1.size
    print "Zero norm:", n_0, "/ per pixel:", n_0*1.0/img1.size

如何比较img1img2是2D SciPy数组:

def compare_images(img1, img2):
    # normalize to compensate for exposure difference, this may be unnecessary
    # consider disabling it
    img1 = normalize(img1)
    img2 = normalize(img2)
    # calculate the difference and its norms
    diff = img1 - img2  # elementwise for scipy arrays
    m_norm = sum(abs(diff))  # Manhattan norm
    z_norm = norm(diff.ravel(), 0)  # Zero norm
    return (m_norm, z_norm)

如果文件是彩色图像,则imread返回3D阵列,平均RGB通道(最后一个阵列轴)以获得强度。不需要为灰度图像(例如.pgm):

def to_grayscale(arr):
    "If arr is a color image (3D array), convert it to grayscale (2D array)."
    if len(arr.shape) == 3:
        return average(arr, -1)  # average over the last axis (color channels)
    else:
        return arr

归一化是微不足道的,您可以选择归一化为[0,1]而不是[0,255]。 arr这里是一个SciPy数组,所以所有操作都是元素的:

def normalize(arr):
    rng = arr.max()-arr.min()
    amin = arr.min()
    return (arr-amin)*255/rng

运行main功能:

if __name__ == "__main__":
    main()

现在你可以把这全部放在一个脚本中,并运行两个图像。如果我们将图像与自身进行比较,没有什么区别:

$ python compare.py one.jpg one.jpg
Manhattan norm: 0.0 / per pixel: 0.0
Zero norm: 0 / per pixel: 0.0

如果我们模糊图像并与原始图像进行比较,则有一些区别:

$ python compare.py one.jpg one-blurred.jpg 
Manhattan norm: 92605183.67 / per pixel: 13.4210411116
Zero norm: 6900000 / per pixel: 1.0

附:整个compare.py脚本。

更新:相关技术

由于问题是关于视频序列,其中帧可能几乎相同,并且您寻找不寻常的东西,我想提一些可能相关的替代方法:

  • 背景减法和分割(以检测前景对象)
  • 稀疏光流(检测运动)
  • 比较直方图或其他一些统计信息而不是图像

我强烈建议您阅读“学习OpenCV”一书,第9章(图像部分和细分)和10(跟踪和运动)。前者教导使用背景减法方法,后者给出了一些关于光流方法的信息。所有方法都在OpenCV库中实现。如果您使用Python,我建议使用OpenCV≥2.3及其cv2 Python模块。
最简单版本的背景减法:

  • 学习背景的每个像素的平均值μ和标准偏差σ
  • 将当前像素值与(μ-2σ,μ+2σ)或(μ-σ,μ+σ)的范围进行比较,

更高级的版本考虑到每个像素的时间序列,并处理非静态场景(如移动树或草)。
光流的想法是采取两个或更多个帧,并为每个像素(密集光流)或其中一些(稀疏光流)分配速度矢量。为了估计稀疏光流,您可以使用Lucas-Kanade method(它也在OpenCV中实现)。显然,如果有大量的流量(速度场的最大平均值),那么有些东西在框架中移动,随后的图像更加不同。
比较直方图可能有助于检测连续帧之间的突变。这种方法在Courbon et al, 2010

 Similarity of consecutive frames.测量两个连续帧之间的距离。如果太高,则意味着第二帧被破坏,因此图像被消除。两帧直方图上的Kullback–Leibler distance或相互熵:
 $$ d(p,q) = \sum_i p(i) \log (p(i)/q(i)) $$
其中pq是使用帧的直方图。阈值固定为0.2。

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: How can I quantify difference between two images?

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

21 − = 13