CV对于图片预处理方式种类繁多, 如果使用不同的图片预处理方式经过模型推理后,得到的结果差距较大。如何能训练好CV模型,首先需要对齐CV模型训练、推理过程的预处理方式。
| 预处理方式 | 接收图片格式 | 读取数据类型 |
| OpenCV | 通道颜色 - BGR;通道顺序 - HWC | numpy数组;uint8的整数类型,范围0 - 255 |
| Pillow(PIL) | 通道颜色 - RGB;通道顺序 - HWC | 特有的数据结构(可以转换成numpy) |
| torchvision(Pytorch) | ||
| matplotlib | 通道颜色 - RGB;通道顺序 - HWC | numpy数组;uint8的整数类型,范围0 - 255 |
代码参考:opencv、matplotlib、pillow和pytorch读取数据的通道顺序
- import cv2
- import matplotlib.pyplot as plt
- from PIL import Image
-
- import numpy as np
- import torch
- from torchvision import transforms
-
- # opencv读取数据通道顺序,默认颜色通道BGR, 数据通道顺序HWC
- def opencv_channel(img_path):
- image = cv2.imread(img_path)
- print("cv2 type is ", type(image))
- print("cv2 shape is ", image.shape)
-
- cv2.imshow("image", image)
- # cv2.waitKey(0)
-
- #opencv将BGR颜色通道顺序 更改为 RGB颜色通道顺序
- # 1.
- cvColor_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
- # 2.
- b, g, r = cv2.split(image)
- cvColor_image = cv2.merge([r, g, b])
- # 3.
- cvColor_image = image[:, :, :: -1]
-
- # matplotlib读取数据通道顺序, 默认颜色通道是RGB, 数据通道顺序是HWC
- def plt_channel(img_path):
- image = plt.imread(img_path)
- print("matplotlib type is ", type(image))
- print("matplotlib shape is ", image.shape)
-
- plt.imshow(image)
- plt.title("plt image")
- plt.show()
-
- # 将numpy数据转换成pillow数据
- pil_image = Image.fromarray(image)
- print("PIL type is ", type(pil_image))
- pil_image.show()
-
- def pillow_channel(img_path):
- image = Image.open(img_path)
- print("PIL type is ", type(image))
- print("PIL shape is ", image.size)
-
- image.show()
-
- # 将pillow数据转换成numpy数据
- np_image = np.array(image)
- print("matplotlib type is ", type(np_image))
- print("matplotlib shape is ", np_image.shape)
- plt.imshow(np_image)
- plt.title("pillow convert to numpy type")
- plt.show()
-
- if __name__ == "__main__":
- img_path = "./bee.jpg"
- pillow_channel(img_path)
- plt_channel(img_path)
- opencv_channel(img_path)
CV模型推理前包含多种预处理的方式,详细介绍部分常用的,且不同预处理方式存在diff的方法【模型预处理方式主要使用gocv、torchvision】
参考:
Python Pillow 和 OpenCV 中 resize 的区别 - 知乎
作用:Resize() 修改图片的大小
OpenCV
作用:ToTensor() 数据归一化 + 图像HWC转换成CHW
作用:Normalize() 数据标准化 【提高数据计算效率、模型准确性】
- def CV_Process(img_path):
- # ToTensor() 归一化到(0,1)之间; (H, W, C)的numpy.ndarray 转换为 (C, H, W)的tensor
- # Normalize() 再 (x-mean)/std 标准化到(-1,1)
-
- image = cv2.imread(img_path)
- print(image.shape)
-
- transform_2 = transforms.Compose([
- transforms.ToTensor(),
- transforms.Normalize(mean = [0.485, 0.456, 0.406], std = [0.229, 0.224, 0.225])
- # 很多代码预处理Normalize中的参数值是从ImageNet训练集抽样计算而来
- ])
-
- transform_3 = transforms.Compose([
- transforms.ToTensor()
- ])
-
- image2 = transform_2(image)
- image3 = torch.from_numpy(image.transpose((2, 0, 1)))
- image3 = image3.float().div(255)
- channel_1 = image3[0].sub_(0.485).div_(0.229).unsqueeze(0)
- channel_2 = image3[1].sub_(0.456).div_(0.224).unsqueeze(0)
- channel_3 = image3[2].sub_(0.406).div_(0.225).unsqueeze(0)
- image3 = torch.cat([channel_1, channel_2, channel_3], dim = 0)
- print(image2.shape)
- print(image3.shape)
- print(image2.equal(image3)) # output: True
1. torch.div & torch.div_ 区别
torch.div和torch.div_的主要区别在于返回值和是否进行就地操作(in-place operations)。
torch.div(a, b)的作用是对两个张量a和b逐元素地做除法操作,返回一个新的张量。如果a和b的shape不一致,torch会尝试将它们广播到一个合适的shape使得操作符合规则。
torch.div_(a, b)的作用和torch.div类似,但是它会就地修改a的值,即直接将结果存储在a中。由于是就地操作,torch.div_只能对可写的(writable)张量使用。
因此,torch.div和torch.div_在实现上略有不同,但作用是一致的,都是进行张量间的逐元素除法操作。一般来说,如果不需要就地修改操作,推荐直接使用torch.div,这样能保留原始张量的值,并且可读性更好。如果需要就地修改操作,需要使用torch.div_。