一、单通道图片(灰度图)的读取和转换
1、OpenCV ——(BGR):
因为大多数显示或处理图像的函数都期望图像是BGR格式(即Blue-Green-Red),而不是灰度格式。
在OpenCV imread函数图片读取出来默认为3通道,当你读取单通道(灰度图)图像时,通道数会自动扩展为三通道。Opecv会将单通道的图复制3次转化为三通道, 因此三个通道的颜色值是相同的。
如果你不想让OpenCV这样做,即想要保持读取的图像为单通道,可以使用以下方法:cv2.IMREAD_UNCHANGED标志。保持图像的原通道数不变。如果图像本来是单通道的,那么读取后它仍然是单通道的。如果图像本来有多个通道,那读取后会是多通道。
cv2.IMREAD_GRAYSCALE 标志。指定读取单通道图像(灰度图),最后的结果是单通道
import cv2
# 读取图像,保持原通道数
image = cv2.imread('image.jpg', cv2.IMREAD_UNCHANGED)
# 读取单通道图像(灰度图)
img_gray = cv2.imread('img.png', cv2.IMREAD_GRAYSCALE)
# 单通道(灰度图)转换到RGB
img_rgb = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2RGB)
# opencv 默认读取的是 BGR 三通道, 从BGR转换为RGB
image_bgr = cv2.imread('image.jpg')
img_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 图片保存
output_file = 'imageRGB.png'
cv2.imwrite(output_file, img_rgb)
# 显示图像
cv2.imshow('Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
2、PIL ——(RGB):
默认模式下PIL会自动检测图像模式,如果是灰度图则为'L'模式,灰度图读出的结果就是单通道
from PIL import Image
# 打开图像(默认模式下PIL会自动检测图像模式,如果是灰度图则为'L'模式)
img_gray = Image.open('your_image.png')
# 转换为RGB图像
# 注意:对于灰度图,直接使用.convert()转为"RGB"模式并不会增加色彩信息,每个像素的R=G=B=灰度值
img_rgb = img_gray.convert('RGB')
# 保存转换后的图像
output_file = 'converted_image.png'
img_rgb.save(output_file)
# 显示图像
img_rgb.show(title='Converted to RGB')
二、图像转换
在图像处理和深度学习中,经常需要在PIL(Python Imaging Library)、OpenCV(cv2)、NumPy和PyTorch之间进行图像数据的转换。
OpenCV: 读取的图片格式直接是numpy,uint8的ndarry数据,形状为HxWxC,颜色通道(C)是 BRG, 如果要使用正常的图片,先使用通道转换方法转化成RGB通道。保存时数据形式是uint8的ndarray数组。
matplotlib: uint8的ndarray数据,形状为HxWxC,颜色通道(C)是RGB。不怎么用
PIL: 可以np.array(image)或者np.asarray(image)转换成numpy数组:uint8的ndarray数据,形状为HxWxC,颜色通道(C)是RGB。可以Image.fromarray(image)将uint8格式numpy数组类型转化为pillow类型。
Pythorch: ToTensor()归一化到[0, 1]的torch数据,通道顺序nchw(只是单张图片的话是chw),颜色通道RGB,查看图片方法及ToPILImage。注意一点:经过transforms.ToTensor()得到的结果就是0-1.0的,而且通道转成(c,h,w)了
总结:除了pytorch得到的是nchw图片,其余方法得到的都是hwc图片;除了cv2得到的c是bgr图片,其余方法得到的c都是rgb图片;
注意,OpenCV和PIL读取图片成numpy array的范围是[0,255]是uint8,而转成tensor的范围就是[0,1.0], 是float
把图片转成成torch的tensor数据,一般采用函数:torchvision.transforms。注意一点是:opencv或者PIL储存图片的格式和torch的储存方式不一样,opencv或者PIL储存图片格式是(H,W,C),而torch储存的格式是(C,H,W)。
1. PIL和numpy之间的转换
import numpy as np
from PIL import Image
image_path = "img.jpg"
image_PIL = Image.open(image_path)
# 从PIL转换到numpy
numpy_array = np.array(image_PIL)
# 从numpy转换到PIL
image_PIL = Image.fromarray(numpy_array)
2. cv2和numpy之间的转换
import cv2
import numpy as np
image_path = "img.jpg"
image_opencv = cv2.imread(image_path)
# 从cv2到numpy的转换
numpy_array = np.array(image_opencv)
# 从numpy到cv2的转换。
image_opencv = cv2.cvtColor(numpy_array)
#注意:如果 opencv_image 是RGB图像,要进行颜色空间转换, 先转换为 BGR 再进行后续操作
opencv_image = cv2.cvtColor(numpy_array, cv2.COLOR_RGB2BGR)
3.numpy和pytorch之间的转换
import torch
import numpy as np
H, W, C = 512, 512, 3
# NumPy数组
numpy_array = np.array([H, W, C])
# 从numpy到torch的转换
torch_tensor = torch.from_numpy(numpy_array)
# 或者
torch_tensor = torch.Tensor(numpy_array)
# 从torch到numpy的转换
numpy_array = torch_tensor.numpy()
4、opencv、PIL 和 tensor之间互相转换
img 的范围是[0, 255]
tensor 当做训练数据时候的范围是[0, 1.0]
如果只是tesnor 张量,则没有限制
(1)、使用transforms内部函数 ToTensor()、ToPILImage() 进行转换
import cv2
from PIL import Image
import torchvision
import torch
import torchvision.transforms as transforms
"--------------------------读取img-------------------------------"
image_path = "/home/ytusdc/Data/Data_Split/Data_seg/ori.jpg"
# 使用 OpenCV 读取
img = cv2.imread(image_path) # 读取到的是 BGR
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转换为 RGB
print(img.shape)
# 使用 PIL 读取
img = Image.open(image_path) # 读取到的是 RGB, img
# 以上img数组格式为 均为(H,W,C)
"--------------------------图片转 tensor---------------------------"
# ToTensor() 函数实现归一化到[0, 1.0]范围和通道转换(H,W,C)-> (C,H,W)
img_tensor = transforms.ToTensor()(img) # tensor数据格式是torch(C,H,W)
print(img_tensor.size())
"--------------------------tensor 转图片----------------------------"
# tensor 转换前都先要到cpu
input_tensor = img_tensor.clone().detach().to(torch.device('cpu')) # 到cpu
# tensor 转PIL
# ToPILImage() 函数实现了反归一化到[0, 255],和通道变换 (C,H,W)->(H,W,C),直接转换成 PIL格式
PIL_img = transforms.ToPILImage()(input_tensor)
PIL_img.save("pil.jpg")
# tensor 直接转图片,并且保存
# utils.save_image() 函数内部实现了反归一化,和通道变换 (C,H,W)->(H,W,C),因此直接可以保存成图片
torchvision.utils.save_image(input_tensor, "out_cv.jpg")
(2)、自己进行归一化和通道变换
下面实现了 numpy 和 tensor 之间的转换,并且实现了归一化、反归一化、通道变换,当变换到numpy后,可以自己再根据需求变换到opecv或者PIL图片格式
import torch
from PIL import Image
import numpy as np
import torchvision.transforms as transforms
# 图像转换为PyTorch张量,转换过程进行了通道变换和归一化,这两个过程可选
def numpy_to_tensor(img):
img_numpy = np.array(img) # 从img转换为NumPy数组
img_numpy = img_numpy.transpose((2, 0, 1)) # (可选)通道变换 (H, W, C) -> (C, H, W)
tensor = torch.from_numpy(img_numpy).float() # 将图像从numpy数组转换为PyTorch张量
tensor = tensor.to(torch.float32) / 255.0 # (可选)在0-1范围内标准化图像
return tensor
# PyTorch张量转换为numpy
def tensor_to_numpy(tensor):
# 将张量转换为numpy数组, 如果张量在GPU上,请先使用.cpu()将其移动到CPU。
image_array = tensor.detach().cpu().clone().numpy()
# image_array = tensor.detach().cpu().numpy()
# (可选)如果图像是在0-1之间标准化的,则需要先反标准化(乘以255)
image_array = (image_array * 255).astype(np.uint8)
image_array = np.clip(image_array, 0, 255).astype(np.uint8) # 区间 0-255 截断
# (可选)如果张量进行了通道变换则要变换回来 (C, H, W) -> (H, W, C)
image_array = image_array.transpose((1, 2, 0))
return image_array
image = Image.open('path_to_your_image.jpg')
tensor_image = numpy_to_tensor(image) # 转换为张量
numpy_image = tensor_to_numpy(tensor_image) # 转换回 numpy