图像的形状相关参数顺序
图像有单通道和多通道两种分类。我们先用 OpenCV 读一张图片看看它是怎么存放的。读入的图片如下:
import cv2 img = cv2.imread('test.jpg') print(img.shape) print(type(img)) """ (36, 100, 3) <class 'numpy.ndarray'> """
可见一幅图像是被保存为 ndarray 类型的多维数组,对于一个三通道的图像而言,多维数组每个维度的含义是:$(height,width,channel)$,
或者简写为 $(h,w,c)$。所以我们想要创建一幅图像就可以创建一个 ndarray,并按这个形状规则给定各个维度的大小。
import cv2 import numpy as np img = np.zeros((36, 100), dtype=np.uint8) + 255 # 创建一个灰度图像,单通道 img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) # 灰度图像转为 BGR 图像,三通道 print(img.shape) print(type(img)) """ (36, 100, 3) <class 'numpy.ndarray'> """
当我们直接通过 img 这个 ndarray 类型来进行访问像素,调整形状等一系列操作时,按 $(h,w,c)$ 的顺序不会有问题,但当我们间接通过
OpenCV 的 API 来操作图像的时候,这个顺序就不对了。这是由于 OpenCV 的坐标系并不是我们平常认为的那么定义的。
OpenCV 的坐标体系:零点坐标为图片的左上角,X 轴为图像矩形的上面那条水平线;Y 轴为图像矩形左边的那条垂直线。
所以当我们调用 API 来操作图像时,提供的坐标 $(x,y)$ 表示的含义是宽和高,即 $(w,h)$,举个例子:
import cv2 img = cv2.imread('test.jpg') print(img.shape) img = cv2.resize(img, (200, 36), interpolation=cv2.INTER_NEAREST) print(img.shape) """ (36, 100, 3) (36, 200, 3) """
可见 cv2.resize 修改的是图像的宽度,在其它提供坐标的地方都是按 $(w,h)$ 的顺序,比如 cv2.circle 等。
另外需要注意的是:OpenCV 读到的图像通道顺序是 $bgr$。
接下来我们用 python 中的 PIL 包来读图片看看,PIL 是一个图像处理的轻量级的库,功能没有 OpenCV 强大。用 PIL 包打开上面的那张图片:
from PIL import Image img = Image.open('test.jpg') print(img.size) """ (100, 36) """
size 返回的是图片的形状是 $(w,h)$。我们再调整一下图片大小:
from PIL import Image img = Image.open('test.jpg') print(img.size) img = img.resize((100, 200)) print(img.size) """ (100, 36) (100, 200) """
所以 resize 的参数顺序依然是 $(w,h)$。
最后我们来看下 skimage 这个包,依然是上面那张图片:
from skimage import io, transform img = io.imread('test.jpg') print(img.shape) img = transform.resize(img, (36, 200)) print(img.shape) """ (36, 100, 3) (36, 200, 3) """
可以看出,这个包无论是读取图像还是间接操作图像都是按 $(h,w)$ 顺序。