这是我想做的:
我定期用网络摄像头拍照。就像时间流逝一样。然而,如果没有什么真正的改变,也就是说,图片看起来几乎相同,我不想存储最新的快照。
我想有某种方法可以量化这种差异,我必须根据经验确定一个阈值。
我追求的是简单而不是完美。 我用的是python。
这是我想做的:
我定期用网络摄像头拍照。就像时间流逝一样。然而,如果没有什么真正的改变,也就是说,图片看起来几乎相同,我不想存储最新的快照。
我想有某种方法可以量化这种差异,我必须根据经验确定一个阈值。
我追求的是简单而不是完美。 我用的是python。
当前回答
看看Haar小波是如何由isk-daemon实现的。你可以使用它的imgdb c++代码来实时计算图像之间的差异:
disk -daemon是一个开源的数据库服务器,能够将基于内容的(可视的)图像搜索添加到任何与图像相关的网站或软件。 这项技术允许任何与图像相关的网站或软件的用户在小部件上绘制他们想要查找的图像,并让网站回复他们最相似的图像或简单地在每个图像详细页面请求更多相似的照片。
其他回答
有很多指标可以用来评估两张图片是否像/有多像。
这里我就不讲代码了,因为我认为这应该是一个科学问题,而不是技术问题。
一般来说,问题与人类对图像的感知有关,因此每种算法都有其对人类视觉系统特征的支持。
经典方法有:
可见差异预测器:一种评估图像保真度的算法(https://www.spiedigitallibrary.org/conference-proceedings-of-spie/1666/0000/Visible-differences-predictor--an-algorithm-for-the-assessment-of/10.1117/12.135952.short?SSO=1)
图像质量评估:从错误可见性到结构相似性(http://www.cns.nyu.edu/pub/lcv/wang03-reprint.pdf)
FSIM:一种用于图像质量评估的特征相似度指数(https://www4.comp.polyu.edu.hk/~cslzhang/IQA/TIP_IQA_FSIM.pdf)
其中,SSIM (Image Quality Assessment: From Error Visibility to Structural Similarity)是最容易计算的,其开销也较小,另一篇论文《基于梯度相似度的图像质量评估》(https://www.semanticscholar.org/paper/Image-Quality-Assessment-Based-on-Gradient-Liu-Lin/2b819bef80c02d5d4cb56f27b202535e119df988)也有报道。
还有很多其他的方法。如果你对艺术感兴趣或真正关心,可以在谷歌Scholar上搜索“视觉差异”、“图像质量评估”等。
两种流行且相对简单的方法是:(a)已经提出的欧几里得距离,或(b)标准化互相关。与简单的互相关相比,归一化互相关对光照变化的影响明显更强。维基百科给出了一个标准化互相关的公式。更复杂的方法也存在,但它们需要更多的工作。
使用numpy-like语法,
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为二维灰度图像阵列。
一般的想法
选项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。
如何计算这两幅图像的曼哈顿距离呢?得到n*n个值。然后你可以做一些事情,比如行平均,把值减少到n个,然后再用一个函数得到一个值。
import os
from PIL import Image
from PIL import ImageFile
import imagehash
#just use to the size diferent picture
def compare_image(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
#through picturu hash to compare
def get_hash_dict(dir):
hash_dict = {}
image_quantity = 0
for _, _, files in os.walk(dir):
for i, fileName in enumerate(files):
with open(dir + fileName, 'rb') as fp:
hash_dict[dir + fileName] = imagehash.average_hash(Image.open(fp))
image_quantity += 1
return hash_dict, image_quantity
def compare_image_with_hash(image_file_name_1, image_file_name_2, max_dif=0):
"""
max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.
recommend to use
"""
ImageFile.LOAD_TRUNCATED_IMAGES = True
hash_1 = None
hash_2 = None
with open(image_file_name_1, 'rb') as fp:
hash_1 = imagehash.average_hash(Image.open(fp))
with open(image_file_name_2, 'rb') as fp:
hash_2 = imagehash.average_hash(Image.open(fp))
dif = hash_1 - hash_2
if dif < 0:
dif = -dif
if dif <= max_dif:
return True
else:
return False
def compare_image_dir_with_hash(dir_1, dir_2, max_dif=0):
"""
max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.
"""
ImageFile.LOAD_TRUNCATED_IMAGES = True
hash_dict_1, image_quantity_1 = get_hash_dict(dir_1)
hash_dict_2, image_quantity_2 = get_hash_dict(dir_2)
if image_quantity_1 > image_quantity_2:
tmp = image_quantity_1
image_quantity_1 = image_quantity_2
image_quantity_2 = tmp
tmp = hash_dict_1
hash_dict_1 = hash_dict_2
hash_dict_2 = tmp
result_dict = {}
for k in hash_dict_1.keys():
result_dict[k] = None
for dif_i in range(0, max_dif + 1):
have_none = False
for k_1 in result_dict.keys():
if result_dict.get(k_1) is None:
have_none = True
if not have_none:
return result_dict
for k_1, v_1 in hash_dict_1.items():
for k_2, v_2 in hash_dict_2.items():
sub = (v_1 - v_2)
if sub < 0:
sub = -sub
if sub == dif_i and result_dict.get(k_1) is None:
result_dict[k_1] = k_2
break
return result_dict
def main():
print(compare_image('image1\\815.jpg', 'image2\\5.jpg'))
print(compare_image_with_hash('image1\\815.jpg', 'image2\\5.jpg', 7))
r = compare_image_dir_with_hash('image1\\', 'image2\\', 10)
for k in r.keys():
print(k, r.get(k))
if __name__ == '__main__':
main()
输出: 假 真正的 image2 jpg image1 5. \ \ 815. jpg image2 jpg image1 6. \ \ 819. jpg image2 jpg image1 7. \ \ 900. jpg image2 jpg image1 8. \ \ 998. jpg image2 jpg image1 9. \ \ 1012. jpg 示例图片: 815. jpg 5. jpg