一、算法介绍
SegNet网络结构如下图所示,Input为输入图片,Output为输出分割的图像,不同颜色代表不同的分类。语义分割的重要性就在于不仅告诉你图片中某个东西是什么,而且告知你他在图片的位置。我们可以看到是一个对称网络,由中间绿色pooling层与红色upsampling层作为分割,左边是卷积提取高维特征,并通过pooling使图片变小,SegNet作者称为Encoder,右边是反卷积(在这里反卷积与卷积没有区别)与upsampling,通过反卷积使得图像分类后特征得以重现,upsampling使图像变大,SegNet作者称为Decoder,最后通过Softmax,输出不同分类的最大值。
![结构示意图](https://i-blog.csdnimg.cn/blog_migrate/061bde7fa018df5c934cea68913d1d9e.jpeg)
Convolution
SegNet的Encoder过程中,卷积的作用是提取特征,SegNet使用的卷积为same卷积,即卷积后不改变图片大小;在Decoder过程中,同样使用same卷积,不过卷积的作用是为upsampling变大的图像丰富信息,使得在Pooling过程丢失的信息可以通过学习在Decoder得到。SegNet中的卷积与传统CNN的卷积并没有区别。
Batch Normalisation
批标准化的主要作用在于加快学习速度,用于激活函数前,在SegNet中每个卷积层都会加上一个bn层,bn层后面为ReLU激活层,bn层的作用过程可以归纳为:
(1)训练时:
1.向前传播,bn层对卷积后的特征值(权值)进行标准化,但是输出不变,即bn层只保存输入权值的均值与方差,权值输出回到卷积层时仍然是当初卷积后的权值。
2.向后传播,根据bn层中的均值与方差,结合每个卷积层与ReLU层进行链式求导,求得梯度从而计算出当前的学习速率。
(2)测试时:每个bn层对训练集中的所有数据,求取总体的均值与方差,假设有一测试图像进入bn层,需要统计输入权值的均值与方差,然后根据训练集中整体的无偏估计计算bn层的输出。注意,测试时,bn层已经改变卷积的权值,所以激活层ReLU的输入也被改变。
二、算法实现
2.1 数据准备
数据包括image和label两部分,图像分割是基于像素点的分类,我这里用的label标记工具是labelme,不清楚的可以了解一下其用法,这里就不详细描述了。
![](https://i-blog.csdnimg.cn/blog_migrate/8d5fe7b131921f3001c941f2438556a8.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/9c16f7267cd3513c7b6a2a55d1a1b379.jpeg)
以上就是图像和标签的文件夹。数据准备的代码如下:
import os
import sys
import numpy as np
from scipy.misc import imsave
import scipy.ndimage
import pydicom
training_dicom_dir = "test\\a"
training_labels_dir = "test\\b"
training_png_dir = "Data\\Training\\Images\\Sunnybrook_Part2"
training_png_labels_dir = "Data\\Training\\Labels\\Sunnybrook_Part2"
for root, dirs, files in os.walk(training_labels_dir):
for file in files:
if file.endswith("-icontour-manual.txt"):
try:
prefix, _ = os.path.split(root)
prefix, _ = os.path.split(prefix)
_, patient = os.path.split(prefix)
file_fn = file.strip("-icontour-manual.txt") + ".dcm"
print(file_fn)
print(patient)
dcm = pydicom.read_file(os.path.join(training_dicom_dir, patient, file_fn))
print(dcm.pixel_array.shape)
img = np.concatenate((dcm.pixel_array[...,None], dcm.pixel_array[...,None], dcm.pixel_array[...,None]), axis=2)
labels = np.zeros_like(dcm.pixel_array)
print(img.shape)
print(labels.shape)
with open(os.path.join(root, file)) as labels_f:
for line in labels_f:
x, y = line.split(" ")
labels[int(float(y)), int(float(x))] = 128
labels = scipy.ndimage.binary_fill_holes(labels)
img_labels = np.concatenate((labels[..., None], labels[..., None]