简体   繁体   English

使用Python Image Library(PIL)标准化一组图像的直方图(亮度和对比度)

[英]Normalize histogram (brightness and contrast) of a set of images using Python Image Library (PIL)

I have a script which uses Google Maps API to download a sequence of equal-sized square satellite images and generates a PDF. 我有一个脚本,它使用Google Maps API下载一系列相同大小的方形卫星图像并生成PDF。 The images need to be rotated beforehand, and I already do so using PIL. 图像需要事先旋转,我已经使用PIL进行了旋转。

I noticed that, due to different light and terrain conditions, some images are too bright, others are too dark, and the resulting pdf ends up a bit ugly, with less-than-ideal reading conditions "in the field" (which is backcountry mountain biking, where I want to have a printed thumbnail of specific crossroads). 我注意到,由于不同的光线和地形条件,一些图像太亮,另一些图像太暗,结果pdf结果有点难看,“在野外”读取条件不太理想(这是偏远的山地自行车,我想要一个特定十字路口的打印缩略图)。

(EDIT) The goal then is to make all images end up with similar apparent brightness and contrast. (编辑)然后目标是使所有图像最终具有相似的表观亮度和对比度。 So, the images that are too bright would have to be darkened, and the dark ones would have to be lightened. 因此,过于明亮的图像必须变暗,而黑暗的图像则必须变亮。 (by the way, I once used imagemagick autocontrast , or auto-gamma , or equalize , or autolevel , or something like that, with interesting results in medical images, but don't know how to do any of these in PIL). (顺便说一句,我曾经使用过imagemagick autocontrast ,或auto-gamma ,或者equalize ,或者autolevel ,或类似的东西,在医学图像中有趣的结果,但是不知道如何在PIL中做任何这些)。

I already used some image corrections after converting to grayscale (had a grayscale printer a time ago), but the results weren't good, either. 转换为灰度后我已经使用了一些图像校正(前一段时间有灰度打印机),但结果也不好。 Here is my grayscale code: 这是我的灰度代码:

#!/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

This code works independently for each image. 此代码对每个图像独立工作。 I wonder if it would be better to analyze all images first and then "normalize" their visual properties (contrast, brightness, gamma, etc). 我想知道首先分析所有图像然后“标准化”它们的视觉特性(对比度,亮度,伽玛等)会更好。

Also, I think it would be necessary to perform some analysis in the image (histogram?), so as to apply a custom correction depending on each image, and not an equal correction for all of them (although any "enhance" function implicitly considers initial contitions). 另外,我认为有必要在图像中进行一些分析(直方图?),以便根据每个图像应用自定义校正,而不是对所有图像进行相等的校正(尽管任何“增强”函数都隐含地考虑初始合同)。

Does anybody had such problem and/or know a good alternative to do this with the colored images (no grayscale)? 有没有人有这样的问题和/或知道用彩色图像(没有灰度)做这个的好方法?

Any help will be appreciated, thanks for reading! 任何帮助将不胜感激,感谢阅读!

What you are probably looking for is a utility that performs "histogram stretching". 您可能正在寻找的是一个执行“直方图拉伸”的实用程序。 Here is one implementation . 这是一个实现 I am sure there are others. 我相信还有其他人。 I think you want to preserve the original hue and apply this function uniformly across all color bands. 我想你想要保留原始色调并在所有色带上均匀地应用这个功能。

Of course there is a good chance that some of the tiles will have a noticeable discontinuity in level where they join. 当然,很有可能一些瓷砖在它们加入的水平上会有明显的不连续性。 Avoiding this, however, would involve spatial interpolation of the "stretch" parameters and is a much more involved solution. 然而,避免这种情况将涉及“拉伸”参数的空间插值,并且是更复杂的解决方案。 (...but would be a good exercise if there is that need.) (......但如果有需要,这将是一个很好的练习。)

Edit: 编辑:

Here is a tweak that preserves image hue: 这是一个保留图像色调的调整:

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)

The following code works on images from a microscope (which are similar), to prepare them prior to stitching. 以下代码适用于显微镜(相似)的图像,以便在拼接之前进行准备。 I used it on a test set of 20 images, with reasonable results. 我在20张图像的测试集上使用它,结果合理。

The brightness average function is from another Stackoverflow question . 亮度平均函数来自另一个Stackoverflow问题

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

At this point, the "correction" values (ie difference between value and mean) are stored in deltaList. 此时,“校正”值(即值和均值之间的差值)存储在deltaList中。 The following section applies this correction to all the images one by one. 以下部分逐一将此校正应用于所有图像。

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

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

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