01 - 语义分割前言以及转置卷积

390 阅读8分钟

语义分割基础

一、语义分割前言

1. 常见的分割任务

  1. 语义分割(semantic segmentation)

    1. 图示

    image.png

    对同一类目标采用相同颜色。只区分类别和背景。

    1. 代表网络:FCN
  2. 实例分割(Instance segmentation)

    1. 图示

    image.png 对同一类中的不同目标使用不同颜色。区分类别和背景的同时,还要区分同一类别中的不同目标。

    1. 代表网络:Mask R-CNN
  3. 全景分割(Panoramic segmentation)

    1. 概念

      前面两者不会关心背景(背景被mask滤除),但是全景分割需要对背景进行分割。所以难度依次递增。

    2. 代表网络:Panoptic FPN

2. 学习规划

  1. 进度表

    image.png

  2. 相关博文

3. 常见的数据集格式

  1. Pascal Voc

    1. 图示

    image.png 2. 原图像和标签图像,采用调色板形式存储(PNG - P模式),比如像素0对应的是(0,0,0)黑色,像素1对应的是(127.0, 0)深红色,像素255对应的是(224, 224, 129)。**python中使用PIL导入的图片是调色板形式。**注意图像边缘和被空白填充的区域。

    1. 注意:可以看出目标边缘以及有一些空白填充;因为图像中的边缘像素点和一些杂点可能会影响我们的分割效率,所以我们在标签图像中直接空白填充,不计入损失。在实际使用中,我们将目标背景使用0填充,边缘使用255填充,目标内部使用15填充。

    2. 相关博文

  2. MS COCO

    1. 图示

    image.png 针对图像中的每一个目标都记录了多边形(polygons)坐标。将所有所有左边点连接起来就形成了我们的目标区域。

    1. 坐标实例

      [428.19, 219.47, 430.94, 209.57, 430.39, 210.12, 421.32, 216.17, 412.8, 217.27, 413.9, 214.24, 422.42, 211.22, 429.29, 201.6, 430.67, 181.8, 430.12, 175.2, 427.09, 168.06, 426.27, 164.21, 430.94, 159.26, 440.29, 157.61, 446.06, 163.93, 448.53, 168.06, 448.53, 173.01, 449.08, 174.93, 454.03, 185.1, 455.41, 188.4, 458.43, 195.0, 460.08, 210.94, 462.28, 226.61, 460.91, 233.76, 454.31, 234.04, 460.08, 256.85, 462.56, 268.13, 465.58, 290.67, 465.85, 293.14, 463.38, 295.62, 452.66, 295.34, 448.26, 294.52, 443.59, 282.7, 446.06, 235.14, 446.34, 230.19, 438.09, 232.39, 438.09, 221.67, 434.24, 221.12, 427.09, 219.74]

      注意每个二位坐标点是没有分隔的,也就是说:第一个坐标点为[428.19, 219.47],第二个坐标点为[430.94, 209.57],依次类推。

    2. 注意:我们需要将这些polygons坐标信息解析为我们的标签图像信息,就是自己染色。另外,由于MS COCO记录了图像中的每个物体的类别信息,所以也可用于实例分割。

    3. 相关博文

4. 语义分割结果的具体形式

  1. 图示

    image.png

  2. 结果图像是mask蒙版(加上调色板之后的效果),每个像素数值对应类别索引。其实我们可以使用灰度图进行显示,但是对于人眼来说很难分辨,所以我们使用了调色板形式。

5. 常见的语义分割评价指标

  1. Pixel Accuracy(Global Acc)

    1. 公式
      iniiiti\frac{\sum_i n_{ii}}{\sum_i t_i}
  2. mean Accuracy

    1. 公式
      1nclsiniiti\frac{1}{n_{cls}} \bullet \sum_i \frac{n_{ii}}{t_i}
  3. mean IoU(一般方式)

    1. 公式
      1nclsiniiti+jnjinii\frac{1}{n_{cls}}\sum_i\frac{n_{ii}}{t_i + \sum_jn_{ji} - n_{ii}}
  4. 说明

    1. nijn_{ij}:类别 i 被预测称类别 j 的像素个数

    2. nclsn_{cls}:目标类别个数(包括背景)

    3. ti=jnijt_i = \sum_j n_{ij}:目标类别 i 的总像素个数(真实标签)

    4. 上面的三个公式都是基于交并集计算所得:

    image.png

  5. 计算示例

    1. 图示

