大概的概念
选项1:将两个图像加载为数组(scipy.misc.imread)并计算元素(逐个像素)的差异。计算差异的标准。
选项2:加载两个图像。计算每个特征向量的某些特征向量(如直方图)。计算特征向量而不是图像之间的距离。
但是,首先要做出一些决定。
问题
你应该先回答这些问题:图像的形状和尺寸是否相同?
如果没有,您可能需要调整大小或裁剪它们。PIL库将有助于在Python中完成它。
如果使用相同的设置和相同的设备,它们可能是相同的。
图像是否良好对齐?
如果没有,您可能希望首先运行互相关,以便首先找到最佳对齐。SciPy具有执行此功能的功能。
如果相机和场景仍然存在,则图像可能会很好地对齐。
曝光的图像总是一样吗?(亮度/对比度是否相同?)
如果没有,您可能想要标准化图像。
但是要小心,在某些情况下,这可能比错误做得更多。例如,暗背景上的单个亮像素将使标准化图像非常不同。
颜色信息重要吗?
如果要注意颜色变化,则每个点都会有一个颜色值矢量,而不是灰度图像中的标量值。编写此类代码时需要更多关注。
图像中是否有明显的边缘?他们可能会搬家吗?
如果是,您可以首先应用边缘检测算法(例如,使用Sobel或Prewitt变换计算梯度,应用一些阈值),然后将第一个图像上的边缘与第二个图像上的边缘进行比较。
图像中有噪音吗?
所有传感器都会以一定的噪音污染图像。低成本传感器具有更多噪音。您可能希望在比较图像之前应用一些降噪功能。模糊是这里最简单(但不是最好)的方法。
你想注意什么样的变化?
这可能会影响用于图像之间差异的规范选择。
考虑使用曼哈顿范数(绝对值的总和)或零范数(元素的数量不等于零)来测量图像的变化程度。前者将告诉您图像关闭了多少,后者只会告诉您有多少像素不同。
例
我假设你的图像是完全对齐的,相同的大小和形状,可能有不同的曝光。为简单起见,我将它们转换为灰度,即使它们是彩色(RGB)图像。
您将需要这些导入:import sysfrom scipy.misc import imreadfrom scipy.linalg import normfrom 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.jpgManhattan norm: 0.0 / per pixel: 0.0Zero norm: 0 / per pixel: 0.0
如果我们模糊图像并与原始图像进行比较,则存在一些差异:$ python compare.py one.jpg one-blurred.jpg
Manhattan norm: 92605183.67 / per pixel: 13.4210411116Zero norm: 6900000 / per pixel: 1.0
更新:相关技术
由于问题是关于视频序列,帧可能几乎相同,并且你寻找一些不寻常的东西,我想提一些可能相关的替代方法:背景减法和分割(检测前景对象)
稀疏光流(检测运动)
比较直方图或一些其他统计数据而不是图像
我强烈建议您查看“学习OpenCV”一书,第9章(图像部分和分割)和第10章(跟踪和动作)。前者教导使用背景减法方法,后者给出了光流方法的一些信息。所有方法都在OpenCV库中实现。如果您使用Python,我建议使用OpenCV≥2.3及其cv2Python模块。
最简单的背景减法版本:学习背景的每个像素的平均值μ和标准偏差σ
将当前像素值与(μ-2σ,μ+2σ)或(μ-σ,μ+σ)的范围进行比较
更高级的版本会考虑每个像素的时间序列并处理非静态场景(如移动树木或草地)。
光流的想法是采用两个或更多帧,并将速度矢量分配给每个像素(密集光流)或其中一些(稀疏光流)。要估计稀疏光流,您可以使用Lucas-Kanade方法(它也在OpenCV中实现)。显然,如果存在大量流量(速度场的最大值高于平均值),那么帧中的某些东西会移动,后续图像会更加不同。
比较直方图可以帮助检测连续帧之间的突然变化。这种方法在Courbon等人,2010年使用:连续帧的相似性。测量两个连续帧之间的距离。如果它太高,则意味着第二帧被破坏,因此图像被消除。所述的Kullback-Leibler距离,或互熵,对两帧的直方图:
其中p和q是帧的直方图。阈值固定为0.2。