PIL Image.open和cv2.imdecode之间的区别

时间:2019-11-14 16:33:26

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

我试图了解这两种使用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会给出:

enter image description here

OpenCV返回正确的图像:

enter image description here

1 个答案:

答案 0 :(得分:3)

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

skimage.data.astronaut()返回带有RGB顺序的ndarray,因为RGB顺序是skimage中的标准。相反,OpenCV在内部使用BGR排序。因此,当我们在此图像的已保存PNG上使用cv2.imread时,我们将得到一个ndarray的BGR排序。此外,OpenCV始终假定BGR为其所有操作订购ndarrays

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

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

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

  • 显示ndarray中的原始skimage.data.astronaut()应该是正确的(RGB顺序)。
  • 显示枕头加载的PNG应该正确(已订购RGB)。
  • 显示OpenCV加载的PNG应该不正确(已订购BGR)。
  • 显示枕头解码字节流应该是错误的(按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()

Etvoilà:

Output

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

enter image description here

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

希望有帮助!