这是我想做的事情:
我定期用网络摄像头拍照。有点像时间流逝的东西。但是,如果没有真正改变,即图片 看起来 几乎相同,我不想存储最新的快照。
我想有一些方法可以量化差异,我必须凭经验确定一个阈值。
我在寻找简单而不是完美。我正在使用python。
选项 1:将两个图像加载为数组 ( scipy.misc.imread) 并计算逐个元素(逐个像素)的差异。计算差异的范数。
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 数组:
img1
img2
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):
imread
.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 数组,所以所有操作都是元素方面的:
arr
def normalize(arr): rng = arr.max()-arr.min() amin = arr.min() return (arr-amin)*255/rng
运行main函数:
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 模块。
cv2
背景减法最简单的版本:
更高级的版本会考虑每个像素的时间序列并处理非静态场景(如移动的树木或草)。
光流的想法是取两个或更多帧,并将速度矢量分配给每个像素(密集光流)或其中的一些(稀疏光流)。要估计稀疏光流,您可以使用Lucas-Kanade 方法(它也在 OpenCV 中实现)。显然,如果有很多流(速度场的最大值高于平均值),那么帧中的某些东西正在移动,随后的图像会更加不同。
比较直方图可能有助于检测连续帧之间的突然变化。Courbon et al, 2010中使用了这种方法:
连续帧的相似性。 测量两个连续帧之间的距离。如果它太高,则意味着第二帧被破坏,因此图像被消除。两帧直方图上的Kullback-eibler 距离或互熵: 其中 p 和 q 是使用帧的直方图。阈值固定为 0.2。
连续帧的相似性。 测量两个连续帧之间的距离。如果它太高,则意味着第二帧被破坏,因此图像被消除。两帧直方图上的Kullback-eibler 距离或互熵:
其中 p 和 q 是使用帧的直方图。阈值固定为 0.2。