【纯小白】动手实现MASK RCNN 实例分割(带全部源码)

这篇具有很好参考价值的文章主要介绍了【纯小白】动手实现MASK RCNN 实例分割(带全部源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

MASK RCNN实例分割

注:本项目目前全部实现均在windonws,后续会部署到服务器上。
纯小白代码实现!!目前数据集是现成数据集,已经实现标注。后续我将会使用label-studio(个人认为比labelme更方便简单!)进行标准,并且自定义数据集,目前只实现人物的实例分割,后续会加入烟草病害实例分割,尽情期待!!

本项目主要内容:

PennFudanPed是一个搜集行人步态信息的数据集,我们想要通过训练模型实现检测图像中的人物,不仅仅局限于边界框检测,我们想要针对每个检测人物生成掩码图片,进而分割图像。我们采用 Mask R-CNN模型来完成这一工作。

【纯小白】动手实现MASK RCNN 实例分割(带全部源码)

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:取消量化操作,使用双线性内插的方法获得坐标为浮点数的像素点上的图像素值,从而将整个特征聚集为一个连续的操作。

【纯小白】动手实现MASK RCNN 实例分割(带全部源码)

- 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 RCNN 实例分割(带全部源码)

  • MASK R-CNN模型图

【纯小白】动手实现MASK RCNN 实例分割(带全部源码)

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 

【纯小白】动手实现MASK RCNN 实例分割(带全部源码)

第一张图片为原人物图片,第二张图片为掩码图片。

掩码图片介绍:

  • 从原视图像中获取感兴趣区域的掩码。
  • 使用掩码和原视图像做云运算得到最后感兴趣的区域的图像。
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.min(pos[0]) # pos[0]为y坐标
            ymax = np.max(pos[0])
            boxes.append([xmin, ymin, xmax, ymax])
 
        # 将boxes转化为tensor
        boxes = torch.as_tensor(boxes, dtype=torch.float32)
        
        # 初始化类别标签
        labels = torch.ones((num_objs,), dtype=torch.int64) # labels[1,1] (2,)的array
        masks = torch.as_tensor(masks, dtype=torch.uint8) # 将masks转换为tensor
 
        # 将图片序号idx转换为tensor
        image_id = torch.tensor([idx])
        # 计算每个边界框的面积
        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])
        # 假设所有实例都不是人群
        iscrowd = torch.zeros((num_objs,), dtype=torch.int64) # iscrowd[0,0] (2,)的array
 
        # 生成一个字典
        target = {}
        target["boxes"] = boxes
        target["labels"] = labels
        target["masks"] = masks
        target["image_id"] = image_id
        target["area"] = area
        target["iscrowd"] = iscrowd
 
        # 变形transform
        if self.transforms is not None:
            img, target = self.transforms(img, target)
 
        return img, target
 
    def __len__(self):
        return len(self.imgs)

4、查看数据接口内部信息
dataset = PennFudanDataset(r'H:\pycharm\deeplearning\tree_sick\mask rcnn\PennFudanPed\PennFudanPed/')
dataset.__getitem__(0)

dataset[0]

输出结果如下:

(<PIL.Image.Image image mode=RGB size=559x536 at 0x276FDCB1AF0>,
 {'boxes': tensor([[159., 181., 301., 430.],
          [419., 170., 534., 485.]]),
  'labels': tensor([1, 1]),
  'masks': tensor([[[0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           ...,
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0]],
  
          [[0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           ...,
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0],
           [0, 0, 0,  ..., 0, 0, 0]]], dtype=torch.uint8),
  'image_id': tensor([0]),
  'area': tensor([35358., 36225.]),
  'iscrowd': tensor([0, 0])})

模型所需库

  • 进入网址:

    vision/references/detection at main · pytorch/vision (github.com)

  • 将所有包下载下来,并且放在此项目所需的文件夹下,我的文件目录如下:

【纯小白】动手实现MASK RCNN 实例分割(带全部源码)

搭建mask rcnn 模型

  • 本文不涉及mask rcnn具体网络实现,如有需要者,可自行搜索Mask Rcnn 源码理解。
