图像分割基本知识

计算机视觉和图像处理

  1. Tensorflow入门
  2. 深度神经网络
  3. 图像分类
  4. 目标检测
  5. 图像分割
  6. OpenCV
  7. Pytorch
  8. NLP自然语言处理

一、目标分割

  • 图像分类旨在判断该图像所属类别
  • 目标检测是在图像分类的基础上,进一步判断图像中的目标具体在图像的什么位置,通常是以外包矩阵的形式表示。
  • 图像分割是目标检测更进阶的任务,目标检测只需要框出每个目标的包围盒,语义分割需要进一步判断图像中哪些像素属于哪个目标。但是,语义分割不区分属于相同类别的不同实例。

1.1 图像分割的定义

在计算机视觉领域,图像分割指的是将数字图像细分为多个图像子区域(像素的集合)的过程,并且同一个子区域内的特征具有一定相似性,不同子区域的特征呈现较为明显的差异。

1.2 任务类型

1.2.1 任务描述

我们的目标是输入一个RGB彩色图片或者一个灰度图,然后输出一个包含各个像素类别标签的分割图。
在这里插入图片描述
预测目标可以采用one-hot编码,即为每一个可能的类创建一个输出通道。通过取每个像素点在各个通道的argmax可以得到最终的预测分割图。
在这里插入图片描述

1.2.2 任务类型

目前的图像分割任务主要有两类: 语义分割和实例分割
在这里插入图片描述

  • 语义分割就是把图像中每个像素赋予一个类别标签
    在这里插入图片描述
  • 实例分割,相对于语义分割来讲,不仅要区分不同类别的像素,还需要需要对同一类别的不同个体进行区分。如下图所示,不仅需要进行类别的划分,还要将各个个体划分出来:羊1,羊2,羊3,羊4,羊5等。
    在这里插入图片描述

二、语义分割

2.1 FCN网络

FCN用于图像语义分割,自从该网络提出后,就成为语义分割的基本框架,后续算法基本都是在该网络框架中改进而来。
简而言之,FCN和CNN的区别就是:CNN卷积层之后连接的是全连接层;FCN卷积层之后仍然连卷积层,输出的是与输入大小相同的特征图。

2.1.1网络结构

FCN是一个端到端,像素对像素的全卷积网络,用于进行图像的语义分割。整体的网络结构分为两个部分:全卷积部分和上采样部分。

  1. 全卷积部分
    全卷积部分使用经典的CNN网络(以AlexNet网络为例),并把最后的全连接层换成1x1卷积,用于特征提取。
  2. 上采用部分
    上采样部分将最终得到的特征图上采样得到原图像大小的语义分割结果。
    在这里采用的上卷积方法是反卷积,也叫转置卷积,反卷积是一种特殊的正向卷积,通俗的讲,就是输入补0+卷积。先按照一定的比例通过补0来扩大输入图像的尺寸,再进行正向卷积即可。

2.2 Unet网络

Unet网络是建立再FCNN网络基础上的。
在这里插入图片描述
整个网络由编码部分(左)和解码部分(右)组成,类似于一个大大的u字母,具体介绍如下:

1.编码部分是典型的卷积网络架构

编码部分的主要功能是提取输入图像的特征。通过一系列的卷积层和池化层(通常是最大池化层),编码部分逐渐减少特征图的尺寸,同时增加特征图的深度(即特征图的数量)。

扫描二维码关注公众号,回复: 17468061 查看本文章
  • 架构中含有一种重复结构,每次重复中有2个3x3卷积层、非线性ReLU层和一个2x2 max pooling层(stride为2)。
  • 每一次下采样后我们都把特征通道的数量加倍。

下采样(编码部分):减少数据点的数量或降低数据的分辨率,用于减少数据量、简化模型训练等

  1. 解码部分也使用了类似的模式:

解码部分的主要功能是恢复特征图的空间分辨率,最终生成与输入图像相同尺寸的分割结果。解码部分通过一系列的上采样操作(如转置卷积或上采样层)和卷积操作来逐步恢复特征图的尺寸。

  • 每一步都首先使用反卷积,每次使用反卷积都将特征通道数量减半,特征图大小加倍。
  • 反卷积过后,将反卷积的结果与编码部分中对应步骤的特征图拼接起来。
  • 编码部分中的特征图尺寸稍大,将其修建过后进行拼接。
  • 对拼接后的map再进行2次3x3的卷积。
  • 最后一层的卷积核大小为1x1,将64通道的特征图转化为特定类比数量的结果。

上采样(解码部分):增加数据点的数量或提高数据的分辨率,用于恢复细节、改善图像质量等。

