这是我想做的:
我定期用网络摄像头拍照。就像时间流逝一样。然而,如果没有什么真正的改变,也就是说,图片看起来几乎相同,我不想存储最新的快照。
我想有某种方法可以量化这种差异,我必须根据经验确定一个阈值。
我追求的是简单而不是完美。 我用的是python。
这是我想做的:
我定期用网络摄像头拍照。就像时间流逝一样。然而,如果没有什么真正的改变,也就是说,图片看起来几乎相同,我不想存储最新的快照。
我想有某种方法可以量化这种差异,我必须根据经验确定一个阈值。
我追求的是简单而不是完美。 我用的是python。
当前回答
I have been having a lot of luck with jpg images taken with the same camera on a tripod by (1) simplifying greatly (like going from 3000 pixels wide to 100 pixels wide or even fewer) (2) flattening each jpg array into a single vector (3) pairwise correlating sequential images with a simple correlate algorithm to get correlation coefficient (4) squaring correlation coefficient to get r-square (i.e fraction of variability in one image explained by variation in the next) (5) generally in my application if r-square < 0.9, I say the two images are different and something happened in between.
这是强大的和快速的在我的实现(Mathematica 7)
这是值得玩转的部分,你感兴趣的图像,并通过裁剪所有的图像到那个小区域,否则一个远离相机但重要的变化将被错过。
我不知道如何使用Python,但我确信它也有相关性,不是吗?
其他回答
一种更有原则的方法是使用全局描述符来比较图像,比如GIST或CENTRIST。这里描述的哈希函数也提供了类似的解决方案。
一个简单的解决方案:
将图像编码为jpeg格式,并寻找文件大小的实质性变化。
我曾经用视频缩略图实现过类似的东西,并且取得了很大的成功和可伸缩性。
一般的想法
选项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。
给出的大多数答案都不涉及照明水平。
在进行比较之前,我首先将图像归一化到标准的光照水平。
可以尝试的小事:
将两个图像重新采样为小的缩略图(例如64 x 64),并将缩略图与某个阈值逐像素进行比较。如果原始图像几乎相同,重新采样的缩略图将非常相似,甚至完全相同。这种方法可以处理特别是在低光场景中可能出现的噪音。如果你调成灰度,效果可能会更好。