飞桨领航团图像分类零基础速成营学习心得【飞桨】【图像分类】【PaddlePaddle】

课程链接:https://aistudio.baidu.com/aistudio/course/introduce/11939?directly=1&shared=1

.OpenCV图像数据增强代码
左右翻转(上下翻转对于许多目标并不常用),随机裁剪,变换颜色(亮度,对比度,饱和度和色调)等等,我们拟用opencv-python实现部分数据增强方法

  1. 结构
class FunctionClass:
    def __init__(self, parameter):
        self.parameter=parameter

    def __call__(self, img):       



  1. 图片缩放
class Resize:
    def __init__(self, size):
        if size is not None:
            self.w = size if type(size) is int else size[0]
            self.h = size if type(size) is int else size[1]
        else:
            raise OperatorParamError("invalid params for ReisizeImage for 'size' is None")   

    def __call__(self, img):
        # TODO
        return cv2.resize(img, (self.w, self.h), interpolation=cv2.INTER_LINEAR)


  1. 图片翻转
class Flip:
    """ 
    random flip image 随机翻转图片
    mode:
        1: Flipped Horizontally 水平翻转
        0: Flipped Vertically 上下翻转
        -1: Flipped Horizontally & Vertically 水平、上下翻转
    """
    def __init__(self, mode=1):
        # 设置一个翻转参数,1、0或-1
        assert mode in [-1, 0, 1], "flip_code should be a value in [-1, 0, 1]"
        self.mode = mode

    def __call__(self, img):

        # 此处插入代码
        if np.random.randint(0, 2) == 1:
            return cv2.flip(img, self.mode)
        else:
            return img


flip = Flip(mode=0)
img2 = flip(img)
plt.imshow(img2)

  1. 图片旋转
class Rotate:
    def __init__(self, degree, size):
        self.degree=degree
        self.size=size

    def __call__(self, img):

        # 此处插入代码
        h, w = img.shape[:2]
        matRotate = cv2.getRotationMatrix2D((w*0.5, h*0.5), self.degree, self.size) # 旋转变化矩阵
        '''
        参数1 必选参数。用于设置旋转中心点,点坐标为OpenCV图像坐标系下的坐标。
        参数2 必选参数。用于设置旋转的角度,单位为度。
        参数3 必选参数。用于设置缩放系数,即对旋转的图像进行缩放。
        '''
        dst = cv2.warpAffine(img, matRotate, (w, h))  #旋转
        return dst

  1. 图片亮度调节
    亮度调整是将图像像素的强度整体变大/变小,对比度调整指的是图像暗处的像素强度变低,亮出的变高,从而拓宽某个区域内的显示精度。
    OpenCV中亮度和对比度应用这个公式来计算:g(x)=αf(x)+β,其中:α(>0)、β常称为增益与偏置值,分别控制图片的对比度和亮度。
class Brightness:
    '''
    亮度调整是将图像像素的强度整体变大/变小,对比度调整指的是图像暗处的像素强度变低,亮出的变高,从而拓宽某个区域内的显示精度。
    OpenCV中亮度和对比度应用这个公式来计算:g</span>(x)=αf</span>(x)+β</span></span>,其中:α(>0)、β常称为增益与偏置值,分别控制图片的对比度和亮度。
    '''
    def __init__(self,brightness_factor):
        self.brightness_factor=brightness_factor

    def __call__(self, img):
        return np.uint8(np.clip((self.brightness_factor* img), 0, 255))


brightness=Brightness(0.6)
img2=brightness(img)
plt.imshow(img2)

  1. 图片随机擦除
import random
import math

