Jak przekonwertować obraz RGB na skalę szarości w Pythonie?

205

Próbuję użyć matplotlibdo odczytania obrazu RGB i przekonwertowania go na skalę szarości.

W Matlabie używam tego:

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

W samouczku matplotlib nie obejmują tego. Po prostu czytają na obrazku

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

a następnie wycinają tablicę, ale to nie to samo, co konwersja RGB na skalę szarości z tego, co rozumiem.

lum_img = img[:,:,0]

Trudno mi uwierzyć, że numpy lub matplotlib nie ma wbudowanej funkcji do konwersji z rgb na grey. Czy to nie jest powszechna operacja przetwarzania obrazu?

Napisałem bardzo prostą funkcję, która działa z obrazem zaimportowanym za imread5 minut. Jest to okropnie nieefektywne, ale dlatego liczyłem na wbudowane profesjonalne wdrożenie.

Sebastian poprawił moją funkcję, ale nadal mam nadzieję znaleźć wbudowaną.

Implementacja matlaba (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
waspinator
źródło
2
Należy pamiętać, że można napisać to samo co swojej rgb2gray funkcji po prostu jako: gray = np.mean(rgb, -1). Może rgb[...,:3]tam, jeśli to rzeczywiście rgba.
seberg
hmm, gray = np.mean(rgb, -1)działa dobrze. dzięki. Czy jest jakiś powód, aby tego nie używać? Dlaczego miałbym zamiast tego korzystać z rozwiązań zawartych w poniższych odpowiedziach?
waspinator
6
Strona Wikipedii w skali szarości mówi, że metoda konwersji RGB na skalę szarości nie jest unikalna, ale podaje powszechnie używane formuły oparte na luminancji. Jest zupełnie inny niż np.mean(rgb, -1).
unutbu
2
więc chyba chcę wersję Matlaba ? 0.2989 * R + 0.5870 * G + 0.1140 * B Zakładam, że to standardowy sposób na zrobienie tego.
waspinator

Odpowiedzi:

303

Co powiesz na robienie tego z Pillow :

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

Korzystanie z matplotlib i formuły

Y' = 0.2989 R + 0.5870 G + 0.1140 B 

mógłbyś:

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()
unutbu
źródło
3
Jeśli musi użyć matplotlibz innego powodu, powinien móc użyć wbudowanego colorsys.rgb_to_yiq()do transformacji oraz plastra, aby uzyskać tylko kanał luma.
Silas Ray
34
dlaczego .convert('LA')? dlaczego nie .convert('gray')? Wydaje się niepotrzebnie tajemniczy. Dokumentacja PIL nie wspomina nic o „LA” dla funkcji konwersji.
waspinator
25
używając PIL: cannot write mode LA as JPEGmusiałem używać trybu L, a nie LA
jsky
6
To img = Image.open('image.png').convert('LA')musi byćimg = Image.open('image.png').convert('L')
nviens
12
@ BluePython: LAtryb ma jasność (jasność) i alfa. Jeśli użyjesz LAtrybu, greyscale.pngbędzie to obraz RGBA z zachowanym kanałem alfa image.png. Jeśli użyjesz Ltrybu, greyscale.pngbędzie to obraz RGB (bez alfa).
unutbu
69

Możesz także użyć scikit-image , który udostępnia niektóre funkcje do konwersji obrazu ndarray, na przykład rgb2gray.

from skimage import color
from skimage import io

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

Uwagi : Odważniki zastosowane w tej konwersji są kalibrowane dla współczesnych luminoforów CRT: Y = 0,2125 R + 0,7154 G + 0,0721 B

Alternatywnie możesz odczytać obraz w skali szarości poprzez:

from skimage import io
img = io.imread('image.png', as_gray=True)
yangjie
źródło
czy to normalne, że dostaję 0 <wartości <1? Czy mam je pomnożyć przez 255, aby uzyskać rzeczywistą skalę szarości?
Sam
wiedząc, że moim celem jest wykorzystanie funkcji GLCM (greycoprops)
Sam
Uwaga do io.imread: „as_grey” jest przestarzałe na rzecz „as_gray”. To samo użycie, tylko amerykańska pisownia. :)
Halogen
1
Uważam, że jest to najbardziej przydatna odpowiedź na pytanie, z której wynik jest również zgodny z matplotlib i numpy.
Mert Beşiktepe
Używam obiektu kolorowego, ale mój obraz jest teraz trochę czerwonawy, a nie szary (czarno-biały). Muszę użyć cmapjako gray' then only the image is shown as gray in pyplot.imshow () `? Jakieś pomysły ? Gdzie się mylę?
GadaaDhaariGeek
63