三、UNet案例

Oxford-IIIT Pet Dataset宠物图像分割数据集,包含37种宠物类别,其中有12种猫的类别和25种狗的类别,每个类别大约有200张图片,所有图像都具有品种,头部ROI和像素级分割的标注,如下图所示:
在这里插入图片描述

import os
from IPython.display import Image,display
from tensorflow.keras.preprocessing.image import load_img
import PIL
from PIL import ImageOps

3.1 数据集获取

3.1.1 设置相关信息

# 图像位置
input_dir = 'segdata/images/'
# 图像路径
input_img_path = sorted([os.path.join(input_dir,fname) for fname in os.listdir(input_dir) if fname.endswith('jpg')])
input_img_path

在这里插入图片描述

# 标注信息
target_dir = 'segdata/annotations/trimaps/'
# 目标值
target_img_path = sorted([os.path.join(target_dir,fname) for fname in os.listdir(target_dir) if fname.endswith('png') and not fname.startswith('.')])
target_img_path

在这里插入图片描述

# 图像大小及类别信息
img_size = (160,160)
batch_size = 32
num_classes = 4

3.1.2 图像展示

display(Image(input_img_path[10]))

在这里插入图片描述

img = PIL.ImageOps.autocontrast(load_img(target_img_path[10]))
display(img)

在这里插入图片描述

3.1.3 数据集生成器

from tensorflow import keras
import numpy as np
# 数据集获取类
class OxfordPets(keras.utils.Sequence):
    # 初始化
    def __init__(self,batch_size,img_size,input_img_path,target_img_path):
        self.batch_size = batch_size
        self.img_size = img_size
        self.input_img_path = input_img_path
        self.target_img_path = target_img_path
    # 迭代次数
    def __len__(self):
        return len(self.target_img_path)//self.batch_size 
    # 或者batch数据
    def __getitem__(self,idx):
        # 当前批次对应的索引值
        i = idx * self.batch_size
        # 图像数据
        batch_input_img_path = self.input_img_path[i:i+self.batch_size]
        # 标签数据
        batch_target_img_path = self.target_img_path[i:i+self.batch_size]
        # 构建送入网络中图像数据
        x = np.zeros((self.batch_size,)+self.img_size+(3,),dtype='float32')
        for j,path in enumerate(batch_input_img_path):
            img = load_img(path,target_size=self.img_size)
            # 将PIL图像对象转换成Numpy数组
            x[j] = keras.preprocessing.image.img_to_array(img)
        y = np.zeros((self.target_size,) + self.img_size +(1,),dtype='uint8')
        for j,path in enumerate(batch_target_img_path):
            img = load_img(path,target_size=self.img_size,color_mode="grayscale")
            # 再数组的末尾增加一个维度
            y[j] = np.expand_dims(img,2)
        return x,y

3.2 模型构建

import tensorflow as tf
import tensorflow.keras as keras
from tensorflow.keras.layers import Input,Conv2D,Conv2DTranspose
from tensorflow.keras.layers import MaxPooling2D,Cropping2D,Concatenate,ZeroPadding2D
from tensorflow.keras.layers import Lambda,Activation,BatchNormalization,Dropout
from tensorflow.keras.models import Model

3.2.1 编码部分

# 下采样:输出张量,卷积核个数
def downsampling_block(input_tensor,filters):
    # 输入层
    x = Conv2D(filters,kernel_size=(3,3),padding='same')(input_tensor)
    # BN层
    x = BatchNormalization()(x)
    # 激活层
    x = Activation('relu')(x)
    # 卷积
    x = Conv2D(filters,kernel_size=(3,3),padding='same')(x)
    # BN层
    x = BatchNormalization()(x)
    # 激活层
    x = Activation('relu')(x)
    # 返回
    return MaxPooling2D(pool_size=(2,2))(x),x

3.2.2 解码部分

