如何量化两个图像之间的差异?
image-processing
python
5
0

这是我想做的:

我定期使用网络摄像头拍照。有点像延时的东西。但是,如果什么都没有真正改变,即图片看起来几乎相同,则我不想存储最新的快照。

我想象有某种量化差异的方法,而我将不得不凭经验确定阈值。

我在寻找简单而不是完美。我正在使用python。

参考资料:
Stack Overflow
收藏
评论
共 10 个回答
高赞 时间 活跃

两种流行且相对简单的方法是:(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) )

假设i1i2是2D灰度图像阵列。

收藏
评论

大概的概念

选项1:将两个图像都加载为数组( scipy.misc.imread ),然后计算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

如何比较。 img1img2是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,我建议使用cv2及其cv2 Python模块。

最简单的背景减法版本:

  • 了解背景每个像素的平均值μ和标准偏差σ
  • 比较当前像素值到(μ-2σ,μ+2σ)或(μ-σ,μ+σ)的范围

更高级的版本会考虑每个像素的时间序列,并处理非静态场景(例如移动的树木或草地)。

光流的想法是拍摄两个或更多帧,并将速度矢量分配给每个像素(密集光流)或分配给其中一些像素(稀疏光流)。要估计稀疏的光流,可以使用Lucas-Kanade方法 (它也在OpenCV中实现)。显然,如果有很多流量(在速度场的最大值上具有较高的平均值),则表示帧中正在移动某些内容,并且后续图像会有所不同。

比较直方图可能有助于检测连续帧之间的突然变化。 Courbon等人,2010年使用了这种方法:

连续帧的相似性。测量两个连续帧之间的距离。如果它太高,则意味着第二帧已损坏,因此图像被消除。两帧直方图上的Kullback-Leibler距离或互熵:

$$ d(p,q)= \ sum_i p(i)\ log(p(i)/ q(i))$$

其中pq是帧的直方图。阈值固定为0.2。

收藏
评论

给出的大多数答案都不会涉及照明水平。

在进行比较之前,我首先将图像标准化为标准亮度。

收藏
评论

您可以使用PIL的功能比较两个图像。

import Image
import ImageChops

im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")

diff = ImageChops.difference(im2, im1)

diff对象是一个图像,其中每个像素都是从第一图像减去第二图像中该像素的颜色值的结果。使用差异图像,您可以做几件事。最简单的一个是diff.getbbox()函数。它将告诉您包含两个图像之间所有更改的最小矩形。

您可能也可以使用PIL的函数来实现此处提到的其他内容的近似值。

收藏
评论

我在工作中遇到了类似的问题,我正在重写图像转换端点,所以我想检查新版本是否与旧版本产生相同或几乎相同的输出。所以我这样写:

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发布)。

收藏
评论

一个简单的解决方案:

将图像编码为jpeg,并在filesize中寻找实质性的变化。

我已经用视频缩略图实现了类似的功能,并且取得了很多成功和可扩展性。

收藏
评论

您是否看到过寻找相似图像算法问题?检查一下以查看建议。

我建议对您的帧进行小波变换(我使用Haar变换为此编写了C扩展);然后,比较两个图片之间最大的(按比例)小波因子的索引,您应该得到数值相似度近似值。

收藏
评论

我专门解决的问题是如何计算它们是否“足够不同”。我假设您可以弄清楚如何一一减去像素。

首先,我将拍摄一堆没有任何变化的图像,并找出仅由于捕获的变化,成像系统中的噪声,JPEG压缩伪像以及照明的瞬时变化而导致的任何像素变化的最大值。 。也许您会发现,即使什么都没有发生,也可能会有1或2位的差异。

然后,对于“真实”测试,您需要这样的条件:

  • 如果最多P个像素相差不超过E,则相同。

因此,也许,如果E = 0.02,P = 1000,则(大约)意味着,如果任何单个像素变化超过约5个单位(假设为8位图像),或者如果超过1000个,它将是“不同的”像素根本没有任何错误。

这主要是作为一种良好的“分类”技术,可以快速识别足够接近不需要进一步检查的图像。然后,“失败”的图像可能更多地是一种更为复杂/昂贵的技术,例如,如果相机晃动了一点,或者对照明的变化更鲁棒,则不会出现误报。

我运行一个开源项目OpenImageIO ,其中包含一个名为“ idiff”的实用程序,该实用程序将差异与类似的阈值进行比较(实际上更加精细)。即使您不想使用该软件,也可能需要查看源代码来了解我们是如何做到的。它已在商业上大量使用,并且开发了此阈值技术,以便我们可以拥有一个用于渲染和图像处理软件的测试套件,其“参考图像”可能与平台之间或平台之间的细微差异有所不同。 tha算法,因此我们需要“公差内匹配”操作。

收藏
评论

尝试的琐碎事情:

将两个图像重新采样为较小的缩略图(例如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)

如果其他人对比较图像相似性的更强大方法感兴趣,我会组装一个教程和Web 应用程序,以使用Tensorflow测量和可视化相似图像。

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号