class RandomErasing(object):
    def __init__(self, EPSILON=0.5, sl=0.02, sh=0.1, r1=0.5,
                 mean=[0., 0., 0.]):
        self.EPSILON = EPSILON
        self.mean = mean
        self.sl = sl
        self.sh = sh
        self.r1 = r1

    def __call__(self, img):
        if random.uniform(0, 1) > self.EPSILON:
            return img

        for attempt in range(100):
            area = img.shape[0] * img.shape[1]

            target_area = random.uniform(self.sl, self.sh) * area  # [sl, sh]*area
            aspect_ratio = random.uniform(self.r1, 1 / self.r1) # [r1, 1/r1] for h/w

            h = int(round(math.sqrt(target_area * aspect_ratio)))
            w = int(round(math.sqrt(target_area / aspect_ratio)))
            
            # TODO
            if w < img.shape[0] and h < img.shape[1]:
                x1 = random.randint(0, img.shape[1] - h)
                y1 = random.randint(0, img.shape[0] - w)
                img2 = img.copy()
                if img.shape[2] == 3:
                    img2[ x1:x1 + h, y1:y1 + w, 0] = self.mean[0]
                    img2[ x1:x1 + h, y1:y1 + w, 1] = self.mean[1]
                    img2[ x1:x1 + h, y1:y1 + w, 2] = self.mean[2]
                else:
                    img2[x1:x1 + h, y1:y1 + w,0] = self.mean[0]

                return img2
        return img2

img = cv2.imread('lena.jpg', 1)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
erase = RandomErasing()
img2 = erase(img)
plt.imshow(img2)    

  1. 图片随机裁剪
class RandomResizeCrop(object):
    """ random crop image """
    """ 随机裁剪图片 """

    def __init__(self, size, scale=None, ratio=None, interpolation=-1):

        self.interpolation = interpolation if interpolation >= 0 else None
        if type(size) is int:
            self.size = (size, size)  # (h, w)
        else:
            self.size = size

        self.scale = [0.08, 1.0] if scale is None else scale
        self.ratio = [3. / 4., 4. / 3.] if ratio is None else ratio

    def __call__(self, img):
        size = self.size
        scale = self.scale
        ratio = self.ratio

        aspect_ratio = math.sqrt(random.uniform(*ratio))
        w = 1. * aspect_ratio
        h = 1. / aspect_ratio

        img_h, img_w = img.shape[:2]

        bound = min((float(img_w) / img_h) / (w**2),
                    (float(img_h) / img_w) / (h**2))
        scale_max = min(scale[1], bound)
        scale_min = min(scale[0], bound)

        target_area = img_w * img_h * random.uniform(scale_min, scale_max)
        target_size = math.sqrt(target_area)
        w = int(target_size * w)
        h = int(target_size * h)

        i = random.randint(0, img_w - w)
        j = random.randint(0, img_h - h)

        img = img[j:j + h, i:i + w, :]
        if self.interpolation is None:
            return cv2.resize(img, size)
        else:
            return cv2.resize(img, size, interpolation=self.interpolation)

crop = RandomResizeCrop(224)
plt.imshow(crop(img))

**二.**图像处理入门基础

  1. 分辨率=画面水平方向的像素值 * 画面垂直方向的像素值
    屏幕分辨率
    例如,屏幕分辨率是1024×768,也就是说设备屏幕的水平方向上有1024个像素点,垂直方向上有768个像素点。像素的大小是没有固定长度的,不同设备上一个单位像素色块的大小是不一样的。

例如,尺寸面积大小相同的两块屏幕,分辨率大小可以是不一样的,分辨率高的屏幕上面像素点(色块)就多,所以屏幕内可以展示的画面就更细致,单个色块面积更小。而分辨率低的屏幕上像素点(色块)更少,单个像素面积更大,可以显示的画面就没那么细致。

图像分辨率
例如,一张图片分辨率是500x200,也就是说这张图片在屏幕上按1:1放大时,水平方向有500个像素点(色块),垂直方向有200个像素点(色块)。

在同一台设备上,图片分辨率越高,这张图片1:1放大时,图片面积越大;图片分辨率越低,这张图片1:1缩放时,图片面积越小。(可以理解为图片的像素点和屏幕的像素点是一个一个对应的)。

