如何在Python中将RGB图像转换为灰度图像?

310

我正在尝试使用matplotlib读取RGB图像并将其转换为灰度图像。

在Matlab中,我使用以下代码:

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

Matplotlib教程中,他们没有涵盖这个问题。他们只是读取了图片。

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

然后他们切片数组,但据我所知,这与将RGB转换为灰度不是同一件事。

lum_img = img[:,:,0]

我觉得很难相信numpy或matplotlib没有内置的将RGB转换为灰度图像的函数。这不是图像处理中常见的操作吗?

我用imread导入的图像写了一个非常简单的函数,只用了5分钟就完成了。它效率极低,但这也是我希望有内置的专业实现的原因。

Sebastian改进了我的函数,但我仍然希望找到内置的函数。

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

2
请注意,您可以使用以下方式编写与rgb2gray相同的函数:gray = np.mean(rgb, -1)。如果实际上是RGBA,则可能需要在此处添加rgb[...,:3] - seberg
嗯,gray = np.mean(rgb, -1) 运行良好。谢谢。不使用这个的原因是什么?为什么要使用下面答案中的解决方案呢? - waspinator
8
“灰度维基百科页面”(http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale)指出将RGB转换为灰度的方法并不唯一,但提供了一些基于亮度的常用公式。这与“np.mean(rgb, -1)”有很大不同。 - unutbu
3
我想您需要Matlab的版本吗?0.2989 * R + 0.5870 * G + 0.1140 * B,我假设这是标准的做法。 - waspinator
1
难道不应该是0.2990 * R + 0.5870 * G + 0.1140 * B吗?权重总和应该等于1而不是0.9999。在这里检查:https://en.wikipedia.org/wiki/Grayscale - Alexandre Cartaxo
您可以在此处找到有关将图像转换为灰度的不同方法的详细讨论 - https://e2eml.school/convert_rgb_to_grayscale.html - truth
15个回答

464

使用 Pillow 呢?

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

如果输入图像中存在 alpha(透明度)通道且应保留该通道,请使用模式LA

img = Image.open('image.png').convert('LA')

使用matplotlib和公式

Y' = 0.2989 R + 0.5870 G + 0.1140 B 

你可以这样做:

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

47
为什么要使用.convert('LA')?为什么不使用.convert('gray')?这似乎过于晦涩难懂了。PIL文档中没有提到'LA'作为convert函数的选项。 - waspinator
31
使用PIL时出现了cannot write mode LA as JPEG的错误提示,我需要使用L模式而不是LA模式。 - jsky
3
为了获得与Matlab版本完全相同的结果(略有不同于ITU-R 601-2亮度变换,我指定了一个调整后的矩阵:img.convert('L', (0.2989, 0.5870, 0.1140, 0)) - dtk
11
这个代码“img = Image.open('image.png').convert('LA')”需要修改为“img = Image.open('image.png').convert('L')”。 - nviens
19
使用LA模式会保留亮度和 alpha 通道。如果使用LA模式,则 greyscale.png 将是一个带有 image.png 的 alpha 通道的 RGBA 图像。如果使用 L 模式,则 greyscale.png 将是一个 RGB 图像(没有 alpha 通道)。 - unutbu
显示剩余9条评论

98

你也可以使用scikit-image,它提供了一些用于将ndarray中的图像进行转换的函数,例如rgb2gray

from skimage import color
from skimage import io

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

:此转换中使用的权重是为当代CRT磷光体校准的:Y = 0.2125 R + 0.7154 G + 0.0721 B

或者,您可以通过以下方式以灰度读取图像:

from skimage import io
img = io.imread('image.png', as_gray=True)

我得到0<values<1是正常的吗?我需要将它们乘以255才能获得真正的灰度值吗? - Sam
知道我的目标是使用GLCM特征(greycoprops) - Sam
注意io.imread: "as_grey"已被弃用,推荐使用"as_gray"。用法相同,只是美式拼写。 :) - Halogen
1
我相信这是对手头问题最有用的答案,其输出也与matplotlib和numpy兼容。 - Mert Beşiktepe
我正在使用颜色对象,但我的图像现在有点偏红,而不是灰色(黑白)。我需要将cmap设置为gray,然后才能在pyplot.imshow()中显示灰度图像。你有什么想法吗?我错在哪里了吗? - GadaaDhaariGeek

