Como posso converter uma imagem RGB em escala de cinza em Python?

206

Estou tentando usar matplotlibpara ler uma imagem RGB e convertê-la em escala de cinza.

No matlab eu uso isso:

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

No tutorial matplotlib, eles não abordam isso. Eles apenas lêem na imagem

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

e eles dividem a matriz, mas isso não é o mesmo que converter RGB em escala de cinza do que eu entendo.

lum_img = img[:,:,0]

Acho difícil acreditar que numpy ou matplotlib não tenha uma função integrada para converter de rgb para cinza. Isso não é uma operação comum no processamento de imagens?

Eu escrevi uma função muito simples que funciona com a imagem importada usando imreadem 5 minutos. É terrivelmente ineficiente, mas é por isso que eu esperava uma implementação profissional integrada.

Sebastian melhorou minha função, mas ainda espero encontrar a incorporada.

implementação do 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
fonte
2
Note que você pode escrever a mesma coisa que a sua função rgb2gray simplesmente como: gray = np.mean(rgb, -1). Talvez rgb[...,:3]exista se for realmente rgba.
seberg 31/08/12
hmm, gray = np.mean(rgb, -1)funciona bem. obrigado. Existe alguma razão para não usar isso? Por que eu usaria as soluções nas respostas abaixo?
Waspinator
6
A página da Wikipedia em escala de cinza diz que o método de conversão de RGB em escala de cinza não é exclusivo, mas fornece as fórmulas usadas com base na luminância. É bem diferente de np.mean(rgb, -1).
Unutbu 31/08/12
2
então eu acho que quero a versão do Matlab ? 0.2989 * R + 0.5870 * G + 0.1140 * B Estou assumindo que é a maneira padrão de fazê-lo.
Waspinator

Respostas:

304

Que tal fazer isso com o Pillow :

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

Usando matplotlib e a fórmula

Y' = 0.2989 R + 0.5870 G + 0.1140 B 

você poderia fazer:

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
fonte
3
Se ele tiver que usar matplotlibpor algum outro motivo, ele poderá usar o built-in colorsys.rgb_to_yiq()para transformar mais uma fatia para obter apenas o canal luma.
Silas Ray
34
por que .convert('LA')? por que não .convert('gray')? Parece desnecessariamente enigmático. A documentação do PIL não menciona nada sobre 'LA' para a função de conversão.
Waspinator
25
usando PIL: cannot write mode LA as JPEG, eu precisava usar o modo L não LA
jsky
6
Isso img = Image.open('image.png').convert('LA')precisa serimg = Image.open('image.png').convert('L')
nviens
12
@BluePython: o LAmodo possui luminosidade (brilho) e alfa. Se você usar o LAmodo, greyscale.pnghaverá uma imagem RGBA com o canal alfa de image.pngpreservado. Se você usar o Lmodo, greyscale.pnghaverá uma imagem RGB (sem alfa).
Unutbu
69

Você também pode usar o scikit-image , que fornece algumas funções para converter uma imagem ndarray, como rgb2gray.

from skimage import color
from skimage import io

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

Notas : Os pesos usados ​​nesta conversão são calibrados para os fósforos de CRT contemporâneos: Y = 0,2125 R + 0,7154 G + 0,0721 B

Como alternativa, você pode ler a imagem em escala de cinza por:

from skimage import io
img = io.imread('image.png', as_gray=True)
yangjie
fonte
é normal que eu esteja recebendo 0 <valores <1? Devo multiplicá-los por 255 para obter a escala de cinza real?
Sam
sabendo que meu objetivo é usar GLCM apresenta (greycoprops)
Sam
Nota para io.imread: "as_grey" foi preterido em favor de "as_gray". Mesmo uso, apenas ortografia americanizada. :)
Halogen
1
Eu acredito que esta é a resposta mais útil para a pergunta em questão, a saída disso também é compatível com matplotlib e numpy.
Mert Beşiktepe 14/11/19
Estou usando o objeto de cor, mas minha imagem está meio avermelhada agora e não cinza (preto e branco). Eu preciso usar cmapcomo gray' then only the image is shown as gray in pyplot.imshow () `? Alguma ideia ? Onde eu estou errado?
GadaaDhaariGeek 17/01
63

Três dos métodos sugeridos foram testados quanto à velocidade com 1000 imagens PNG RGBA (224 x 256 pixels) em execução com Python 3.5 no Ubuntu 16.04 LTS (Xeon E5 2670 com SSD).

Tempos médios de execução

pil : 1.037 segundos

scipy: 1.040 segundos

sk : 2.120 segundos

PIL e SciPy deram numpymatrizes idênticas (variando de 0 a 255). O SkImage fornece matrizes de 0 a 1. Além disso, as cores são convertidas ligeiramente diferentes, veja o exemplo do conjunto de dados CUB-200.

SkImage: SkImage

PIL : PIL

SciPy : SciPy

Original: Original

Diff : insira a descrição da imagem aqui

Código

  1. atuação

    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. Resultado
    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. Comparação
    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. Importações
    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. Versões
    skimage.version
    0.13.0
    scipy.version
    0.19.1
    np.version
    1.13.1