image.png

  1. 我们需要构造一个混淆矩阵(不要求召回率,要求查准率),列表示标签类别,行表示预测类别。我们首先看标签为0的像素点,可以看出我们的预测结果中,有两个3标签像素点被预测为了0标签像素点,所以我们需要在第0行第3填上2,其余的16个0标签像素点预测准确;再看对标签1预测,有一个0标签像素点被预测为1,所以我们需要在第1行的第0列填上1.依次类推。得到的混淆结果如下所示:

    image.png

  2. Pixel Accuracy(Global Accuracy)

    1. 分子为混淆矩阵对角线之和,分母自然是所有像素点个数。

    2. 结果

      16+3+16+12+8640.859\frac{16 + 3 + 16 + 12 + 8}{64} \approx 0.859
  3. 各分类的预测精度(查准率)

    1. 分子为该列的位于对角线的像素个数,分母为该列像素总个数;就额是预测为该类的结果里面,有多少标签为该类的。

    2. 结果

      acc0=1620,acc1=34,,acc4=88acc_0 = \frac{16}{20} , acc_1 = \frac{3}{4}, \dots, acc_4 = \frac{8}{8}

      最后平均精度需要将所有的分类查准度相加后取均值。

  4. 各类别的IoU

    1. 分子为该列位于对角线的像素个数,分母为 该行 + 该列 - 分子。mean IoU是计算各类IoU的均值。分子为交集——也就是预测和标签值一致的像素点数,分母为并集——也就是标签为该值的像素点加上预测为该值的像素点再减去交集所得。

    2. 结果

      IoU0=1616+2+16+1+1+216=1622,IoU1=31+3+1+33=34,,IoU4=88+2+2+88=812IoU_0 = \frac{16}{16 + 2 + 16 + 1 + 1 + 2 - 16} = \frac{16}{22} , IoU_1 = \frac{3}{1 + 3 + 1 + 3 - 3} = \frac{3}{4},\dots, IoU_4 = \frac{8}{8 + 2 + 2 + 8 - 8} = \frac{8}{12}

      平均IoU将所有的IoU相加后取平均。

6. 语义分割标注工具

  1. labelme

    1. 工具比较老,逐个点标记很费事。

    image.png

    1. 项目地址

    2. 相关博文

  2. EISeg

    1. 百度的,半自动,点一下就行。不常见的物体可以使用labelme来标注。

    image.png

    1. 项目地址

    2. 相关博文

二、转置卷积

1. 概念

  1. Transposed Convolution(fractionally-strided convolution / deconvolution)

    1. 转置卷积并非卷积的逆运算,这是一种常用的上采样方式(在图像领域还可以使用线性插值,非线性插值等方法)。
    2. 转置卷积也是卷积。
  2. 和卷积的对比图示

    1. 卷积

    no_padding_no_strides.gif

    Conv参数:padding = 0,strides = 1

    1. 转置卷积

    no_padding_no_strides_transposed.gif

    transposed Conv参数:padding = 0(和卷积的参数含义不同,这里指的是像素之间的填充值),strides = 1

  3. 相关论文(A guide to convolution arithmetic for deep learning )

2. 具体过程

  1. 运算步骤(内看步长,外看ksp)

    1. 在输入特征图元素间填充s - 1行,s - 10 值

    2. 在输入特征图四周填充 k - p - 1行,k - p - 10 值

    3. 将原始卷积核参数上下、左右翻转

    4. 做正常卷积运算(padding = 0,stride = 1)

    5. 图示

image.png

  1. 输出维度

    Hout=(Hin1)×stride[0]2×padding[0]+kernelsize[0]Wout=(Win1)×stride[1]2×padding[1]+kernelsize[1]H_{out} = (H_{in} - 1) \times stride[0] - 2 \times padding[0] + kernelsize[0] \\ W_{out} = (W_{in} - 1) \times stride[1] - 2 \times padding[1] + kernelsize[1]
  2. 注意

    这里的s和p和k指的是,原来做卷积操作的时候的s和p和k,不是反卷积的spk。反卷积只是把原来卷积操作的结果给长采样回去而已(不是逆运算)

  3. 过程图源

