Come posso convertire un'immagine RGB in scala di grigi in Python?


206

Sto cercando di usare matplotlibper leggere un'immagine RGB e convertirla in scala di grigi.

In matlab uso questo:

img = rgb2gray(imread('image.png'));

Nel tutorial di matplotlib non lo coprono. Hanno appena letto nell'immagine

import matplotlib.image as mpimg
img = mpimg.imread('image.png')

e poi dividono l'array, ma non è la stessa cosa che convertire RGB in scala di grigi da quello che ho capito.

lum_img = img[:,:,0]

Trovo difficile credere che numpy o matplotlib non abbiano una funzione integrata per convertire da rgb a grigio. Non è un'operazione comune nell'elaborazione delle immagini?

Ho scritto una funzione molto semplice che funziona con l'immagine importata usando imreadin 5 minuti. È orribilmente inefficiente, ma è per questo che speravo in un'implementazione professionale integrata.

Sebastian ha migliorato la mia funzione, ma spero ancora di trovare quello integrato.

implementazione di matlab (NTSC / PAL):

import numpy as np

def rgb2gray(rgb):

    r, g, b = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]
    gray = 0.2989 * r + 0.5870 * g + 0.1140 * b

    return gray

2
Si noti che è possibile scrivere la stessa cosa che la vostra funzione rgb2gray semplicemente come: gray = np.mean(rgb, -1). Forse rgb[...,:3]lì se in realtà è rgba.
seberg,

hmm, gray = np.mean(rgb, -1)funziona benissimo. Grazie. C'è qualche motivo per non usarlo? Perché invece dovrei usare le soluzioni nelle risposte di seguito?
waspinator

6
La pagina di Wikipedia in scala di grigi afferma che il metodo di conversione da RGB a scala di grigi non è univoco, ma fornisce formule di uso comune basate sulla luminanza. È abbastanza diverso da np.mean(rgb, -1).
unutbu,

2
quindi immagino di volere la versione di Matlab ? 0.2989 * R + 0.5870 * G + 0.1140 * B Suppongo che sia il modo standard di farlo.
waspinator

Risposte:


304

Che ne dici di farlo con Pillow :

from PIL import Image
img = Image.open('image.png').convert('LA')
img.save('greyscale.png')

Usando matplotlib e la formula

Y' = 0.2989 R + 0.5870 G + 0.1140 B 

potresti fare:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

def rgb2gray(rgb):
    return np.dot(rgb[...,:3], [0.2989, 0.5870, 0.1140])

img = mpimg.imread('image.png')     
gray = rgb2gray(img)    
plt.imshow(gray, cmap=plt.get_cmap('gray'), vmin=0, vmax=1)
plt.show()

3
Se deve usare matplotlibper qualche altro motivo, dovrebbe essere in grado di usare l'integrato colorsys.rgb_to_yiq()per trasformare più una porzione per ottenere solo il canale di luminanza.
Silas Ray,

34
perchè .convert('LA')? perchè no .convert('gray')? Sembra inutilmente criptico. La documentazione PIL non menziona nulla di "LA" per la funzione di conversione.
waspinator

25
usando PIL cannot write mode LA as JPEG
:,

6
Questo img = Image.open('image.png').convert('LA')deve essereimg = Image.open('image.png').convert('L')
nviens,

12
@BluePython: la LAmodalità ha luminosità (luminosità) e alfa. Se usi la LAmodalità, allora greyscale.pngsarà un'immagine RGBA con il canale alfa image.pngconservato. Se usi la Lmodalità, allora greyscale.pngsarà un'immagine RGB (senza alfa).
unutbu,

69

Puoi anche usare scikit-image , che fornisce alcune funzioni per convertire un'immagine ndarray, come rgb2gray.

from skimage import color
from skimage import io

img = color.rgb2gray(io.imread('image.png'))

Note : I pesi utilizzati in questa conversione sono calibrati per fosfori CRT contemporanei: Y = 0,2125 R + 0,7154 G + 0,0721 B

In alternativa, puoi leggere l'immagine in scala di grigi:

from skimage import io
img = io.imread('image.png', as_gray=True)

è normale che ottenga 0 <valori <1? Devo moltiplicarli per 255 per ottenere la scala dei grigi reale?
Sam,

sapendo che il mio obiettivo è usare le funzionalità GLCM (greycoprops)
Sam

Nota per io.imread: "as_grey" è stato deprecato a favore di "as_gray". Stesso utilizzo, solo ortografia americanizzata. :)
Alogena il

1
Credo che questa sia la risposta più utile alla domanda attuale, l'output di questo è anche compatibile con matplotlib e numpy.
Mert Beşiktepe,

