我怎样才能量化两个图像之间的差异?
这是我想要做的:
我正在定期用摄像头拍照。 有点像时间stream逝的东西。 但是,如果没有什么变化,也就是说,图片几乎看起来一样,我不想存储最新的快照。
我想有一些量化差异的办法,我必须经验地确定一个门槛。
我在寻找简单而不是完美。 我正在使用python。
大概的概念
选项1:将两个图像加载为数组( scipy.misc.imread
),并计算逐个像素(pixel-by-pixel)的差异。 计算差异的标准。
选项2:加载两个图像。 计算每个特征向量(如直方图)。 计算特征向量之间的距离而不是图像。
但是,有一些决定要先做。
问题
你应该首先回答这些问题:
-
是相同的形状和尺寸的图像?
否则,您可能需要resize或裁剪它们。 PIL库将帮助在Python中做到这一点。
如果他们采用相同的设置和相同的设备,他们可能是相同的。
-
图像是否alignment?
如果不是,则可能需要首先运行互相关,以便首先find最佳alignment方式。 SciPy有这样做的function。
如果相机和场景仍然存在,图像可能会很好地alignment。
-
图像的曝光总是一样的吗? (亮度/对比度是否一样?)
如果没有,你可能想要规范图像。
但要小心,在某些情况下,这可能会做比错误更多的错误。 例如,黑暗背景上的单个亮像素会使归一化的图像非常不同。
-
色彩信息重要吗?
如果你想注意到颜色的变化,你将有一个向量,每个点的颜色值,而不是像在灰度图像的标量值。 编写这样的代码时需要更多的注意力。
-
图像中有明显的边缘吗? 他们可能会移动吗?
如果是,则可以先应用边缘检测algorithm(例如,使用Sobel或Prewitt变换计算梯度,应用一些阈值),然后比较第一个图像上的边缘和第二个边缘上的边缘。
-
图像中是否有噪音?
所有传感器都会对图像造成一定程度的噪音污染。 低成本的传感器有更多的噪音。 比较图像之前,您可能希望应用一些降噪function。 模糊是最简单的(但不是最好的)方法。
-
你想注意什么样的改变?
这可能会影响用于图像之间差异的标准的select。
考虑使用曼哈顿范数(绝对值的总和)或零范数(不等于零的元素的数量)来测量图像已经改变了多less。 前者会告诉你多less图像是closures的,后者只会告诉有多less像素不同。
例
我认为你的图像是完全一致的,相同的大小和形状,可能有不同的曝光。 为了简单起见,即使它们是彩色(RGB)图像,我也将它们转换为灰度。
您将需要这些导入:
import sys from scipy.misc import imread from scipy.linalg import norm from scipy import sum, average
主要function:读取两幅图像,转换成灰度图,比较和打印结果:
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
标准化是微不足道的,你可以select标准化为[0,1]而不是[0,255]。 arr
在这里是一个SciPy数组,所以所有的操作都是元素明智的:
def normalize(arr): rng = arr.max()-arr.min() amin = arr.min() return (arr-amin)*255/rng
运行main
function:
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脚本。
更新:相关技术
由于这个问题是关于一个video序列的,那里的帧几乎是相同的,而你寻找不寻常的东西,我想提一些可能相关的替代方法:
- 背景减法和分割(检测前景物体)
- 稀疏光stream(检测运动)
- 比较直方图或其他统计数据而不是图像
我强烈build议看看“学习OpenCV”书,第9章(图像部分和分割)和10(跟踪和议案)。 前者教导使用背景减法方法,后者给出一些关于光stream方法的信息。 所有的方法都在OpenCV库中实现。 如果你使用Python,我build议使用cv2
,以及它的Python Python模块。
背景减法最简单的版本:
- 学习背景每个像素的平均值μ和标准偏差σ
- 将当前像素值与(μ-2σ,μ+2σ)或(μ-σ,μ+σ)的范围进行比较,
更高级的版本考虑到每个像素的时间序列,并处理非静态场景(如移动树木或草地)。
光stream的思想是采取两帧或更多帧,并为每个像素(密集光stream)或其中的一些(稀疏光stream)分配速度vector。 为了估计稀疏的光stream,你可以使用Lucas-Kanade方法 (它也在OpenCV中实现)。 显然,如果有很多的stream动(速度场的最大值的高平均值),那么在框架中有东西在移动,并且随后的图像会更加不同。
比较直方图可能有助于检测连续帧之间的突然变化。 Courbon等人在2010年采用了这种方法:
连续帧的相似性。 测量两个连续帧之间的距离。 如果太高,则意味着第二帧被破坏,因此图像被消除。 两个帧的直方图上的Kullback-Leibler距离或相互熵:
其中p和q是使用帧的直方图。 门槛固定在0.2。
简单的解决scheme:
将图像编码为jpeg,然后查找文件大小的实质性更改。
我已经实现了一些类似的video缩略图,并取得了很大的成功和可扩展性。
您可以使用PIL中的函数比较两个图像。
import Image import ImageChops im1 = Image.open("splash.png") im2 = Image.open("splash2.png") diff = ImageChops.difference(im2, im1)
diff对象是其中每个像素是从第一图像中减去第二图像中的该像素的颜色值的结果的图像。 使用diff图像,你可以做几件事情。 最简单的是diff.getbbox()
函数。 它会告诉你包含你的两个图像之间的所有变化的最小矩形。
你也可以使用PIL中的函数来实现这里提到的其他东西的近似值。
两种stream行和相对简单的方法是:(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
是二维灰度图像数组。
一个微不足道的事情来尝试:
将两个图像重新采样为小缩略图(例如64 x 64),并将缩略图逐像素地与某个阈值进行比较。 如果原始图像几乎相同,重新采样的缩略图将非常相似,甚至完全相同。 这种方法照顾可能发生的噪点,尤其是在昏暗的场景中。 如果你灰度级,可能会更好。
我正在具体解决如何计算它们是否“足够不同”的问题。 我假设你可以弄清楚如何逐个减去像素。
首先,我会拍摄一大堆没有任何变化的图像,然后找出由于拍摄变化,成像系统中的噪声,JPEG压缩伪影以及光照瞬间变化而导致的任何像素变化的最大量。 也许你会发现,即使在没有任何动作的情况下,也可能会有1或2位的差异。
那么对于“真正的”testing,你需要一个这样的标准:
- 相同的,如果高达P像素相差不超过E.
因此,如果E = 0.02,P = 1000,那么如果任何单个像素的变化超过〜5个单位(假设是8位图像),或者如果超过1000个像素有任何错误。
这主要是作为一个很好的“分类”技术,以快速识别足够接近的图像,不需要进一步检查。 那么“失败”的图像可能更多地是更复杂/更昂贵的技术,例如,如果相机摇晃,则不会有误报,或者对照明变化更稳健。
我运行了一个开源项目OpenImageIO ,它包含一个名为“idiff”的实用程序,将差异与阈值进行比较(实际上更加详细)。 即使你不想使用这个软件,你也可以看看源代码,看看我们是如何做到的。 它的商业用途相当多,这个阈值技术的开发,使我们可以有一个testing套件的渲染和image processing软件,“参考图像”,可能有一个小的差异,从平台到平台,或者我们做了小的调整algorithm,所以我们想要一个“在容差范围内匹配”的操作。
大多数答案不会涉及照明水平。
在进行比较之前,我会首先将图像标准化为标准光照水平。
推土机的距离可能正是你所需要的。 然而,实时实施可能有些沉重。
怎么计算两幅图像的曼哈顿距离 。 这给你n * n个值。 然后,你可以做一些行平均减less到n值和一个函数,以获得一个单一的值。
你见过find类似图像的algorithm问题吗? 检查出来看看build议。
我会build议你的帧的小波变换(我已经写了一个使用哈尔变换的C扩展); 然后,比较两幅图像之间最大(比例)小波因子的指标,应该得到数值相似性近似。
我最近在Processing.org上阅读了这篇文章,发现它藏在我的最爱中。 也许它可以帮助你…
http://processing.org/discourse/yabb_beta/YaBB.cgi?board=Video;action=display;num=1159141301
(1)大幅简化(如从3000像素宽到100像素宽甚至更less)(2)将每个jpg数组平整成一个单一的向量(3)将序列图像与简单相关algorithm成对关联以获得相关系数(4)平方相关系数以获得r平方(即,通过下一个变化解释的一个图像中的可变性分数)(5)通常在我的应用如果r <0.9,我说这两个图像是不同的,而且发生了什么。
这在我的实现中是强大而快速的(Mathematica 7)
这是值得玩的感兴趣的图像的一部分,通过裁剪所有图像集中在那个小区域,否则遥远的相机,但重要的变化将被错过。
我不知道如何使用Python,但肯定它也有相关性,不是吗?
你可以计算两个图像的直方图,然后计算Bhattacharyya系数 ,这是一个非常快的algorithm,我用它来检测在板球video中的镜头变化(在C中使用openCV)
看看如何使用isk-daemon实现Haar小波。 你可以使用它的imgdb C ++代码来实时计算图像之间的差异:
isk-daemon是一个开源的数据库服务器,能够将基于内容的(可视化)图像search添加到任何图像相关的网站或软件。
该技术允许任何图像相关网站或软件的用户在小部件上勾画出他们想要find的图像,并使网站向他们回答最相似的图像,或者只是在每个图像细节页面处请求更多类似的照片。
我有同样的问题,并写了一个简单的Python模块比较使用枕头的ImageChops两个相同大小的图像创build一个黑色/白色差异图像和总结直方图值。
你可以直接得到这个分数,或者是一个比较完整的黑色与白色差异的百分比值。
它还包含一个简单的is_equal函数,可以提供一个模糊阈值(包括)图像传递相等。
这个方法不是非常精细,但也许对于那些在同一个问题上挣扎的人来说也是有用的。
我想你可以简单地计算两幅图像的亮度之间的欧几里得距离(即sqrt(差异的平方和)之和),如果它落在某个经验阈值之下,则认为它们是相等的。 而且你最好做它包装一个C函数。
#encoding:utf8 from PIL import Image from PIL import ImageFile def equal(img_file1, img_file2): if img_file1 == img_file2: return True fp1 = open(img_file1,'rb') fp2 = open(img_file2,'rb') img1 = Image.open(fp1) img2 = Image.open(fp2) ImageFile.LOAD_TRUNCATED_IMAGES = True b = img1 == img2 fp1.close() fp2.close() return b