简体   繁体   English

使用Python和PIL的两个图像之间的均方差

[英]Root mean square difference between two images using Python and PIL

I need to have a function like the one found here: http://effbot.org/zone/pil-comparing-images.htm that calculates the root mean square difference between two images. 我需要有一个像这里找到的函数: http//effbot.org/zone/pil-comparing-images.htm ,它计算两个图像之间的均方根差。 The code looks like this: 代码如下所示:

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]))

Trying to run this code leads to the following error: TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int'. 尝试运行此代码会导致以下错误:TypeError:**或pow()的不支持的操作数类型:'NoneType'和'int'。 That's the matter with it? 这是问题吗?

The problem is that it is creating a histogram that has no values (or really None values) where there is no corresponding pixel value. 问题是它创建的直方图没有值(或实际上是无值),其中没有对应的像素值。

ie when you are finding the diff of the two images, the resulting image doesn't have any pixels that are, say, 43 units apart, so h[43] = None. 即,当您找到两个图像的差异时,生成的图像没有任何像素,例如,相隔43个单位,因此h [43] =无。

Later, you try to access the number of pixels at each brightness in the range(256), and square it, which is causing it to get confused about what None**2 should be. 稍后,您尝试访问范围内每个亮度的像素数(256),并将其平方,这会导致它对None ** 2应该是什么感到困惑。

Consider changing range(256) to h.keys() . 考虑将range(256)更改为h.keys()

Also, you are using h to mean two different things, consider changing the name of one or, better still, both of them to meaningful names. 此外,您使用h表示两个不同的东西,考虑更改一个的名称,或者更好的是,它们都更改为有意义的名称。

Wild guess here, but try this in your last line and see if it works: 在这里疯狂猜测,但在你的最后一行尝试这个,看看它是否有效:

return math.sqrt(sum(h*(i**2) for i, h in enumerate(h))) / (float(im1.size[0]) * im1.size[1]))

I'm not sure offhand why you'd get the TypeError you're describing, but if you use the above line of code and continue to get it, something seriously weird is going on. 我不确定你为什么会得到你正在描述的TypeError ,但是如果你使用上面的代码并继续得到它,那就会发生一些非常奇怪的事情。

It seems that map and reduce are not really needed here. 看来mapreduce在这里并不是真的需要。

An improved version of rmsdiff could be: 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

Here is the source . 这是来源 The improvement suggested by Mark Krautheim is important for at least one reason according to my tests: contrary to the original version, it leads to a return value of 0.0 when comparing an image with itself. 根据我的测试,Mark Krautheim建议的改进至少有一个重要原因:与原始版本相反,当将图像与自身进行比较时,它会导致返回值为0.0。

See here https://gist.github.com/bo858585/5377492 . 请参见https://gist.github.com/bo858585/5377492 This script divides all jpg images from user directory (specify it) to groups by their similarity using root-mean-square (without dividing to sqrt(3) - pixel is 3-number RGB vector) of the difference between each pair of corresponding (by position at matrix 20*20) pixels of two comparing images. 这个脚本将所有jpg图像从用户目录(指定它)除以它们的相似性,使用均方根(不分为sqrt(3) - 像素是3位RGB矢量),每对相应之间的差异(通过两个比较图像的矩阵20 * 20的像素的位置。 Script summarize these distances between pairs of pixels and divide this sum into maximum possible distance - this way script gets the procent of similarity of two images. 脚本总结了像素对之间的这些距离,并将此总和除以最大可能距离 - 这样脚本就可以获得两个图像的相似性。 Before comparing all images resized to 20*20. 在比较所有调整大小为20 * 20的图像之前。 You may vary MAX_DISTANCE (from 0 to 400) and script will group more or less similar images to one group. 您可以更改MAX_DISTANCE(从0到400),脚本会将更多或更少相似的图像分组到一个组。

Consider solving this using an existing solution such as scikit-image : 考虑使用现有解决方案(如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)))

Alternatively write something short yourself using NumPy : 或者使用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)))

Note that both methods treat pixel intensity as in the range [0.0, 1.0] instead of [0, 255]. 请注意,这两种方法都将像素强度视为[0.0,1.0]而不是[0,255]。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM