能看到这篇博客的同学相信不会不知道paddlepaddle,因此就不像别人写的博文那样对paddlepaddle做普及。
paddleclas的官方文档:https://paddleclas.readthedocs.io/zh_CN/latest/tutorials/
之前使用paddleapddle时也使用过数据增强。但是,当时参考的官网上的一个公开项目:https://aistudio.baidu.com/aistudio/projectdetail/474524
这个项目在当时看来应该说算是很好很细的一个项目了(在这个项目中从手动实现基本的数据增强到构建YOLOV3的网络结构在到loss函数都有相关代码,感兴趣的同学可以学习一下不过前提是了解YOLOV3)。
现在,自己再次需要使用到数据增强时又开始在找paddlepaddle应该如何进行数据增强。我刚开始时找到了paddleX。但是,据说的封装很复杂,难以将通过paddleX读取的数据列表应用在自定义的网络中。于是在官方里的推荐下开始使用了paddleClas。网上关于paddleClas的说明并不多。所以在使用的过程中也是踩了很多的坑。(这个工具是2020年新推出的。所以,相关的资料远远不如paddleX,paddlepaddle多。)
安装:
没装上.......我直接从github上下载的源码使用的。 github网址:
https://github.com/PaddlePaddle/PaddleClas.git
我们需要的库函数在其中的ppclas下。本来应该是直接把这个放到Anaconda的库函数文件(XXXX/Anaconda3\Lib\site-packages)中,然后在进行使用。
使用:
class DataSet(object):
def __init__(self,ata_dir = None,
file_list=None,
label_list=None,
transforms=None):
self.file_list = []
self.num_samples = 0
self.labels = []
self.transfroms = transforms
if(file_list != None):
with open(file_list) as file:
while True:
s = file.readline().split(" ")
if(len(s) < 2):
break
s[1] = int(s[1])
self.file_list.append(s)
if(label_list != None):
with open(label_list) as file:
while True:
s = file.readline()
if(s == ""):
break
self.labels.append(int(s))
def dataRead(img_dir,data_list_file,label_list_file,mode = ""):
'''
img_dir:图片数据所在的位置
data_list_file:数据集列表所在的位置
label_list_file:标签列表所在的位置
return:
dataset:读取之后的数据集
'''
#数据增强部分,参数可调
dataset = DataSet(ata_dir=img_dir,
file_list=data_list_file,
label_list=label_list_file,
transforms=None)
return dataset
import cv2
import numpy as np
import ppcls.data.imaug as imgaug
def img_augment(img):
'''
operators:
DecodeImage 二进制解码
ResizeImage 重定义大小
CropImage 图片裁剪
RandCropImage 随机裁剪
RandFlipImage 图片翻转
NormalizeImage 归一化
ToCHWImage 改变形式为C-通道,H-高,W-宽
'''
decode_op = imgaug.DecodeImage()
resize_op = imgaug.ResizeImage(size = (235,235))
crop_op = imgaug.RandCropImage(size = (224,224))
flip1_op = imgaug.RandFlipImage(flip_code = 1)
flip2_op = imgaug.RandFlipImage(flip_code=0)
flip3_op = imgaug.RandFlipImage(flip_code=-1)
normalize_op = imgaug.NormalizeImage(order = "hwc")
CHWchange_op = imgaug.ToCHWImage()
ops = [decode_op,resize_op,crop_op,flip1_op,flip2_op,flip3_op,normalize_op,CHWchange_op]
#ops = [decode_op,resize_op,crop_op,flip1_op,flip2_op,flip3_op]
#ops = [decode_op]
img = imgaug.transform(img,ops)
return img
def readImg(path):
with open(file = path,mode = 'rb') as f:
img = f.read()
img = img_augment(img)
return img
def MyCV2Show(img):
imgd = img[:,:,::-1]
cv2.imshow("imgd",imgd)
cv2.waitKey(0)
cv2.destroyAllWindows()
return
def data_reader(batch, dataset):
data_list = []
label_list = []
for i, file_data in enumerate(dataset.file_list):
img = readImg(file_data[0])
#MyCV2Show(img) #如需要使用则不能使用NormalizeImage(归一化)和ToCHWImage(通道转换)
label = file_data[1]
data_list.append(img)
label_list.append(label)
if ((i + 1) % batch == 0):
yield np.array(data_list), np.array(label_list)
data_list.clear()
label_list.clear()
if (len(data_list) > 0):
yield np.array(data_list), np.array(label_list)
if __name__ == "__main__":
train_dataset = dataRead(r"../min_data/17flowers/jpg/",
r"../min_data/17flowers/train_data.txt",
r"../min_data/17flowers/label_list.txt",
"train")
for batch_id,batch_data in enumerate(data_reader(10,train_dataset)):
batch_img_data,batch_label = batch_data
接下来就逐步拆解上述代码,以涉及paddleclas的部分为主:
dataRead函数就是把txt中的数据列表进行打包存在dataset中,其功能相似于paddleX中的pdx.datasets.ImageNet。(可参考https://paddlex.readthedocs.io/zh_CN/develop/data/format/classification.html)
其中的数据文件(“../min_data/17flowers/jpg/”)结构如下图:
其中的train_data.txt是图片集合,其中的内容如下图:
其中的label_list.txt是标签集合,其中的内容如下图:
在dataset中的transfroms其实并没有用上删掉也不影响。
def data_reader(batch, dataset):
# 没有归一化和数据增强
data_list = []
label_list = []
for i, file_data in enumerate(dataset.file_list):
img = readImg(file_data[0])
#MyCV2Show(img) #如需要使用则不能使用NormalizeImage(归一化)和ToCHWImage(通道转换)
label = file_data[1]
data_list.append(img)
label_list.append(label)
if ((i + 1) % batch == 0):
yield np.array(data_list), np.array(label_list)
data_list.clear()
label_list.clear()
if (len(data_list) > 0):
yield np.array(data_list), np.array(label_list)
data_reader就仅仅只是一个数据读取器。其中我仅仅只在readImg中使用了paddleclas。readImg中所做的就是读取一张图片并且进行数据增强。
def readImg(path):
with open(file = path,mode = 'rb') as f:
img = f.read()
img = img_augment(img)
return img
如果是一般情况下读取图片数据,应该是使用opencv或者PIL下的一些方法来进行读取。但是,在数据增强中有一个是数据二进制解码。这个 函数(DecodeImage函数)就是将一个二进制的数据转化成是H,W,C的三维矩阵。我也试过如果我们一定要使用opencv或者PIL的方法来读取图片则去掉DecodeImage这个函数即可。至于,效率哪种方法更快我就没有作过统计了。
def img_augment(img):
'''
operators:
DecodeImage 二进制解码
ResizeImage 重定义大小
CropImage 图片裁剪
RandCropImage 随机裁剪
RandFlipImage 图片翻转
NormalizeImage 归一化
ToCHWImage 改变形式为C-通道,H-高,W-宽
'''
decode_op = imgaug.DecodeImage()
resize_op = imgaug.ResizeImage(size = (235,235))
crop_op = imgaug.RandCropImage(size = (224,224))
flip1_op = imgaug.RandFlipImage(flip_code = 1)
flip2_op = imgaug.RandFlipImage(flip_code=0)
flip3_op = imgaug.RandFlipImage(flip_code=-1)
normalize_op = imgaug.NormalizeImage(order = "hwc")
CHWchange_op = imgaug.ToCHWImage()
ops = [decode_op,resize_op,crop_op,flip1_op,flip2_op,flip3_op,normalize_op,CHWchange_op]
#ops = [decode_op,resize_op,crop_op,flip1_op,flip2_op,flip3_op]
#ops = [decode_op]
img = imgaug.transform(img,ops)
return img
一些基本的用于图像分类的数据增强函数都在这了。如果是其它方向的任务例如目标检测的数据增强应该也有对应的方法。下面是对以上这些函数的详细说明:
to_rgb:如果是True,在解码之后就会变成H,W,C的RGB图像,如果是Flase就是BGR图像。如果使用opencv的cv2.imshow来查看图片就应当使用BGR的图像。
to_np:决定最后存储是否存储为numpy的数据类型。(这个不太清楚,好像最后都变成在numpy的数据类型。欢迎补充,\(-_-)/)
channel_first:这决定最后图像的数据格式是H,W,C还是C,H,W。如果是True就是C,H,W。PIL的图片用的C,H,W。而opencv用的是H,W,C。另外,paddlepaddle的框架下主要使用的就是C,H,W为主。
size:如果size是一个int,则图片会被resize成是size*size大小的图片。如果size是一个tuple,(h,w)则图片会被resize成是一个h*w大小的图片。
resize_short:把图片按照一定比例缩小
interpolation:resize过程中换像素处理方式。
此处,这个函数可以参考cv2的imresize()
普通的图片裁剪直接放完整的代码:
class CropImage(object):
""" crop image """
def __init__(self, size):
if type(size) is int:
self.size = (size, size)
else:
self.size = size # (h, w)
def __call__(self, img):
w, h = self.size
img_h, img_w = img.shape[:2]
w_start = (img_w - w) // 2
h_start = (img_h - h) // 2
w_end = w_start + w
h_end = h_start + h
return img[h_start:h_end, w_start:w_end, :]
size:从图片中裁剪出size大小的一部分
size:裁剪出的图片的大小。
scale:根据长宽比例进行裁剪
ratio:也是一个比例系数。目的是通过这个参数以保证长宽比不变。
interpolation:在裁剪完成后会得到一张非size尺寸的图片。在那之后会进行resize,interpolation是在这时需要使用的参数
flip_code:其中是一个列表。列表中的元素只有0,1,-1.其中0代表水平翻转,1代表竖直翻转,-1代表先水平后坚直翻转。
scale:缩小一定的比例,先将图片中的所有数值变成小于1的小数
mean:均值
std:方差
order:图片的类型,C,H,W的图片和H,W,C的图片的归一化的过程是不一样的
均值默会是[0.485, 0.456, 0.406],方差默认是[0.229, 0.224, 0.225],可以根据实际问题进行修改
没有参数,就是简单的把H,W,C的图片转变成是C,H,W的图片。
transform函数中的内容比较容易理解,就是按照顺序依次作对应的变幻。
到目前为止先作这些记录,等后续做到目标检测的时候。会记继续记录相应的内容。
如果,在上面有错误的内容,欢迎指正。如果有疑问我看到后也会尽快回复。