Wie kann ich ein RGB-Bild in Python in Graustufen konvertieren?

204

Ich versuche zu benutzen matplotlib ein RGB-Bild einzulesen und in Graustufen umzuwandeln.

In Matlab benutze ich Folgendes:

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

Im matplotlib-Tutorial wird es nicht behandelt. Sie lesen nur das Bild ein

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

und dann schneiden sie das Array auf, aber das ist nicht dasselbe wie das Konvertieren von RGB in Graustufen, soweit ich weiß.

lum_img = img[:,:,0]

Es fällt mir schwer zu glauben, dass Numpy oder Matplotlib keine eingebaute Funktion zum Konvertieren von RGB in Grau haben. Ist dies nicht eine übliche Operation in der Bildverarbeitung?

Ich habe eine sehr einfache Funktion geschrieben, die mit dem importierten Bild imreadin 5 Minuten funktioniert . Es ist schrecklich ineffizient, aber deshalb habe ich auf eine integrierte professionelle Implementierung gehofft.

Sebastian hat meine Funktion verbessert, aber ich hoffe immer noch, die eingebaute zu finden.

Implementierung von 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
Waspinator
quelle
2
Beachten Sie, dass Sie dasselbe wie Ihre rgb2gray-Funktion einfach schreiben können als : gray = np.mean(rgb, -1). Vielleicht rgb[...,:3]da, wenn es tatsächlich rgba ist.
Seberg
hmm, gray = np.mean(rgb, -1)funktioniert gut. Vielen Dank. Gibt es einen Grund, dies nicht zu verwenden? Warum sollte ich stattdessen die Lösungen in den folgenden Antworten verwenden?
Waspinator
6
Auf der Wikipedia-Seite in Graustufen heißt es, dass die Methode zur Konvertierung von RGB in Graustufen nicht eindeutig ist, sondern häufig verwendete Formeln basierend auf der Luminanz enthält. Es ist ganz anders als np.mean(rgb, -1).
Unutbu
2
Also will ich wohl Matlabs Version ? 0.2989 * R + 0.5870 * G + 0.1140 * B Ich gehe davon aus, dass dies die Standardmethode ist.
Waspinator

Antworten:

302

Wie wäre es mit Pillow :

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

Mit matplotlib und der Formel

Y' = 0.2989 R + 0.5870 G + 0.1140 B 

du könntest es tun:

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
quelle
3
Wenn er matplotlibaus einem anderen Grund verwenden muss, sollte er in der Lage sein, das eingebaute colorsys.rgb_to_yiq()zu transformieren und ein Slice, um nur den Luma-Kanal zu erhalten.
Silas Ray
34
warum .convert('LA')? warum nicht .convert('gray')? Scheint unnötig kryptisch. In der PIL-Dokumentation wird nichts über 'LA' für die Konvertierungsfunktion erwähnt.
Waspinator
25
mit PIL : cannot write mode LA as JPEG, musste ich den L-Modus verwenden, nicht LA
jsky
6
Dies img = Image.open('image.png').convert('LA')muss seinimg = Image.open('image.png').convert('L')
nviens
12
@BluePython: LAModus hat Leuchtkraft (Helligkeit) und Alpha. Wenn Sie den LAModus verwenden, greyscale.pngwird ein RGBA-Bild mit dem erhaltenen Alphakanal angezeigt image.png. Wenn Sie den LModus verwenden, greyscale.pngwird ein RGB-Bild (ohne Alpha) angezeigt.
Unutbu
69

Sie können auch ein Scikit-Bild verwenden , das einige Funktionen zum Konvertieren eines Bildes bietet ndarray, z rgb2gray.

from skimage import color
from skimage import io

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

Anmerkungen : Die bei dieser Umrechnung verwendeten Gewichte sind für moderne CRT-Leuchtstoffe kalibriert: Y = 0,2125 R + 0,7154 G + 0,0721 B.

Alternativ können Sie das Bild in Graustufen lesen, indem Sie:

