这是我想做的:

我定期用网络摄像头拍照。就像时间流逝一样。然而,如果没有什么真正的改变,也就是说,图片看起来几乎相同,我不想存储最新的快照。

我想有某种方法可以量化这种差异,我必须根据经验确定一个阈值。

我追求的是简单而不是完美。 我用的是python。


当前回答

你可以计算这两个图像的直方图,然后计算Bhattacharyya系数,这是一个非常快速的算法,我已经用它来检测板球视频中的镜头变化(在C中使用openCV)

其他回答

我认为你可以简单地计算两幅图像亮度之间的欧几里得距离(即平方根(像素对像素的差异平方和)),如果这低于某个经验阈值,就认为它们相等。你最好包装一个C函数。

一般的想法

选项1:以数组的形式加载两个图像(scipy.misc.imread),并计算逐元素(逐像素)的差值。计算差值的范数。

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

然而,首先要做一些决定。

问题

你应该先回答这些问题:

Are images of the same shape and dimension? If not, you may need to resize or crop them. PIL library will help to do it in Python. If they are taken with the same settings and the same device, they are probably the same. Are images well-aligned? If not, you may want to run cross-correlation first, to find the best alignment first. SciPy has functions to do it. If the camera and the scene are still, the images are likely to be well-aligned. Is exposure of the images always the same? (Is lightness/contrast the same?) If not, you may want to normalize images. But be careful, in some situations this may do more wrong than good. For example, a single bright pixel on a dark background will make the normalized image very different. Is color information important? If you want to notice color changes, you will have a vector of color values per point, rather than a scalar value as in gray-scale image. You need more attention when writing such code. Are there distinct edges in the image? Are they likely to move? If yes, you can apply edge detection algorithm first (e.g. calculate gradient with Sobel or Prewitt transform, apply some threshold), then compare edges on the first image to edges on the second. Is there noise in the image? All sensors pollute the image with some amount of noise. Low-cost sensors have more noise. You may wish to apply some noise reduction before you compare images. Blur is the most simple (but not the best) approach here. What kind of changes do you want to notice? This may affect the choice of norm to use for the difference between images. Consider using Manhattan norm (the sum of the absolute values) or zero norm (the number of elements not equal to zero) to measure how much the image has changed. The former will tell you how much the image is off, the latter will tell only how many pixels differ.

例子

我假设你的照片对齐得很好,同样的大小和形状,可能曝光不同。为了简单起见,我将它们转换为灰度,即使它们是彩色(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()

现在,您可以将所有这些放在一个脚本中,并针对两个图像运行。如果我们比较image和它本身,没有区别:

$ 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

P.S.完整的compare.py脚本。

更新:相关技术

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

背景减法和分割(用于检测前景对象) 稀疏光流(用于检测运动) 比较直方图或其他统计数据,而不是图像

我强烈建议你看一看“学习OpenCV”的书,第9章(图像部分和分割)和第10章(跟踪和运动)。前者介绍了背景减法,后者介绍了光流法。所有方法都在OpenCV库中实现。如果你使用Python,我建议使用OpenCV≥2.3,以及它的cv2 Python模块。

背景减法最简单的版本:

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

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

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

比较直方图可以帮助检测连续帧之间的突然变化。Courbon等人在2010年使用了这种方法:

连续帧的相似度。测量两个连续帧之间的距离。如果它太高,这意味着第二帧被损坏,因此图像被消除。两帧直方图上的Kullback-Leibler距离,或相互熵: 其中p和q是帧的直方图。阈值固定在0.2。

另一个衡量两张图片相似度的好方法是:

import sys
from skimage.measure import compare_ssim
from skimage.transform import resize
from scipy.ndimage import imread

# get two images - resize both to 1024 x 1024
img_a = resize(imread(sys.argv[1]), (2**10, 2**10))
img_b = resize(imread(sys.argv[2]), (2**10, 2**10))

# score: {-1:1} measure of the structural similarity between the images
score, diff = compare_ssim(img_a, img_b, full=True)
print(score)

如果其他人对更强大的比较图像相似性的方法感兴趣,我将使用Tensorflow测量和可视化相似图像的教程和web应用程序放在一起。

我也遇到了同样的问题,写了一个简单的python模块,使用pillow的ImageChops来比较两个相同大小的图像,创建一个黑白差异图像,并总结直方图值。

你可以直接得到这个分数,也可以得到一个百分比值,与完整的黑白差异进行比较。

它还包含一个简单的is_equal函数,可以在图像传递为相等的情况下(并包括)提供一个模糊阈值。

这种方法不是很详细,但可能对其他正在与相同问题斗争的人有用。

https://pypi.python.org/pypi/imgcompare/

一个简单的解决方案:

将图像编码为jpeg格式,并寻找文件大小的实质性变化。

我曾经用视频缩略图实现过类似的东西,并且取得了很大的成功和可伸缩性。