图像处理那些事儿——图像读取、保存、处理

图像处理那些事儿

在计算机视觉中,总少不了与图像打交道。最近在处理血管瘤分割时,需要用到不少内容,包括图像增强、图像可视化、在图像上显示掩模等。正好在这里记录一下平时常用的处理方法,包括但不限于图像读取、图像转换、图像处理、图像保存。

在python中,一般用到的库是PIL、cv2、numpy和matplotlib。在深度学习中,还会用到torch和torchvision等

import os
import cv2
import torch
import numpy as np
from torchvision import transforms
from PIL import Image
import matplotlib.pyplot as plt

 

图像打开

PIL读取

path = './data/Angeioma/img/11.png'
pil = Image.open(path)
​
plt.imshow(pil)
print('size: ', pil.size)
​
array = np.asarray(pil)
print('shape: ', array.shape)

 

 

 

此时的img是一个PIL对象,可以直接由matplotlib展示出来。img常用的属性有size,返回一个图片(宽,高)的元组。

若要进行进一步的操作,可以将PIL对象转换成numpy数组,即

pil = np.asarray(pil)

 

opencv读取

path = './data/Angeioma/img/11.png'
​
cv = cv2.imread(path)
cv = cv2.cvtColor(cv ,cv2.COLOR_BGR2RGB)
​
print('type: ', type(cv))
print('shape: ', cv.shape)
​
plt.imshow(cv)

 

 

 

此时的img是一个numpy数组,也可以直接由matplotlib展示。常用的属性有shape,返回这个图片numpy数组的(高、宽、通道)元组。

但是由cv2.imread读取的图片,图像通道是BGR格式,一般情况下需要我们转变成RGB格式,即通过

img = cv2.CvtColor(img, cv2.COLOR_BGR2RGB)

注意

  1. opencv读取图片时,默认情况会读取3通道,若是灰度图,则会将其复制三次,需要指定。

  2. 通过这两种方式最后得到的numpy图像数组,像素取值是0-255之间的整数(np.uint8)

  3. numpy数组的格式是(height, width, channel

图像保存

PIL保存

对于Image对象而言,图像保存可以通过调用Image.save(),即

save = './data/Angeioma/img/pic_save.jpg'
image.save(save)

 

opencv保存

对于numpy数组,可以通过opencv的imwrite保存

save = './data/Angeioma/img/pic_save.jpg'
cv2.imwrite(save, img)

 

注意

对于opencv而言,图像存储时,也应将图像转换成BGR通道。像素灰度值应该是uint8类型,即[0—255]的整型数

图像处理

Resize

更改图像尺寸,是计算机视觉任务中常见的操作。

在PIL、opencv、torchvision中,均有Resize操作。

在PIL中

pil = Image.open(path)
pil = pil.resize((100, 200))

 

 

 

 

在opencv中

cv = cv2.imread(path)
cv = cv2.CvtColor(cv, cv2.COLOR_BGR2RGB)
cv = cv2.resize(cv, (100, 200))

 

 

 

 

在torchvision中,用于resize的是transforms.Resize()。可以处理PILimage,tensor

pil = Image.open(path)
pil = transforms.Resize((100, 200))(pil)

 

 

 

 

注意,在PIL和opencv中,resize 的参数是先宽后高。在transofroms.Resize中,是先高后宽。

 

不同的数据类型

在深度学习中,图像一般由tensor(张量)表示。将PILImage或numpy数组与Tensor进行相互转换的工具一般是

transforms.ToTensor()和transforms.ToPILImage

pil = Image.open(path)
cv = cv2.imread(path)

pil_tensor = transforms.ToTensor()(pil)
print(pil_tensor.shape)

pil_re = transforms.ToPILImage()(pil_tensor)
plt.imshow(pil_re)

 

由PIL图像转成的tensor,取值在0-1之间。tensor的形状是由(height,width,channel)变成了(channel,height,width)

图像tensor可以由transforms.ToPILImage直接变换成PILiamge。

同理,numpy数组也可以有类似的操作。

cv = cv2.imread(path)
cv = cv2.cvtColor(cv, cv2.COLOR_BGR2RGB)

cv_tensor = transforms.ToTensor()(cv)
cv_re = transforms.ToPILImage()(cv_tensor)

plt.imshow(cv_re)

 

注意

  1. 在transforms.ToTensor中,将元素缩放至0-1之间并不是都会发生。只有在处理的对象是PILimage或者dtype=np.uint8时,才会进行缩放。

  2. 在transfroms.ToPILImage中。可以将tensor转换成PILImage,也可以将numpy数组转换成PILImage。形状都是从(C,H,W)到(H,W,C)

可视化掩模

最近在做语义分割的项目,我们会将预测的mask与图片一起显示。

这里有两种处理方式。

通过图像与mask融合

即 混合图像为  mix = image * (1 - alpha) + mask * alpha

需要使image和mask保证形状一致

img_path = './data/Angeioma/img/102.png'
msk_path = './data/Angeioma/mask/102.png'

img = Image.open(img_path)
msk = Image.open(msk_path).convert('RGB').resize(img.size)

plt.figure(figsize=(16, 12))

plt.subplot(1, 3, 1)
plt.imshow(img)

plt.subplot(1, 3, 2)
plt.imshow(msk, cmap='gray')

mix = Image.blend(img, msk, alpha=0.3)

plt.subplot(1, 3, 3)
plt.imshow(mix)

 

 

 

这种做法可能会导致原图的部分像素变暗。

 

通过mask轮廓

使用cv2.findContours得到mask的轮廓,然后使用cv2.drawContours将轮廓绘制到image中。

img_path = './data/Angeioma/img/108.png'
msk_path = './data/Angeioma/mask/108.png'

img = Image.open(img_path)
msk = Image.open(msk_path).convert('RGB').resize(img.size)

img = np.asarray(img)
msk = np.asarray(msk)[:, :, 0]  # BGR 三层通道相等,取一个通道

contours, _ = cv2.findContours(msk, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

img_copy = img.copy()

cv2.drawContours(img_copy, contours, -1, (0, 0, 255), 2)  # 原图,轮廓,轮廓编号,颜色,线宽

plt.imshow(img_copy)

 

 

 

 

后记

对于上述的各个操作,只描述了一些常用的操作和属性,还有更多的细节有待补充。

如果所写内容有误,敬请批评指正。

 

 

 

posted @ 2022-05-31 21:24  Brisling  阅读(420)  评论(0编辑  收藏  举报