python - 如何量化两个图像之间的差异?

这是我想做的事情:

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

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

我在寻找简单而不是完美。
我正在使用 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 channel (最后一个数组轴)以获得强度。无需为灰度图像执行此操作(例如 .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脚本。

    更新:相关技术

    由于问题是关于视频序列的,其中帧可能几乎相同,并且您寻找不寻常的东西,我想提一些可能相关的替代方法:
  • 背景减法和分割(检测前景对象)
  • 稀疏光流(检测运动)
  • 比较直方图或其他一些统计数据而不是图像

  • 我强烈建议您阅读“Learning OpenCV”一书,第 9 章(图像部分和分割)和第 10 章(跟踪和运动)。前者教导使用背景减法方法,后者提供了一些关于光流方法的信息。所有方法都在 OpenCV 库中实现。如果你使用 Python,我建议使用 OpenCV ≥ 2.3,它的 cv2 Python 模块。

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

  • 更高级的版本会考虑每个像素的时间序列并处理非静态场景(如移动的树木或草地)。

    光流的思想是取两帧或更多帧,并为每个像素(密集光流)或其中一些(稀疏光流)分配速度向量。要估计稀疏光流,您可以使用 Lucas-Kanade method (它也在 OpenCV 中实现)。显然,如果有很多流量(速度场最大值的高平均值),那么帧中有东西在移动,随后的图像就会更加不同。

    比较直方图可能有助于检测连续帧之间的突然变化。在 Courbon et al, 2010 中使用了这种方法:

    Similarity of consecutive frames. The distance between two consecutive frames is measured. If it is too high, it means that the second frame is corrupted and thus the image is eliminated. The Kullback–Leibler distance, or mutual entropy, on the histograms of the two frames:

    where p and q are the histograms of the frames is used. The threshold is fixed on 0.2.

    https://stackoverflow.com/questions/189943/

    相关文章:

    python - 如何更改绘图背景颜色?

    python - 无法安装 Python 包 [SSL : TLSV1_ALERT_PROTOCOL

    python - 无法使用 Ctrl-C 终止 Python 脚本

    python - 多处理:如何在类中定义的函数上使用 Pool.map?

    linux - 抑制makefile中命令调用的回声?

    linux - 创建 .tar.gz 文件时排除目录

    python - 在 Python 2.6 中不推荐使用 BaseException.message

    linux - 如何在 linux 上监控进程的线程数?

    linux - 在 bash 中检查当前分区的可用磁盘空间

    python - 如何让 Flask 在端口 80 上运行?