Trzy z sugerowanych metod przetestowano pod kątem prędkości przy 1000 obrazów PNG RGBA (224 x 256 pikseli) działających z Pythonem 3.5 na Ubuntu 16.04 LTS (Xeon E5 2670 z dyskiem SSD).

Średni czas pracy

pil : 1.037 sekund

scipy: 1.040 sekund

sk : 2.120 sekund

PIL i SciPy dały identyczne numpytablice (od 0 do 255). SkImage podaje tablice od 0 do 1. Ponadto kolory są konwertowane nieco inaczej, patrz przykład z zestawu danych CUB-200.

SkImage: SkImage

PIL : PIL

SciPy : SciPy

Original: Oryginalny

Diff : wprowadź opis zdjęcia tutaj

Kod

  1. Występ

    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. Wynik
    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. Porównanie
    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. Import
    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. Wersje
    skimage.version
    0.13.0
    scipy.version
    0.19.1
    np.version
    1.13.1
Maksymilian Peters
źródło
6
Obraz SciPy we / wy to dosłownie PIL / Pillow. Stąd testowanie SciPy skutecznie testuje PIL / Pillow z nieznacznym narzutem wprowadzonym przez funkcje owijania SciPy. Byłoby o wiele bardziej użyteczne zastąpienie OpenCV (który nie wykorzystuje PIL / Pillow) zamiast SciPy (co robi). Niemniej jednak dzięki za dedykowane testy porównawcze! Widoczne spowolnienie narzucone przez SciKit jest fascynujące ... i przerażające.
Cecil Curry
@CecilCurry Dzięki za pomysł z OpenCV! Dodam to, kiedy znajdę trochę wolnego czasu.
Maximilian Peters
Pozytywne! Nie szukałem odpowiedzi, ale mimo to bardzo bardzo interesującego :)
Cyril N.
29

Zawsze możesz odczytać plik obrazu w skali szarości od samego początku, używając imreadOpenCV:

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

Ponadto, jeśli chcesz odczytać obraz jako RGB, przeprowadź przetwarzanie, a następnie przekonwertuj na skalę szarą, której możesz użyć cvtcolorz OpenCV:

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Diamantatos Paraskevas
źródło
6
Ftr: 0Flaga jest cv2.CV_LOAD_IMAGE_GRAYSCALE.
dtk,
24

Najszybszym i aktualnym sposobem jest użycie poduszki , zainstalowanej za pośrednictwem pip install Pillow.

Kod jest wtedy:

from PIL import Image
img = Image.open('input_file.jpg').convert('L')
img.save('output_file.jpg')
YPCrumble
źródło
3
zwróć uwagę, że jeśli nie łączysz metod jak w powyższym przykładzie, convertzwraca przekonwertowaną kopię obrazu
Matt
nie działa dla 32-bitowego formatu PNG, wartości zostaną ustalone na 255
Andrew Matuk
11

