大概的概念
选项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数组:
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,我建议使用cv2
及其cv2
Python模块。
最简单的背景减法版本:
- 了解背景每个像素的平均值μ和标准偏差σ
- 比较当前像素值到(μ-2σ,μ+2σ)或(μ-σ,μ+σ)的范围
更高级的版本会考虑每个像素的时间序列,并处理非静态场景(例如移动的树木或草地)。
光流的想法是拍摄两个或更多帧,并将速度矢量分配给每个像素(密集光流)或分配给其中一些像素(稀疏光流)。要估计稀疏的光流,可以使用Lucas-Kanade方法 (它也在OpenCV中实现)。显然,如果有很多流量(在速度场的最大值上具有较高的平均值),则表示帧中正在移动某些内容,并且后续图像会有所不同。
比较直方图可能有助于检测连续帧之间的突然变化。 Courbon等人,2010年使用了这种方法:
连续帧的相似性。测量两个连续帧之间的距离。如果它太高,则意味着第二帧已损坏,因此图像被消除。两帧直方图上的Kullback-Leibler距离或互熵:

其中p和q是帧的直方图。阈值固定为0.2。
0
这是我想做的:
我定期使用网络摄像头拍照。有点像延时的东西。但是,如果什么都没有真正改变,即图片看起来几乎相同,则我不想存储最新的快照。
我想象有某种量化差异的方法,而我将不得不凭经验确定阈值。
我在寻找简单而不是完美。我正在使用python。