8

Ho uno script che utilizza l'API di Google Maps per scaricare una sequenza di immagini satellitari quadrate di dimensioni uguali e genera un PDF. Le immagini devono essere ruotate in anticipo, e lo faccio già usando PIL.Normalizzare l'istogramma (luminosità e contrasto) di un insieme di immagini utilizzando Python Image Library (PIL)

Ho notato che, a causa delle diverse condizioni di luce e del terreno, alcune immagini sono troppo luminose, altre sono troppo scure e il PDF risultante risulta un po 'brutto, con condizioni di lettura non ideali "sul campo" (che è il backcountry mountain bike, dove voglio avere una miniatura stampata di incroci specifici).

(EDIT) L'obiettivo è quindi di rendere tutte le immagini con luminosità e contrasto apparenti simili. Quindi, le immagini che sono troppo luminose dovrebbero essere oscurate, e quelle scure dovrebbero essere alleggerite. (a proposito, una volta ho usato imagemagick autocontrast, o auto-gamma o equalize o autolevel, o qualcosa del genere, con risultati interessanti in immagini mediche, ma non so come fare una di queste cose in PIL).

Ho già utilizzato alcune correzioni di immagine dopo la conversione in scala di grigi (aveva una stampante in scala di grigi qualche tempo fa), ma i risultati non erano neanche buoni. Ecco il mio codice in scala di grigio:

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

Questo codice funziona in modo indipendente per ogni immagine. Mi chiedo se sarebbe meglio analizzare prima tutte le immagini e poi "normalizzare" le loro proprietà visive (contrasto, luminosità, gamma, ecc.).

Inoltre, penso che sarebbe necessario eseguire alcune analisi nell'immagine (istogramma?), In modo da applicare una correzione personalizzata a seconda di ogni immagine, e non una correzione uguale per tutti loro (anche se "migliorare" "la funzione considera implicitamente le contizioni iniziali).

Qualcuno ha avuto tale problema e/o conosce una buona alternativa per farlo con le immagini colorate (senza scala di grigi)?

Qualsiasi aiuto sarà apprezzato, grazie per la lettura!

+0

Buona domanda! Tuttavia sono necessari alcuni chiarimenti. Inoltre, pubblicare immagini di esempio sarebbe molto utile per le persone da utilizzare come casi di test. Innanzitutto, il problema è che i bordi delle tessere non corrispondono bene quando li scarichi? O stai cercando un modo per illuminare le piastrelle scure e offuscare quelle luminose? O hai bisogno di fare quest'ultimo mentre manteni la continuità del bordo? – Paul

+0

I bordi non sono un problema, perché il set di immagini non è continuo. L'obiettivo è scurire i più brillanti e illuminare quelli oscuri, come hai detto tu. – heltonbiker

risposta

4

Quello che stai probabilmente cercando è un'utilità che esegue "stiramento dell'istogramma". Here is one implementation. Sono sicuro che ce ne sono altri. Penso che tu voglia preservare la tonalità originale e applicare questa funzione uniformemente su tutte le bande di colore.

Naturalmente ci sono buone probabilità che alcune delle piastrelle abbiano una notevole discontinuità nel livello in cui si uniscono. Evitare questo, tuttavia, comporterebbe l'interpolazione spaziale dei parametri "stretch" ed è una soluzione molto più coinvolgente. (... ma sarebbe un buon esercizio se c'è quel bisogno.)

Edit:

Ecco un tweak che conserva immagine tonalità:

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) 
+0

Wow, sembra esattamente quello che volevo. Farò un rapido tentativo e posto qualche feedback molto presto! – heltonbiker

+0

In realtà, questa implementazione sembra funzionare un'immagine alla volta e stavo pensando di analizzare prima tutte le immagini e quindi applicare l'equalizzazione. Inoltre, le immagini non saranno affiancate, provengono da posizioni diverse e di solito non si sovrappongono. Metterò alla prova il tuo suggerimento e vedrò cosa ottengo. Grazie! – heltonbiker

+0

L'ho provato con le normali immagini digitali dalla mia fotocamera con ottimi risultati. Tuttavia, quando l'ho provato su uno screencap di un'immagine satellitare di Google, è stato terribile. Penso che le immagini dei sat siano altamente posterizzate o qualcosa del genere. – Paul

1

Il seguente codice funziona su immagini da un microscopio (che sono simili), per prepararle prima della cucitura. L'ho usato su un set di prova di 20 immagini, con risultati ragionevoli.

La funzione di media della luminosità è da un altro 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 

A questo punto, i valori di "correzione" (cioè differenza tra valore e media) sono memorizzati in deltaList.La seguente sezione applica questa correzione a tutte le immagini una alla volta.

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 
Problemi correlati