  # How can I convert an RGB image into grayscale in Python?

StackOverflow

I"m trying to use `matplotlib` to read in an RGB image and convert it to grayscale.

In matlab I use this:

``````img = rgb2gray(imread("image.png"));
``````

In the matplotlib tutorial they don"t cover it. They just read in the image

``````import matplotlib.image as mpimg
``````

and then they slice the array, but that"s not the same thing as converting RGB to grayscale from what I understand.

``````lum_img = img[:,:,0]
``````

I find it hard to believe that numpy or matplotlib doesn"t have a built-in function to convert from rgb to gray. Isn"t this a common operation in image processing?

I wrote a very simple function that works with the image imported using `imread` in 5 minutes. It"s horribly inefficient, but that"s why I was hoping for a professional implementation built-in.

Sebastian has improved my function, but I"m still hoping to find the built-in one.

matlab"s (NTSC/PAL) implementation:

``````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
``````

How about doing it with Pillow:

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

Using matplotlib and the formula

``````Y" = 0.2989 R + 0.5870 G + 0.1140 B
``````

you could do:

``````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])

gray = rgb2gray(img)
plt.imshow(gray, cmap=plt.get_cmap("gray"), vmin=0, vmax=1)
plt.show()
``````

You can also use scikit-image, which provides some functions to convert an image in `ndarray`, like `rgb2gray`.

``````from skimage import color
from skimage import io

``````

Notes: The weights used in this conversion are calibrated for contemporary CRT phosphors: Y = 0.2125 R + 0.7154 G + 0.0721 B

Alternatively, you can read image in grayscale by:

``````from skimage import io
``````

Three of the suggested methods were tested for speed with 1000 RGBA PNG images (224 x 256 pixels) running with Python 3.5 on Ubuntu 16.04 LTS (Xeon E5 2670 with SSD).

Average run times

`pil :` 1.037 seconds

`scipy:` 1.040 seconds

`sk :` 2.120 seconds

PIL and SciPy gave identical `numpy` arrays (ranging from 0 to 255). SkImage gives arrays from 0 to 1. In addition the colors are converted slightly different, see the example from the CUB-200 dataset.

`SkImage:` `PIL :` `SciPy :` `Original:` `Diff :` 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)
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)
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. Output
``````z = "Cardinal_0007_3025810472.jpg"
IPython.display.display(PIL.Image.fromarray(img1).convert("RGB"))
img2 = np.array(Image.open(z).convert("L"))
IPython.display.display(PIL.Image.fromarray(img2))
IPython.display.display(PIL.Image.fromarray(img3))
``````
3. Comparison
``````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. Imports
``````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
``````