使用Python和PIL计算两张图片的均方根差异

6
我需要一个类似于此处找到的函数:http://effbot.org/zone/pil-comparing-images.htm ,它可以计算两个图像之间的均方根差异。代码如下:
import ImageChops
import math, operator

def rmsdiff(im1, im2):
    "Calculate the root-mean-square difference between two images"

    h = ImageChops.difference(im1, im2).histogram()

    # calculate rms
    return math.sqrt(reduce(operator.add,
        map(lambda h, i: h*(i**2), h, range(256))
        ) / (float(im1.size[0]) * im1.size[1]))

尝试运行此代码会导致以下错误:TypeError:不支持的操作数类型 ** 或 pow():'NoneType' 和 'int'。这是什么问题?
5个回答

5
问题是创建了一个直方图,其中没有值(或者真正的None值),在没有相应像素值的地方。
例如,当您找到两个图像之间的差异时,生成的图像不具有任何像素,比如说,43个单位,因此h [43] = None。
稍后,您尝试访问范围(256)中每个亮度的像素数,并将其平方,这导致它对None ** 2的含义感到困惑。
考虑将 range(256)更改为 h.keys()
此外,您正在使用h表示两个不同的内容,请考虑更改一个名称,或更好的是,将两个名称更改为有意义的名称。

3

看起来这里并不真正需要使用mapreduce

rmsdiff的改进版本可能如下:

def rmsdiff(im1, im2):
    "Calculate the root-mean-square difference between two images"
    diff = ImageChops.difference(im1, im2)
    h = diff.histogram()
    sq = (value*((idx%256)**2) for idx, value in enumerate(h))
    sum_of_squares = sum(sq)
    rms = math.sqrt(sum_of_squares/float(im1.size[0] * im1.size[1]))
    return rms

这里是源码。Mark Krautheim提出的改良在我的测试中至少有一个重要原因:与原版本相反,当比较一张图像与其本身时,它会返回0.0。


2
野蛮猜测一下,但请尝试在您的最后一行中使用此代码,并查看是否有效:
return math.sqrt(sum(h*(i**2) for i, h in enumerate(h))) / (float(im1.size[0]) * im1.size[1]))

我不确定为什么会出现您描述的TypeError,但如果您使用上面的代码行并仍然出现此错误,那么肯定发生了非常奇怪的事情。


0
考虑使用现有的解决方案,例如scikit-image来解决这个问题:
from PIL import Image # No need for ImageChops
import math
from skimage import img_as_float
from skimage.measure import compare_mse as mse

def rmsdiff(im1, im2):
    """Calculates the root mean square error (RSME) between two images"""
    return math.sqrt(mse(img_as_float(im1), img_as_float(im2)))

或者你可以使用NumPy自己编写一些简短的内容:

from PIL import Image, ImageChops
import math
import numpy as np

def rmsdiff(im1, im2):
    """Calculates the root mean square error (RSME) between two images"""
    errors = np.asarray(ImageChops.difference(im1, im2)) / 255
    return math.sqrt(np.mean(np.square(errors)))

请注意,这两种方法都将像素强度视为范围为[0.0, 1.0]而不是[0, 255]。

0
查看这里https://gist.github.com/bo858585/5377492 。 该脚本将用户目录(指定)中的所有jpg图像按其相似性分组,使用两个比较图像对应位置(在20 x 20矩阵上)的每一对相应像素之间的均方根(不分成sqrt(3) - 像素是3数字RGB向量)来计算它们之间的差异。脚本汇总这些像素对之间的距离,并将此总和除以最大可能距离 - 这样脚本就得到了两个图像相似性的百分比。在将所有图像调整为20 * 20之前进行比较。您可以改变MAX_DISTANCE(从0到400),脚本将把更相似或更不相似的图像分组到一个组中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接