这就是我想做的事情:
我定期用网络摄像头拍照.有点像时间流逝的事情.但是,如果没有真正改变,也就是说,图片几乎看起来一样,我不想存储最新的快照.
我想有一些量化差异的方法,我必须凭经验确定一个阈值.
我在寻求简洁而不是完美.我正在使用python.
选项1:将两个图像加载为数组(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,我建议使用OpenCV≥2.3及其cv2
Python模块.
最简单的背景减法版本:
学习背景的每个像素的平均值μ和标准偏差σ
将当前像素值与(μ-2σ,μ+2σ)或(μ-σ,μ+σ)的范围进行比较
更高级的版本会考虑每个像素的时间序列并处理非静态场景(如移动树木或草地).
光流的想法是采用两个或更多帧,并将速度矢量分配给每个像素(密集光流)或其中一些(稀疏光流).要估计稀疏光流,您可以使用Lucas-Kanade方法(它也在OpenCV中实现).显然,如果存在大量流量(速度场的最大值高于平均值),那么帧中的某些东西会移动,后续图像会更加不同.
比较直方图可以帮助检测连续帧之间的突然变化.这种方法在Courbon等人,2010年使用:
连续帧的相似性.测量两个连续帧之间的距离.如果它太高,则意味着第二帧被破坏,因此图像被消除.所述的Kullback-Leibler距离,或互熵,对两帧的直方图:
其中p和q是帧的直方图.阈值固定为0.2.
简单的解决方案:
将图像编码为jpeg并查找文件大小的重大更改.
我已经实现了类似于视频缩略图的东西,并且具有很多成功和可扩展性.
您可以使用PIL中的功能比较两个图像.
import Image import ImageChops im1 = Image.open("splash.png") im2 = Image.open("splash2.png") diff = ImageChops.difference(im2, im1)
diff对象是这样的图像,其中每个像素是从第一图像中减去第二图像中的该像素的颜色值的结果.使用差异图像,您可以做几件事.最简单的就是diff.getbbox()
功能.它将告诉您包含两个图像之间所有更改的最小矩形.
您可以使用PIL中的函数实现此处提到的其他内容的近似值.
两种流行且相对简单的方法是:(a)已经建议的欧几里德距离,或(b)归一化的互相关.与简单的互相关相比,归一化的互相关对于光照变化往往更加稳健.维基百科给出了归一化互相关的公式.也存在更复杂的方法,但它们需要更多的工作.
使用类似numpy的语法,
dist_euclidean = sqrt(sum((i1 - i2)^2)) / i1.size dist_manhattan = sum(abs(i1 - i2)) / i1.size dist_ncc = sum( (i1 - mean(i1)) * (i2 - mean(i2)) ) / ( (i1.size - 1) * stdev(i1) * stdev(i2) )
假定i1
和i2
是2D灰度图像阵列.
尝试一件小事:
将两个图像重新采样为小缩略图(例如64 x 64),并将缩略图逐个像素地与特定阈值进行比较.如果原始图像几乎相同,则重新采样的缩略图将非常相似或甚至完全相同.该方法处理可能发生的噪声,尤其是在低光场景中.如果你去灰度可能会更好.
我正在具体解决如何计算它们是否"足够不同"的问题.我假设你可以弄清楚如何逐个减去像素.
首先,我会拍摄一堆没有变化的图像,并找出任何像素变化的最大量,因为捕获的变化,成像系统中的噪声,JPEG压缩伪像以及照明中的瞬间变化.也许你会发现,即使没有任何动作,也会有1或2位的差异.
然后,对于"真实"测试,您需要这样的标准:
同样,如果P像素相差不超过E.
所以,也许,如果E = 0.02,P = 1000,这意味着(大约)如果任何单个像素变化超过~5个单位(假设8位图像),或者如果超过1000个,那么它将是"不同的"像素有任何错误.
这主要是为了快速识别足够接近不需要进一步检查的图像的良好"分类"技术.然后,"失败"的图像可能更多地是更精细/昂贵的技术,例如,如果相机抖动位,则不会产生误报,或者对于照明变化更加稳健.
我运行一个开源项目OpenImageIO,它包含一个名为"idiff"的实用程序,它将差异与这样的阈值进行比较(实际上更精细).即使您不想使用此软件,您也可以查看源代码以了解我们是如何使用该软件的.它在商业上得到了很大的应用,并开发了这种阈值技术,以便我们可以拥有一个用于渲染和图像处理软件的测试套件,其中"参考图像"可能与平台到平台之间存在细微差别,或者我们对其进行了小幅调整.算法,所以我们想要一个"容忍内匹配"操作.
给出的大多数答案都不涉及照明水平.
在进行比较之前,我首先将图像标准化为标准光照水平.
我在工作时遇到类似的问题,我正在重写图像转换端点,并且想检查新版本与旧版本产生的输出相同或几乎相同。所以我这样写:
https://github.com/nicolashahn/diffimg
它在相同大小的图像上并且在每个像素级别上运行,测量每个通道上的值差:R,G,B(,A),取这些通道的平均差,然后对所有像素,并返回比率。
例如,对于一个10x10的白色像素图像,而同一图像但一个像素变为红色,则该像素的差为1/3或0.33 ...(RGB 0,0,0与255,0,0 ),其他所有像素均为0。总共100像素,则0.33 ... / 100 =图像差异约0.33%。
我相信这对于OP的项目将是完美的(我意识到这是一个非常老的帖子,但是为将来想要也比较python中的图像的StackOverflowers发布)。