小编典典

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

all

这是我想做的事情:

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

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

我在寻找简单而不是完美。我正在使用python。


阅读 124

收藏
2022-06-22

共1个答案

小编典典

大概的概念

选项 1:将两个图像加载为数组 ( scipy.misc.imread) 并计算逐个元素(逐个像素)的差异。计算差异的范数。

选项 2:加载两个图像。为它们中的每一个计算一些特征向量(如直方图)。计算特征向量而不是图像之间的距离。

但是,首先要做出一些决定。

问题

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

  • 图像的形状和尺寸是否相同?

如果没有,您可能需要调整它们的大小或裁剪它们。PIL 库将有助于在 Python 中完成它。

如果它们采用相同的设置和相同的设备,它们可能是相同的。

  • 图像是否对齐?

如果没有,您可能需要先运行互相关,以首先找到最佳对齐方式。SciPy 具有执行此操作的功能。

如果相机和场景静止,图像很可能对齐良好。

  • 图像的曝光总是一样的吗?(亮度/对比度是否相同?)

如果没有,您可能需要标准化图像。

但要小心,在某些情况下,这可能弊大于利。例如,深色背景上的单个明亮像素会使归一化图像非常不同。

  • 颜色信息重要吗?

如果您想注意颜色变化,您将拥有每个点的颜色值向量,而不是灰度图像中的标量值。编写此类代码时需要多加注意。

  • 图像中是否有明显的边缘?他们有可能搬家吗?

如果是,您可以先应用边缘检测算法(例如,使用 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

如何比较。img1这里img2是 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

PS 整个compare.py脚本。

更新:相关技术

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

  • 背景减法和分割(检测前景物体)
  • 稀疏光流(检测运动)
  • 比较直方图或其他一些统计数据而不是图像

我强烈建议看一下《学习 OpenCV》一书,第 9 章(图像部分和分割)和第 10
章(跟踪和运动)。前者教导使用背景减法方法,后者提供一些关于光流方法的信息。所有方法都在 OpenCV 库中实现。如果你使用 Python,我建议使用
OpenCV ≥ 2.3,以及它的cv2Python 模块。

背景减法最简单的版本:

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

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

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

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

连续帧的相似性。 测量两个连续帧之间的距离。如果它太高,则意味着第二帧被破坏,因此图像被消除。两帧直方图上的Kullback-eibler
距离
或互熵:

$$ d(p,q) = \sum_i p(i) \log (p(i)/q(i))
$$

其中 pq 是使用帧的直方图。阈值固定为 0.2。

2022-06-22