# 上采样:输入张量,特征融合张量(编码部分产生的特征图),卷积核个数
def upsampling_block(input_tensor,skip_tensor,filters):
    # 反卷积
    x = Conv2DTranspose(filters,kernel_size=(3,3),strides=2,padding='same')(input_tensor)
    # 获取当前特征图的尺寸
    _,x_hight,x_width,_ = x.shape
    # 获取特征融合图的尺寸
    _,s_hight,s_width,_ = skip_tensor.shape
    # 获取特征图大小差异
    h_crop = x_hight - s_hight
    w_crop = x_width - s_width
    # 若特征图大小相同则不进行裁剪
    if h_crop == 0 and w_crop ==0:
        y = skip_tensor
    else:
        cropping = ((h_crop//2,h_crop-h_crop//2),(w_crop//2,w_crop-w_crop//2))
        y = ZeroPadding2D(cropping=cropping)(skip_tensor)
    # 特征融合
    x = Concatenate()([x,y])
    # 卷积
    x = Conv2D(filters,kernel_size=(3,3),padding='same')(x)
    # BN层
    x = BatchNormalization()(x)
    # 激活层
    x = Activation('relu')(x)
    # 卷积
    x = Conv2D(filters,kernel_size=(3,3),padding='same')(x)
    # BN层
    x = BatchNormalization()(x)
    # 激活层
    x = Activation('relu')(x)
    return x 

3.2.3 模型构建

def unet(imagesize,classes,fetures=64,depth=3):
    # 定义输入
    inputs = keras.Input(shape=(img_size)+(3,))
    x = inputs
    # ⽤来存放进⾏特征融合的特征图
    skips=[]
    # 构建编码部分
    for i in range(depth):
        # 下采样
        x,x0 = downsampling_block(x,fetures)
        skips.append(x0)
        # 特征翻倍
        fetures *=2
    # 卷积
    x = Conv2D(fetures,kernel_size=(3,3),padding="same")(x)
    # BN层
    x = BatchNormalization()(x)
    # 激活层
    x = Activation("relu")(x)
    # 卷积
    x = Conv2D(fetures,kernel_size=(3,3),padding='same')(x)
    # BN层
    x = BatchNormalization()(x)
    # 激活层
    x = Activation("relu")(x)
    # 构建解码部分
    for i in reversed(range(depth)):
        # 深度增加,特征图通道减半
        fetures //= 2
        # 下采样
        x = upsampling_block(x,skips[i],fetures)
    # 卷积
    x = Conv2D(fetures,kernel_size=(1,1),padding="same")(x)
    # 激活
    outputs = Activation("softmax")(x)
    # 模型定义
    model = keras.Model(inputs,outputs)
    return model
model = unet(img_size,4)
model.summary()

在这里插入图片描述

3.3 模型训练

3.3.1 数据集划分

import random
# 验证集数量
val_sample = 500
# 将数据集打乱
random.Random(100).shuffle(input_img_path)
random.Random(100).shuffle(target_img_path)
# 训练集
train_input_img_path = input_img_path[:-val_sample]
train_target_img_path = target_img_path[:-val_sample]
# 验证集
test_input_img_path = input_img_path[-val_sample:]
test_target_img_path = target_img_path[-val_sample:]

3.3.2 数据获取

train_gen = OxfordPets(batch_size,img_size,train_input_img_path,train_target_img_path)
test_gen = OxfordPets(batch_size,img_size,test_input_img_path,test_target_img_path)

3.3.3 模型编译

model.compile(optimizer=tf.keras.optimizers.RMSprop(),loss=tf.keras.losses.sparse_categorical_crossentropy)

3.3.4 模型训练

model.fit(train_gen,epochs=8,validation_data=test_gen,steps_per_epoch=1,validation_steps=1)

在这里插入图片描述

3.4 模型预测

# predict = model.predict(test_gen)
predictions = []
 
# 获取数据集的大小
steps = len(test_gen)

# 分批进行预测
for i in range(steps):
    # 获取下一个批次的数据
    batch = next(iter(test_gen))
    
    # batch 是一个元组,第一个元素是输入数据,第二个元素是标签
    batch_inputs, batch_labels = batch
    
    # 使用输入数据进行预测
    batch_predictions = model.predict(batch_inputs)
    predictions.append(batch_predictions)

# 合并所有的预测结果
final_predictions = np.concatenate(predictions, axis=0)

在这里插入图片描述

def display_mask(i):
    # 获取第i个样本预测结果
    mask = np.argmax(final_predictions[i],axis=-1)
    # 扩展维度
    mask =np.expand_dims(mask,axis=-1)
    # 将掩码转换为PIL Image对象
    img = keras.preprocessing.image.array_to_img(mask)
    # 增强图像对比度
    img = PIL.ImageOps.autocontrast(img)
    display(img)
display(Image(filename=test_input_img_path[5]))

在这里插入图片描述

img = PIL.ImageOps.autocontrast(load_img(test_target_img_path[5]))
display(img)

在这里插入图片描述

display_mask(5)

在这里插入图片描述
**这人工智能不学也罢,没有个好电脑根本跑不出来,只能降低epoch,降低batch_size,分批次去预测模型,勉强可以训练预测模型,但结果就有点不敬人意了

猜你喜欢

转载自blog.csdn.net/qq_56246012/article/details/142310710