opencv,numpy,tensor格式转换

深度学习中,涉及的图片格式有诸多转换方式,写此篇用于区别记录。

目前接触到的读取图片的方式主要是两种,一是使用opencv的cv2模块,二是PIL.Image模块,两者的使用有不同。

一、cv2的读取方式,格式转换

最先一个问题是读取格式。

1. cv2读取的图片格式直接是numpy的ndarry格式,图片是形状为HxWxC的BGR图片。

jm = cv2.imread(path)  # cv读取的是BGR格式图片
print(type(jm))
print(jm.shape)
# 输出
<class 'numpy.ndarray'>
(240, 300, 3)

直接打印是BGR格式

plt.imshow(jm)
plt.show()

在这里插入图片描述
经过格式转换之后得到正常的RGB格式图片

jm = cv2.cvtColor(jm, cv2.COLOR_BGR2RGB)

在这里插入图片描述

2. 在pytorch中,输入网络时将图片转化为tensor格式,并对数据做归一化处理,数值缩放到0-1之间。如:

jm = cv2.imread(path)  # cv读取的是BGR格式图片
# jm = cv2.cvtColor(jm, cv2.COLOR_BGR2RGB)
img_tensor = torchvision.transforms.ToTensor()(jm)  # 转换成tensor格式
print(img_tensor)
print(type(img_tensor))
print(img_tensor.shape)
# 输出
tensor([[[0.1647, 0.1608, 0.1608,  ..., 0.2431, 0.2431, 0.2431],
         [0.1647, 0.1608, 0.1608,  ..., 0.2431, 0.2431, 0.2431],
         [0.1647, 0.1608, 0.1647,  ..., 0.2431, 0.2431, 0.2431],
         ...,
         [0.2902, 0.2902, 0.2902,  ..., 0.1412, 0.1451, 0.1490],
         [0.2863, 0.2863, 0.2863,  ..., 0.1412, 0.1451, 0.1490],
         [0.2863, 0.2863, 0.2863,  ..., 0.1412, 0.1451, 0.1490]],

        [[0.1216, 0.1176, 0.1176,  ..., 0.2784, 0.2784, 0.2784],
         [0.1216, 0.1176, 0.1176,  ..., 0.2784, 0.2784, 0.2784],
         [0.1216, 0.1176, 0.1216,  ..., 0.2784, 0.2784, 0.2784],
         ...,
         [0.2745, 0.2745, 0.2745,  ..., 0.1176, 0.1216, 0.1255],
         [0.2706, 0.2706, 0.2706,  ..., 0.1176, 0.1176, 0.1216],
         [0.2706, 0.2706, 0.2706,  ..., 0.1176, 0.1176, 0.1216]],

        [[0.3412, 0.3373, 0.3373,  ..., 0.3843, 0.3843, 0.3843],
         [0.3412, 0.3373, 0.3373,  ..., 0.3843, 0.3843, 0.3843],
         [0.3412, 0.3373, 0.3412,  ..., 0.3843, 0.3843, 0.3843],
         ...,
         [0.2706, 0.2706, 0.2706,  ..., 0.1843, 0.1961, 0.2000],
         [0.2667, 0.2667, 0.2667,  ..., 0.1922, 0.2000, 0.2039],
         [0.2667, 0.2667, 0.2667,  ..., 0.1922, 0.2000, 0.2039]]])
<class 'torch.Tensor'>
torch.Size([3, 240, 300])

3. tensor类型的数据转换到图片的类型
假设数据未经过normalization处理到[-1,1],还是在0-1之间。
步骤:

# 1. 首先tensor转化到numpy形式
img_np = img_tensor.numpy()
print(img_np)
print(type(img_np))
print(img_np.shape)
# 输出
[[[0.16470589 0.16078432 0.16078432 ... 0.24313726 0.24313726 0.24313726]
  [0.16470589 0.16078432 0.16078432 ... 0.24313726 0.24313726 0.24313726]
  [0.16470589 0.16078432 0.16470589 ... 0.24313726 0.24313726 0.24313726]
  ...
  [0.2901961  0.2901961  0.2901961  ... 0.14117648 0.14509805 0.14901961]
  [0.28627452 0.28627452 0.28627452 ... 0.14117648 0.14509805 0.14901961]
  [0.28627452 0.28627452 0.28627452 ... 0.14117648 0.14509805 0.14901961]]

 [[0.12156863 0.11764706 0.11764706 ... 0.2784314  0.2784314  0.2784314 ]
  [0.12156863 0.11764706 0.11764706 ... 0.2784314  0.2784314  0.2784314 ]
  [0.12156863 0.11764706 0.12156863 ... 0.2784314  0.2784314  0.2784314 ]
  ...
  [0.27450982 0.27450982 0.27450982 ... 0.11764706 0.12156863 0.1254902 ]
  [0.27058825 0.27058825 0.27058825 ... 0.11764706 0.11764706 0.12156863]
  [0.27058825 0.27058825 0.27058825 ... 0.11764706 0.11764706 0.12156863]]

 [[0.34117648 0.3372549  0.3372549  ... 0.38431373 0.38431373 0.38431373]
  [0.34117648 0.3372549  0.3372549  ... 0.38431373 0.38431373 0.38431373]
  [0.34117648 0.3372549  0.34117648 ... 0.38431373 0.38431373 0.38431373]
  ...
  [0.27058825 0.27058825 0.27058825 ... 0.18431373 0.19607843 0.2       ]
  [0.26666668 0.26666668 0.26666668 ... 0.19215687 0.2        0.20392157]
  [0.26666668 0.26666668 0.26666668 ... 0.19215687 0.2        0.20392157]]]