Sto usando l'oggetto a colori ma la mia immagine è una specie di rossastro ora e non grigia (bianco e nero). Devo usare cmapcome gray' then only the image is shown as gray in pyplot.imshow () `? Qualche idea ? Dove sbaglio?
GadaaDhaariGeek

63

Tre dei metodi suggeriti sono stati testati per la velocità con 1000 immagini PNG RGBA (224 x 256 pixel) in esecuzione con Python 3.5 su Ubuntu 16.04 LTS (Xeon E5 2670 con SSD).

Tempi di esecuzione medi

pil : 1.037 secondi

scipy: 1.040 secondi

sk : 2.120 secondi

PIL e SciPy hanno fornito numpyarray identici (compresi tra 0 e 255). SkImage fornisce array da 0 a 1. Inoltre, i colori vengono convertiti in modo leggermente diverso, vedere l'esempio dal set di dati CUB-200.

SkImage: SkImage

PIL : PIL

SciPy : SciPy

Original: Originale

Diff : inserisci qui la descrizione dell'immagine

Codice

  1. Prestazione

    run_times = dict(sk=list(), pil=list(), scipy=list())
    for t in range(100):
        start_time = time.time()
        for i in range(1000):
            z = random.choice(filenames_png)
            img = skimage.color.rgb2gray(skimage.io.imread(z))
        run_times['sk'].append(time.time() - start_time)

    start_time = time.time()
    for i in range(1000):
        z = random.choice(filenames_png)
        img = np.array(Image.open(z).convert('L'))
    run_times['pil'].append(time.time() - start_time)
    
    start_time = time.time()
    for i in range(1000):
        z = random.choice(filenames_png)
        img = scipy.ndimage.imread(z, mode='L')
    run_times['scipy'].append(time.time() - start_time)
    

    for k, v in run_times.items(): print('{:5}: {:0.3f} seconds'.format(k, sum(v) / len(v)))

  2. Produzione
    z = 'Cardinal_0007_3025810472.jpg'
    img1 = skimage.color.rgb2gray(skimage.io.imread(z)) * 255
    IPython.display.display(PIL.Image.fromarray(img1).convert('RGB'))
    img2 = np.array(Image.open(z).convert('L'))
    IPython.display.display(PIL.Image.fromarray(img2))
    img3 = scipy.ndimage.imread(z, mode='L')
    IPython.display.display(PIL.Image.fromarray(img3))
  3. Confronto
    img_diff = np.ndarray(shape=img1.shape, dtype='float32')
    img_diff.fill(128)
    img_diff += (img1 - img3)
    img_diff -= img_diff.min()
    img_diff *= (255/img_diff.max())
    IPython.display.display(PIL.Image.fromarray(img_diff).convert('RGB'))
  4. importazioni
    import skimage.color
    import skimage.io
    import random
    import time
    from PIL import Image
    import numpy as np
    import scipy.ndimage
    import IPython.display
  5. versioni
    skimage.version
    0.13.0
    scipy.version
    0.19.1
    np.version
    1.13.1

6
L'I / O dell'immagine di SciPy è letteralmente PIL / Pillow. Quindi, testare SciPy sta effettivamente testando nuovamente PIL / Pillow con costi generali trascurabili introdotti dalle funzioni wrapper di SciPy. Sarebbe stato molto più utile sostituire OpenCV (che non sfrutta PIL / Pillow) a SciPy (che lo fa). Tuttavia, grazie per il benchmarking dedicato! Il sensibile rallentamento imposto da SciKit è affascinante ... e terrificante.
Cecil Curry,

@CecilCurry Grazie per l'idea con OpenCV! Lo aggiungerò quando troverò del tempo libero.
Maximilian Peters,

Upvoted! Non una risposta che stavo cercando, ma comunque molto molto interessante :)
Cyril N.

29

Puoi sempre leggere il file immagine come scala di grigi fin dall'inizio usando imreadda OpenCV:

img = cv2.imread('messi5.jpg', 0)

Inoltre, nel caso in cui desideri leggere l'immagine come RGB, esegui alcune elaborazioni e poi converti in scala di grigi che potresti utilizzare cvtcolorda OpenCV:

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

6
Ftr: la 0bandiera è cv2.CV_LOAD_IMAGE_GRAYSCALE.
dtk

24

Il modo più veloce e attuale è utilizzare Pillow , installato tramite pip install Pillow.

Il codice è quindi:

from PIL import Image
img = Image.open('input_file.jpg').convert('L')
img.save('output_file.jpg')

3
nota che, se non stai concatenando i tuoi metodi come nell'esempio sopra, convertrestituisce una copia convertita dell'immagine
Matt

non funziona per PNG a 32 bit, i valori saranno bloccati a 255
Andrew Matuk

11

Il tutorial sta barando perché inizia con un'immagine in scala di grigi codificata in RGB, quindi stanno semplicemente tagliando un singolo canale di colore e trattandolo come in scala di grigi. I passaggi di base che devi fare sono trasformare dallo spazio colore RGB in uno spazio colore che codifica con qualcosa che si avvicina al modello luma / chroma, come YUV / YIQ o HSL / HSV, quindi tagliare il canale simile a luma e usarlo come la tua immagine in scala di grigi. matplotlibnon sembra fornire un meccanismo per la conversione in YUV / YIQ, ma consente di convertire in HSV.

Prova a usare matplotlib.colors.rgb_to_hsv(img)quindi a tagliare l'ultimo valore (V) dall'array per la tua scala di grigi. Non è esattamente lo stesso di un valore luma, ma significa che puoi fare tutto matplotlib.

Sfondo:

In alternativa, è possibile utilizzare PIL o l'integrato colorsys.rgb_to_yiq()per convertire in uno spazio colore con un valore luma reale. Potresti anche andare all-in e lanciare il tuo convertitore solo luma, anche se probabilmente è eccessivo.


9

Usando questa formula

Y' = 0.299 R + 0.587 G + 0.114 B 

Possiamo fare

import imageio
import numpy as np
import matplotlib.pyplot as plt

pic = imageio.imread('(image)')
gray = lambda rgb : np.dot(rgb[... , :3] , [0.299 , 0.587, 0.114]) 
gray = gray(pic)  
plt.imshow(gray, cmap = plt.get_cmap(name = 'gray'))

Tuttavia, GIMP che converte il colore in software di immagine in scala di grigi ha tre algoritmi per eseguire l'operazione.


8

Se stai già utilizzando NumPy / SciPy puoi anche usare :

scipy.ndimage.imread(file_name, mode='L')


5
Entrambi scipy.ndimage.imread()e scipy.misc.imread()sono formalmente deprecati in SciPy 1.0.0 e saranno rimossi permanentemente in SciPy 1.2.0. Mentre la documentazione di SciPy raccomanda di imageio.imread()sostituirla in modo adeguato, l'API di questa funzione è completamente spoglia al punto di assurdità. Esso fornisce alcun supporto per la conversione in scala di grigi e rimane quindi inadatto per molte applicazioni - tra cui la nostra. </sigh>
Cecil Curry,

5
@CecilCurry, come si converte un'immagine colorata in scala di grigi usando imageio?
0x90

5

potresti fare:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

def rgb_to_gray(img):
        grayImage = np.zeros(img.shape)
        R = np.array(img[:, :, 0])
        G = np.array(img[:, :, 1])
        B = np.array(img[:, :, 2])

        R = (R *.299)
        G = (G *.587)
        B = (B *.114)

        Avg = (R+G+B)
        grayImage = img

        for i in range(3):
           grayImage[:,:,i] = Avg

        return grayImage       

image = mpimg.imread("your_image.png")   
grayImage = rgb_to_gray(image)  
plt.imshow(grayImage)
plt.show()

5

Usa img.Convert (), supporta "L", "RGB" e "CMYK". modalità

import numpy as np
from PIL import Image

img = Image.open("IMG/center_2018_02_03_00_34_32_784.jpg")
img.convert('L')

print np.array(img)

Produzione:

[[135 123 134 ...,  30   3  14]
 [137 130 137 ...,   9  20  13]
 [170 177 183 ...,  14  10 250]
 ..., 
 [112  99  91 ...,  90  88  80]
 [ 95 103 111 ..., 102  85 103]
 [112  96  86 ..., 182 148 114]]

1
dovrebbe essere la 5a linea img = img.convert('L')?
Allan Ruin

3

Sono arrivato a questa domanda tramite Google, cercando un modo per convertire in scala di grigio un'immagine già caricata.

Ecco un modo per farlo con SciPy:

import scipy.misc
import scipy.ndimage

# Load an example image
# Use scipy.ndimage.imread(file_name, mode='L') if you have your own
img = scipy.misc.face()

# Convert the image
R = img[:, :, 0]
G = img[:, :, 1]
B = img[:, :, 2]
img_gray = R * 299. / 1000 + G * 587. / 1000 + B * 114. / 1000

# Show the image
scipy.misc.imshow(img_gray)

1
Bello. Voglio solo notare che una soluzione più breve sarebbeimg_gray = numpy.average(img, weights=[0.299, 0.587, 0.114], axis=2)
Akavall il

@Akavall Bello da sapere, grazie! Sai se il tuo collegamento è più veloce? In caso contrario, terrei il mio perché è più facile da capire.
Martin Thoma,

Non ho avuto tempo, il mio istinto numpy.averageè un po 'più veloce ma non praticamente diverso. La tua soluzione è chiara e contiene informazioni pertinenti su R, G, B, quindi la terrei. Il mio commento è stato più di un'opzione aggiuntiva, non di una sostituzione.
Akavall,

Entrambi scipy.ndimage.imread()e scipy.misc.imread()sono formalmente deprecati in SciPy 1.0.0 e saranno rimossi permanentemente in SciPy 1.2.0. Probabilmente vuoi semplicemente usare il supporto di conversione in scala di grigi integrato di Pillow (la risposta di ala unutbu ), invece.
Cecil Curry,

-3
image=myCamera.getImage().crop(xx,xx,xx,xx).scale(xx,xx).greyscale()

È possibile utilizzare greyscale()direttamente per la trasformazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.