什么是图像

像素坐标

一张图片是一个高 \(H\)、宽 \(W\) 的像素矩阵,其大小简称作 \(W\times H\)

在图片上建立坐标系,是以左上角为原点,\(x\) 轴正半轴向延伸、\(y\) 轴正半轴向延伸,如图所示:

图源:https://interfacelift.com/wallpaper/details/3852/red_demons.html

图像上一个像素点的坐标就是指某像素点在上述坐标系中的坐标,如下图所示,图片大小 \(5120\times 2880\),狐狸耳朵尖的坐标是 \((1206,948)\)

在目标检测任务中,经常与检测框打交道。检测框一般有两种表示方式:\((x_\min,y_\min,x_\max,y_\max)\)\((x_\min,y_\min,w,h)\),均是相对上述坐标系而言的,例如下图中,检测框的两种表示方式分别是 \((3444,435,4771,2332)\)\((3444,435,1327,1897)\)

图源:https://interfacelift.com/wallpaper/details/3852/red_demons.html


图像的加载

概述

众所周知,一张图片就是一个像素矩阵,且对于 RGB 格式而言,每个像素点都有 3 个值:R、G、B,它们的取值范围都是 \([0,255]\),分别可以用 1 个字节存储。因而,一个高 \(H\)\(W\) 通道数 \(C\) 的图片,就需要 \(C\times H\times W\) 个字节表示出来。这个 \(CHW\) 矩阵通常就是我们神经网络的输入。

但是在磁盘上,一张图片的存在形式并不一定是这个 \(CHW\) 矩阵,而是被编码(encode)成了各种格式,例如 jpegpngbmp 等等。jpegpng 都是会压缩图片的,所以它们实际占的存储空间会比 \(C\times H\times W\) 小。从被编码的格式恢复到 \(CHW\) 矩阵的过程称作解码(decode)。

举个例子,这张图是图像处理领域最经典的图了:

lena.jpeg

查看图片信息,可以看到它的尺寸是 \(200\times 200\),色彩空间是 \(\text{RGB}\),那么它解码之后应占用 \(200\times200\times3=120\text{KB}\) 的空间,但它在磁盘上只占用了 \(11\text{KB}\);类似地,这张图片的 png 格式占用磁盘空间为 \(91\text{KB}\)

torchvision.io

torchvision.io文档)提供了许多图片加载和存储的函数,它们的作用就是让我们能够对图像编码、解码以及对图像文件读写。我画了一个图直观理解:

注意到文档里 decode_jpeg 有一个与众不同的参数:

device (str or torch.device) – The device on which the decoded image will be stored. If a cuda device is specified, the image will be decoded with nvjpeg. This is only supported for CUDA version >= 10.1

nvjpeg 是 nvidia 的 gpu 上加速 jpeg 图像解码、编码和转码的库。所以我们在写 DataSet 时用这个库,可能会加快数据的读取,因而加快训练。

实验

用几个常用的库:cv2matplotlib.pylotPILskimagetorchvision.io,读取图像文件为矩阵形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
st_time = time.time()
for i in range(0, 100):
img = cv2.imread(paths[i])
print(time.time() - st_time, '\tcv2')

st_time = time.time()
for i in range(0, 100):
img = plt.imread(paths[i])
print(time.time() - st_time, '\tplt')

st_time = time.time()
for i in range(0, 100):
img = np.array(Image.open(paths[i]))
print(time.time() - st_time, '\tPIL')

st_time = time.time()
for i in range(0, 100):
img = skimage.io.imread(paths[i])
print(time.time() - st_time, '\tskimage')

st_time = time.time()
for i in range(0, 100):
img = torchvision.io.read_image(paths[i])
print(time.time() - st_time, '\ttorchvision')

本地(cpu)运行结果如下:

1
2
3
4
5
1.4684679508209229 	cv2
2.0799291133880615 plt
2.2181248664855957 PIL
2.2203478813171387 skimage
1.4794716835021973 torchvision

可以看到,pltPILskimage 落后 cv2torchvision 很多,而后两者之间 cv2 略胜一筹。我反复运行了多次均是类似的结果。

有趣的是,这段代码在服务器上的运行结果是:

1
2
3
4
5
1.348557472229004 	cv2
1.6608097553253174 plt
1.7273526191711426 PIL
1.660323143005371 skimage
0.5512754917144775 torchvision

torchvision 完胜其他方式。


什么是图像
https://xyfjason.github.io/blog-main/2021/07/03/什么是图像/
作者
xyfJASON
发布于
2021年7月3日
许可协议