Comment puis-je convertir une image RVB en niveaux de gris en Python?

206

J'essaie d'utiliser matplotlibpour lire une image RVB et la convertir en niveaux de gris.

Dans matlab j'utilise ceci:

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

Dans le tutoriel matplotlib, ils ne le couvrent pas. Ils lisent juste dans l'image

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

puis ils coupent le tableau, mais ce n'est pas la même chose que la conversion RVB en niveaux de gris de ce que je comprends.

lum_img = img[:,:,0]

J'ai du mal à croire que numpy ou matplotlib n'a pas de fonction intégrée pour convertir de rgb en gris. N'est-ce pas une opération courante dans le traitement d'image?

J'ai écrit une fonction très simple qui fonctionne avec l'image importée imreaden 5 minutes. C'est horriblement inefficace, mais c'est pourquoi j'espérais une implémentation professionnelle intégrée.

Sebastian a amélioré ma fonction, mais j'espère toujours trouver celle intégrée.

Implémentation de 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
la source
2
Notez que vous pouvez écrire la même chose que votre fonction rgb2gray simplement: gray = np.mean(rgb, -1). Peut-être rgb[...,:3]là si c'est réellement rgba.
seberg
hmm, ça gray = np.mean(rgb, -1)marche bien. Merci. Y a-t-il une raison de ne pas l'utiliser? Pourquoi utiliserais-je plutôt les solutions des réponses ci-dessous?
waspinator
6
La page wikipedia en niveaux de gris indique que la méthode de conversion RVB en niveaux de gris n'est pas unique, mais donne une formule couramment utilisée basée sur la luminance. C'est assez différent de np.mean(rgb, -1).
unutbu
2
donc je suppose que je veux la version de Matlab ? 0.2989 * R + 0.5870 * G + 0.1140 * B Je suppose que c'est la façon standard de procéder.
waspinator

Réponses:

304

Que diriez-vous de le faire avec Pillow :

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

Utilisation de matplotlib et de la formule

Y' = 0.2989 R + 0.5870 G + 0.1140 B 

vous pourriez faire:

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
la source
3
S'il doit utiliser matplotlibpour une autre raison, il devrait pouvoir utiliser la fonction intégrée colorsys.rgb_to_yiq()pour se transformer plus une tranche pour obtenir uniquement le canal luma.
Silas Ray
35
pourquoi .convert('LA')? pourquoi pas .convert('gray')? Semble inutilement cryptique. La documentation PIL ne mentionne rien sur «LA» pour la fonction de conversion.
waspinator
25
en utilisant PIL cannot write mode LA as JPEG
:,
6
Cela img = Image.open('image.png').convert('LA')doit êtreimg = Image.open('image.png').convert('L')
nviens
12
@BluePython: le LAmode a une luminosité (luminosité) et alpha. Si vous utilisez le LAmode, alors greyscale.pngsera une image RGBA avec le canal alpha de image.pngpréservé. Si vous utilisez le Lmode, alors greyscale.pngsera une image RVB (sans alpha).
unutbu
69

Vous pouvez également utiliser scikit-image , qui fournit certaines fonctions pour convertir une image en ndarray, comme rgb2gray.

from skimage import color
from skimage import io

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

Remarques : Les poids utilisés dans cette conversion sont calibrés pour les luminophores CRT contemporains: Y = 0,2125 R + 0,7154 G + 0,0721 B

Alternativement, vous pouvez lire l'image en niveaux de gris en:

from skimage import io
img = io.imread('image.png', as_gray=True)
yangjie
la source
est-il normal que j'obtienne 0 <valeurs <1? Suis-je censé les multiplier par 255 pour obtenir la véritable échelle de gris?
Sam
sachant que mon objectif est d'utiliser les fonctionnalités GLCM (greycoprops)
Sam
Remarque pour io.imread: "as_grey" a été déconseillé au profit de "as_gray". Même usage, juste une orthographe américanisée. :)
Halogène
1
Je crois que c'est la réponse la plus utile à la question, la sortie de ceci est également compatible avec matplotlib et numpy.
Mert Beşiktepe
J'utilise l'objet couleur mais mon image est maintenant plutôt rougeâtre et non grise (noir et blanc). Je dois utiliser cmapcomme gray' then only the image is shown as gray in pyplot.imshow () `? Des pensées ? Où ai-je tort?
GadaaDhaariGeek
63

Trois des méthodes suggérées ont été testées pour la vitesse avec 1000 images PNG RGBA (224 x 256 pixels) fonctionnant avec Python 3.5 sur Ubuntu 16.04 LTS (Xeon E5 2670 avec SSD).

Temps d'exécution moyens

pil : 1,037 secondes

scipy: 1.040 secondes

sk : 2.120 secondes

PIL et SciPy ont donné identique numpy tableaux (allant de 0 à 255). SkImage donne des tableaux de 0 à 1. De plus, les couleurs sont converties légèrement différentes, voir l'exemple du jeu de données CUB-200.

SkImage: SkImage

PIL : PIL

SciPy : SciPy

Original: Original

Diff : entrez la description de l'image ici

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. Production
    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. Comparaison
    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. Importations
    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. Versions
    skimage.version
    0.13.0
    scipy.version
    0.19.1
    np.version
    1.13.1
