【问题标题】:Why is ImageStat returning incorrect statistics for signed integer images (`mode='I'`)?为什么 ImageStat 返回有符号整数图像的错误统计信息(`mode='I'`)?
【发布时间】:2019-07-01 23:33:07
【问题描述】:

我的深度图像具有非常大的值范围,大约 [0, 10,000],保存为 png。这些默认情况下作为 PIL 图像加载,带有mode='I',32 位有符号整数像素,但有一些意外行为。

例如,Image.show() 显示空白屏幕。这在某种程度上是意料之中的,因为我猜该函数期望输入 [0, 1] 或 [0, 255] 并且正在截断我的图像中超出该范围的像素值。

更令人担忧的是,ImageStat 为大多数统计数据返回了不正确的值。我已经通过numpy 和 Matlab 独立获得的结果确认了这两个值。这不会发生在更传统的 RGB 图像mode='RGB' 上。

问题

这里发生了什么?这也是截断问题吗?

MCVE

使用以下图片为test.png

from PIL import Image
im = Image.open("test.png")
im.show() # shows white blank screen 
print(im.mode) # prints 'I'

from PIL.ImageStat import Stat
stat = Stat(im)
print(stat.mean)    # [85.10539415869734]
print(stat.extrema) # [(0, 255)]
print(stat.sum)     # [32927277.0]

import numpy as np
print(np.mean(im)) # 28051.3720754717
print(np.min(im))  # 9992
print(np.max(im))  # 63816
print(np.sum(im))  # 10853075856

matlab的结果和numpy差不多

【问题讨论】:

    标签: python numpy python-imaging-library


    【解决方案1】:

    Image.show() 显示空白屏幕

    之所以会这样,是因为Image.show() 使用了一个临时缓冲区来存储图像对象。然后在显示之前将 Image 对象转换为.bmp (bitmap)。位图格式不支持 32 位单通道图像(灰度),因此通过show() 得到的结果是不可取的。

    ImageStat 为大多数统计信息返回了不正确的值

    在对ImageStat 模块进行源代码分析后,我了解到该模块是专门为处理具有 0 ≤ 样本空间 > 256的图像而构建的,或像素值在 (0 ≤ value ≤ 255) 范围内的图像。

    例如,如果我们分析Stat.extreme:-

    def _getextrema(self):
        """Get min/max values for each band in the image"""
    
        def minmax(histogram):
            n = 255
            x = 0
            for i in range(256):
                if histogram[i]:
                    n = min(n, i)
                    x = max(x, i)
            return n, x  # returns (255, 0) if there's no data in the histogram
    
        v = []
        for i in range(0, len(self.h), 256):
            v.append(minmax(self.h[i:]))
        return v
    

    我们可以清楚地看到,用于计算图像像素值上下界的函数不能返回小于0的值,也不能返回大于255的值. 即 (value ∈ {0 - 255}).

    Stat 函数是这样制作的,它只有在图像的像素值范围低于 256 时才能正常工作。

    这也是截断问题吗

    没有。在计算Stat 中的值期间,不会发生截断。 Stat 模块的制作方式不是它应该能够将像素值展平/截断超出其正常范围。相反,当一个图像对象被传递给Stat 时,它只会对其进行正常计算(假设像素值的范围在 0 到 255 之间)。然后输出仅取决于输入参数是否导致值计算中的中断/异常,即如果一切顺利,则输出将是不可取的。但这并不总是正确的。

    例如:-

    img = Image.new("I", (300, 300), (3000))
    
    stat = Stat(img)
    print(stat.mean)
    print(stat.extrema)
    print(stat.sum)     
    

    执行代码:-

    Traceback (most recent call last):
      File "C:/Users/Vasu/AppData/Local/Programs/Python/Python37-32/Lib/site-packages/PIL/testing.py", line 10, in <module>
        print(stat.mean)
      File "C:\Users\Vasu\AppData\Local\Programs\Python\Python37-32\lib\site-packages\PIL\ImageStat.py", line 48, in __getattr__
        v = getattr(self, "_get" + id)()
      File "C:\Users\Vasu\AppData\Local\Programs\Python\Python37-32\lib\site-packages\PIL\ImageStat.py", line 104, in _getmean
        v.append(self.sum[i] / self.count[i])
    ZeroDivisionError: float division by zero
    

    因此,输出要么是不可取的,要么最终会导致错误/异常。

    这是否意味着PIL 不支持I 色彩模式

    没有。 PIL 完全支持 32 位有符号整数。

    例如,如果我们尝试获取图像的像素值:-

    im = Image.open(r"Input_img")
    im = list(im.getdata())
    
    print(im)
    

    我们得到实际的像素值:-

    40384, 40384, 40384, 40384, 40384, 40600, 40600, 40600, 40808, 41016, 41232, 41440, 41656.....22232]
    

    结论:- PIL 原生支持 32 位有符号整数像素,但 ImageStat 不支持。

    P.S.:- PIL/pillow 文档的当前状态不是很好,即它不善于解释.....无论它正在尝试什么解释。因此,我们应该尽可能地添加更多的intel。因此,我建议您在 PIL repository on Github 上发布问题/拉取请求,以改进 ImageStat 模块的描述。就像添加以下行:-

    模块本身不支持大于 8 位的像素值

    或其他类似的东西。

    【讨论】:

      猜你喜欢
      • 2019-08-18
      • 2021-03-26
      • 2016-06-11
      • 2015-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多