【问题标题】:Differences between PIL Image.open and cv2.imdecodePIL Image.open 和 cv2.imdecode 的区别
【发布时间】:2020-03-10 16:40:51
【问题描述】:

我试图了解这两种使用 PIL 与 OpenCV 从字节加载图像的方法之间的区别。

def bytes_to_ndarray(bytes):
    bytes_io = bytearray(bytes)
    img = Image.open(BytesIO(bytes_io))
    return np.array(img)

img = cv2.imdecode(bytes, cv2.IMREAD_ANYCOLOR)

问题是他们似乎对使用 OpenCV 创建的图像给出了不同的答案。如果imagendarray,那么对于

bytes = cv2.imencode('.jpg', image)

这两种方式会给出不同的输出,例如skimage.data.astronaut()

PIL 将给出:

而 OpenCV 将返回正确的图像:

【问题讨论】:

    标签: python opencv image-processing python-imaging-library


    【解决方案1】:

    简而言之:这只是通常的 RGB 与 BGR 排序 - 但是,将 OpenCV 的 imencodeimdecode 与此特定图像结合使用,使一切变得非常复杂。 ;-)

    skimage.data.astronaut() 返回带有 RGB 排序的 ndarray,因为 RGB 排序是 skimage 中的标准。相比之下,OpenCV 内部使用 BGR 排序。因此,当我们在保存的这张图片的 PNG 上使用 cv2.imread 时,我们会得到一个带有 BGR 排序的 ndarray。此外,OpenCV 的所有操作始终假定 BGR 为 ndarrays

    现在,您使用cv2.imencode 生成字节流。如前所述,OpenCV 假设输入到该函数的ndarray 具有 BGR 排序。这很重要,因为生成的字节流将具有 RGB 排序(cv2.imencode 模仿 cv2.imwrite,并且 OpenCV 正确写入 RGB 图像)。因此,创建的字节流具有错误的 BGR 排序。

    对于解码,Pillow 和 OpenCV 都假设一个 RGB 有序字节流。因此,由“枕头方式”创建的ndarray 实际上具有 BGR 排序(这不是枕头标准),而由 OpenCV 的 imdecode 创建的 ndarray 具有 RGB 排序(这不是 OpenCV 标准)。

    最后,Matplotlib 的(或 pyplot 的)imshow 假定 RGB 有序 ndarrays 用于可视化。因此,会发生以下情况:

    • 显示来自skimage.data.astronaut() 的原始ndarray 应该是正确的(RGB 排序)。
    • 显示 Pillow 加载的 PNG 应该是正确的(RGB 排序)。
    • 显示 OpenCV 加载的 PNG 应该不正确(BGR 已排序)。
    • 显示 Pillow 解码字节流应该不正确(BGR 排序)。
    • 显示 OpenCV 解码的字节流应该是正确的(RGB 排序)。

    让我们看看:

    import cv2
    from io import BytesIO
    from matplotlib import pyplot as plt
    import numpy as np
    from PIL import Image
    import skimage
    
    
    def bytes_to_ndarray(bytes):
        bytes_io = bytearray(bytes)
        img = Image.open(BytesIO(bytes_io))
        return np.array(img)
    
    
    # skimage returns a ndarray with RGB ordering
    img_sk = skimage.data.astronaut()
    
    # Opening a saved PNG file of this image using Pillow returns a ndarray with RGB ordering
    img_pil = Image.open('astronaut.png')
    
    # Opening a saved PNG file of this image using OpenCV returns a ndarray with BGR ordering
    img_cv = cv2.imread('astronaut.png', cv2.IMREAD_COLOR)
    
    # OpenCV uses BGR ordering, thus OpenCV's encoding treats img_sk[:, :, 0] as blue channel,
    # although it's the actual red channel (the same for img_sk[:, :, 2]
    # That means, the encoded byte stream now has BGR ordering!!
    _, bytes = cv2.imencode('.png', img_sk)
    
    # OpenCV uses BGR ordering, but OpenCV's decoding assumes a RGB ordered byte stream, so
    # the blue and red channels are swapped again here, such that img_cv again is a ndarray with
    # RGB ordering!!
    img_byte_cv = cv2.imdecode(bytes, cv2.IMREAD_ANYCOLOR)
    
    # Pillow uses RGB ordering, and also assumes a RGB ordered byte stream, but the actual byte
    # stream is BGR ordered, such that img_pil actually is a ndarray with BGR ordering
    img_byte_pil = bytes_to_ndarray(bytes)
    
    # Matplotlib pyplot imshow uses RGB ordering for visualization!!
    plt.figure(figsize=(8, 12))
    plt.subplot(3, 2, 1), plt.imshow(img_pil), plt.ylabel('PNG loaded with Pillow')
    plt.subplot(3, 2, 2), plt.imshow(img_cv), plt.ylabel('PNG loaded with OpenCV')
    plt.subplot(3, 2, 3), plt.imshow(img_sk), plt.ylabel('Loaded with skimage')
    plt.subplot(3, 2, 5), plt.imshow(img_byte_pil), plt.ylabel('Decoded with Pillow')
    plt.subplot(3, 2, 6), plt.imshow(img_byte_cv), plt.ylabel('Decoded with OpenCV')
    plt.show()
    

    等等:

    这是图像的 PNG 副本,用于重现代码:

    底线:使用 OpenCV 的 imencode 时,请确保传递的 ndarray 具有 BGR 排序!

    希望有帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 2018-04-07
      • 2018-08-28
      • 1970-01-01
      • 2019-02-28
      • 2013-09-26
      • 2021-06-25
      相关资源
      最近更新 更多