PIL skimage opencv torch各种图像格式的转换

在增强项目中因为涉及到多种相片格式的转换,感觉一直在花精力在网页搜索各种图形图片格式的互相转换,因此决定对相关知识点进行总结和输出。本质上感觉这一块知识点也蛮有意思,而且可以明显感知到 opencv是各种图片格式的中转站。

1、通用介绍

1.1 PIL数据

PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了。PIL功能非常强大,但API却非常简单易用。在使用的时候只需要 from PIL import Image,就可以无限畅游在PIL Image的畅快海洋了。基于PIL,延伸出Pillow: 出发点在于PIL只支持python2.7,添加了一些新特性;Pillow-SIMD:Pillow增强版本,约有5倍的增益,比CV2还要快。

数据格式:颜色通道RGB,通道HWC,范围[0,255],类型为 uint8;PIL是有自己的数据结构(图像类型PngImageFile),但是可以转换成numpy数组;有.mode方法可以查看其rgb信息(RGB或者L模式)

插播一下PIL中的滤波器:
对于将多个输入像素映射为一个输出像素的几何操作,PIL提供了4个不同的采样滤波器:
NEAREST:最近邻滤波。从输入图像中选取最近的像素作为输出像素。(PIL Resize默认的采样方式)
BILINEAR:双线性滤波。在输入图像的2x2矩阵上进行线性插值。注意:PIL的当前版本,做下采样时该滤波器使用了固定输入模板。
BICUBIC:双立方滤波。在输入图像的4x4矩阵上进行立方插值。注意:PIL的当前版本,做下采样时该滤波器使用了固定输入模板。
ANTIALIAS:平滑滤波。这是PIL 1.1.3版本中新的滤波器。对所有可以影响输出像素的输入像素进行高质量的重采样滤波,以计算输出像素值。在当前的PIL版本中,这个滤波器只用于改变尺寸和缩略图方法。
BOE论文edge–SR: Super–Resolution For The Masses中有提到alias起因来自于:原图中的高频细节与低频细节下采样之后,难以区分;The problem with such downscalers is that groups of high and low frequency components of the HR image can end up in the same low frequency component at LR, leading to well known aliasing artifacts
注意:在当前的PIL版本中,ANTIALIAS滤波器是下采样(例如,将一个大的图像转换为小图)时唯一正确的滤波器。BILIEAR和BICUBIC滤波器使用固定的输入模板,用于固定比例的几何变换和上采样是最好的。

1.2 Opencv

opencv:本质上是C++库,只是提供python接口,是目前最为常用、功能最为强大的图像处理库。
数据格式:颜色通道读取BGR,通道HWC,范围[0,255] ,类型uint8;图像类型numpy.ndarray;

1.3 Skimage

Skimage:scikit-image是基于scipy的一款图像处理包,它将图片作为numpy数组进行处理。使用时需要同时引入numpy和skimage,目前使用范围不是很广,不熟悉不推荐使用。

总结文章 提出skimge数据格式紊乱,比如彩色图片数据格式原本为uint8 [0,255],经过resize 操作之后则会变成 float [0,1],这也可以解释在Guided Filter中作者在resize之前对图像进行 img_as_float操作,在最终将结果又乘255进行输出。

 origin_full = img_as_float(imread('a.png'))
 lr_origin_full = resize(origin_full, (w, h), order=0, mode='reflect')
 ······
 r_ = np.asarray(r_.clip(0, 1)*255, dtype=np.uint8)

数据格式:读取RGB,通道HWC,范围[0,255],类型uint8;图像类型numpy.ndarray

1.4 Matlab

数据格式与skimage完全相同,目前使用也非常少;不建议使用。
数据格式:读取RGB,通道HWC,范围[0,1] ,类型float;图像类型numpy.ndarray

1.5 Torch

Torch作为目前最为主流的深度学习框架,了解图象数据在其内部的流向非常有意义。Pytorch数据前后处理整理 文章整理的非常详细,在本文基础上进行以下梳理。

Pytorch前置部分的transform函数与 PIL 耦合在一起,Pytorch通过PIL Image来读取图像文件, 然后再转成tensor送给network。以下部分不以Transform的compose函数进行介绍,而是将各函数分开进行介绍。

from PIL import Image
img = Image.open('image_path')

数据格式:颜色通道RGB,通道HWC,范围[0,255],类型为 uint8

from torchvision.transforms import ToTensor
tensor = ToTensor()(PIL_img)

ToTensor()接收PIL格式的数据,或者是直接从PIL转来的np.ndarray格式数据,只要保证进来的数据取值范围是[0, 255], 形状是[h, w, c],,像素顺序是RGB,它就会帮你做下面的事情
  取值范围[0, 255] / 255.0 => [0, 1.0], 数据格式从int8变成了float32
  形状(shape)转为[c, h, w]
  像素顺序依旧是RGB

很多时候引入 Varible 之后Tensor会增加一维转变为[N,c, h, w],此时也需要针对性进行转换。

numpy与torch两种类型使用from_numpy和numpy两个方法可以很方便的相互转换。
当然也可以用列表初始化torch
numpy具有abs,mean,abs等对每个元素的操作,torch都有相同名称的函数方法。
torch有mm方法来实现二维张量的矩阵乘法,torch的dot方法不同于矩阵点乘,torch会先自动展成一维张量在进行向量点乘,得到的结果等于张量所有元素的平方和。

np_data = np.arange(6).reshape((2, 3))
torch_data = torch.from_numpy(np_data)
tensor2array = torch_data.numpy()

data = [[1,2], [3,4]]
tensor = torch.FloatTensor(data)

torch.abs(tensor)
torch.sin(tensor)
torch.mean(tensor)

torch.mm(tensor, tensor)
tensor.dot(tensor)
1.6 imageio