Maximilian Peters
la source
6
Les E / S d'image de SciPy sont littéralement PIL / Oreiller. Par conséquent, tester SciPy consiste à retester efficacement PIL / Pillow avec un surcoût négligeable introduit par les fonctions d'emballage de SciPy. Il aurait été beaucoup plus utile de substituer OpenCV (qui ne tire pas parti de PIL / Pillow) à SciPy (qui le fait). Néanmoins, merci pour l'analyse comparative dédiée! Le ralentissement perceptible imposé par SciKit est fascinant ... et horrible.
Cecil Curry,
@CecilCurry Merci pour l'idée avec OpenCV! Je l'ajouterai quand je trouverai du temps libre.
Maximilian Peters
A voté! Pas une réponse que je cherchais, mais très très intéressante quand même :)
Cyril N.
29

Vous pouvez toujours lire le fichier image en niveaux de gris dès le début en utilisant imreaddepuis OpenCV:

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

De plus, si vous souhaitez lire l'image en RVB, effectuez un traitement puis convertissez-le en niveaux de gris que vous pourriez utiliser à cvtcolorpartir d'OpenCV:

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Diamantatos Paraskevas
la source
6
Ftr: Le 0drapeau est cv2.CV_LOAD_IMAGE_GRAYSCALE.
dtk
24

Le moyen le plus rapide et le plus actuel consiste à utiliser Pillow , installé viapip install Pillow .

Le code est alors:

from PIL import Image
img = Image.open('input_file.jpg').convert('L')
img.save('output_file.jpg')
YPCrumble
la source
3
notez que, si vous ne chaînez pas vos méthodes comme dans l'exemple ci-dessus, convertretourne une copie convertie de l'image
Matt
ne fonctionne pas pour 32 bits PNG, les valeurs seront serré à 255
Andrew Matuk
11

Le didacticiel triche car il commence avec une image en niveaux de gris encodée en RVB, donc ils découpent simplement un canal de couleur unique et le traitent en niveaux de gris. Les étapes de base que vous devez faire sont de passer de l'espace colorimétrique RVB à un espace colorimétrique qui code avec quelque chose qui se rapproche du modèle luma / chroma, tel que YUV / YIQ ou HSL / HSV, puis de couper le canal de type luma et de l'utiliser comme votre image en niveaux de gris. matplotlibne semble pas fournir un mécanisme pour convertir en YUV / YIQ, mais il vous permet de convertir en HSV.

Essayez d'utiliser matplotlib.colors.rgb_to_hsv(img)puis de trancher la dernière valeur (V) du tableau pour votre échelle de gris. Ce n'est pas tout à fait la même chose qu'une valeur de luma, mais cela signifie que vous pouvez tout faire enmatplotlib .

Contexte:

Alternativement, vous pouvez utiliser PIL ou la fonction intégrée colorsys.rgb_to_yiq()pour convertir en un espace colorimétrique avec une véritable valeur de luma. Vous pouvez également vous lancer et lancer votre propre convertisseur luma uniquement, bien que ce soit probablement exagéré.

Silas Ray
la source
9

Utilisation de cette formule

Y' = 0.299 R + 0.587 G + 0.114 B 

Nous pouvons faire

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

Cependant, le logiciel de conversion de couleurs GIMP en niveaux de gris dispose de trois algorithmes pour effectuer la tâche.

iNet
la source
8

Si vous utilisez déjà NumPy / SciPy, vous pouvez également utiliser :

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

dtk
la source
5
Les deux scipy.ndimage.imread()et scipy.misc.imread()sont officiellement obsolètes dans SciPy 1.0.0 et seront définitivement supprimés dans SciPy 1.2.0. Alors que la documentation de SciPy recommande imageio.imread()comme remplacement approprié, l'API de cette fonction est nue jusqu'à l'absurdité. Il ne fournit aucun support pour la conversion en niveaux de gris et reste donc inadapté à de nombreuses applications - y compris la nôtre. </sigh>
Cecil Curry
5
@CecilCurry, comment convertir une image colorée en niveaux de gris en utilisant imageio?
0x90
5

vous pourriez faire:

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
la source
5

Utilisez img.Convert (), prend en charge «L», «RGB» et «CMYK». mode

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)

Production:

[[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
la source
1
devrait être la 5ème ligne img = img.convert('L')?
Allan Ruin
3

Je suis arrivé à cette question via Google, à la recherche d'un moyen de convertir une image déjà chargée en niveaux de gris.

Voici une façon de le faire avec 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
la source
1
Agréable. Je veux juste noter qu'une solution plus courte seraitimg_gray = numpy.average(img, weights=[0.299, 0.587, 0.114], axis=2)
Akavall
@Akavall Bon à savoir, merci! Savez-vous si votre raccourci est plus rapide? Sinon, je garderais le mien car il est plus facile à comprendre.
Martin Thoma
Je ne l'ai pas chronométré, mon instinct numpy.averageest un peu plus rapide mais pas pratiquement différent. Votre solution est claire et contient des informations pertinentes sur R, G, B, donc je la garderais. Mon commentaire était plus une option supplémentaire, pas un remplacement.
Akavall
Les deux scipy.ndimage.imread()et scipy.misc.imread()sont officiellement obsolètes dans SciPy 1.0.0 et seront définitivement supprimés dans SciPy 1.2.0. Vous voulez probablement juste utiliser le support de conversion en niveaux de gris de builtin Oreiller (ala unutbu de réponse ), au lieu.
Cecil Curry
-3
image=myCamera.getImage().crop(xx,xx,xx,xx).scale(xx,xx).greyscale()

Vous pouvez utiliser greyscale()directement pour la transformation.

Qian Han
la source