¿Cómo puedo convertir una imagen RGB en escala de grises en Python?

206

Estoy tratando de usar matplotlibpara leer una imagen RGB y convertirla a escala de grises.

En matlab uso esto:

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

En el tutorial matplotlib no lo cubren. Acaban de leer en la imagen

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

y luego cortan la matriz, pero eso no es lo mismo que convertir RGB a escala de grises de lo que entiendo.

lum_img = img[:,:,0]

Me resulta difícil creer que numpy o matplotlib no tengan una función incorporada para convertir de rgb a gris. ¿No es esta una operación común en el procesamiento de imágenes?

Escribí una función muy simple que funciona con la imagen importada usando imreaden 5 minutos. Es terriblemente ineficiente, pero es por eso que esperaba una implementación profesional integrada.

Sebastian ha mejorado mi función, pero todavía espero encontrar el integrado.

Implementación 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
fuente
2
Tenga en cuenta que usted puede escribir lo mismo que su función rgb2gray simplemente como: gray = np.mean(rgb, -1). Tal vez rgb[...,:3]allí si en realidad es rgba.
seberg
hmm, gray = np.mean(rgb, -1)funciona bien Gracias. ¿Hay alguna razón para no usar esto? ¿Por qué usaría las soluciones en las respuestas a continuación?
waspinator
66
La página de Wikipedia en escala de grises dice que el método de convertir RGB a escala de grises no es único, pero ofrece fórmulas de uso común basadas en la luminancia. Es muy diferente a np.mean(rgb, -1).
unutbu
2
así que supongo que quiero la versión de Matlab ? 0.2989 * R + 0.5870 * G + 0.1140 * B Supongo que es la forma estándar de hacerlo.
waspinator

Respuestas:

304

¿Qué tal hacerlo con Pillow :

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

Usando matplotlib y la fórmula

Y' = 0.2989 R + 0.5870 G + 0.1140 B 

Podrías hacerlo:

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
fuente
3
Si tiene que usarlo matplotlibpor alguna otra razón, debería poder usar el builtin colorsys.rgb_to_yiq()para transformar más un corte para obtener solo el canal luma.
Silas Ray
34
¿Por qué .convert('LA')? ¿por qué no .convert('gray')? Parece innecesariamente críptico. La documentación PIL no menciona nada sobre 'LA' para la función de conversión.
waspinator
25
usando PIL: cannot write mode LA as JPEGnecesitaba usar el modo L, no LA
jsky
66
Esto img = Image.open('image.png').convert('LA')tiene que serimg = Image.open('image.png').convert('L')
nviens
12
@BluePython: el LAmodo tiene luminosidad (brillo) y alfa. Si usa el LAmodo, greyscale.pngserá una imagen RGBA con el canal alfa de image.pngpreservado. Si usa el Lmodo, greyscale.pngserá una imagen RGB (sin alfa).
unutbu
69

También puede usar scikit-image , que proporciona algunas funciones para convertir una imagen ndarray, como rgb2gray.

from skimage import color
from skimage import io

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

Notas : Los pesos utilizados en esta conversión están calibrados para fósforos CRT contemporáneos: Y = 0.2125 R + 0.7154 G + 0.0721 B

Alternativamente, puede leer la imagen en escala de grises de la siguiente manera:

from skimage import io
img = io.imread('image.png', as_gray=True)
yangjie
fuente
¿Es normal que obtenga 0 <valores <1? ¿Se supone que debo multiplicarlos por 255 para obtener la escala de grises real?
Sam
sabiendo que mi objetivo es usar las funciones de GLCM (greycoprops)
Sam
Nota para io.imread: "as_grey" ha quedado en desuso a favor de "as_gray". El mismo uso, solo ortografía americanizada. :)
Halógeno
1
Creo que esta es la respuesta más útil a la pregunta en cuestión, la salida de esto también es compatible con matplotlib y numpy.
Mert Beşiktepe
Estoy usando el objeto de color, pero mi imagen ahora es algo rojiza y no gris (blanco y negro). Necesito usar cmapcomo gray' then only the image is shown as gray in pyplot.imshow () `? Alguna idea ? Donde me equivoco
GadaaDhaariGeek
63

Se probó la velocidad de tres de los métodos sugeridos con 1000 imágenes PNG RGBA (224 x 256 píxeles) ejecutándose con Python 3.5 en Ubuntu 16.04 LTS (Xeon E5 2670 con SSD).

Tiempos de ejecución promedio

pil : 1.037 segundos

scipy: 1.040 segundos

sk : 2.120 segundos

PIL y SciPy dieron numpymatrices idénticas (que van de 0 a 255). SkImage proporciona matrices de 0 a 1. Además, los colores se convierten ligeramente diferentes, consulte el ejemplo del conjunto de datos CUB-200.

SkImage: SkImage

PIL : PIL

SciPy : Ciencia

Original: Original

Diff : ingrese la descripción de la imagen aquí

Código

  1. Actuación

    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. Salida
    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. Comparación
    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. Importaciones
    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. Versiones
    skimage.version
    0.13.0
    scipy.version
    0.19.1
    np.version
    1.13.1