本数据格式是在应用SRFBN模型的时候遇到的,目前来说这个图片的读取格式也是相对较为少见的,我在使用的过程中也是遇到了一些问题;部分问题到现在也是没有解决。
数据格式:未得到确切的格式,根据python读写图像 imageio所说:imageio浮点图像会自动亮度调节,手动先转换为uint8会没有问题;怀疑也是SRFBN中问题解决不理想的原因。
具体的数据格式可以参见下文中的代码:想说明的是数据格式有差异,不能放在一起混用。

       # v1 传入的是torch tensor
       # tensor 转 opencv (RGB数据格式)
		img = tensor.detach().cpu().mul(255.0).numpy().squeeze(0).transpose((1,2,0))
   
       np_transpose = np.ascontiguousarray(img.transpose((2, 0, 1)))
       tensor = torch.from_numpy(np_transpose).float()
       tensor.mul_(255 / 255.)
       tensor = torch.unsqueeze(tensor, 0)
  
       self.solver.feed_data_direct(tensor)

       # v2 传入的是文件路径
 	   lr = imageio.imread(image_path, pilmode='RGB')
       np_transpose = np.ascontiguousarray(lr.transpose((2, 0, 1)))
       tensor = torch.from_numpy(np_transpose).float()
       tensor.mul_(255 / 255.)

       tensor = torch.unsqueeze(tensor, 0)
       print('---  debug - tensor shape ---', tensor.shape)

       # 上面步骤之后转换为torch tensor,因此再转换回opencv mat格式;验证无问题
       import cv2 
       tensor = tensor.detach().cpu().mul(255.0).numpy().squeeze(0).transpose((1,2,0))
       out = cv2.cvtColor(tensor, cv2.COLOR_RGB2BGR)
       cv2.imwrite('./cv2_input.png',out)
       self.solver.feed_data_direct(lr_tensor)

2、数据格式之间的转换

2.1 PIL 与 Opencv数据格式转换:

opencv转PIL:

import cv2  
from PIL import Image  
import numpy as np
  
img = cv2.imread("plane.jpg")  
cv2.imshow("OpenCV",img)  
image = Image.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))  
image.show()  
cv2.waitKey()  

PIL转换为 opencv

import cv2  
from PIL import Image  
import numpy as np
  
image = Image.open("plane.jpg")  
image.show()  
img = cv2.cvtColor(np.asarray(image),cv2.COLOR_RGB2BGR)  
cv2.imshow("OpenCV",img)  
cv2.waitKey()  

array和asarray都可以将结构数据转化为ndarray,但是主要区别就是当数据源是ndarray时,array仍然会copy出一个副本,占用新的内存,但asarray不会。可以根据需要进行使用。

2.2 Pytorch 与 Opencv数据格式转换:

pytorch转Opencv

tensor = tensor.detach().cpu().mul(255.0).numpy().squeeze(0).transpose((1,2,0))
#detach与data类似,将变量从图中分离;不同的是detach更为安全;cpu负责转换数据为cpu格式,mul则是[0,1]到[0,255]
#squeeze撤去N通道,transpose更改tensor数据[c, h, w]为[h, w,C]
out = cv2.cvtColor(tensor, cv2.COLOR_RGB2BGR).astype(np.uint8)
#在numpy.ndarray的基础上,将其1、3通道互换,转换数据格式为np.uint8
cv2.imwrite(out_dir,out)

Opencv转pytorch
可以按照 Opencv - PIL - Pytorch的流程进行转换

2.3 Skimage与Torch互转:
#input_img为opencv格式 或者直接写作:img_as_float(imread('origin.png'))
origin_full = img_as_float(input_img) 
lr_gt  = img_as_float(enhancedImg) 
w, h = lr_gt.shape[:2]
lr_origin_full = resize(origin_full, (w, h), order=0, mode='reflect')
    
inputs = (torch.from_numpy(lr_origin_full.transpose((2, 0, 1))[None]).float().cuda())
f = FastGuidedFilter(20, 1e-8).cuda()
r = f(*inputs)
   
r_ = r.data.cpu().numpy().squeeze().transpose(1, 2, 0)
r_ = np.asarray(r_.clip(0, 1)*255, dtype=np.uint8)
imsave(FLAGS['image_test'], r_)

3、RGB与BGR转换的探究

实际上在opencv里面借助于cv2.COLOR_RGB2BGR; cv2.COLOR_BGR2RGB; numpy[:,:,::-1]所起的效果完全相同,个人判断cv里引进的两个变换函数完全相同;只是为了帮助代码撰写者捋清楚逻辑而已。

import cv2
import numpy as np
from PIL import Image

import numpy as np
from PIL import Image

im_bgr = np.asarray(Image.open('./lena_bgr_cv.jpg'))
im_rgb = im_bgr[:, :, ::-1]
Image.fromarray(im_rgb).save('./lena_swap_2.png')

im_bgr_1 = cv2.cvtColor(im_bgr, cv2.COLOR_RGB2BGR)
cv2.imwrite('./lena_bgr_cv_2.png', im_bgr_1)

im_bgr_2 = cv2.cvtColor(im_bgr, cv2.COLOR_BGR2RGB)
cv2.imwrite('./lena_bgr_cv_3.png', im_bgr_2)

im_bgr_11 = cv2.cvtColor(im_rgb, cv2.COLOR_RGB2BGR)
cv2.imwrite('./lena_bgr_cv_22.png', im_bgr_11)

im_bgr_22 = cv2.cvtColor(im_rgb, cv2.COLOR_BGR2RGB)
cv2.imwrite('./lena_bgr_cv_32.png', im_bgr_22)

你可能感兴趣的:(OpenCV,opencv,python,深度学习)