from skimage import io
img = io.imread('image.png', as_gray=True)
Yangjie
quelle
Ist es normal, dass ich 0 <Werte <1 bekomme? Soll ich sie mit 255 multiplizieren, um die echte Graustufe zu erhalten?
Sam
zu wissen, dass mein Ziel darin besteht, GLCM-Funktionen (Greycoprops) zu verwenden
Sam
Hinweis für io.imread: "as_grey" wurde zugunsten von "as_gray" abgelehnt. Gleiche Verwendung, nur amerikanische Rechtschreibung. :)
Halogen
1
Ich glaube, dies ist die nützlichste Antwort auf die vorliegende Frage. Die Ausgabe ist auch mit matplotlib und numpy kompatibel.
Mert Beşiktepe
Ich verwende das Farbobjekt, aber mein Bild ist jetzt etwas rötlich und nicht grau (schwarz und weiß). Ich muss cmapals gray' then only the image is shown as gray in pyplot.imshow () `verwenden? Irgendwelche Gedanken? Wo irre ich mich
GadaaDhaariGeek
63

Drei der vorgeschlagenen Methoden wurden mit 1000 RGBA-PNG-Bildern (224 x 256 Pixel), die mit Python 3.5 unter Ubuntu 16.04 LTS (Xeon E5 2670 mit SSD) ausgeführt wurden, auf Geschwindigkeit getestet.

Durchschnittliche Laufzeiten

pil : 1,037 Sekunden

scipy: 1,040 Sekunden

sk : 2.120 Sekunden

PIL und SciPy ergaben identische numpyArrays (im Bereich von 0 bis 255). SkImage gibt Arrays von 0 bis 1 an. Außerdem werden die Farben leicht unterschiedlich konvertiert (siehe Beispiel aus dem CUB-200-Datensatz).

SkImage: SkImage

PIL : PIL

SciPy : SciPy

Original: Original

Diff : Geben Sie hier die Bildbeschreibung ein

Code

  1. Performance

    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. Ausgabe
    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. Vergleich
    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. Importe
    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. Versionen
    skimage.version
    0.13.0
    scipy.version
    0.19.1
    np.version
    1.13.1
Maximilian Peters
quelle
6
Die Bild-E / A von SciPy ist buchstäblich PIL / Pillow. Beim Testen von SciPy wird PIL / Pillow daher effektiv erneut getestet, wobei der durch die Wrapper-Funktionen von SciPy verursachte vernachlässigbare Overhead gering ist. Es wäre viel nützlicher gewesen, SciPy (das PIL / Pillow nicht nutzt) durch OpenCV (das PIL / Pillow nicht nutzt) zu ersetzen . Trotzdem vielen Dank für das engagierte Benchmarking! Die erkennbare Verlangsamung durch SciKit ist faszinierend ... und schrecklich.
Cecil Curry
@ CecilCurry Danke für die Idee mit OpenCV! Ich werde es hinzufügen, wenn ich etwas Freizeit finde.
Maximilian Peters
Upvoted! Keine Antwort, die ich gesucht habe, aber trotzdem sehr, sehr interessant :)
Cyril N.
29

Sie können die Bilddatei jederzeit von Anfang an als Graustufen mit imreadOpenCV lesen :

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

Wenn Sie das Bild als RGB lesen möchten, führen Sie eine Verarbeitung durch und konvertieren Sie es dann in Graustufen, die Sie cvtcolorvon OpenCV aus verwenden können:

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Diamantatos Paraskevas
quelle
6
Ftr: Die 0Flagge ist cv2.CV_LOAD_IMAGE_GRAYSCALE.
dtk
24

Der schnellste und aktuellste Weg ist die Verwendung von Pillow , installiert über pip install Pillow.

Der Code lautet dann:

from PIL import Image
img = Image.open('input_file.jpg').convert('L')
img.save('output_file.jpg')
YPCrumble
quelle
3
Beachten Sie, dass, wenn Sie Ihre Methoden nicht wie im obigen Beispiel verketten, converteine konvertierte Kopie des Bildes zurückgegeben wird
Matt
funktioniert nicht für 32-Bit-PNG, Werte werden auf 255 geklemmt
Andrew Matuk
11

