1. cv2读取的图片格式直接是numpy的ndarry格式,图片是形状为HxWxC的BGR图片。
jm = cv2.imread(path) # cv读取的是BGR格式图片 print(type(jm)) print(jm.shape) # 输出(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]]])
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]]](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正常图。
用PIL.Image的open方法得到的是一个特定图片类型的PIL对象,需要用numpy的array方法或者asarray方法转化成numpy的ndarray形式,不同于cv2,Image读取的图片是RGB的。
img = Image.open(path) print(img) # 输出
这里可以用convert转化成RGB格式。其他博客说不加convert就会得到一个四通道的图,包含一个透明通道,但是上面没有显示。
img = Image.open(path).convert('RGB')
print(img)
# 输出
# 还是加一个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) # 输出
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()保存。



