【问题标题】:How can I convert an RGB image into grayscale in Python?如何在 Python 中将 RGB 图像转换为灰度图像?
【发布时间】:2012-08-25 11:00:51
【问题描述】:

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

在 matlab 中我使用这个:

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

matplotlib tutorial 中,他们没有覆盖它。他们只是在图像中阅读

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

然后他们对数组进行切片,但这与根据我的理解将 RGB 转换为灰度不同。

lum_img = img[:,:,0]

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

我编写了一个非常简单的函数,可以在 5 分钟内处理使用 imread 导入的图像。这是非常低效的,但这就是为什么我希望内置一个专业的实现。

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

【问题讨论】:

  • 请注意,您可以编写与 rgb2gray 函数相同的内容:gray = np.mean(rgb, -1)。也许rgb[...,:3]在那里,如果它实际上是rgba。
  • 嗯,gray = np.mean(rgb, -1) 工作正常。谢谢。有什么理由不使用它吗?我为什么要改用下面答案中的解决方案?
  • grayscale wikipedia page说RGB转灰度的方法不唯一,但是给出了一个基于亮度的常用公式。它与np.mean(rgb, -1) 完全不同。
  • 所以我想我想要Matlab's version0.2989 * R + 0.5870 * G + 0.1140 * B 我假设这是标准的做法。
  • 不应该是 0.2990 * R + 0.5870 * G + 0.1140 * B 吗?权重总和应等于 1 而不是 0.9999。在这里查看:en.wikipedia.org/wiki/Grayscale

标签: python matplotlib


【解决方案1】:

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 和the formula

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

【讨论】:

  • 如果他因为其他原因必须使用matplotlib,他应该能够使用内置的colorsys.rgb_to_yiq() 进行变换,再加上一个切片来获得亮度通道。
  • 为什么是.convert('LA')?为什么不.convert('gray')?似乎不必要的神秘。 PIL documentation 没有提到任何关于转换功能的“LA”。
  • 使用 PIL:cannot write mode LA as JPEG,我需要使用 L 模式而不是 LA
  • 这个img = Image.open('image.png').convert('LA')需要是img = Image.open('image.png').convert('L')
  • @BluePython:LA 模式具有亮度(亮度)和 alpha。如果您使用LA 模式,那么greyscale.png 将是保留image.png alpha 通道的RGBA 图像。如果您使用L 模式,那么greyscale.png 将是RGB 图像(没有alpha)。
【解决方案2】:

当所有 3 个颜色通道 (RGB) 中的像素值相同时,该像素将始终为灰度格式。

将 RGB 图像转换为灰度的一种简单直观的方法是获取每个像素中所有颜色通道的平均值并将值分配回该像素。

import numpy as np
from PIL import Image

img=np.array(Image.open('sample.jpg')) #Input - Color image
gray_img=img.copy()

for clr in range(img.shape[2]):
    gray_img[:,:,clr]=img.mean(axis=2) #Take mean of all 3 color channels of each pixel and assign it back to that pixel(in copied image)

#plt.imshow(gray_img) #Result - Grayscale image

输入图像:

输出图像:

【讨论】:

【解决方案3】:

你可以这样做:

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

