背景
最近在做一些图片读取上的研究,有单独保存好的三通道图片,现在需要将其整合起来,一开始选择是先创建数组,比如 np.zeros(shape=(512,512,4)),然后每个通道使用 PIL.Image.open()
读取,最后替换创建的原始数组。另外一种方式使用opencv
读取,然后使用 np.satck()
整合。
以下是验证结果:
1. PIL.Image.open 和 cv2.imread()读取到的图片数据一致吗?
前提:只读取单通道的图片,所以 opencv
使用 cv2.IMREAD_GRAYSCALE
读取格式,其实就是0
。
代码及结果:
import numpy as np
import cv2
from PIL import Image
filename = "100_red.png"
flags = cv2.IMREAD_GRAYSCALE
pil_image = np.array(Image.open(filename)) #将 pil 格式的图片转换成 numpy
cv_image = cv2.imread(filename,flags)
print(pil_image == cv_image)
# 结果如下
array([[ True, True, True, ..., True, True, True],
[ True, True, True, ..., True, True, True],
[ True, True, True, ..., True, True, True],
...,
[ True, True, True, ..., True, True, True],
[ True, True, True, ..., True, True, True],
[ True, True, True, ..., True, True, True]])
实验证明,读取单通道下,读取结果确实一致,且数据类型都是 np.uint8
那么三通道的图片呢?
因为 opencv
读取的数据通道数在前面,需要做一下转换,验证结果也是一致的,附上结果:
array([[[ True, True, True],
[ True, True, True],
[ True, True, True],
...,
[ True, True, True],
[ True, True, True],
[ True, True, True]],
...,
[[ True, True, True],
[ True, True, True],
[ True, True, True],
...,
[ True, True, True],
[ True, True, True],
[ True, True, True]],
[[ True, True, True],
[ True, True, True],
[ True, True, True],
...,
[ True, True, True],
[ True, True, True],
[ True, True, True]]])
2. 如果在读取的过程中直接将图片除以 255 报错?
pytorch
如果要利用默认的转变方式,需要将 numpy
格式的数据转换成 PIL
,但是只支持 np.uint8
不支持 np.float
类型。因此,在传入 torchvision.transforms
中的图片时,不能提前做这一步。
那么问题来了,如果不除以 255,图片数据就没办法转变到 [0,1] 之间,而一般使用预训练模型时都需要进行 normalize
操作,究竟是怎么处理的?肯定不能在原值上进行,这个问题也是纠结了我好久,直到我发现了一个问题,normalize 都是在 T.ToTensor() 这一步之后进行的
,答案应该就在 ToTensor
中
3. torchvision.ToTensor() 以及 Normalize()
源码是这么写的:
class ToTensor(object):
"""Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor.
Converts a PIL Image or numpy.ndarray (H x W x C) in the range
[0, 255] to a torch.FloatTensor of shape (C x H x W) in the range [0.0, 1.0].
"""
def __call__(self, pic):
"""
Args:
pic (PIL Image or numpy.ndarray): Image to be converted to tensor.
Returns:
Tensor: Converted image.
"""
return F.to_tensor(pic)
def __repr__(self):
return self.__class__.__name__ + '()'
大概的意思就是将 PIL
或者 numpy.ndarray
转换成 tensor
形式,那我们是不是可以直接使用opencv
中的数据变换方式,然后使用 torch.tensor(image)
或 torch.from_numpy(image)
传入网络呢?
可以是可以这么做,只不过这个时候需要单独增加维度,比较麻烦,所以还是建议直接使用 ToTensor()
来做转换。
看看到底做了哪些操作
源码在 F.to_tensor(pic)
- 转换维度:img = torch.from_numpy(pic.transpose((2, 0, 1)))
- 转变数据类型 :float/int
- divide 255: img.float().div(255)
说明:源码中不是一步到位操作的,只给出了大概操作类型,详情移步源码。
4. 总结
经过一些列的验证,总算排除了数据方面的不同处理可能带给模型的影响,这下就可以放心去魔改模型了,过段时间想整理一下如何魔改原有模型,以及如何初始化和注意事项。