目标检测之Mask R-CNN:小白动手实现项目Pytorch
引言
- 首先声明本项目的运行环境。操作系统为Windows,编写环境为Anaconda3 + Jupyter Notebook。Pytorch版本为 torch 1.7.1+cu110 、torchvision版本为 0.8.2+cu110,cuda版本为cu100,显卡为Nvidia 2060。
- 我是目标检测领域的小白,刚刚完成了Pytorch的相关入门学习任务,所以我认为学习本篇博客的基础要求是:熟练的python技巧、初步接触Pytorch、了解基本tensor运算以及卷积网络的知识。
- 本篇博客的代码部分并非全部原创,对领域小白来说自己搭建框架,寻找合适的模型和数据集都比较困难,因此我参照Pytorch官方文档来进行学习。我会尽力针对每一句代码书写自己的理解,写博客的目的是帮助自己整理思路同时也能给志同道合的初学者们一点点经验建议。
- 本博客参照的Pytorch官方文档为:Mask R-CNN项目官方文档
- 简单叙述一下本项目的主要内容:PennFudanPed是一个搜集行人步态信息的数据集,我们想要通过训练模型实现检测图像中的人物,不仅仅局限于边界框检测,我们想要针对每个检测人物生成掩码图片,进而分割图像。我们采用 Mask R-CNN模型来完成这一工作。

Mask R-CNN原理简述
- 目标检测领域的深度学习算法可以分为两类,一阶段算法代表为YOLO,二阶段算法代表为R-CNN。阐述一下二阶段算法和一阶段算法的区别,二阶段算法首先生成候选区域(提议区域),然后针对候选区域进行筛选和预测。一阶段算法并没有单独的步骤用来生成候选区域,而是将候选区域的生成、筛选与预测同步进行。
- R-CNN 中文名字是区域提议卷积网络,其发展大体经历了R-CNN、Fast R-CNN、Faster R-CNN到Mask R-CNN的过程。每一种新模型都是在旧模型的基础上优化改善而得。总体来说优化的目的都是在于,提高模型的计算速度,以及优化模型的计算精读。
- R-CNN大体经过了以下步骤:1.通过选择性搜索算法得到大量提议区域。2.针对每一个提议区域使用卷积网络提取特征,计算特征图。3.针对每一个特征图训练SVM实现类别预测。4.针对每一个特征图训练边框回归,实现边框坐标偏移量预测。
- Fast R-CNN针对于R-CNN中存在的大量卷积计算进行了优化。Fast R-CNN首先针对全图进行卷积网络特征提取,然后同样使用选择性搜索算法生成大量提议区域,使用RoIPooling算法针对每一个提议区域从全图特征中挑选特征值,并调整维度。标签值与偏移量的预测与R-CNN相同。
- Faster R-CNN针对提议区域的生成方式进行了修改。Faster R-CNN采用RPN网络的方式生成提议区域,接受全图特征图作为输入,筛选提议区域,极大减少了提议区域的生成数量。
- Mask R-CNN为了实现像素级别的分类进行了优化。使用RoIAlign代替RoIPooling,将多尺寸提议区域进行特征对齐,原RoIPooling算法无法支持高精度的像素级别的图像分类。除了标签值和边界框偏移量的预测以外,单独开设一个分支通过全卷积网络实现像素级别分类。

代码、注释及详细解释
1.安装COCOAPI(pycocotools)
- Linux操作系统下COCOAPI的安装十分简单,具体方法百度即可。因此大部分的深度学习模型与平台也都部署在Linux系统下。
- Windows操作系统的设计目的就是为了让更多的计算机非专业人士能够更简单直接地操作计算机,而针对于计算机从业人员来说,Linux系统有其独特地优势是我们绕不开地话题。
- 言归正传COCOAPI在Windows系统下安装需要进行一些特殊操作。具体内容请见CSDN博客在 Windows 下安装 COCO API(pycocotools)
2.下载PennFudan数据集
3.初步查看数据集图片
- 注意第一点的是,本博客中所有路径都需要更改成自己的路径。具体方法只需要建立对应文件夹,存放好数据集和图片即可。本文均采用绝对路径,有关绝对路径与相对路径的区别请自行学习。
- Image.open()读取的图片有多种模式,每种模式有不同的作用并且拥有相对应可调用的函数,这里也不做赘述,感兴趣的可以自行查阅。下面代码中读取的mask默认为“L”模式,需要转换为“P”模式才可以调用调色板函数putpalette()。
- 我们对掩码图片进行一下了解。普通图片的矩阵元素是一个RGB数值,当然有三个通道。而掩码图片的矩阵元素是0,1,2,3…的类别序号。掩码矩阵可以理解为像素级别的分类结果,如果掩码图片上实体只有一个那么就是二色掩码图,矩阵元素只有0(背景)、1(实体);下图所示的掩码图片实体不唯一,那么就是多色掩码图。掩码图片可能有三个通道也可能只有一个通道,具体情况请.shape分析一下。
from PIL import Image
Image.open(r'C:\Users\HP\Anaconda3\envs\pytorch\data\PennFudanPed\PNGImages\FudanPed00001.png')
mask = Image.open(r'C:\Users\HP\Anaconda3\envs\pytorch\data\PennFudanPed\PedMasks\FudanPed00001_mask.png')
mask = mask.convert("P")
mask.putpalette([
0, 0, 0,
255, 0, 0,
255, 255, 0,
255, 153, 0,
])
mask

4.编写针对PennFudan的数据集类
- 我们知道深度学习的训练函数,一般是接受DataLoader的实例作为输入,而DataLoader则需要接受自定义的数据集类实例作为输入。因此本章节就用来完成数据集接口的编写。
- PennFudanDataset类一共有三个函数,四个属性。首先介绍四个属性,self.root是根路径,后面跟上训练集或测试集的文件夹便可以得到最终路径,无论是训练集还是测试集,都需要变形参数self.transforms,也都有普通图片self.img和掩码图片self.masks。
- 三个函数中重点在于__getitem__()函数。该函数的功能是根据图片编号,获得对应的图片信息。该函数需要返回两个变量:

import os
import torch
import numpy as np
import torch.utils.data
from PIL import Image
class PennFudanDataset(torch.utils.data.Dataset):
def __init__(self, root, transforms=None):
self.root = root
self.transforms = transforms
self.imgs = list(sorted(os.listdir(os.path.join(root, "PNGImages"))))
self.masks = list(sorted(os.listdir(os.path.join(root, "PedMasks"))))
def __getitem__(self, idx):
img_path = os.path.join(self.root, "PNGImages", self.imgs[idx])
mask_path = os.path.join(self.root, "PedMasks", self.masks[idx])
img = Image.open(img_path).convert("RGB")
mask = Image.open(mask_path)
mask = np.array(mask)
obj_ids = np.unique(mask)
obj_ids = obj_ids[1:]
masks = mask == obj_ids[:, None, None]
num_objs = len(obj_ids)
boxes = []
for i in range(num_objs):
pos = np.where(masks[i])
xmin = np