Samouczek oszukuje, ponieważ zaczyna się od obrazu w skali szarości zakodowanego w RGB, więc wycinają tylko jeden kanał koloru i traktują go jako skalę szarości. Podstawowe kroki, które musisz zrobić, to przekształcić z przestrzeni kolorów RGB w przestrzeń kolorów, która koduje się z czymś zbliżonym do modelu luma / chroma, takim jak YUV / YIQ lub HSL / HSV, a następnie odciąć kanał podobny do luma i użyć tego jako twój obraz w skali szarości. matplotlibnie wydaje się zapewniać mechanizmu konwersji do YUV / YIQ, ale umożliwia konwersję do HSV.

Spróbuj użyć, matplotlib.colors.rgb_to_hsv(img)a następnie wyciąć ostatnią wartość (V) z tablicy dla skali szarości. Nie jest to dokładnie to samo co wartość luma, ale oznacza, że ​​możesz to wszystko zrobićmatplotlib .

Tło:

Alternatywnie możesz użyć PIL lub wbudowanego colorsys.rgb_to_yiq()do konwersji do przestrzeni kolorów z prawdziwą wartością luma. Możesz także zagrać za wszystko i rzucić własny konwerter tylko dla lumy, choć to prawdopodobnie przesada.

Silas Ray
źródło
9

Za pomocą tej formuły

Y' = 0.299 R + 0.587 G + 0.114 B 

Możemy zrobić

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'))

Jednak oprogramowanie GIMP do konwersji kolorów na obrazy w skali szarości ma trzy algorytmy do wykonania tego zadania.

iNet
źródło
8

Jeśli używasz już NumPy / SciPy, równie dobrze możesz użyć :

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

dtk
źródło
5
Zarówno scipy.ndimage.imread()i scipy.misc.imread()formalnie niezalecane w scipy 1.0.0 i zostaną trwale usunięte w scipy 1.2.0. Podczas gdy dokumentacja SciPy zaleca imageio.imread()jako odpowiedni zamiennik, API tej funkcji jest gołe aż do absurdu. Zapewnia on żadnego wsparcia dla skali szarości nawrócenia i pozostaje zatem nadają się do wielu zastosowań - w tym nasze. </sigh>
Cecil Curry
5
@CecilCurry, jak przekonwertować kolorowy obraz w skali szarości za pomocą imageio?
0x90,
5

mógłbyś:

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()
am.mansour
źródło
5

Użyj img.Convert (), obsługuje „L”, „RGB” i „CMYK”. tryb

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)

Wynik:

[[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]]
naren
źródło
1
powinna być piąta linia img = img.convert('L')?
Allan Ruin
3

Przyszedłem do tego pytania za pośrednictwem Google, szukając sposobu na konwersję już załadowanego obrazu na skalę szarości.

Oto sposób, aby to zrobić za pomocą 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)
Martin Thoma
źródło
1
Miły. Chcę tylko zauważyć, że krótszym rozwiązaniem byłobyimg_gray = numpy.average(img, weights=[0.299, 0.587, 0.114], axis=2)
Akavall
@Akavall Miło wiedzieć, dziękuję! Czy wiesz, czy Twój skrót jest szybszy? Jeśli nie, zatrzymałbym mój, ponieważ łatwiej to zrozumieć.
Martin Thoma,
Nie spóźniłem się, moje przeczucie jest numpy.averagenieco szybsze, ale praktycznie inne. Twoje rozwiązanie jest jasne i zawiera istotne informacje na temat R, G, B, więc chciałbym je zachować. Mój komentarz był raczej dodatkową opcją, a nie zamiennikiem.
Akavall,
Zarówno scipy.ndimage.imread()i scipy.misc.imread()formalnie niezalecane w scipy 1.0.0 i zostaną trwale usunięte w scipy 1.2.0. Prawdopodobnie po prostu chcesz użyć skali szarości wbudowane wsparcie konwersji na poduszce (ala unutbu „s odpowiedź ), zamiast.
Cecil Curry
-3
image=myCamera.getImage().crop(xx,xx,xx,xx).scale(xx,xx).greyscale()

Możesz użyć greyscale()bezpośrednio do transformacji.

Qian Han
źródło