MASK RCNN实例分割
文章目录
注:本项目目前全部实现均在windonws,后续会部署到服务器上。
纯小白代码实现!!目前数据集是现成数据集,已经实现标注。后续我将会使用label-studio(个人认为比labelme更方便简单!)进行标准,并且自定义数据集,目前只实现人物的实例分割,后续会加入烟草病害实例分割,尽情期待!!
本项目主要内容:
PennFudanPed是一个搜集行人步态信息的数据集,我们想要通过训练模型实现检测图像中的人物,不仅仅局限于边界框检测,我们想要针对每个检测人物生成掩码图片,进而分割图像。我们采用 Mask R-CNN模型来完成这一工作。
MASK R-CNN原理简述
-
本模型的算法代表为R-CNN,首先生成候选区域(提议区域),然后针对候选区域进行筛选与预测
-
R-CNN 中文名:区域提议卷积网络
-
R-CNN大致步骤:
- 1、通过选择性搜索得到大量的提议区域
- 2、对每一个提议区域使用卷积网络提取特征,计算特征图。
- 3、针对每一个特征图训练SVM实现类别预测
SVM简要介绍: 中文名:支持向量机。 SVM采用监督学习方式,对数据进行二分类,属于线性分类器,与逻辑回归(LR)类似。
-
4、针对每一个特征图训练边框回归,实现边框坐标偏移量预测。
-
5、MASK R-CNN使用RolAlign代替RolPooling(提升检测模型的准确性。),将多尺寸提议区域进行特征对齐,原RolPooling算法无法支持高精度的像素级别的图像分类。除了标签值和边框的偏移量预测之外,单独开设一个分支通过全卷积网络实现像素级别分类。
- 通俗解释:
RolAlign:取消量化操作,使用双线性内插的方法获得坐标为浮点数的像素点上的图像素值,从而将整个特征聚集为一个连续的操作。
- 通俗解释:
- RolPooling:比方我们映射到featuremap上的锚框为5*7,对选择的区域要分成2*2的区域,而5/2=2.5,所以我们对于高,分割成2+3,7/2=3.5,所以我们对于宽,分割成3+4,那么我们会将该处分割成如下右边的4个小块。并对4个小块做maxpooling,最终得到一个2*2的featuremap。
- MASK R-CNN模型图
MASK R-CNN Pytorch实现
数据准备
1、安装cocoAPI。
在windos下安装cocoAPI。
本文将实现最简单的COCOAPI下载方法,使用PIP命令。
pip install git+https://github.com/philferriere/cocoapi.git#subdirectory=PythonAPI
如遇报错,请各位君自行百度咨询。
2、下载PennFudan数据集
-
下载地址:https://www.cis.upenn.edu/~jshi/ped_html/
-
数据集查看。
注:请将路径更换为自己的本地路径。
from PIL import Image
Image.open(r'H:\pycharm\deeplearning\tree_sick\mask rcnn\PennFudanPed\PennFudanPed\PNGImages/FudanPed00001.png')
# 读取对应图片的掩码图片
mask = Image.open(r'H:\pycharm\deeplearning\tree_sick\mask rcnn\PennFudanPed\PennFudanPed\PedMasks\FudanPed00001_mask.png')
# 读取的mask默认为“L”模式,需要转换为“P”模式调用调色板函数
mask = mask.convert("P")
# 针对“P”类图片调用调色板函数
# 看来掩码图片存的不是RGB数值,而是类别index
mask.putpalette([
0, 0, 0, # 0号像素为黑色
255, 0, 0, # 1号像素为红色
255, 255, 0, # 2号像素为黄色
255, 153, 0, # 3号像素为黄色
])
mask
第一张图片为原人物图片,第二张图片为掩码图片。
掩码图片介绍:
- 从原视图像中获取感兴趣区域的掩码。
- 使用掩码和原视图像做云运算得到最后感兴趣的区域的图像。
3、编写数据类
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.imgs 是一个全部待训练图片文件名的有序列表
self.masks = list(sorted(os.listdir(os.path.join(root, "PedMasks")))) # self.masks 是一个全部掩码图片文件名的有序列表
# 根据idx对应读取待训练图片以及掩码图片
def __getitem__(self, idx):
# 根据idx针对img与mask组合路径
img_path = os.path.join(self.root, "PNGImages", self.imgs[idx])
mask_path = os.path.join(self.root, "PedMasks", self.masks[idx])
# 根据路径读取三色图片并转为RGB格式
img = Image.open(img_path).convert("RGB")
# 根据路径读取掩码图片默认“L”格式
mask = Image.open(mask_path)
# 将mask转为numpy格式,h*w的矩阵,每个元素是一个颜色id
mask = np.array(mask)
# 获取mask中的id组成列表,obj_ids=[0,1,2]
obj_ids = np.unique(mask)
# 列表中第一个元素代表背景,不属于我们的目标检测范围,obj_ids=[1,2]
obj_ids = obj_ids[1:]
# obj_ids[:,None,None]:[[[1]],[[2]]],masks(2,536,559)
# 为每一种类别序号都生成一个布尔矩阵,标注每个元素是否属于该颜色
masks = mask == obj_ids[:, None, None]
# 为每个目标计算边界框,存入boxes
num_objs = len(obj_ids) # 目标个数N
boxes = [] # 边界框四个坐标的列表,维度(N,4)
for i in range(num_objs):
pos = np.where(masks[i]) # pos为mask[i]值为True的地方,也就是属于该颜色类别的id组成的列表
xmin = np.min(pos[1]) # pos[1]为x坐标,x坐标的最小值
xmax = np.max(pos[1])
ymin = np.