深度学习中,涉及的图片格式有诸多转换方式,写此篇用于区别记录。
目前接触到的读取图片的方式主要是两种,一是使用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图片对象。文章来源:https://www.toymoban.com/news/detail-599431.html
三、概括
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()保存。文章来源地址https://www.toymoban.com/news/detail-599431.html
到了这里,关于opencv,numpy,tensor格式转换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!