但是,在屏幕上把图片超过100%放大时,为什么图片上像素色块也变的越大,其实是设备通过算法对图像进行了像素补足,我们把图片放的很大后看到的一块一块的方格子,虽然理解为一个图像像素,但是其实是已经补充了很多个屏幕像素;同理,把图片小于100%缩小时,也是通过算法将图片像素进行减少。
在这里插入图片描述
使用OpenCV加载并保存图片

加载图片,显示图片,保存图片
OpenCV函数:cv2.imread(), cv2.imshow(), cv2.imwrite()
说明
大部分人可能都知道电脑上的彩色图是以RGB(红-绿-蓝,Red-Green-Blue)颜色模式显示的,但OpenCV中彩色图是以B-G-R通道顺序存储的,灰度图只有一个通道。

OpenCV默认使用BGR格式,而RGB和BGR的颜色转换不同,即使转换为灰度也是如此。一些开发人员认为R+G+B/3对于灰度是正确的,但最佳灰度值称为亮度(luminosity),并且具有公式:0.21R+0.72G+0.07*B

图像坐标的起始点是在左上角,所以行对应的是y,列对应的是x。

加载图片
使用cv2.imread()来读入一张图片:

参数1:图片的文件名

如果图片放在当前文件夹下,直接写文件名就行了,如’lena.jpg’
否则需要给出绝对路径,如’D:\OpenCVSamples\lena.jpg’
参数2:读入方式,省略即采用默认值

cv2.IMREAD_COLOR:彩色图,默认值(1)
cv2.IMREAD_GRAYSCALE:灰度图(0)
cv2.IMREAD_UNCHANGED:包含透明通道的彩色图(-1)
经验之谈:路径中不能有中文噢,并且没有加载成功的话是不会报错的,print(img)的结果为None,后面处理才会报错,算是个小坑。

阈值分割

使用固定阈值、自适应阈值和Otsu阈值法"二值化"图像
OpenCV函数:cv2.threshold(), cv2.adaptiveThreshold()

.基于PaddlePaddle2.0的蝴蝶图像识别分类——利用预训练残差网络ResNet101模型
https://aistudio.baidu.com/aistudio/projectdetail/1617500
准备数据

#以下代码用于建立样本数据读取路径与样本标签之间的关系
import os
import random

data_list = [] #用个列表保存每个样本的读取路径、标签

#由于属种名称本身是字符串,而输入模型的是数字。需要构造一个字典,把某个数字代表该属种名称。键是属种名称,值是整数。
label_list=[]
with open("/home/aistudio/data/species.txt") as f:
    for line in f:
        a,b = line.strip("\n").split(" ")
        label_list.append([b, int(a)-1])
label_dic = dict(label_list)

#获取Butterfly20目录下的所有子目录名称,保存进一个列表之中
class_list = os.listdir("/home/aistudio/data/Butterfly20")
class_list.remove('.DS_Store') #删掉列表中名为.DS_Store的元素,因为.DS_Store并没有样本。

for each in class_list:
    for f in os.listdir("/home/aistudio/data/Butterfly20/"+each):
        data_list.append(["/home/aistudio/data/Butterfly20/"+each+'/'+f,label_dic[each]])

#按文件顺序读取,可能造成很多属种图片存在序列相关,用random.shuffle方法把样本顺序彻底打乱。
random.shuffle(data_list)

#打印前十个,可以看出data_list列表中的每个元素是[样本读取路径, 样本标签]。
print(data_list[0:10])

#打印样本数量,一共有1866个样本。
print("样本数量是:{}".format(len(data_list)))

#以下代码用于构造读取器与数据预处理
#首先需要导入相关的模块
import paddle
from paddle.vision.transforms import Compose, ColorJitter, Resize,Transpose, Normalize
import cv2
import numpy as np
from PIL import Image
from paddle.io import Dataset