import torchvision
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
from torchvision.models.detection.mask_rcnn import MaskRCNNPredictor
 
      
def get_instance_segmentation_model(num_classes):
    # 准备预训练模型(Mask R-CNN模型)
    model = torchvision.models.detection.maskrcnn_resnet50_fpn(pretrained=True)
 
    # 获取预训练模型的打分模块的输入维度,也就是特征提取模块的输出维度
    in_features = model.roi_heads.box_predictor.cls_score.in_features
 
    # 将预训练模型的预测部分修改为FastR-CNN的预测部分(Fast R-CNN与Faster R-CNN的预测部分相同)
    model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)
 
    # 获取预训练模型中像素级别预测器的输入维度
    in_features_mask = model.roi_heads.mask_predictor.conv5_mask.in_channels
    hidden_layer = 256
 
    num_classes=2

    # 使用自己的参数生成Mask预测器替换预训练模型中的Mask预测器部分
    # 三个参数,输入维度,中间层维度,输出维度(类别个数)
    model.roi_heads.mask_predictor = MaskRCNNPredictor(in_features_mask,
                                                       hidden_layer,
                                                       num_classes)
 
    return model

数据增强

  • 在数据集较少的情况下,可以采用数据增强来扩张数据集,常见方法有:1、平移变化,2、翻转变化,3、随机裁剪,4、噪声扰动,5、对比度变化,6、缩放变化,7、尺度变化。

  • 我们采用随机水平翻转。

import utils
# import detection.engine
from detection import  transforms as T
from engine import train_one_epoch, evaluate
 

# 定义变形函数
# 首先将PIL图片转变为tensor格式
# 如果是训练集,那么加入随机水平翻转,否则不进行任何操作
def get_transform(train):
    transforms = []
    transforms.append(T.ToTensor())
    if train:
        transforms.append(T.RandomHorizontalFlip(0.5))
    return T.Compose(transforms)

加载数据,设置参数,训练

# 定义训练集和测试集类
# get_transform分别为训练集和测试集获得transform参数
dataset = PennFudanDataset(r'H:\pycharm\deeplearning\tree_sick\mask rcnn\PennFudanPed\PennFudanPed/', get_transform(train=True))
dataset_test = PennFudanDataset(r'H:\pycharm\deeplearning\tree_sick\mask rcnn\PennFudanPed\PennFudanPed/', get_transform(train=False))
 
# 使用相同的随机化种子,确保每次参数初始化的值相同
torch.manual_seed(1)
# 返回一个包含数据集标号的随机列表,相当于随机化打乱标号
# torch.randperm(4).tolist() [2,1,0,3]
indices = torch.randperm(len(dataset)).tolist()
# 完成训练集和测试集的分割,dataset取dataset第一个到第倒数50个,dataset_test取倒数五十个?
dataset = torch.utils.data.Subset(dataset, indices[:-80])
dataset_test = torch.utils.data.Subset(dataset_test, indices[-80:])
 
# 定义数据集的读取实例DataLoader
# 注意windows需要把num_workers设定为0
data_loader = torch.utils.data.DataLoader(
    dataset, batch_size=2, shuffle=True, num_workers=0,
    collate_fn=utils.collate_fn) # collate_fn是取样本的方法参数
 
data_loader_test = torch.utils.data.DataLoader(
    dataset_test, batch_size=1, shuffle=False, num_workers=0,
    collate_fn=utils.collate_fn)

# 定义训练设备device
# device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
device=torch.device("cpu")
 
# 目标分类个数只有两个,person与background
num_classes = 2
 
# 获取之前定义的实例分割模型
model = get_instance_segmentation_model(num_classes)
# 将模型放到设备device上
model.to(device)
 
# 定义待训练的参数以及优化器
params = [p for p in model.parameters() if p.requires_grad] # 模型中的参数,凡是需要计算梯度的,统统列为待训练参数
optimizer = torch.optim.SGD(params, lr=0.005,
                            momentum=0.9, weight_decay=0.0005)
 
# 学习率的动态调整,每3个epochs将学习率缩小0.1倍
lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer,
                                               step_size=3,
                                               gamma=0.1)

开始训练:设置训练次数10轮

# 训练
num_epochs = 10
if need_training == True:
    for epoch in range(num_epochs):
        # train for one epoch, printing every 10 iterations
        train_one_epoch(model, optimizer, data_loader, device, epoch, print_freq=10,)
 
        # 手动更新学习率
        lr_scheduler.step()
 
        # 在测试集上评估模型
        evaluate(model, data_loader_test, device=device)

保存模型:

