【问题标题】:Normalize histogram (brightness and contrast) of a set of images using Python Image Library (PIL)使用 Python 图像库 (PIL) 标准化一组图像的直方图(亮度和对比度)
【发布时间】:2011-10-30 06:36:24
【问题描述】:

我有一个脚本,它使用 Google Maps API 下载一系列大小相等的方形卫星图像并生成 PDF。图像需要预先旋转,我已经使用 PIL 这样做了。

我注意到,由于不同的光线和地形条件,一些图像太亮,另一些太暗,最终生成的 pdf 有点难看,“现场”的阅读条件不太理想(这是野外山地自行车,我想要打印特定十字路口的缩略图)。

(编辑)然后目标是使所有图像最终具有相似的明显亮度和对比度。因此,太亮的图像必须变暗,而太暗的图像必须变亮。 (顺便说一下,我曾经使用过imagemagick autocontrast,或auto-gamma,或equalize,或autolevel,或类似的东西,在医学图像中得到了有趣的结果,但不知道如何做这些在 PIL 中)。

我在转换为灰度后已经使用了一些图像校正(之前有一台灰度打印机),但结果也不是很好。这是我的灰度代码:

#!/usr/bin/python

def myEqualize(im)
    im=im.convert('L')
    contr = ImageEnhance.Contrast(im)
    im = contr.enhance(0.3)
    bright = ImageEnhance.Brightness(im)
    im = bright.enhance(2)
    #im.show()
    return im

此代码对每个图像独立工作。我想知道是否最好先分析所有图像,然后“标准化”它们的视觉属性(对比度、亮度、伽马等)。

另外,我认为有必要对图像进行一些分析(直方图?),以便根据每个图像应用自定义校正,而不是对所有图像进行同等校正(尽管任何“增强”函数隐含地考虑初始条件)。

有没有人遇到过这样的问题和/或知道用彩色图像(无灰度)做这件事的好方法?

任何帮助将不胜感激,感谢阅读!

【问题讨论】:

  • 好问题!但是,需要进行一些澄清。此外,发布示例图像对于人们用作测试用例非常有帮助。首先,是不是下载的时候瓦片边缘不匹配的问题?或者您是否正在寻找一种方法来使深色瓷砖变亮并使明亮的瓷砖变暗?还是您需要在保持边缘连续性的同时做后者?
  • 边缘不是问题,因为图像集不是连续的。正如你所说,我们的目标是让最亮的部分变暗,让最暗的部分变亮。

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


【解决方案1】:

您可能正在寻找的是一个执行“直方图拉伸”的实用程序。 Here is one implementation。我确信还有其他人。我认为您希望保留原始色调并将此功能统一应用于所有色带。

当然,一些图块在它们连接的水平上很有可能会出现明显的不连续性。然而,避免这种情况将涉及“拉伸”参数的空间插值,并且是一个更复杂的解决方案。 (......但如果有需要,这将是一个很好的练习。)

编辑:

这是一个保留图像色调的调整:

import operator

def equalize(im):
    h = im.convert("L").histogram()
    lut = []
    for b in range(0, len(h), 256):
        # step size
        step = reduce(operator.add, h[b:b+256]) / 255
        # create equalization lookup table
        n = 0
        for i in range(256):
            lut.append(n / step)
            n = n + h[i+b]
    # map image through lookup table
    return im.point(lut*im.layers)

【讨论】:

  • 哇,这似乎正是我想要的。我会快速尝试一下,并尽快发布一些反馈!
  • 实际上,这个实现似乎一次只处理一张图像,我正在考虑先分析所有图像,然后再应用均衡。此外,图像不会被平铺,它们来自不同的位置,通常不会重叠。我会测试你的建议,看看我得到了什么。谢谢!
  • 我在相机中的常规数字图像上进行了尝试,效果很好。然而,当我在谷歌卫星图像的屏幕截图上尝试它时,它太糟糕了。我认为卫星图像是高度分色的。
  • 我尝试运行您的代码,但在类 Image(代码的最后一行)上存在不存在属性 layers 的错误。我正在使用 Python 2.7,但在 PIL 的文档中找不到此属性。任何想法? :o(
  • @heltonbiker im.layers 对于 RBG 图像为 3,对于 RGBA 图像为 4。只需替换适当的值即可。
【解决方案2】:

以下代码适用于来自显微镜的图像(相似),以便在拼接之前准备它们。我在包含 20 张图像的测试集上使用了它,得到了合理的结果。

亮度平均功能来自另一个Stackoverflow question

from PIL import Image
from PIL import ImageStat
import math

# function to return average brightness of an image
# Source: https://stackoverflow.com/questions/3490727/what-are-some-methods-to-analyze-image-brightness-using-python

def brightness(im_file):
   im = Image.open(im_file)
   stat = ImageStat.Stat(im)
   r,g,b = stat.mean
   return math.sqrt(0.241*(r**2) + 0.691*(g**2) + 0.068*(b**2))   #this is a way of averaging the r g b values to derive "human-visible" brightness

myList = [0.0]
deltaList = [0.0]
b = 0.0
num_images = 20                         # number of images   

# loop to auto-generate image names and run prior function  
for i in range(1, num_images + 1):      # for loop runs from image number 1 thru 20
    a = str(i)
    if len(a) == 1: a = '0' + str(i)    # to follow the naming convention of files - 01.jpg, 02.jpg... 11.jpg etc.
    image_name = 'twenty/' + a + '.jpg'
    myList.append(brightness(image_name))

avg_brightness = sum(myList[1:])/num_images
print myList
print avg_brightness

for i in range(1, num_images + 1):
   deltaList.append(i)
   deltaList[i] = avg_brightness - myList[i] 

print deltaList

此时,“校正”值(即值与平均值之间的差)存储在 deltaList 中。以下部分将这一校正一一应用于所有图像。

for k in range(1, num_images + 1):      # for loop runs from image number 1 thru 20
   a = str(k)
   if len(a) == 1: a = '0' + str(k)       # to follow the naming convention of files - 01.jpg, 02.jpg... 11.jpg etc.
   image_name = 'twenty/' + a + '.jpg'
   img_file = Image.open(image_name)
   img_file = img_file.convert('RGB')     # converts image to RGB format
   pixels = img_file.load()               # creates the pixel map
   for i in range (img_file.size[0]):
      for j in range (img_file.size[1]):
         r, g, b = img_file.getpixel((i,j))  # extracts r g b values for the i x j th pixel
         pixels[i,j] = (r+int(deltaList[k]), g+int(deltaList[k]), b+int(deltaList[k])) # re-creates the image
   j = str(k)
   new_image_name = 'twenty/' +'image' + j + '.jpg'      # creates a new filename
   img_file.save(new_image_name)                         # saves output to new file name

【讨论】:

    猜你喜欢
    • 2012-12-03
    • 1970-01-01
    • 2017-01-14
    • 1970-01-01
    • 1970-01-01
    • 2013-10-14
    • 2020-05-31
    • 1970-01-01
    • 2011-05-07
    相关资源
    最近更新 更多