Das Tutorial betrügt, weil es mit einem in RGB codierten Graustufenbild beginnt. Sie schneiden also nur einen einzelnen Farbkanal und behandeln ihn als Graustufen. Die grundlegenden Schritte, die Sie ausführen müssen, bestehen darin, den RGB-Farbraum in einen Farbraum umzuwandeln, der mit etwas codiert, das dem Luma / Chroma-Modell nahekommt, wie z. B. YUV / YIQ oder HSL / HSV. Anschließend wird der Luma-ähnliche Kanal abgeschnitten und als verwendet Ihr Graustufenbild. matplotlibscheint keinen Mechanismus für die Konvertierung in YUV / YIQ bereitzustellen, aber Sie können damit in HSV konvertieren.

Versuchen Sie, matplotlib.colors.rgb_to_hsv(img)den letzten Wert (V) aus dem Array für Ihre Graustufen zu schneiden. Es ist nicht ganz dasselbe wie ein Luma-Wert, aber es bedeutet, dass Sie alles in tun können matplotlib.

Hintergrund:

Alternativ können Sie PIL oder das integrierte Element verwenden colorsys.rgb_to_yiq(), um in einen Farbraum mit einem echten Luma-Wert zu konvertieren. Sie könnten auch All-In gehen und Ihren eigenen Luma-Konverter rollen, obwohl das wahrscheinlich übertrieben ist.

Silas Ray
quelle
9

Mit dieser Formel

Y' = 0.299 R + 0.587 G + 0.114 B 

Wir können es tun

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

Das GIMP , das Farbe in Graustufenbildsoftware konvertiert, verfügt jedoch über drei Algorithmen, um diese Aufgabe zu erledigen.

iNet
quelle
8

Wenn Sie NumPy / SciPy bereits verwenden, können Sie auch Folgendes verwenden :

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

dtk
quelle
5
Beide scipy.ndimage.imread()und scipy.misc.imread()sind in SciPy 1.0.0 formal veraltet und werden in SciPy 1.2.0 dauerhaft entfernt . Während die Dokumentation von SciPy imageio.imread()einen geeigneten Ersatz empfiehlt , ist die API dieser Funktion bis zur Absurdität ein absolutes Muss. Es bietet keine Unterstützung für die Graustufenkonvertierung und ist daher für viele Anwendungen - einschließlich unserer - ungeeignet. </sigh>
Cecil Curry
5
@CecilCurry, wie konvertiert man mit imageio ein farbiges Bild in Graustufen?
0x90
5

du könntest es tun:

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
quelle
5

Verwenden Sie img.Convert (), unterstützt "L", "RGB" und "CMYK". Modus

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)

Ausgabe:

[[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
quelle
1
sollte die 5. Zeile sein img = img.convert('L')?
Allan Ruin
3

Ich bin über Google auf diese Frage gekommen und habe nach einer Möglichkeit gesucht, ein bereits geladenes Bild in Graustufen umzuwandeln.

Hier ist eine Möglichkeit, dies mit SciPy zu tun:

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
quelle
1
Nett. Ich möchte nur darauf hinweisen, dass eine kürzere Lösung wäreimg_gray = numpy.average(img, weights=[0.299, 0.587, 0.114], axis=2)
Akavall
@ Akavall Schön zu wissen, danke! Wissen Sie, ob Ihre Verknüpfung schneller ist? Wenn nicht, würde ich meine behalten, weil es leichter zu verstehen ist.
Martin Thoma
Ich habe es nicht zeitlich festgelegt, mein Bauchgefühl ist numpy.averageetwas schneller, aber praktisch nicht anders. Ihre Lösung ist klar und enthält relevante Informationen zu R, G, B, daher würde ich sie behalten. Mein Kommentar war eher eine zusätzliche Option, kein Ersatz.
Akavall
Beide scipy.ndimage.imread()und scipy.misc.imread()sind in SciPy 1.0.0 formal veraltet und werden in SciPy 1.2.0 dauerhaft entfernt . Sie wollen wahrscheinlich nur Pillow builtin Graustufen - Konvertierung unterstützen verwenden (ala unutbu ‚s Antwort ), statt.
Cecil Curry
-3
image=myCamera.getImage().crop(xx,xx,xx,xx).scale(xx,xx).greyscale()

Sie können greyscale()direkt für die Transformation verwenden.

Qian Han
quelle