PATH = r"H:\pycharm\deeplearning\tree_sick\mask rcnn\TorchVision_Maskrcnn\Maskrcnn/FQmodel.pth"
torch.save(model, PATH)

预测

import os
import numpy as np
import torch
from PIL import Image

import torchvision
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
from torchvision.models.detection.mask_rcnn import MaskRCNNPredictor

import sys
sys.path.append("./detection")
from engine import train_one_epoch, evaluate
import utils
import transforms as T
import cv2
import cv2_util

import random
import time
import datetime

def random_color():
    b = random.randint(0,255)
    g = random.randint(0,255)
    r = random.randint(0,255)
 
    return (b,g,r)


def toTensor(img):
    assert type(img) == np.ndarray,'the img type is {}, but ndarry expected'.format(type(img))
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img = torch.from_numpy(img.transpose((2, 0, 1)))
    return img.float().div(255)  # 255也可以改为256

def PredictImg( image, model,device):
    #img, _ = dataset_test[0] 
    img = cv2.imread(image)
    result = img.copy()
    dst=img.copy()
    img=toTensor(img)

    names = {'0': 'background', '1': 'person'}
    # put the model in evaluati
    # on mode

    prediction = model([img.to(device)])

    boxes = prediction[0]['boxes']
    labels = prediction[0]['labels']
    scores = prediction[0]['scores']
    masks=prediction[0]['masks']

    m_bOK=False
    for idx in range(boxes.shape[0]):
        if scores[idx] >= 0.6:
            m_bOK=True
            color=random_color()
            mask=masks[idx, 0].mul(255).byte().cpu().numpy()
            thresh = mask
            contours, hierarchy = cv2_util.findContours(
                thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE
            )
            cv2.drawContours(dst, contours, -1, color, -1)

            x1, y1, x2, y2 = boxes[idx][0], boxes[idx][1], boxes[idx][2], boxes[idx][3]
            name = names.get(str(labels[idx].item()))
            cv2.rectangle(result,((int(x1),int(y1))),((int(x2),int(y2))),color,thickness=2)
            cv2.putText(result, text=name, org=(int(x1), int(y1+10)), fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                fontScale=0.5, thickness=1, lineType=cv2.LINE_AA, color=color)

            dst1=cv2.addWeighted(result,0.7,dst,0.5,0)

            
    if m_bOK:
        cv2.namedWindow('result', 0)
        cv2.resizeWindow("result",1200,700)
        cv2.imshow('result',dst1)
        cv2.waitKey()
        cv2.destroyAllWindows()

if __name__ == "__main__":

    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
    num_classes = 2
    model = torchvision.models.detection.maskrcnn_resnet50_fpn(pretrained=False,num_classes=num_classes)
    model.to(device)
    model.eval()
    save = torch.load('model.pth')
    model.load_state_dict(save['model'])
    start_time = time.time()
    PredictImg(r'C:\Users\Lenovo\Desktop/222.jpg',model,device)
    total_time = time.time() - start_time
    total_time_str = str(datetime.timedelta(seconds=int(total_time)))
    print('Training time {}'.format(total_time_str))
    print(total_time)

预测结果如下:

【纯小白】动手实现MASK RCNN 实例分割(带全部源码)

整个模型的训练预测到此结束。文章来源地址https://www.toymoban.com/news/detail-451585.html

写在最后:

  • 本文档依据Pytorch官方,CSDN,哔哩哔哩,Github多方总结改变实现,如有侵权,联系速删!!
  • 此文档旨在帮助小白使用pytorch一步步实现Mask Rcnn 网络。
  • 后续将会推出如何使用此网络训练自己的数据集。
  • 如在搭建过程中出现种种错误,欢迎骚扰我,我会尽力解决。

