图像分割综述:
图像分割的分类
1、语义分割:每一类目标是一个个体
1.1 基本概念:像素级分类
1.2语义分割的性能指标:
1.2.1mAcc(准确率):
1.2.2mIoU(交并比):
像素比
2、实例分割:每一个目标是一个个体
3、全景分割:每一个目标是一个个体,但包括背景
ps:卷积网络处理的数据是Tensor形式,是个4维的张量,B,C,H,W四个纬度分别表示batchsize和图像的通道,高,宽
class 1 Basic_model 代码实现:
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph import Pool2D
from paddle.fluid.dygraph import Conv2D
from paddle.fluid.dygraph import to_variable
import numpy as np
np.set_printoptions(precision=2)
#继承paddle.fluid.dygraph动态图的内容,basicmodel是一个网络要做forward
class BasicModel(fluid.dygraph.Layer):
# BasicModel contains:
# 1. pool: 4x4 max pool op, with stride 4
# 2. conv: 3x3 kernel size, takes RGB image as input and output num_classes channels,
# note that the feature map size should be the same
# 3. upsample: upsample to input size
#
# TODOs:
# 1. The model takes an random input tensor with shape (1, 3, 8, 8)
# 2. The model outputs a tensor with same HxW size of the input, but C = num_classes
# 3. Print out the model output in numpy format
def __init__(self, num_classes=59):
super(BasicModel, self).__init__()#用super()_init()对继承的fluid.dygraph.Layer进行初始化
def __init__(self,num_classes=59)
self.pool=Pool2D(pool_size=2,pool_stride=2)#池化核大小为[2,2],池化层步长为[2,2],将图片面积缩小了4倍既8*8变4*4
self.conv=Conv2D(num_channels=3,num_filters=num_classes,filter_size=1)#num_channels为input(图片)的channels
# num_filters为output数(类数)对应卷积核数量,filter_size既卷积尺寸大小,这里是一个1*1的卷积,输入为3个维度,输出为59个维度。
def forward(self, inputs):
x=self.pool(inputs)
x = fluid.layers.interpolate(x, out_shape=(inputs.shape[2], inputs.shape[3]))#池化将feature map(输入)变小,在此将它
# 放大回去,这个操作叫上采样,通过插值将x的尺寸变为输入,input的尺寸。
x=self.conv(x)
return x
def main():
place = paddle.fluid.CPUPlace()#程序跑的位置,CPU,或gpu,括号内的0表示单卡。
with fluid.dygraph.guard(place):#这个place用到 paddle的动态图,这是一个固定用法。
model = BasicModel(num_classes=59)
model.eval()#加model.eval表示预测模式,加model.train表示训练模式,
input_data = np.random.rand(1,3,3,8).astype(np.float32)#一般图像数据格式都是四维(n,c,h,w)(数量,通道channel,高度,宽度),
# 输入数据类型记得转换为np.float32浮点数
print('Input data shape: ', input_data.shape)
input_data = to_variable(input_data)#将输入数据的类型由np.array转换为paddle的tensor,同时将它放在模型可以找到的地方
output_data = model(input_data)#前向,这里output_data数据类型是tensor
output_data = output_data.numpy#将output_data转化为np。array格式,方便打印。
print('Output data shape: ', output_data.shape)
if __name__ == "__main__":
main()
class 1 Dataloader 代码实现:
import os
import random
import numpy as np
import cv2
import paddle.fluid as fluid
class Transform(object): #写成面向对象更好扩展,写成一个方法也可以。
def __init__(self,size=256):
self.size=size
def __call__(self, input,label):
input = cv2.resize(input, (self.size, self.size), interpolation=cv2.INTER_LINEAR)#加interpolation在分割领域很重要,因为做图像时通常会用
# resize,既通过双线性差值,三线性差值来更改尺寸。
label = cv2.resize(label, (self.size, self.size), interpolation=cv2.INTER_NEAREST)#INTER_NEAREST使用最近的pixel作为插出来的值,
# 新生成的label的pixel不会有越界,比如原来是0-60,共59类,用INTER_NEAREST能保证还在这个范围。
return input,label
class Basic_DataLoader():
def __init__(self,
image_folder,
image_list_file,
transform=None,
shuffle=True):
self.image_folder=image_folder#将成员变量写进去,因为下面定义的成员方法有时候会用到这些初始化时的值,如果不将它们变成成员变量
self.image_list_file=image_list_file #可能用的时候就看不到了,下面加了方法self,但是找不到参数列表里的东西
self.transform=transform
self.shuffle=shuffle
self.data_list = self.read_list() #data_list存的是读到的所有list文件里的路径。
def read_list(self):
data_list=[]
with open(self.image_list_file) as infile:
for line in infile:
#list中是相对路径,因此需要改路径,os.path.join将image_folder路径和第一个文件夹名拼接,该路径即是datapath
data_path=os.path.join(self.image_folder,line.split()[0])# os.path.join 自动填/ 或者是 \
label_path=os.path.join(self.image_folder,line.split()[1])
'''str.split(“o”)[0]得到的是第一个o之前的内容
str.split(“o”)[1]得到的是第一个o和第二个o之间的内容'''
data_list.append((data_path,label_path))
return data_list
def preprocess(self, data, label):
h,w,c = data.shape
h_gt,w_gt = label.shape
assert h==h_gt,"Error"
assert w==w_gt,"Error"
if self.transform:
data,label = self.transform(data,label)
label = label[:,:,np.newaxis]
return data, label
def __len__(self):
return len(self.data_list)
def __call__(self):
for data_path,label_path in self.data_list:#将data_path,label_path从data_list中一对对读出来,一张图对应一张label
data=cv2.imread(data_path,cv2.IMREAD_COLOR)#将路径放进来,用OpenCV去读,将图读出来,cv2.IMREAD_COLOR为读取格式
data = cv2.cvtColor(data,cv2.COLOR_BGR2RGB) #opencv读进来时bgr,这里转换为rgb
label = cv2.imread(label_path,cv2.IMREAD_GRAYSCALE)#这是label图,直接读成灰度图(单通道)
print(data.shape,label.shape) #这里data、label是np.array类型
data,label = self.preprocess(data,label)#初始化,预处理
yield data,label #相当于return
def main():
batch_size = 5
place = fluid.CPUPlace()
transform = Transform(256)
with fluid.dygraph.guard(place):
#这basic_dataloader是自己实现的,怎么处理数据,怎么读数据。BasicDataLoader这个类相当于python的generator,生成器。
basic_dataloader=Basic_DataLoader(
image_folder = r'./work/dummy_data',
image_list_file= r'./work/dummy_data/list.txt',
transform = transform,
shuffle=True # 打乱数据
)
dataloader=fluid.io.DataLoader.from_generator(capacity=1,use_multiprocess=False)#这是padele自己定义有一个class叫
# BasicDataLoader,能够让我们在paddle中去做训练,不断调取数据。要创建一个这样的instance可以调用from_generator。
dataloader.set_sample_generator(basic_dataloader,
batch_size=batch_size,
places=place)
# 通过第一个方式创建的fluid dataloader的instance为了给他初始化,让他按照我们的
# 方式去读取数据。所以需要使用set_sanple_generator方法,其中第一个输入参数是我们自己写的basic_dataloader,第二个是batch_size,第三个是place。
num_epoch = 2
for epoch in range(1, num_epoch + 1):
print(f'Epoch [{epoch}/{num_epoch}]')
for idx, (data, label) in enumerate(dataloader):
print(f'Iter {idx}, Data shape:{data.shape}, Label shape:{label.shape}')
if __name__ == '__main__':
main()