Maximilian Peters
fuente
66
La E / S de la imagen de SciPy es literalmente PIL / Pillow. Por lo tanto, probar SciPy es volver a probar PIL / Pillow con una sobrecarga insignificante introducida por las funciones de envoltura de SciPy. Hubiera sido mucho más útil sustituir OpenCV (que no aprovecha PIL / Pillow) por SciPy (que sí lo hace). No obstante, ¡gracias por la evaluación comparativa dedicada! La desaceleración discernible impuesta por SciKit es fascinante ... y horrible.
Cecil Curry
@CecilCurry ¡Gracias por la idea con OpenCV! Lo agregaré cuando encuentre algo de tiempo libre.
Maximilian Peters
¡Votado! No era una respuesta que estaba buscando, pero de todos modos es muy interesante :)
Cyril N.
29

Siempre puede leer el archivo de imagen como escala de grises desde el principio utilizando imreadOpenCV:

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

Además, en caso de que desee leer la imagen como RGB, realice un procesamiento y luego conviértalo a Escala de grises que podría usar cvtcolordesde OpenCV:

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Diamantatos Paraskevas
fuente
66
Ftr: La 0bandera es cv2.CV_LOAD_IMAGE_GRAYSCALE.
dtk
24

La forma más rápida y actual es usar Pillow , instalado a través de pip install Pillow.

El código es entonces:

from PIL import Image
img = Image.open('input_file.jpg').convert('L')
img.save('output_file.jpg')
YPCrumble
fuente
3
tenga en cuenta que, si no está encadenando sus métodos como en el ejemplo anterior, convertdevuelve una copia convertida de la imagen
Matt
no funciona para PNG de 32 bits, los valores se fijarán a 255
Andrew Matuk
11

El tutorial es engañoso porque comienza con una imagen en escala de grises codificada en RGB, por lo que solo cortan un solo canal de color y lo tratan como escala de grises. Los pasos básicos que debe hacer son transformar del espacio de color RGB a un espacio de color que codifique con algo que se aproxime al modelo luma / croma, como YUV / YIQ o HSL / HSV, luego corte el canal similar a luma y úselo como tu imagen en escala de grises. matplotlibno parece proporcionar un mecanismo para convertir a YUV / YIQ, pero le permite convertir a HSV.

Intente usar y matplotlib.colors.rgb_to_hsv(img)luego cortar el último valor (V) de la matriz para su escala de grises. No es lo mismo que un valor de luma, pero significa que puede hacerlo todo matplotlib.

Antecedentes:

Alternativamente, puede usar PIL o el incorporado colorsys.rgb_to_yiq()para convertir a un espacio de color con un verdadero valor de luma. También podrías entrar y rodar tu propio convertidor solo de luma, aunque eso probablemente sea exagerado.

Rayo Silas
fuente
9

Usando esta fórmula

Y' = 0.299 R + 0.587 G + 0.114 B 

Podemos hacer

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

Sin embargo, el software de conversión de color GIMP a imagen en escala de grises tiene tres algoritmos para realizar la tarea.

iNet
fuente
8

Si ya estás usando NumPy / SciPy, también puedes usar :

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

dtk
fuente
55
Ambos scipy.ndimage.imread()y scipy.misc.imread()están formalmente en desuso en SciPy 1.0.0 y se eliminarán permanentemente en SciPy 1.2.0. Si bien la documentación de SciPy recomienda imageio.imread()como un reemplazo adecuado, la API de esta función es básica hasta el punto de lo absurdo. Proporciona sin soporte para la conversión de escala de grises y por lo tanto sigue siendo inadecuada para muchas aplicaciones - incluyendo el nuestro. </sigh>
Cecil Curry
55
@CecilCurry, ¿cómo convierte una imagen en color en escala de grises con imageio?
0x90
5

Podrías hacerlo:

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

Use img.Convert (), admite "L", "RGB" y "CMYK". modo

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)

Salida:

[[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
fuente
1
debería ser la quinta línea img = img.convert('L')?
Allan Ruin
3

Llegué a esta pregunta a través de Google, buscando una forma de convertir una imagen ya cargada en escala de grises.

Aquí hay una manera de hacerlo 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)
Martin Thoma
fuente
1
Agradable. Solo quiero señalar que una solución más corta seríaimg_gray = numpy.average(img, weights=[0.299, 0.587, 0.114], axis=2)
Akavall
@Akavall Es bueno saberlo, ¡gracias! ¿Sabes si tu atajo es más rápido? Si no, me quedaría con la mía porque es más fácil de entender.
Martin Thoma
No lo cronometré, mi instinto numpy.averagees un poco más rápido pero no prácticamente diferente. Su solución es clara y tiene información relevante sobre R, G, B, por lo que la conservaría. Mi comentario fue más una opción adicional, no un reemplazo.
Akavall
Ambos scipy.ndimage.imread()y scipy.misc.imread()están formalmente en desuso en SciPy 1.0.0 y se eliminarán permanentemente en SciPy 1.2.0. Probablemente solo desee utilizar el soporte de conversión de escala de grises incorporado de Pillow ( la respuesta de ala unutbu ), en su lugar.
Cecil Curry
-3
image=myCamera.getImage().crop(xx,xx,xx,xx).scale(xx,xx).greyscale()

Puede usar greyscale()directamente para la transformación.

Qian Han
fuente