以下是我想做的事情:
我想定期使用网络摄像头拍照片。有点像时间流逝的东西。但是,如果没有真正发生改变,也就是说,图片基本上看起来相同,我不想存储最新的快照。
我想象有一种量化差异的方法,我将不得不凭经验确定阈值。
我寻求简单而非完美。 我正在使用Python。
以下是我想做的事情:
我想定期使用网络摄像头拍照片。有点像时间流逝的东西。但是,如果没有真正发生改变,也就是说,图片基本上看起来相同,我不想存储最新的快照。
我想象有一种量化差异的方法,我将不得不凭经验确定阈值。
我寻求简单而非完美。 我正在使用Python。
选项1:将两张图像加载为数组(scipy.misc.imread
),然后计算元素间的差异(按像素计算)。计算差异的范数。
选项2:加载两张图像。为每个图像计算某种特征向量(例如直方图)。计算特征向量之间的距离而不是图像。
但是,首先需要做出一些决定。
您应该首先回答以下问题:
图像是否具有相同的形状和尺寸?
如果没有,则可能需要调整它们的大小或裁剪它们。PIL库可以在Python中帮助您完成这项工作。
如果它们使用相同的设置和相同的设备拍摄,则它们可能是相同的。
图像是否对齐良好?
如果没有,则您可能需要首先运行交叉关联,以首先找到最佳对齐方式。SciPy具有执行此操作的功能。
如果相机和场景静止,则图像可能对齐良好。
图像曝光是否始终相同?(亮度/对比度是否相同?)
如果不是,则可能需要规范化图像。
但要小心,在某些情况下,这可能会比有益更加错误。例如,暗背景上的单个亮像素将使规范化的图像非常不同。
颜色信息是否重要?
如果要注意颜色变化,则每个点将具有一个颜色值向量,而不是灰度图像中的标量值。编写此类代码时需要更多注意。
图像中是否存在明显的边缘?它们可能会移动吗?
如果是,则可以首先应用边缘检测算法(例如,使用Sobel或Prewitt变换计算梯度,应用一些阈值),然后将第一张图像上的边缘与第二张上的边缘进行比较。
图像中是否存在噪声?
所有传感器都会使图像受到某些噪声污染。低成本传感器具有更多噪声。在比较图像之前,您可能希望应用一些噪声减少。模糊是最简单(但不是最好)的方法。
您希望注意到什么样的更改?
这可能会影响用于图像之间差异的范数选择。
考虑使用曼哈顿范数(绝对值之和)或零范数(不等于0的元素数)来测量图像的变化程度。前者将告诉您图像偏离多少,而后者仅告诉有多少像素不同。
我假设您的图像对齐良好,具有相同的大小和形状,可能具有不同的曝光。为简单起见,即使它们是彩色(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
返回一个三维数组,平均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
P.S. 整个 compare.py 脚本。
因为问题涉及到视频序列,其中帧很可能几乎相同,你在寻找异常,我想提及一些可能相关的替代方法:
我强烈推荐看一下 “Learning OpenCV” 书,第9章(图像部分和分割)和第10章(跟踪和运动)。前者教你使用背景减法方法,后者给出了一些关于光流方法的信息。所有方法都在OpenCV库中实现。如果您使用Python,我建议使用OpenCV≥2.3及其cv2
Python模块。
背景减法的最简单版本:
更高级的版本可能会考虑每个像素的时间序列,处理非静态场景(如移动的树木或草地)。
光流的思想是取两个或多个帧,并为每个像素分配速度向量(密集光流)或其中一些像素(稀疏光流)。要估计稀疏光流,可以使用Lucas-Kanade方法(它也在OpenCV中实现)。显然,如果有很多流(速度场的最大值超过平均值),那么帧中有东西在移动,后续图像将更加不同。
比较直方图可以帮助检测连续帧之间的突变。这种方法在Courbon等人, 2010中使用:
连续帧的相似性。测量两帧之间的距离。如果太高,则意味着第二帧已损坏,因此该图像被消除。使用两个帧的直方图的Kullback-Leibler距离或互熵:
其中p和q是帧的直方图。阈值固定为0.2。
RuntimeWarning: invalid value encountered in double_scalars
,并且在第30行收到了一个ValueError: array must not contain infs or NaNs
。第44行代码为return (arr-amin)*255/rng
,第30行代码为z_norm = norm(diff.ravel(), 0)
。 - BioGeekrng
等于零。只需添加一个检查并将rng
设置为1。 - haisi一个简单的解决方法:
将图片编码为 jpeg 格式,并查看文件大小是否有大幅度变化。
我曾经在视频缩略图上实现过类似的方法,获得了很大的成功和可扩展性。
cv2.imencode('jpg', img)
。 - PangolinPawsimport Image
import ImageChops
im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")
diff = ImageChops.difference(im2, im1)
diff对象是一张图像,其中每个像素的值都是第一张图片对应像素颜色值减去第二张图片对应像素颜色值的结果。使用diff图像,你可以做很多事情。最简单的方法是使用diff.getbbox()
函数,它将告诉你包含两张图片之间所有变化的最小矩形。你也可以使用PIL库的其他函数来实现这里提到的其他功能的近似版本。3.10
中测试过,你必须使用 from PIL import Image, ImageChops
。 - nck两种流行且相对简单的方法是:(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),并使用一定的阈值逐像素比较缩略图。如果原始图片几乎相同,那么重新采样的缩略图将非常相似甚至完全相同。这种方法可以处理低光场景中可能出现的噪点。如果你转为灰度,效果可能会更好。
另一种简单好用的测量两张图片相似度的方法:
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 应用程序。
skimage
非常适合用于这个应用。我经常使用from skimage.measure import compare_ssim, compare_mse
。skimage.measure文档。 - ximikicompare_ssim
现在是structural_similarity
。请使用from skimage.metrics import structural_similarity
和from imageio import imread
。 - shredEngineer大部分给出的答案都没有涉及照明水平。
在进行比较之前,我首先会将图像归一化到标准光照水平。
你是否看过寻找相似图像的算法问题?可以查看一下以获取建议。
我建议对帧进行小波变换(我使用哈尔变换编写了一个C扩展程序),然后比较两张图片中最大(按比例计算)小波因子的索引,这样应该能得到数值上的相似度近似值。