python - PIL Image.open 和 cv2.imdecode 的区别
问题描述
我试图了解这两种使用 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 创建的图像给出了不同的答案。如果image
是ndarray
,那么对于
bytes = cv2.imencode('.jpg', image)
这两种方式将给出不同的输出,例如skimage.data.astronaut()
PIL 将提供:
而 OpenCV 将返回正确的图像:
解决方案
简而言之:这只是通常的 RGB 与 BGR 排序 - 但是,结合使用 OpenCV 的方式imencode
以及imdecode
此处与此特定图像的结合,使一切变得非常复杂。;-)
skimage.data.astronaut()
返回ndarray
带有 RGB 排序的 a,因为 RGB 排序是skimage
. 相比之下,OpenCV 内部使用 BGR 排序。因此,当我们cv2.imread
在保存的这张图片的 PNG 上使用时,我们会得到一个ndarray
带有 BGR 的排序。此外,OpenCV 始终假定 BGRndarrays
对其所有操作都是有序的。
现在,您使用cv2.imencode
来生成字节流。如前所述,OpenCV 假设该ndarray
函数的馈送具有 BGR 排序。这很重要,因为生成的字节流将具有 RGB 排序(cv2.imencode
模拟cv2.imwrite
,并且 OpenCV 正确写入 RGB 图像)。因此,创建的字节流具有错误的 BGR 排序。
对于解码,Pillow 和 OpenCV 假设一个 RGB 有序字节流。因此,ndarray
由“枕头方式”创建的实际上具有 BGR 排序(这不是枕头标准),而ndarray
由 OpenCV 创建的imdecode
具有 RGB 排序(这不是 OpenCV 标准)。
最后,Matplotlib(或 pyplot)imshow
假设 RGBndarrays
是为了可视化而排序的。因此,将发生以下情况:
- 显示原件应该是正确的
ndarray
(skimage.data.astronaut()
RGB 排序)。 - 显示枕头加载的 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 排序!
希望有帮助!
推荐阅读
- r - R - 根据条件排除参与者(超过 10% 的响应——变量:延迟——小于 300 毫秒)
- javascript - 无法绑定到“ngModel”,因为它不是“输入”的已知属性,如何在角度 9 中修复?
- outlook -
(false) 更新“HTMLBody”时触发 - java - 尝试在 cmd 中运行可执行 Jar 时出错
- javascript - React - 如何刷新组件?
- javascript - Rest API 列表项目计数代码适用于 SP 2013 而不是 SPO
- node.js - 在 TypeScript 中将 Chai 自定义插件声明为 NodeJS 全局变量
- reactjs - javascript / react中点前的问号
- python-3.x - 如何从函数返回修改后的矩阵?
- python-3.x - Python 从某个数字读取数字