如何量化两个图像之间的差异?
Posted
技术标签:
【中文标题】如何量化两个图像之间的差异?【英文标题】:How can I quantify difference between two images? 【发布时间】:2010-09-16 10:31:57 【问题描述】:这是我想做的:
我定期用网络摄像头拍照。有点像时间流逝的东西。但是,如果没有真正改变,即图片几乎看起来相同,我不想存储最新的快照。
我想有一些方法可以量化差异,我必须凭经验确定一个阈值。
我正在寻找简单而不是完美。 我正在使用 python。
【问题讨论】:
相关:***.com/questions/25977/… 【参考方案1】:使用 SSIM 测量 2 张图像之间的结构相似性指数度量。
【讨论】:
【参考方案2】:这是我编写的一个函数,它以 2 个图像(文件路径)作为参数,并返回两个图像的像素组件之间的平均差异。这对我来说非常有效,可以确定视觉上“相等”的图像(当它们不 ==
相等时)。
(我发现 8 是确定图像是否基本相同的一个很好的限制。)
(如果不添加预处理,图像必须具有相同的尺寸。)
from PIL import Image
def imagesDifference( imageA, imageB ):
A = list(Image.open(imageA, r'r').convert(r'RGB').getdata())
B = list(Image.open(imageB, r'r').convert(r'RGB').getdata())
if (len(A) != len(B)): return -1
diff = []
for i in range(0, len(A)):
diff += [abs(A[i][0] - B[i][0]), abs(A[i][1] - B[i][1]), abs(A[i][2] - B[i][2])]
return (sum(diff) / len(diff))
【讨论】:
【参考方案3】: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\5.jpg image1\815.jpg image2\6.jpg image1\819.jpg image2\7.jpg image1\900.jpg image2\8.jpg image1\998.jpg image2\9.jpg image1\1012.jpg
示例图片:
815.jpg
5.jpg
【讨论】:
【参考方案4】:有一个使用 numpy 通过计算均方误差的简单快速的解决方案:
before = np.array(get_picture())
while True:
now = np.array(get_picture())
MSE = np.mean((now - before)**2)
if MSE > threshold:
break
before = now
【讨论】:
【参考方案5】:有许多指标可用于评估两张图片的外观/外观。
这里我不会涉及任何代码,因为我认为这应该是一个科学问题,而不是技术问题。
一般来说,这个问题与人类对图像的感知有关,因此每种算法都有其对人类视觉系统特征的支持。
经典方法是:
可见差异预测器:用于评估图像保真度的算法 (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) .
还有更多其他方法。如果您对艺术感兴趣/真正关心艺术,请查看 Google Scholar 并搜索“视觉差异”、“图像质量评估”等内容。
【讨论】:
【参考方案6】:如果回复太晚了,我深表歉意,但由于我一直在做类似的事情,我想我可以以某种方式做出贡献。
也许使用 OpenCV,您可以使用模板匹配。假设您按照您所说的那样使用网络摄像头:
-
简化图像(可能是阈值?)
应用模板匹配并使用 minMaxLoc 检查 max_val
提示:max_val(或 min_val,取决于使用的方法)会给你数字,大数字。要获得百分比差异,请使用与相同图像匹配的模板 - 结果将是您的 100%。
伪代码举例:
previous_screenshot = ...
current_screenshot = ...
# simplify both images somehow
# get the 100% corresponding value
res = matchTemplate(previous_screenshot, previous_screenshot, TM_CCOEFF)
_, hundred_p_val, _, _ = minMaxLoc(res)
# hundred_p_val is now the 100%
res = matchTemplate(previous_screenshot, current_screenshot, TM_CCOEFF)
_, max_val, _, _ = minMaxLoc(res)
difference_percentage = max_val / hundred_p_val
# the tolerance is now up to you
希望对你有帮助。
【讨论】:
【参考方案7】:我在工作中遇到了类似的问题,我正在重写我们的图像转换端点,我想检查新版本产生的输出是否与旧版本相同或几乎相同。所以我写了这个:
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 的项目来说是完美的(我意识到这是一篇非常古老的帖子,但为未来的 ***ers 发布,他们也想在 python 中比较图像)。
【讨论】:
【参考方案8】:另一种衡量两张图片相似度的好方法:
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)
如果其他人对比较图像相似度的更强大方法感兴趣,我将tutorial 和网络app 放在一起,用于使用 Tensorflow 测量和可视化相似图像。
【讨论】:
是的,skimage
非常适合用于此应用程序。我经常使用from skimage.measure import compare_ssim, compare_mse
。 skimage.measure docs.【参考方案9】:
更有原则的方法是使用全局描述符来比较图像,例如 GIST 或 CENTRIST。如here 所述,哈希函数也提供了类似的解决方案。
【讨论】:
【参考方案10】:我遇到了同样的问题,写了一个简单的 python 模块,它使用枕头的 ImageChops 比较两个相同大小的图像以创建黑白差异图像并总结直方图值。
您可以直接获取此分数,也可以获取与全黑与全白差异相比的百分比值。
它还包含一个简单的 is_equal 函数,可以提供一个模糊阈值,低于(包括)图像的通过率。
该方法不是很复杂,但可能对其他在同一问题上苦苦挣扎的人有用。
https://pypi.python.org/pypi/imgcompare/
【讨论】:
【参考方案11】:总体思路
选项 1:将两个图像加载为数组 (scipy.misc.imread
) 并计算逐个元素(逐个像素)的差异。计算差异的范数。
选项 2:加载两个图像。为它们中的每一个计算一些特征向量(如直方图)。计算特征向量而不是图像之间的距离。
但是,首先要做出一些决定。
问题
你应该先回答这些问题:
图像的形状和尺寸是否相同?
如果没有,您可能需要调整它们的大小或裁剪它们。 PIL 库将有助于在 Python 中执行此操作。
如果它们是使用相同的设置和相同的设备拍摄的,它们可能是相同的。
图像是否对齐良好?
如果没有,您可能需要先运行互相关,以首先找到最佳对齐方式。 SciPy 具有执行此操作的功能。
如果相机和场景是静止的,则图像可能对齐良好。
图像的曝光总是一样的吗? (亮度/对比度是否相同?)
如果没有,您可能需要to normalize 图像。
但要小心,在某些情况下,这可能弊大于利。例如,深色背景上的单个明亮像素会使归一化图像非常不同。
颜色信息重要吗?
如果您想注意颜色变化,您将有一个每个点的颜色值向量,而不是灰度图像中的标量值。编写此类代码时需要多加注意。
图像中是否有明显的边缘?他们可能会搬家吗?
如果是,您可以先应用边缘检测算法(例如,使用 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
附:整个compare.py 脚本。
更新:相关技术
由于问题是关于视频序列,其中帧可能几乎相同,并且您寻找不寻常的东西,我想提一些可能相关的替代方法:
背景减法和分割(检测前景物体) 稀疏光流(用于检测运动) 比较直方图或其他一些统计数据而不是图像我强烈建议您阅读“Learning OpenCV”一书,第 9 章(图像部分和分割)和第 10 章(跟踪和运动)。前者教导使用背景减法方法,后者提供一些关于光流方法的信息。所有方法都在 OpenCV 库中实现。如果你使用 Python,我建议使用 OpenCV ≥ 2.3,以及它的cv2
Python 模块。
最简单的背景减法:
学习背景每个像素的平均值 μ 和标准差 σ 将当前像素值与 (μ-2σ,μ+2σ) 或 (μ-σ,μ+σ) 的范围进行比较更高级的版本会考虑每个像素的时间序列并处理非静态场景(如移动的树木或草)。
光流的思想是取两个或更多帧,并将速度向量分配给每个像素(密集光流)或其中一些像素(稀疏光流)。要估计稀疏光流,您可以使用Lucas-Kanade method(它也在 OpenCV 中实现)。显然,如果有很多流(速度场的平均值高于最大值),则表明帧中有些东西在移动,随后的图像会更加不同。
比较直方图可能有助于检测连续帧之间的突然变化。在Courbon et al, 2010中使用了这种方法:
连续帧的相似度。 测量两个连续帧之间的距离。如果它太高,则意味着第二帧被破坏,因此图像被消除。两帧直方图上的Kullback–Leibler distance,即互熵:
其中 p 和 q 是使用帧的直方图。阈值固定为 0.2。
【讨论】:
我在第 44 行 (return (arr-amin)*255/rng
) 上收到了一个 RuntimeWarning: invalid value encountered in double_scalars
,在第 30 行上收到了一个 ValueError: array must not contain infs or NaNs
(z_norm = norm(diff.ravel(), 0)
)
@BioGeek 即如果rng
等于零。只需添加一个检查并设置rng = 1
【参考方案12】:
查看isk-daemon 是如何实现 Haar 小波的。您可以使用它的 imgdb C++ 代码即时计算图像之间的差异:
isk-daemon 是一个开源数据库服务器,能够将基于内容(视觉)的图像搜索添加到任何与图像相关的网站或软件中。
该技术允许任何与图像相关的网站或软件的用户在小部件上绘制他们想要查找的图像,并让网站回复他们最相似的图像,或者只是在每个图像详细信息页面上请求更多相似的照片。
【讨论】:
【参考方案13】:您可以计算两个图像的直方图,然后计算 Bhattacharyya Coefficient,这是一个非常快速的算法,我已经用它来检测板球视频中的镜头变化(在 C 中使用 openCV)
【讨论】:
你能计算图像本身的系数吗? 您必须计算图像的直方图(根据要求使用直方图的 bin 大小)。【参考方案14】:我一直很幸运能在三脚架上使用同一台相机拍摄 jpg 图像 (1) 大大简化(例如从 3000 像素宽到 100 像素宽甚至更少) (2) 将每个 jpg 数组展平为单个向量 (3) 用简单的相关算法对序列图像进行成对相关以获得相关系数 (4) 对相关系数进行平方以获得 r 平方(即一张图像中的可变性分数由下一张图像中的变化解释) (5) 通常在我的应用程序中,如果 r-square
这在我的实现中既健壮又快速(Mathematica 7)
值得玩转您感兴趣的图像部分,并通过将所有图像裁剪到那个小区域来专注于该部分,否则将错过远离相机但重要的变化。
我不知道如何使用 Python,但我确定它也有关联,不是吗?
【讨论】:
【参考方案15】:我正在专门解决如何计算它们是否“足够不同”的问题。我假设您可以弄清楚如何逐个减去像素。
首先,我会拍摄一堆没有任何变化的图像,并找出任何像素变化的最大量,这仅仅是因为捕获的变化、成像系统中的噪声、JPEG 压缩伪影,以及照明的瞬间变化。也许您会发现即使没有任何移动,也会出现 1 或 2 位差异。
那么对于“真正的”测试,您需要这样的标准:
如果最多 P 个像素相差不超过 E,则相同。所以,也许,如果 E = 0.02,P = 1000,这将意味着(大约)如果任何单个像素的变化超过约 5 个单位(假设是 8 位图像),或者如果超过 1000 个像素根本没有任何错误。
这主要是作为一种很好的“分类”技术来快速识别足够接近而无需进一步检查的图像。然后,“失败”的图像可能会更多地使用更精细/更昂贵的技术,例如,如果相机晃动一点,也不会出现误报,或者对光照变化更加稳健。
我运行一个开源项目OpenImageIO,其中包含一个名为“idiff”的实用程序,可以将差异与这样的阈值进行比较(实际上更精细)。即使您不想使用此软件,您也可能需要查看源代码以了解我们是如何做到的。它在商业上使用了很多,并且开发了这种阈值技术,以便我们可以拥有一个用于渲染和图像处理软件的测试套件,其中的“参考图像”可能会因平台而异,或者我们对tha 算法,所以我们想要一个“在容差范围内匹配”的操作。
【讨论】:
【参考方案16】:您可以使用PIL 中的函数比较两个图像。
import Image
import ImageChops
im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")
diff = ImageChops.difference(im2, im1)
diff 对象是一个图像,其中每个像素都是从第一张图像中减去第二张图像中该像素的颜色值的结果。使用差异图像,您可以做几件事。最简单的是diff.getbbox()
函数。它会告诉你包含两个图像之间所有变化的最小矩形。
您也可以使用 PIL 中的函数来实现此处提到的其他内容的近似值。
【讨论】:
我想保存差异图像。表示保存图像差异的差异对象。我该如何保存它? @Anthony 您可以在 diff 对象上调用 save() 来指定图像名称。像这样:diff.save("diff.png") 它会为你保存不同的图像。 注意:这已经改变了导入,它将不再起作用,至少在 python3.10
中测试过,你必须使用from PIL import Image, ImageChops
【参考方案17】:
一个简单的解决方案:
将图像编码为 jpeg 并寻找 filesize 的实质性变化。
我已经用视频缩略图实现了类似的东西,并且取得了很大的成功和可扩展性。
【讨论】:
这是一个非常简单的解决方案,比任何像素比较都要好得多。如果您的网络摄像头图像中有一点点噪点,或者图像移动了一个像素,那么直接比较就会发现所有这些毫无意义的变化。一种更稳健的方法是计算离散余弦变换,然后在频域中比较图像。像这样使用 JPEG 压缩可以获得大部分好处,而无需深入研究傅立叶理论。 喜欢它。尽管其他解决方案也有效,但这对于常见情况有很大的优势:如果您不想保存“基本”图像怎么办?只需将文件大小保存为哈希,然后仅将数字与减法进行比较。就我而言,我有 4 张图像,其中一张非常相似,而其他 3 张完全不同。只需缩放到相同的尺寸,以 jpg 和减去。真的很好。【参考方案18】:我认为您可以简单地计算两个图像的亮度之间的欧几里德距离(即 sqrt(差异平方和,逐个像素)),如果这低于某个经验阈值,则认为它们相等。你最好包装一个 C 函数。
【讨论】:
【参考方案19】:给出的大多数答案都与光照水平无关。
在进行比较之前,我会先将图像标准化为标准光照水平。
【讨论】:
如果您正在拍摄周期性图像并比较相邻的对,您可能可以在有人打开灯后保留第一个。【参考方案20】:小事一试:
将两张图片重新采样为小缩略图(例如 64 x 64),并将缩略图与特定阈值逐个像素进行比较。如果原始图像几乎相同,则重新采样的缩略图将非常相似甚至完全相同。这种方法可以处理尤其是在低光场景中可能出现的噪点。如果你去灰度可能会更好。
【讨论】:
但是你会如何比较像素呢? 获得缩略图后,您可以简单地逐个比较像素。如果您使用颜色,您将计算 RGB 值的“距离”,或者如果您使用灰度,则仅计算灰度之间的差异。 "逐一比较像素"。那是什么意思?如果每像素 64^2 像素测试中的一个失败,测试是否应该失败? 我所说的“将缩略图与某个阈值逐个像素进行比较”的意思是提出一种模糊算法来比较像素。如果计算出的差异(取决于您的模糊算法)超过某个阈值,则图像“不一样”。 非常简单的例子,没有“模糊算法”:并行循环遍历每个像素(将图像#1 的像素#n 与像素#n进行比较> image#2),并将值的差异添加到变量中【参考方案21】:两种流行且相对简单的方法是:(a) 已经提出的欧几里得距离,或 (b) 归一化互相关。与简单的互相关相比,归一化互相关对光照变化的鲁棒性明显更强。***给出了normalized cross-correlation 的公式。也有更复杂的方法,但它们需要更多的工作。
使用类似 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
是二维灰度图像数组。
【讨论】:
图像互相关函数内置于 SciPy (docs.scipy.org/doc/scipy/reference/generated/…) 中,stsci python (stsci.edu/resources/software_hardware/pyraf/stsci_python) 中提供了使用 FFT 的快速版本【参考方案22】:您看过Algorithm for finding similar images 的问题吗?查看它以查看建议。
我建议对您的帧进行小波变换(我已经使用 Haar 变换为此编写了 C 扩展);然后,比较两张图片之间最大(按比例)小波因子的索引,你应该得到一个数值相似性近似。
【讨论】:
【参考方案23】:如何计算两个图像的Manhattan Distance。这给了你 n*n 个值。然后你可以做一些类似行平均的事情来减少到 n 个值,然后用一个函数来得到一个值。
【讨论】:
【参考方案24】:Earth movers distance 可能正是您所需要的。 不过,实时实施可能abit很繁重。
【讨论】:
我真的不觉得这个答案很好解决:“我正在寻找简单而不是完美。我正在使用 python。” 我认为由于这个问题线程获得了大量流量,并且吸引大多数观众的标题是关于如何量化两个图像之间的差异,所以它在这里很有价值。以上是关于如何量化两个图像之间的差异?的主要内容,如果未能解决你的问题,请参考以下文章