以下放在开头,备忘:
img = Image.open(ImgPath)
打开的图片是PIL类型,默认RGB。
将PIL类型转化为numpy类型:im = numpy.array(img)
才能看到shape属性,是(height, width, channel)数组,channel的通道数据是RGB。cv2.imread(path, 读取方式):
path: 图片的路径;
读取方式: cv2.IMREAD_COLOR:读入一副彩色图片;cv2.IMREAD_GRAYSCALE:以灰度模式读入图片;cv2.IMREAD_UNCHANGED:读入一幅图片,并包括其alpha通道。
默认为cv2.IMREAD_COLOR。
返回值是(height,width,channel)数组,channel的顺序是BGR顺序两者之间的相互转换
PIL Image转化为OpenCV格式:
img = Image.open()
img = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
OpenCV转化为PIL Image格式:
img = cv2.imread()
img2 = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))plt.imshow()和cv2.imshow()
这个显示是和读取对着的,也就是说要想可视化出来正常的RGB格式,给plt.imshow()的应该是rgb格式的图片,给cv2.imshow()的应该是bgr格式的图片。多说一些plt.imshow()
,因为我平时几乎不用cv2.imshow()
。在plt.imshow()
中,接受的图片类型可以是np.ndarray,tensor, PIL Image这些任意的类型。
在SSD和YOLO的源码中,使用opencv读取图片,其处理图片时都有以下图片通道转换的代码,做以下记录:
以https://github.com/ultralytics/yolov3中的一部分为例:
img0 = cv2.imread(path) # BGR
# Padded resize
img = letterbox(img0, self.img_size, stride=self.stride)[0]
# Convert
img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
img = np.ascontiguousarray(img)
其中的Padded resize
不用管,是YOLO对输入图片按Padded方式进行Resize。
我们要分析的是img = img[:, :, ::-1].transpose(2, 0, 1)这句代码
先放结论:
img[:,:,::-1] 这句的作用,是说:图像张量有三个维度,分别表示宽度、长度和颜色通道。既然image[:,:,::-1]的作用是对颜色通道把RGB转换成BGR。
怎么转换的呢?
对于列表img进行img[:,:,::-1]的作用是列表数组左右翻转,例如:
import numpy as np
a = np.arange(27).reshape(3,3,3)
print(a)
'''
[[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]]
[[ 9 10 11]
[12 13 14]
[15 16 17]]
[[18 19 20]
[21 22 23]
[24 25 26]]]
'''
b = a[:,:,::-1]
print(b)
'''
[[[ 2 1 0]
[ 5 4 3]
[ 8 7 6]]
[[11 10 9]
[14 13 12]
[17 16 15]]
[[20 19 18]
[23 22 21]
[26 25 24]]]
'''
可以明确看出,[:,:,::-1]的作用就是对数组进行左右翻转。那为什么对于图像而言却能实现RGB通道转换为BGR通道呢?先看一些读取图片的方法是怎么读取吧
一般有opencv(cv2)和matplotlib(plt)的读取方式:(cv2把图片读取后是把图片读成BGR形式的,plt则是读成RGB形式)
证明如下:
以一张图片为样例:
正常显示就是以RGB为格式显示的。当我们用plt读取时:
from matplotlib import pyplot as plt
import cv2
img_name = r'C:\Users\Administrator\Desktop\airball\val\35.jpg'
img = plt.imread(img_name)
print(img)
打印出来的是:
取第一个值,(129,80,73),在网上以查,颜色是深棕色的,确实跟图片的左上角是一致的:
证明plt读取图片的方式确实是以RGB的格式读取。
接着:
当执行 [:,:,::-1]后,数组会左右翻折:
from matplotlib import pyplot as plt
import cv2
img_name = r'C:\Users\Administrator\Desktop\airball\val\35.jpg'
img = plt.imread(img_name)
print(img)
print('-----------------------')
img = img[:, :, ::-1]
print(img)
,
可以看到(129 80 73)变成(73 80 129)。
通过网上查询(73 80 129)是什么颜色的,发现是蓝色的。
所以,如果拆测没错的话,这是后显示出来的图片应该左上角就是蓝色的,我们试试:
from matplotlib import pyplot as plt
import cv2
img_name = r'C:\Users\Administrator\Desktop\airball\val\35.jpg'
img = plt.imread(img_name)
print(img)
print('-----------------------')
img = img[:, :, ::-1]
plt.imshow(img)
plt.show()
果然左上角就是蓝色的。因此,这应该是BRG格式的图片了。所以得证,[:,:,::-1]的作用就是把RGB(或BRG)转换成BGR(或者RGB)。
=============================
插一句题外话,用opencv(即cv2)读取图片,是以BGR的形式来读取的。我们用cv2的imshow()函数显示图片发现跟我们打开图片的样子一样,是因为cv2的imshow()又把BGR转回RGB再显示。但cv2确实是以BGR形式读取图片的,而plt则是以RGB形式。
代码中有时也会有img[:, :, (2, 1, 0)]
这种操作,这个的意义也是通道的转换。其实都是因为opencv中imread读取出来的图片是BGR格式,也就是说下列三组代码是完全等价的,操作的结果都是将imread读取的BGR格式的图片转换为RGB格式
#第一组
image = cv2.imread(path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 转化为RGB
#第二组
image = cv2.imread(path)
image = image[:, :, (2, 1, 0)]
#第三组
image = cv2.imread(path)
image = image[:,:,::-1]
回到最初的问题,img = img[:, :, ::-1].transpose(2, 0, 1)这句代码,transpose(2,0,1)
有什么用,一句话总结:
这时的image是H,W,C的顺序,因此需要转化为C, H, W,维度变换正好2,0,1
变换维度的目的仅仅是为了方便计算。
常见如下代码
image = torch.from_numpy(image).permute(2, 0, 1)
PIL读取很正常,没什么操作,通常也不进行转换
image = Image.open(os.path.join(self.image_path, image_name))
# 读取到的是RGB, C, H, W
参考:
图像领域img[:,:,::-1]的理解
pytorch:读取图像的两种方法