#自定义的数据预处理函数,输入原始图像,输出处理后的图像,可以借用paddle.vision.transforms的数据处理功能
def preprocess(img):
    transform = Compose([
        Resize(size=(224, 224)), #把数据长宽像素调成224*224
        Normalize(mean=[127.5, 127.5, 127.5], std=[127.5, 127.5, 127.5], data_format='HWC'), #标准化
        Transpose(), #原始数据形状维度是HWC格式,经过Transpose,转换为CHW格式
        ])
    img = transform(img).astype("float32")
    return img

#自定义数据读取器
class Reader(Dataset):
    def __init__(self, data, is_val=False):
        super().__init__()
        #在初始化阶段,把数据集划分训练集和测试集。由于在读取前样本已经被打乱顺序,取20%的样本作为测试集,80%的样本作为训练集。
        self.samples = data[-int(len(data)*0.2):] if is_val else data[:-int(len(data)*0.2)]

    def __getitem__(self, idx):
        #处理图像
        img_path = self.samples[idx][0] #得到某样本的路径
        img = Image.open(img_path)
        if img.mode != 'RGB':
            img = img.convert('RGB')
        img = preprocess(img) #数据预处理--这里仅包括简单数据预处理,没有用到数据增强

        #处理标签
        label = self.samples[idx][1] #得到某样本的标签
        label = np.array([label], dtype="int64") #把标签数据类型转成int64
        return img, label

    def __len__(self):
        #返回每个Epoch中图片数量
        return len(self.samples)

#生成训练数据集实例
train_dataset = Reader(data_list, is_val=False)

#生成测试数据集实例
eval_dataset = Reader(data_list, is_val=True)

#打印一个训练样本
#print(train_dataset[1136][0])
print(train_dataset[1136][0].shape)
print(train_dataset[1136][1])


建立模型

#定义模型
class MyNet(paddle.nn.Layer):
    def __init__(self):
        super(MyNet,self).__init__()
        self.layer = paddle.vision.models.resnet50(pretrained=True)
        self.fc = paddle.nn.Linear(1000, 512)
        self.fc2 = paddle.nn.Linear(512, 20)
        self.flatten = paddle.nn.Flatten()
        self.relu = paddle.nn.ReLU()
        self.dropout = paddle.nn.Dropout(0.2)
    #网络的前向计算过程
    def forward(self,x):
        x = self.layer(x)
        x = self.flatten(x)
        x = self.fc(x)
        x = self.relu(x)
        x = self.dropout(x)
        x = self.fc2(x)
        return x

应用高阶API训练模型

#定义输入
input_define = paddle.static.InputSpec(shape=[-1,3,224,224], dtype="float32", name="img")
label_define = paddle.static.InputSpec(shape=[-1,1], dtype="int64", name="label")

#实例化网络对象并定义优化器等训练逻辑
model = MyNet()
model = paddle.Model(model,inputs=input_define,labels=label_define) #用Paddle.Model()对模型进行封装
optimizer = paddle.optimizer.Adam(learning_rate=0.0001, parameters=model.parameters())
#上述优化器中的学习率(learning_rate)参数很重要。要是训练过程中得到的准确率呈震荡状态,忽大忽小,可以试试进一步把学习率调低。

model.prepare(optimizer=optimizer, #指定优化器
              loss=paddle.nn.CrossEntropyLoss(), #指定损失函数
              metrics=paddle.metric.Accuracy()) #指定评估方法

model.fit(train_data=train_dataset,     #训练数据集
          eval_data=eval_dataset,         #测试数据集
          batch_size=64,                  #一个批次的样本数量
          epochs=70,                      #迭代轮次
          save_dir="/home/aistudio/iterhui", #把模型参数、优化器参数保存至自定义的文件夹
          save_freq=1,                    #设定每隔多少个epoch保存模型参数及优化器参数
          log_freq=100               #打印日志的频率
)
result = model.evaluate(eval_dataset,batch_size=64,log_freq=100, verbose=2, num_workers=0, callbacks=None)
print(result)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_34331865

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值