3. 实例

  1. 输入feature map图示(忽略偏置项bias)

    image.png

  2. 原卷积核图示(stride = 1,padding = 0)

    image.png

  3. 具体步骤

    1. 首先按照之前的规则进行填充0:内看步长(s - 1 = 0),外看ksp(ks - p -1 = 3 - 1 = 2),所以特征映射内部无需填充,但是外部需要填充padding = 2的0行和0列,得到6×66 \times 6 的特征映射。图示:

    image.png

    1. 在转置卷积之前需要将整个原卷积核进行上下,左右翻转(和矩阵转置不同,不要弄混了!!!)

    image.png

    1. 然后按照原来的步长进行卷积即可。

    image.png

    1. 先计算输出长宽
    2. 然后逐一填入卷积所得数值

4. PyTorch中转置卷积对应参数

  1. torch.nn.ConvTransposed2d参数

    1. stride controls the stride for the cross-correlation.

    2. padding controls the amount of implicit zero padding on both sides for dilation * (kernel_size - 1) - padding number of points. See note below for details.

    3. output_padding controls the additional size added to one side of the output shape. See note below for details.

    4. dilation controls the spacing between the kernel points; also known as the à trous algorithm. It is harder to describe, but the link here has a nice visualization of what dilation does.

    5. groups controls the connections between inputs and outputs. in_channels and out_channels must both be divisible by groups. For example,

      • At groups=1, all inputs are convolved to all outputs.
      • At groups=2, the operation becomes equivalent to having two conv layers side by side, each seeing half the input channels and producing half the output channels, and both subsequently concatenated.
      • At groups= in_channels, each input channel is convolved with its own set of filters (of size out_channelsin_channels\frac{\text{out\_channels}}{\text{in\_channels}}).
  2. 公式

    Hout =(Hin 1)× stride [0]2× padding [0]+ dilation [0]×( kernelsize [0]1)+ outputpadding [0]+1Wout =(Win 1)× stride [1]2× padding [1]+ dilation [1]×( kernelsize [1]1)+ outputpadding [1]+1\begin{array}{l} H_{\text {out }}=\left(H_{\text {in }}-1\right) \times \text { stride }[0]-2 \times \text { padding }[0]+\text { dilation }[0] \times(\text { kernelsize }[0]-1)+\text { outputpadding }[0]+1 \\ W_{\text {out }}=\left(W_{\text {in }}-1\right) \times \text { stride }[1]-2 \times \text { padding }[1]+\text { dilation }[1] \times(\text { kernelsize }[1]-1)+\text { outputpadding }[1]+1 \end{array}

5. 过程解释

  1. 普通卷积计算(忽略bias)

image.png 2. 卷积运算的高级计算方式

  1. 首先我们会将卷积核按照输入图像大小进行0填充,注意,我们每移动一次卷积核就会重构一次填充矩阵。

    image.png

  2. 一次性构建出所有需要构建的等效矩阵(个数和我们计算卷积的次数一致)

  3. 展平我们的输入矩阵 I

    image.png

  4. 然后展平所有的等效矩阵,并按行叠加得到矩阵 C

    image.png

  5. 将我们的向量 I 和叠加的等效矩阵相乘得到最终结果。

    image.png

    I1×16C16×4=O1×4I ^{1 \times 16} C^{16 \times 4} = O^{1 \times 4}

    一般情况下我们的卷积是不可逆的,因为我们的矩阵 C 一般不是方阵。

  6. 上面说明了我们并不能通过一个卷积核和卷积结果得到原输入阵列,那么我们可以通过一些方法得到相似阵列吗(只要求个数一致就行),显然是可以的,**我们直接将得到的结果矩阵和 C转置相乘便可。**所以转置卷积和逆卷积不等同。

    1. 我们将转置所得的矩阵CTC^T的每一列作为一个等效矩阵分离出来,逐个和原输出相乘,得到转置卷积的结果。

    image.png

    1. 我们可以将我们对原卷积核进行水平,左右翻转后的卷积核和0填充后的输出阵列进行卷积,会发现我们的每步的卷积结果和第一步的每一个等效矩阵相乘的结果一致。

    image.png