【问题标题】:Rescale intensities of a PIL Image重新调整 PIL 图像的强度
【发布时间】:2009-08-25 12:25:51
【问题描述】:

重新调整 PIL 图像强度的最简单/最干净的方法是什么?

假设我有一个来自 12 位相机的 16 位图像,所以只有 0–4095 的值在使用中。我想重新调整强度,以便使用 0-65535 的整个范围。当图像表示为 PIL 的图像类型时,最简单/最干净的方法是什么?

目前我想出的最佳解决方案是:

pixels = img.getdata()
img.putdata(pixels, 16)

这可行,但始终将四个最低有效位留空。理想情况下,我想将每个值向左移动四位,然后将四个最高有效位复制到四个最低有效位。我不知道怎么做那么快。

【问题讨论】:

    标签: python image-processing python-imaging-library


    【解决方案1】:

    既然你知道像素值是 0-4095,我找不到比这更快的方法:

    new_image= image.point(lambda value: value<<4 | value>>8)
    

    根据documentation,lambda 函数将被调用最多 4096 次,无论您的图像大小。

    编辑:由于在I 图像中赋予点必须的函数形式为argument * scale + offset,那么这是使用point 函数的最佳方法:

    new_image= image.point(lambda argument: argument*16)
    

    最大输出像素值为 65520。

    第二次拍摄:

    您自己的解决方案的修改版本,使用itertools 提高效率:

    import itertools as it # for brevity
    import operator
    
    def scale_12to16(image):
        new_image= image.copy()
        new_image.putdata(
            it.imap(operator.or_,
                it.imap(operator.lshift, image.getdata(), it.repeat(4)),
                it.imap(operator.rshift, image.getdata(), it.repeat(8))
            )
        )
        return new_image
    

    这避免了point函数参数的限制。

    【讨论】:

    • 这是一个非常好的主意,但是因为 16 位图像如果 PIL 类型为“I”,point 的参数必须是 argument * scale + offset 形式的函数。
    • 你是对的,当然。希望“第二次拍摄”对您来说足够高效。
    【解决方案2】:

    为什么要将 4 msb 复制回 4 lsb?每个像素只有 12 个有效位信息。您所做的任何事情都不会改善这一点。如果您可以接受只有 4K 的强度,这对于大多数应用程序来说都很好,那么您的解决方案是正确的并且可能是最佳的。如果您需要更多级别的阴影,那么正如大卫发布的那样,使用直方图重新计算。但是,这将明显变慢。

    但是,将 4 msb 复制到 4 lsb 不是可行的方法:)

    【讨论】:

    • 我想复制 4 MSB 的原因是它会使用整个范围 0–(2^16-1),因此原始图像中的饱和像素在重新缩放的图像中会显得饱和好吧。
    • 确实,这是升级到每个通道更高位数的标准方法。
    【解决方案3】:

    你需要做一个直方图拉伸link to a similar question I answered)而不是直方图均衡histogram stretch http://cct.rncan.gc.ca/resource/tutor/fundam/images/linstre.gif

    Image source

    在您的情况下,您需要将所有像素值乘以 16,这是两个动态范围 (65536/4096) 之间的因子。

    【讨论】:

      【解决方案4】:

      您需要做的是Histogram Equalization。 关于如何使用 python 和 pil:

      编辑: 代码将每个值向左移动四位,然后将四个最高有效位复制到四个最低有效位...

      def f(n):
         return  n<<4 + int(bin(n)[2:6],2)
      
      print(f(0))
      print(f(2**12))
      
      # output
      >>> 0
          65664 # Oops > 2^16
      

      【讨论】:

      • 两个链接完全一样
      • 不,直方图均衡以不同方式拉伸直方图的不同部分。我要做的只是均匀地拉伸直方图,以便使用整个强度范围。
      【解决方案5】:

      也许您应该传递 16.(浮点数)而不是 16(整数)。我试图对其进行测试,但由于某种原因,putdata 根本不会成倍增加......所以我希望它对你有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-31
        • 2012-12-01
        • 2017-04-01
        • 2015-07-22
        • 2018-07-31
        • 1970-01-01
        • 2018-01-29
        • 1970-01-01
        相关资源
        最近更新 更多