【讨论】:

    【解决方案4】:

    使用 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)
    

    【讨论】:

      【解决方案5】:

      如果你已经在使用 NumPy/SciPy,你可以 as well use:

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

      【讨论】:

      • scipy.ndimage.imread()scipy.misc.imread() 在 SciPy 1.0.0 中都是 formally deprecated,将在 SciPy 1.2.0 中永久删除。虽然 SciPy 的文档推荐 imageio.imread() 作为合适的替代品,但这个函数的 API 是赤裸裸的到荒谬的地步。它no支持灰度转换,因此仍然不适合许多应用程序——包括我们的应用程序。 </sigh>
      • @CecilCurry,如何使用 imageio 将彩色图像转换为灰度?
      【解决方案6】:

      你也可以使用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
      • 知道我的目标是使用 GLCM 功能(greycoprops)
      • io.imread 的注意事项:“as_grey”已被弃用,取而代之的是“as_gray”。用法相同,只是美国化的拼写。 :)
      • 我相信这是对手头问题最有用的答案,它的输出也与 matplotlib 和 numpy 兼容。
      • 我正在使用颜色对象,但我的图像现在有点偏红,而不是灰色(黑白)。我需要使用cmap 作为gray' then only the image is shown as gray in pyplot.imshow()` 吗?有什么想法吗 ?我哪里错了?
      【解决方案7】:

      使用this formula

      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 将颜色转换为灰度图像的软件有三种算法来完成这项任务。

      【讨论】:

        【解决方案8】:

        使用 img.Convert(),支持“L”、“RGB”和“CMYK”。模式

        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)
        

        输出:

        [[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]]
        

        【讨论】:

        • 第 5 行应该是img = img.convert('L') 吗?
        【解决方案9】:

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

        平均运行时间

        pil : 1.037 秒

        scipy: 1.040 秒

        sk : 2.120 秒

        PIL 和 SciPy 给出了相同的 numpy 数组(范围从 0 到 255)。 SkImage 给出从 0 到 1 的数组。此外颜色转换略有不同,请参见CUB-200 dataset. 中的示例

        SkImage:

        PIL :

        SciPy :

        Original:

        Diff :

        代码

        1. 性能

          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. 输出
          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. 比较
          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. 进口
          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. 版本
          skimage.version
          0.13.0
          scipy.version
          0.19.1
          np.version
          1.13.1
          

        【讨论】:

        • SciPy 的图像 I/O字面意思是 PIL/Pillow。因此,测试 SciPy 可以有效地重新测试 PIL/Pillow,而 SciPy 的包装函数引入的开销可以忽略不计。用 OpenCV(它利用 PIL/Pillow)代替 SciPy(它确实)会更有用。尽管如此,感谢专门的基准测试! SciKit 造成的明显减速令人着迷……而且令人恐惧。
        • @CecilCurry 感谢 OpenCV 的创意!有空我会补充的。
        • 点赞!不是我正在寻找的答案,但非常非常有趣:)
        【解决方案10】:

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

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

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

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

        【讨论】:

        【解决方案11】:

        我通过谷歌来这个问题,寻找一种将已经加载的图像转换为灰度的方法。

        这是使用 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)
        

        【讨论】:

        • 不错。我只想指出一个较短的解决方案是img_gray = numpy.average(img, weights=[0.299, 0.587, 0.114], axis=2)
        • @Akavall 很高兴知道,谢谢!你知道你的捷径是否更快吗?如果没有,我会保留我的,因为它更容易理解。
        • 我没有计时,我的直觉是numpy.average 有点快但实际上并没有什么不同。你的解决方案很清楚,并且有关于 R、G、B 的相关信息,所以我会保留它。我的评论更多的是一种额外的选择,而不是替代品。
        • scipy.ndimage.imread()scipy.misc.imread() 在 SciPy 1.0.0 中都是 formally deprecated,将在 SciPy 1.2.0 中永久删除。您可能只想使用 Pillow 的内置灰度转换支持(ala unutbu's answer)。
        • 你的代码中有哪些神奇的数字? 299、587、114...
        【解决方案12】:
        image=myCamera.getImage().crop(xx,xx,xx,xx).scale(xx,xx).greyscale()
        

        您可以直接使用greyscale()进行转换。

        【讨论】:

          【解决方案13】:

          目前最快的方法是使用Pillow,通过pip install Pillow安装。

          那么代码是:

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

          【讨论】:

          • 请注意,如果您没有像上面的示例那样链接您的方法,convert 会返回图像的转换副本
          • 不适用于 32 位 PNG,值将 clamped 到 255
          【解决方案14】:

          本教程是作弊的,因为它从以 RGB 编码的灰度图像开始,因此他们只是将单个颜色通道切片并将其视为灰度。您需要做的基本步骤是从 RGB 颜色空间转换为使用近似亮度/色度模型(例如 YUV/YIQ 或 HSL/HSV)进行编码的颜色空间,然后切掉类似亮度的通道并将其用作你的灰度图像。 matplotlib 似乎没有提供转换为 YUV/YIQ 的机制,但它确实允许您转换为 HSV。

          尝试使用matplotlib.colors.rgb_to_hsv(img),然后从数组中切出最后一个值 (V) 以获得灰度。它与亮度值不太一样,但这意味着您可以在matplotlib 中完成所有操作。

          背景:

          或者,您可以使用 PIL 或内置 colorsys.rgb_to_yiq() 转换为具有真实亮度值的色彩空间。您也可以全力以赴并推出自己的 luma-only 转换器,尽管这可能有点矫枉过正。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-09-16
            • 2014-02-26
            • 2014-12-22
            • 1970-01-01
            • 2016-11-05
            相关资源
            最近更新 更多