Maximilian Peters
fonte
6
A imagem de E / S do SciPy é literalmente PIL / Pillow. Portanto, testar o SciPy está efetivamente testando novamente o PIL / Pillow com sobrecarga insignificante introduzida pelas funções de invólucro do SciPy. Teria sido muito mais útil substituir o SciPy pelo OpenCV (que não utiliza PIL / Pillow) pelo SciPy. No entanto, obrigado pelo benchmarking dedicado! A desaceleração discernível imposta pelo SciKit é fascinante ... e horrível.
Cecil Curry
@CecilCurry Obrigado pela ideia com o OpenCV! Adicionarei quando encontrar algum tempo livre.
Maximilian Peters #
Voto a favor! Não é uma resposta que eu estava procurando, mas muito, muito interessante, no entanto :)
Cyril N.
29

Você sempre pode ler o arquivo de imagem como escala de cinza desde o início, usando o imreadOpenCV:

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

Além disso, caso você queira ler a imagem como RGB, faça algum processamento e, em seguida, converta em Escala de cinza que você pode usar cvtcolorno OpenCV:

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Diamantatos Paraskevas
fonte
6
Ftr: A 0bandeira é cv2.CV_LOAD_IMAGE_GRAYSCALE.
dtk
24

A maneira mais rápida e atual é usar o Pillow , instalado via pip install Pillow.

O código é então:

from PIL import Image
img = Image.open('input_file.jpg').convert('L')
img.save('output_file.jpg')
YPCrumble
fonte
3
note que, se você não está encadeamento seus métodos como no exemplo acima, convertretorna uma cópia convertida do imagem
Matt
não funciona para PNG de 32 bits, os valores serão fixados em 255
Andrew Matuk
11

O tutorial está enganando, porque está começando com uma imagem em escala de cinza codificada em RGB, para que eles apenas cortem um canal de cor única e o tratem como em escala de cinza. As etapas básicas que você precisa fazer são transformar o espaço de cores RGB em um espaço de cores que codifique algo semelhante ao modelo luma / chroma, como YUV / YIQ ou HSL / HSV, em seguida, corte o canal tipo luma e use-o como sua imagem em escala de cinza. matplotlibparece não fornecer um mecanismo para converter em YUV / YIQ, mas permite converter em HSV.

Tente usar e matplotlib.colors.rgb_to_hsv(img)depois fatiar o último valor (V) da matriz para sua escala de cinza. Não é exatamente o mesmo que um valor luma, mas significa que você pode fazer tudomatplotlib .

Fundo:

Como alternativa, você pode usar o PIL ou o builtin colorsys.rgb_to_yiq()para converter em um espaço de cores com um verdadeiro valor de luma. Você também pode usar o seu próprio conversor de luma, mas isso provavelmente é um exagero.

Silas Ray
fonte
9

Usando esta fórmula

Y' = 0.299 R + 0.587 G + 0.114 B 

Nós podemos fazer

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

No entanto, o software de conversão de cores em escala de cinza do GIMP possui três algoritmos para executar a tarefa.

iNet
fonte
8

Se você já usa o NumPy / SciPy, também pode usar :

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

dtk
fonte
5
Ambos scipy.ndimage.imread()e scipy.misc.imread()são formalmente obsoleto em SciPy 1.0.0 e será removido permanentemente em SciPy 1.2.0. Embora a documentação do SciPy recomende imageio.imread()como um substituto adequado, a API dessa função é essencial para o absurdo. Ele não oferece suporte à conversão em escala de cinza e, portanto, permanece inadequado para muitos aplicativos - incluindo o nosso. </sigh>
Cecil Curry
5
@CecilCurry, como você converte uma imagem colorida em escala de cinza usando o imageio?
0x90
5

você poderia fazer:

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

Use img.Convert (), suporta "L", "RGB" e "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)

Resultado:

[[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
fonte
1
a quinta linha deve ser img = img.convert('L')?
Allan Ruin
3

Cheguei a essa pergunta pelo Google, procurando uma maneira de converter uma imagem já carregada em escala de cinza.

Aqui está uma maneira de fazer isso com o 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
fonte
1
Agradável. Eu só quero observar a solução a mais curto seriaimg_gray = numpy.average(img, weights=[0.299, 0.587, 0.114], axis=2)
Akavall
@ Akavall É bom saber, obrigado! Você sabe se o seu atalho é mais rápido? Caso contrário, eu ficaria com o meu porque é mais fácil de entender.
Martin Thoma
Eu não cronometrei, meu pressentimento numpy.averageé um pouco mais rápido, mas não é praticamente diferente. Sua solução é clara e possui informações relevantes sobre R, G, B, portanto, eu a manteria. Meu comentário foi mais uma opção adicional, não uma substituição.
Akavall 4/17/17
Ambos scipy.ndimage.imread()e scipy.misc.imread()são formalmente obsoleto em SciPy 1.0.0 e será removido permanentemente em SciPy 1.2.0. Você provavelmente só quer usar suporte de conversão embutida em tons de cinza do Pillow (ala unutbu de resposta ), em vez.
Cecil Curry
-3
image=myCamera.getImage().crop(xx,xx,xx,xx).scale(xx,xx).greyscale()

Você pode usar greyscale()diretamente para a transformação.

Qian Han
fonte