到了这里,关于【纯小白】动手实现MASK RCNN 实例分割(带全部源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Mask RCNN详解

            Mask R-CNN是对Faster R-CNN的直观扩展,网络的主干有RPN转换为主干网络为ResNet的 特征金字塔网络(FPN),同时 添加了一个分支用于预测每个感兴趣区域(RoI)上的分割掩模,与现有的用于分类和边界盒回归的分支并行(图1)。 掩模分支是一个应用于每个RoI的小FCN,以像素

    2023年04月11日
    浏览(24)
  • Mask RCNN训练自己的数据集

    Mask RCNN作为实例分割的经典算法,对于图像分割的初学者来说,还是很有必要了解下的。 原mask rcnn的Tensorflow版本是1.13,这里提供tf2.5的mask rcnn的github源码地址:https://github.com/zouyuelin/MASK_RCNN_2.5.0 目录 一、制作数据集 1.下载安装labelme  2.标注数据集 3.labelme数据集转化 二、模

    2024年02月15日
    浏览(29)
  • MASK-RCNN tensorflow环境搭建

    此教程默认你已经安装了Anaconda,且tensorflow 为cpu版本。为什么不用gpu版本,原因下面解释。 因为tensorflow2.1后的gpu版·,不支持windows。并且·只有高版本的tensorflow才对应我的CUDA12.2; 而且,我之前安装了pytorch跑tolov8,cuda都很高。安装tensorflow-gpu的话,需要重新安装我的cuda,

    2024年02月06日
    浏览(85)
  • Mask RCNN 超详细图文入门(含代码+原文)

    代码地址:maskrcnn-benchmark(PyTorch) 我在入门学习计算机视觉的适合,看一些经典的论文原文比较吃力。于是通过看各种参考文献及查阅各路资料,入门的角度写了一些博客,希望能够和大家一起进步。 笔者在阅读《Mask R-CNN》原文后,根据自身理解及查阅资料,以入门角度尽可

    2024年02月07日
    浏览(45)
  • Mask RCNN网络结构以及整体流程的详细解读

    Mask RCNN是在Faster RCNN的基础上增加了mask head用于实例分割的模型。 总体来说,Mask RCNN网络结构可以分为: BackBone(ResNet+FPN) — RPN网络(Region Proposal Network) — ROI Head(ROIAlign + cls head + bbox head + mask head) 整体网络结构如下(来自原论文https://arxiv.org/pdf/1703.06870.pdf): Backbone主要由R

    2024年02月13日
    浏览(27)
  • 使用Mask-RCNN训练自己的数据集看这一篇就够了,从制作数据集开始一步步教你如何玩转Mask-RCNN(保姆级教程)

    一、安装labelme  深度学习算法等基于神经网络的算法都是基于数据驱动的,数据的好坏会影响你最后生成的模型的好坏,在使用Mask-RCNN时,第一件事就是标注数据集,这里我们默认你已经配置好了anaconda的环境,如果你没有配置好可以参考一下其他人的博客,在已经配置好的

    2024年02月16日
    浏览(46)
  • mmdetection 中 Mask Rcnn检测结果可视化(DICE计算、PR曲线绘制等)

    mmdetection中的Mask Rcnn是一个很不错的检测网络,既可以实现目标检测,也可以实现语义分割。官方也有很详细的doc指导,但是对新手来说并不友好,刚好之前笔者写的mmlab系列里面关于可视化都还没有一个详细的文档,也在此一并介绍。 具体怎么制作自己的数据集和训练自己

    2024年02月12日
    浏览(36)
  • OpenCV中使用Mask R-CNN实现图像分割的原理与技术实现方案

    本文详细介绍了在OpenCV中利用Mask R-CNN实现图像分割的原理和技术实现方案。Mask R-CNN是一种先进的深度学习模型,通过结合区域提议网络(Region Proposal Network)和全卷积网络(Fully Convolutional Network),实现了对图像中物体的精确分割。本文首先阐述了Mask R-CNN的原理,然后详细

    2024年02月03日
    浏览(33)
  • 《动手深度学习》 线性回归从零开始实现实例

    🎈 作者: Linux猿 🎈 简介: CSDN博客专家🏆,华为云享专家🏆,Linux、C/C++、云计算、物联网、面试、刷题、算法尽管咨询我,关注我,有问题私聊! 🎈 欢迎小伙伴们点赞👍、收藏⭐、留言💬 本文是《动手深度学习》线性回归从零开始实现实例的实现和分析。 实现代码

    2024年02月11日
    浏览(36)
  • 语义分割中mask二值图转json格式

    本次文章针对mask黑白图转json格式进行分析 主要分两个步骤: 1、将mask二值图中的目标区域轮廓 ,按照每个点之间的步长进行选取保存,并将每个轮廓的点坐标信息保存至csv文件中。 csv文件格式如下图所示: 程序如下所示: 使用时,注意修改路径:

    2024年02月09日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包