91

测试了三种方法的速度,使用1000张RGBA PNG图片(224 x 256像素),在Ubuntu 16.04 LTS(Xeon E5 2670 with SSD)上使用Python 3.5运行。

平均运行时间

PIL : 1.037秒

SciPy: 1.040秒

Sk : 2.120秒

PIL和SciPy提供相同的numpy数组(范围从0到255)。SkImage提供从0到1的数组。此外,颜色转换略有不同,可以查看CUB-200数据集中的示例。

SkImage: SkImage

PIL : PIL

SciPy : SciPy

原图 : Original

区别 : enter image description here

代码

  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. Output
    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. 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.<strong>version</strong>
    0.13.0
    scipy.<strong>version</strong>
    0.19.1
    np.<strong>version</strong>
    1.13.1
    

14
SciPy的图像输入/输出功能实际上就是PIL/Pillow。因此,测试SciPy实际上等同于重新测试PIL/Pillow,只是由于SciPy的封装函数引入的开销可以忽略不计。如果用不依赖PIL/Pillow的OpenCV替换SciPy将会更有用得多。尽管如此,感谢您的专业基准测试!SciKit施加的明显减速令人着迷...并且令人恐惧。 - Cecil Curry

42

你可以始终使用OpenCV中的imread函数从一开始就将图像文件读取为灰度:

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

此外,如果您想将图像读取为RGB格式,进行一些处理然后转换为灰度图像,则可以使用OpenCV中的cvtcolor

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

12
Ftr:0标志是cv2.CV_LOAD_IMAGE_GRAYSCALE - dtk

29

最快且当前的方法是使用通过pip install Pillow安装的Pillow

代码如下:

from PIL import Image
img = Image.open('input_file.jpg').convert('L')
img.save('output_file.jpg')

3
注意,如果您不像上面的示例一样链接方法,convert将返回图像的转换副本。 - Matt
1
不支持32位PNG格式,值将被“夹紧”到255。 - Andrew Matiuk

12

使用此公式

Y' = 0.299 R + 0.587 G + 0.114 B 

我们能够做到。

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

然而,GIMP将彩色图像转换为灰度图像的软件有三种算法可供选择。


12
该教程存在作弊行为,因为它从RGB编码的灰度图像开始,所以他们只是分离一个单一的颜色通道,并将其视为灰度。你需要做的基本步骤是将RGB颜色空间转换为编码近似于luma / chroma模型的颜色空间,例如YUV / YIQ或HSL / HSV,然后切掉类似luma的通道并将其用作灰度图像。matplotlib似乎没有提供转换为YUV / YIQ的机制,但它确实允许您转换为HSV。
尝试使用matplotlib.colors.rgb_to_hsv(img),然后从数组中分离出最后一个值(V)作为您的灰度值。这不完全等同于luma值,但它意味着您可以全部在matplotlib中完成。
背景: 或者,您可以使用PIL或内置的colorsys.rgb_to_yiq()将其转换为具有真正luma值的颜色空间。您也可以全力以赴并自己编写仅luma的转换器,但那可能有些过头了。

9
您可以这样做:
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.copy()

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

8
如果您已经在使用NumPy/SciPy,您可能同样可以使用以下内容: scipy.ndimage.imread(file_name, mode='L')

8
scipy.ndimage.imread()scipy.misc.imread()在SciPy 1.0.0中已经正式弃用,并将在SciPy 1.2.0中被永久删除。虽然SciPy的文档推荐使用imageio.imread()作为合适的替代,但是该函数的API简单到令人匪夷所思,不提供任何灰度转换支持,因此对于许多应用(包括我们的应用)仍然不适用。</sigh> - Cecil Curry

6

使用OpenCV非常简单:

import cv2

im = cv2.imread("flower.jpg")

# To Grayscale
im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
cv2.imwrite("grayscale.jpg", im)

# To Black & White
im = cv2.threshold(im, 127, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite("black-white.jpg", im)

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接