<class 'numpy.ndarray'>
(3, 240, 300)

# 2. 改变矩阵形状,因为cv2的图片格式是HxWxC,使用转置transpose
img_np = img_np.transpose(1, 2, 0)  # 形状变为HxWxC
(240, 300, 3)

# 3. 改变数值,矩阵乘以255
img_np = img_np * 255
[[[42. 31. 87.]
  [41. 30. 86.]
  [41. 30. 86.]
  ...
  [62. 71. 98.]
  [62. 71. 98.]
  [62. 71. 98.]]
  .......
  .......
# 4.  图片格式是uint8,无符号8位整型,使用numpy的astype方法
img = img_np.astype(np.uint8)
print(img)
[[[42 31 87]
  [41 30 86]
  [41 30 86]
  ...
  [62 71 98]
  [62 71 98]
  [62 71 98]]
  ......
plt.imshow(img)
plt.show()

在这里插入图片描述
:这里是BGR格式,如果图片在tensor化之前经过BGR到RGB的处理,那么这里是输出RGB正常图。

二、Image的读取方式,格式转换

用PIL.Image的open方法得到的是一个特定图片类型的PIL对象,需要用numpy的array方法或者asarray方法转化成numpy的ndarray形式,不同于cv2,Image读取的图片是RGB的。

img = Image.open(path)
print(img)
# 输出
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=300x240 at 0x18943488>

这里可以用convert转化成RGB格式。其他博客说不加convert就会得到一个四通道的图,包含一个透明通道,但是上面没有显示。

img = Image.open(path).convert('RGB')
print(img)
# 输出
<PIL.Image.Image image mode=RGB size=300x240 at 0x1899CE88>
# 还是加一个convert('RGB')吧,看着专业一点

这里拓展一个array和asarray方法的区别。
array方法和asarray方法的区别为是否在内存中创建新的副本。
当数据源是非ndarray类型时,两者没有区别,都是创建新的副本。而当数据源是ndarray时,前者array创建副本,asarray直接在原内存修改。

a = [[1, 2], [3, 4]]  # 数据源不是ndarray
b = np.array(a)
c = np.asarray(a)
a[1] = 0
print(a)
print(b)
print(c)
print(id(a), id(b), id(c), sep='\n')
# 输出
[[1, 2], 0]
[[1 2]
 [3 4]]
[[1 2]
 [3 4]]
385734024
395981328
355290640

上面代码里,当数据源不是ndarray格式,np.array和np.asarray都创建副本,原数据改变不影响新建对象。

a = np.array([[1, 2], [3, 4]])  # 数据源先转换成ndarray形式
b = np.array(a)
c = np.asarray(a)
a[1] = 0
print(a)
print(b)
print(c)
print(id(a), id(b), id(c), sep='\n')
# 输出
[[1 2]
 [0 0]]
[[1 2]
 [3 4]]
[[1 2]
 [0 0]]
414413712
397155760
414413712

可以看出,上面代码,当输入数据格式是ndarray,asarray方法会使用原数据内存,array是创建副本。
剩余的步骤和cv2一致了。

# 到了下面这一步,可以使用Image.fromarray方法返回一个Image图片对象。
img = img_np.astype(np.uint8)  # img是uint8的ndarray
img = Image.fromarray(img)  
print(img)
#  输出
<PIL.Image.Image image mode=RGB size=300x240 at 0x180CF448>

cv2和Image保存图片也不一样

  • cv2保存方式:
img = img_np.astype(np.uint8)
cv2.imwrite('路径', img)

cv2保存的是uint8格式的ndarray数组。

  • PIL.Image保存方式
img = Image.fromarray(img)  # 转成PIL对象
img.save('路径')

PIL.Image保存的是PIL图片对象。

三、概括

cv2:
cv2读取和保存都是ndarray数组,形状为HxWxC。
cv2读取的图片通道顺序是BGR,如果要使用正常的图片,先使用通道转换方法转化成RGB通道。
保存时数据形式是uint8的ndarray数组。
变成tensor数据后,形状变为CxHxW,数值0-1之间。
PIL.Image:
Image读取和保存都是PIL图片对象,形状为HxWxC,使用前先用np.array或者np.asarray方法转换成ndarray数组类型。
通道顺序是RGB,和cv2不同。
保存时保存PIL对象,所以使用Image.fromarray方法将uint8格式转成PIL图片对象,使用img.save()保存。

  • 29
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值