图像通道转换——从np.ndarray的[w, h, c]转为Tensor的[c, w, h]

时间:2024-03-20 19:16:51

在神经网络中,图像被表示成[c, h, w]格式或者[n, c, h, w]格式,但如果想要将图像以np.ndarray形式输入,因np.ndarray默认将图像表示成[h, w, c]个格式,需要对其进行转化。

n:样本数量
c:图像通道数
w:图像宽度
h:图像高度

np.ndarray如何表示图像


我们可以用PIL打开一张图像,然后通过array()方法将其转为np.ndarray形式,最后打印出它的shape即能得到图像时如何存储在np.ndarray中的。

from PIL import Image
imoprt numpy as np

img_path = ('./test.jpg')
img = Image.open(img_path)
img_arr = np.array(img)
print(img_arr.shape)

# 输出的结果是(500, 300, 3)

从上面的试验结果我们可以知道,图像以[h, w, c]的格式存储在np.ndarray中的。

np.ndarray与Tensor中图像格式区别


两者均以三维数组来表示一张图像,他们的区别在于图像信息被保存在数组中的不同位置,具体来说:

  • np.ndarray[h, w, c]格式:数组中第一层元素为图像的每一行像素,第二层元素为每一列像素,最后一层元素为每一个通道的像素值,它将图片中的每一个像素作为描述单元,记录它三个通道的像素值。
  • Tensor[c, h, w]格式:数组中第一层元素为图像的三个通道,第二层元素为某个通道上的一行像素,第三层为该通道上某列的像素值,它将图像某个通道的某行像素值作为描述单元。

上面的说法可能有点抽象,我们可以用图像来说明两者的区别:

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt


# 用随机数模拟一张图像
image = np.random.randint(256, size=60)
image = image.reshape((5,4,3))
image_hwc = np.uint8(image)

# 展示图像
image_show = Image.fromarray(image_hwc)
plt.imshow(image_show)
plt.show()

# 打印图像像素值,[h, w, c]格式
print(image_hwc)

# 打印像素值,[c, h, w]格式
image_chw = np.transpose(image_hwc, (2,0,1))
print(image_chw)

以上代码模拟生成的图像如下图所示,图中有5行4列总共20个像素。
图像通道转换——从np.ndarray的[w, h, c]转为Tensor的[c, w, h]
上图的所有像素及其像素值如下图所示,[h, w, c]格式。可以看出,最里层的括号内为单个像素在三个通道上的像素值。
图像通道转换——从np.ndarray的[w, h, c]转为Tensor的[c, w, h]
如果以[c, h, w]格式表示的话,应该是下图这样的:
图像通道转换——从np.ndarray的[w, h, c]转为Tensor的[c, w, h]

如何从[w, h, c]转为[c, w, h]


可以借助numpytranspose()函数来实现这个转换。是的只要像下面简简单单的一句话即可实现。

image_chw = np.transpose(image_hwc, (2,0,1))

以某维度为(3, 2, 3),即3行2列共6个像素的图像转换为例,简要地说明一下上述语句产生的转换过程:

# step 1: 转换前的图像及其像素值
[[[ 33 155  80]
  [155 245  29]]

 [[ 25 238 206]
  [164  99 182]]

 [[213  21 194]
  [ 11   9 124]]]

# step 2: 轴与索引之间的对应关系
  意义  轴  索引  -->  索引  轴  意义
  高    3    0         2    3   通道
  宽    2    1         0    3   高
  通道  3    2         1    2# step 3: 转换过程
  1. 其实数据可以看做是一堆无序的数据,轴的存在让这些数据按照一定层级及次序排布
  2. 转换前的数据是这样排布的,先按照图像高分成3堆,对这3堆的每一堆按照图像图像宽分2堆,分好的2堆分别按照通道数分成33. 转换后的数据排布顺序变了,它先按照通道数分成3堆,分好的3堆各自按照图像高分成3堆,再按照图像宽分成2堆。

# step 4: 转换结果
[[[ 33 155]
  [ 25 164]
  [213  11]]

 [[155 245]
  [238  99]
  [ 21   9]]

 [[ 80  29]
  [206 182]
  [194 124]]]

transpose()的说明及具体的转换原理,可以参考文章《转置(transpose)和轴对换》,其他一些转换方式可以参考文章《图像通道转换[n c h w] 转 [n h w c]

参考资料


[1] https://www.cnblogs.com/sunshinewang/p/6893503.html
[2] https://blog.csdn.net/zhangxin4832/article/details/83015480