本章主要内容为常见的数据读取、数据扩增方法和使用Pytorch框架读取数据。
赛题链接:https://tianchi.aliyun.com/competition/entrance/531795/information
Python有很多库可以完成图像读取,比较常见的有Pillow和OpenCV。
from PIL import Image
img =Image.open('./mchar_train/000100.png') #读取图片
img.show() #显示图片
pillow官方文档
:https://pillow.readthedocs.io/en/stable/
OpenCV包含了众多图像处理算法,还内置了很多图像特征处理算法,如关键点检测、边缘检测等。
import cv2
#读取显示图片
img = cv2.imread('./mchar_train/000100.png')
cv2.namedWindow('image', cv2.WINDOW_AUTOSIZE) #创建一个窗口
cv2.imshow('image', img) #在指定的窗口中显示一幅图像
cv2.waitKey(0)
cv2.destroyAllWindows() #删除建立的全部窗口,释放资源
OpenCV官网
:https://opencv.org/
OpenCV Github
:https://github.com/opencv/opencv
OpenCV扩展算法库
:https://github.com/opencv/opencv_contrib
在深度学习中数据扩增方法非常重要,数据扩增可以增加训练集的样本,同时也可以有效缓解模型过拟合的情况,也可以给模型带来的更强的泛化能力。
数据扩增方法有很多:从颜色空间、尺度空间到样本空间,同时根据不同任务数据扩增都有相应的区别。对于图像分类,数据扩增一般不会改变标签;对于物体检测,数据扩增会改变物体坐标位置;对于图像分割,数据扩增会改变像素标签。
from PIL import Image,ImageFilter,ImageEnhance
img = Image.open('./mchar_train/000100.png') #读取图片
#### 1 创建缩略图 ####
w, h = img.size # 获得图片宽高
img.thumbnail((w//2, h//2)) # 缩略图
#### 2 剪贴、粘贴、合并图像 ####
box = (25,5,55,35) #坐标位置:左、上、右、下
img_box = img.crop(box) #图片裁剪
img.paste(img_box,(0,0,30,30)) #原图粘贴子图,注意box的像素与子图的宽高必须吻合
#### 3 分离、合并通道 ####
r,g,b=img.split()
img=Image.merge("RGB",(b,g,r)) #交换图像rgb波段
#### 4 几何变换 ####
resized_img = img.resize((200,100)) #调整图片大小 (w, h)
img_rotate = img.rotate(45) #旋转图片,逆时钟旋转45度
img_transpose = img.transpose(Image.FLIP_LEFT_RIGHT) # 图片转换;左右转换 FLIP_LEFT_RIGHT,上下转换 FLIP_TOP_BOTTOM,逆时针90度 ROTATE_90
#### 5 颜色变换 ####
img.convert('L') #转为灰度图
#### 6 图像增强 ####
img_blur = img.filter(ImageFilter.BLUR) #模糊滤波
img_detail = img.filter(ImageFilter.DETAIL) #细节增强
img.point(lambda i:i*1.2) #每个像素点扩大1.2倍
source=img.split() #R,G,B=0,1,2
source[0].point(lambda i : i *1.2)
img=Image.merge(img.mode,source)
#对比度
contrast_img = ImageEnhance.Contrast(img)
contrast_img.enhance(1.8).show('180%enhance Contrast') #对比度增强1.8倍
#亮度
lighten_img = ImageEnhance.Brightness(img)
lighten_img.enhance(1.2).show('120%enchance brightness') #亮度增强1.2倍
'''
ImageEnhance.Contrast(img) 对比度
ImageEnhance.Color(img) 色彩饱和度
ImageEnhance.Brightness(img) 亮度
ImageEnhance.Sharpness(img) 清晰度
'''
import cv2
img = cv2.imread('./mchar_train/000100.png')
################################## 1 图像缩放 #################################
'''
函数原型: cv2.resize(InputArray src, OutputArray dst, Size, fx, fy, interpolation)
InputArray src:输入图片
OutputArray dst:输出图片
Size:图片尺寸
fx,fy:沿x,y轴的缩放系数
interpolation:插入方式
'''
#方法一
result=cv2.resize(img,None,fx=2,fy=2,interpolation=cv2.INTER_CUBIC)
#方法二
height, width = img.shape[:2] #获取图片大小(长,宽,通道数),注意imread()读入的是按照(h, w)
result = cv2.resize(img,(2*width, 2*height),interpolation = cv2.INTER_CUBIC) #resize()的第二个参数为目标尺寸,格式为(w, h)
#方法三 不用resize函数
import numpy as np
height, width = img.shape[:2]
dstHeight,dstWidth = int(height*2),int(width*2) #设定缩放后的图片大小
dstImage = np.zeros([dstHeight, dstWidth, 3], np.uint8)
for i in range(dstHeight):
for j in range(dstWidth):
iNew = i * ( height * 1.0 / dstHeight )
jNew = j * ( width * 1.0 / dstWidth )
dstImage[i,j] = img[int(iNew),int(jNew)]
################################## 2 图像平移 ##################################
'''
向(x,y)平移,创建变换矩阵M=[[1 0 x],[0 1 y]],并将其传递给cv.warpAffine()函数
'''
#方法一
height, width = img.shape[:2]
M = np.float32([[1,0,40],[0,1,20]]) #向(40,20)平移
dst = cv2.warpAffine(img,M,(width,height))
#方法二 创建空矩阵赋值
height, width = img.shape[:2]
x,y=40,20 #向(40,20)平移
dst=np.zeros(img.shape,np.uint8)
for i in range(y,height):
for j in range(x,width):
dst[i,j]=img[i-y,j-x]
################################## 3 图像旋转 ##################################
rows,cols = img.shape[:2]
M = cv2.getRotationMatrix2D((cols/2,rows/2),45,1) #参数:旋转中心,旋转角度,缩放比例
res = cv2.warpAffine(img,M,(cols,rows)) #参数:图像,旋转矩阵,变换后的图像大小
################################## 4 图像增强 ##################################
#亮度和对比度
#OpenCV中亮度和对比度公式:g(x) = αf(x) + β,其中:α(>0)、β常称为增益与偏置值,分别控制图片的对比度和亮度。
#numpy.clip(a, a_min, a_max, out=None, **kwargs)限定数据范围
alpha = 0.3
beta = 80
img = np.uint8(np.clip((alpha * img + beta), 0, 255))
import torchvision
from PIL import Image
img = Image.open('./mchar_train/000100.png')
#中心裁剪
centercrop = torchvision.transforms.CenterCrop((20,20)) #生成一个CenterCrop类的对象,用来将图片从中心裁剪成20*20
image = centercrop(img)
#上下左右中心裁剪,获得5张图片
fivecrop=torchvision.transforms.FiveCrop((20,20))
image=fivecrop(img)
#随机裁剪
randomcrop=torchvision.transforms.RandomResizedCrop((20,20))
image=randomcrop(img)
#上下左右中心裁剪后翻转,获得10张图片
tencrop=torchvision.transforms.TenCrop((20,20), vertical_flip=False) #vertical_flip 是否垂直翻转,默认为水平翻转
image=tencrop(img)
#依概率p水平翻转
randomHfilp=torchvision.transforms.RandomHorizontalFlip(p=0.5)
image=randomHfilp(img)
#依概率p垂直翻转
randomVfilp=torchvision.transforms.RandomVerticalFlip(p=0.5)
image=randomVfilp(img)
#随机旋转
randomrotate=torchvision.transforms.RandomRotation(30, center=None) #degress若为30,表示在(-30,30)之间随机旋转,若为(30,60),表示在30-60度之间随机旋转,center可选中心旋转还是左上角旋转
image=randomrotate(img)
####重置图像分辨率####
resize=torchvision.transforms.Resize((5*8), interpolation=2) #size设定为h*w,interpolation插值方法,默认为PIL.Image.BILINEAR
image=resize(img)
####归一化####
#方法一
normalize = torchvision.transforms.Compose([torchvision.transforms.ToTensor(),torchvision.transforms.Normalize(std=(0.5,0.5,0.5),mean=(0.5,0.5,0.5))])# 归一化到(0,1)之后,再(x-mean)/std,归一化到(-1,1)
image = normalize(img)
'''
transforms.Normalize标准化
transforms.ToTensor将PIL Image或者 ndarray 转换为tensor,并且归一化至(0,1)
'''
#方法二 手动归一化
import torch
import numpy as np
img_temp1 = torch.from_numpy(np.array(img).transpose((2,0,1))) #重置图像的shape,(H,W,C)转为(C,H,W)
img_temp2 = img_temp1.float().div(255)
imgnorm = img_temp2.sub_(0.5).div_(0.5)
image.equal(imgnorm) #True
####修改亮度、对比度和饱和度####
colorjitter=torchvision.transforms.ColorJitter(brightness=0, contrast=2, saturation=2) #参数分别为亮度、对比度和饱和度
image=colorjitter(img)
####转灰度图####
grayimg=torchvision.transforms.Grayscale(1)
image=grayimg(img)
####填充####
imgpad=torchvision.transforms.Pad((10,5), fill=255, padding_mode='constant') #第一个参数设置填充多少个pixel
image=imgpad(img)
####仿射变换####
imgaffie=torchvision.transforms.RandomAffine(30, fillcolor=255)
image=imgaffie(img)
常用的数据扩增库:
torchvision
:https://github.com/pytorch/vision
imgaug
:https://github.com/aleju/imgaug
albumentations
:https://albumentations.readthedocs.io
在Pytorch中数据是通过DataSet进行封装,并通过DataLoder进行读取。
import os, sys, glob, shutil, json
import cv2
from PIL import Image
import numpy as np
import torch
import torchvision.transforms as transforms
from torch.utils.data.dataset import Dataset
#构建dataset
class SVHNDataset(Dataset):
def __init__(self, img_path, img_label, transform=None):
self.img_path = img_path
self.img_label = img_label
if transform is not None:
self.transform = transform
else:
self.transform=None
def __getitem__(self, item):
img = Image.open(self.img_path[item]).convert('RGB')#PIL读取数据为‘RGBA’格式
if self.transform is not None:
img = self.transform(img)
lbl = np.array(self.img_label[item], dtype=np.int)
#训练集图片的最长字符数为6,所以设置长度为6的label,并将空值填充0.
lbl = list(lbl) + (6 - len(lbl)) * [0]
return img, torch.from_numpy(np.array(lbl[:6]))
def __len__(self):
return len(self.img_path)
train_path = glob.glob('F:/tianchi_CV_entrance/mchar_train/*.png')
train_path.sort()
train_json = json.load(open('F:/tianchi_CV_entrance/mchar_train.json'))
train_label = [train_json[x]['label'] for x in train_json]
#加载dataset
train_loader = torch.utils.data.DataLoader(
SVHNDataset(train_path, train_label,
#transforms.Compose()类用来组合多个torchvision.transforms操作
transforms.Compose([
transforms.Resize((64, 128)),
transforms.ColorJitter(0.3, 0.3, 0.2),
transforms.RandomRotation(10),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])),
batch_size=10, #每批样本个数
shuffle=False, #是否打乱顺序
num_workers=0, #线程数
)
#构造iter对象,通过next函数对数据进行检查
x = iter(train_loader)
data = next(x)
img, label = data[0], data[1]
print(img.size()) #torch.Size([10, 3, 64, 128]) [batchsize , chanel , height , width]
print(label.size()) #torch.Size([10, 6]) [batchsize, 字符数]