[深度学习 - 实战项目] 实例分割 - yolact++-程序员宅基地

技术标签: 深度学习  pytorch  

参考代码来源于 https://github.com/dbolya/yolact

实例分割 yolact

最近进入新项目,又开始忙了,赶着项目没什么时间写博客。
最近项目是分割类的,主要训练慢,而且成效也慢,加上小目标检测效果不好,一直忙着在想技巧,分析数据分析问题。利用周末记录一下用到的实例分割模型。


更新:(这上面看一些技巧挺有用的,我也用了一些,对模型多少会有些提升。https://www.aiuai.cn/aifarm1370.html

1. 环境配置

首先将项目安装下来,或者git clone到自己本地。

因为我这边跑的是yolact++(在data/config.py内设置),所以按官方要求是要安装DCNv2

即执行下面代码。

cd external/DCNv2
python setup.py build develop

这里因为我一开始用的pytorch1.6版本,一直安装不下,也可能是对应的cuda版本问题。后来把版本降低后就可以正常执行安装了。版本:torch 1.4.0; torchvision 0.5.0 所以这里最好自己用conda创建一个专门用来跑yolact的虚拟环境。其他环境配置可以参照官方说明environment.yml

2. 实例分割数据集准备

yolact官方代码训练用的coco数据集,所以如果要训练自己的数据集,就要先把数据集转换成coco的格式。我这边是用labelme标注数据集的,所以在网上找了个labelme2coco的转换脚本。

(1)labelme2coco

我这里主要数据标注的时候有多种类别,但是最终用来训练的只有两种类别,所以用label_1和lable_2将不同的类别归为一类。

import argparse
import json
import matplotlib.pyplot as plt
import skimage.io as io
import cv2
from labelme import utils
import numpy as np
import glob
import PIL.Image
import sys, os, time
from shapely.geometry import Polygon
labels_1 = ['a','b']
labels_2 = ['c','d']
class labelme2coco(object):
    def __init__(self,labelme_json=[], path="", save_json_path=r''):
        '''
        :param labelme_json: 所有labelme的json文件路径组成的列表
        :param save_json_path: json保存位置
        '''
        self.labelme_json=labelme_json
        self.save_json_path=save_json_path
        self.info = {
    }
        self.licenses = []
        self.images=[]
        self.categories=[]
        self.annotations=[]
        # self.data_coco = {}
        self.label=[]
        self.annID=1
        self.height=0
        self.width=0
        self.path = path
        self.save_json()

    def data_transfer(self):
        self.info = self.info_dict()
        self.licenses = self.licenses_dict()
        count=0
        for num,json_file in enumerate(self.labelme_json):
            # print(num)

            with open(json_file,'r') as fp:
                print('[{}/{} count {} {}]'.format(num,len(self.labelme_json),count,json_file))
                try:
                    data = json.load(fp)  # 加载json文件
                    self.images.append(self.image(data,count))
                    for shapes in data['shapes']:
                        #label=shapes['label'].split('_')
                        if shapes['label'] in labels_1:
                            label = '1'
                        elif shapes['label'] in labels_2:
                            label = '2'
                        else:
                            print(shapes['label'],'error')
                            continue
                        # label=shapes['label']
                        #print(shapes['label'])
                        if label not in self.label:
                            self.categories.append(self.categorie(label))
                            self.label.append(label)
                        points=shapes['points']
                        # points = np.array(points,'int').tolist()
                        if shapes['shape_type'] == 'rectangle':
                            x0,y0,x1,y1 = points[0][0],points[0][1],points[1][0],points[1][1]
                            points = [[x0, y0], [x1, y0], [x1, y1], [x0, y1]]
                        if shapes['shape_type'] == 'polygon':
                            if len(points) <= 2:
                                continue
                        self.annotations.append(self.annotation(shapes['shape_type'],points,label,count))
                        self.annID+=1
                    count+=1
                except:
                    print(data['imagePath'],'is error')
        print(self.label)

    def info_dict(self):
        info = {
    }
        info['description'] = None
        info['url'] = None
        info['contributor'] = None
        info['day'] = time.strftime('%Y.%m.%d',time.localtime(time.time()))
        info['data_created'] = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
        return info

    def licenses_dict(self):
        license = []
        license = [{
    'url':None,'id':0,'name':None}]
        return license

    def image(self,data,num):
        image={
    }
        img = cv2.imdecode(np.fromfile(os.path.join(self.path, data['imagePath']), dtype=np.uint8), 0)
        height, width = img.shape[:2]
        image['license'] = 0
        image['url'] = None
        image['file_name'] = data['imagePath'].split('/')[-1]
        image['height'] = height
        image['width'] = width
        image['date_captured'] = None
        image['id'] = num
        self.height=height
        self.width=width
        return image

    def categorie(self,label):
        categorie={
    }
        categorie['supercategory'] = None
        categorie['id']=len(self.label)+1 # 0 默认为背景
        categorie['name'] = label
        return categorie

    def annotation(self,type,points,label,num):
        annotation={
    }
        annotation['id'] = self.annID
        annotation['image_id'] = num
        annotation['category_id'] = self.getcatid(label)
        annotation['segmentation']=[list(map(int, list(np.asarray(points).flatten())))]
        poly = Polygon(points)
        area_ = round(poly.area,6)
        annotation['area'] = area_
        annotation['bbox'] = list(map(float, self.getbbox(points)))
        annotation['iscrowd'] = 0
        # annotation['bbox'] = str(self.getbbox(points)) # 使用list保存json文件时报错(不知道为什么)
        # list(map(int,a[1:-1].split(','))) a=annotation['bbox'] 使用该方式转成list
        return annotation

    def getcatid(self,label):
        for categorie in self.categories:
            if label==categorie['name']:
                return categorie['id']
        return -1

    def getbbox(self,points):
        # img = np.zeros([self.height,self.width],np.uint8)
        # cv2.polylines(img, [np.asarray(points)], True, 1, lineType=cv2.LINE_AA)  # 画边界线
        # cv2.fillPoly(img, [np.asarray(points)], 1)  # 画多边形 内部像素值为1
        polygons = points
        mask = self.polygons_to_mask([self.height,self.width], polygons)
        return self.mask2box(mask)

    def mask2box(self, mask):
        '''从mask反算出其边框
        mask:[h,w]  0、1组成的图片
        1对应对象,只需计算1对应的行列号(左上角行列号,右下角行列号,就可以算出其边框)
        '''
        # np.where(mask==1)
        index = np.argwhere(mask == 1)
        rows = index[:, 0]
        clos = index[:, 1]
        # 解析左上角行列号
        left_top_r = np.min(rows)  # y
        left_top_c = np.min(clos)  # x
        # 解析右下角行列号
        right_bottom_r = np.max(rows)
        right_bottom_c = np.max(clos)
        # return [(left_top_r,left_top_c),(right_bottom_r,right_bottom_c)]
        # return [(left_top_c, left_top_r), (right_bottom_c, right_bottom_r)]
        # return [left_top_c, left_top_r, right_bottom_c, right_bottom_r]  # [x1,y1,x2,y2]
        return [left_top_c, left_top_r, right_bottom_c-left_top_c, right_bottom_r-left_top_r]  # [x1,y1,w,h] 对应COCO的bbox格式

    def polygons_to_mask(self,img_shape, polygons):
        mask = np.zeros(img_shape, dtype=np.uint8)
        mask = PIL.Image.fromarray(mask)
        xy = list(map(tuple, polygons))
        PIL.ImageDraw.Draw(mask).polygon(xy=xy, outline=1, fill=1)
        mask = np.array(mask, dtype=bool)
        return mask

    def data2coco(self):
        data_coco={
    }
        data_coco['info']=self.info
        data_coco['licenses']=self.licenses
        data_coco['images']=self.images
        data_coco['categories']=self.categories
        data_coco['annotations']=self.annotations
        return data_coco

    def save_json(self):
        self.data_transfer()
        self.data_coco = self.data2coco()
        # 保存json文件
        json.dump(self.data_coco, open(self.save_json_path, 'w'), indent=4)  # indent=4 更加美观显示

if __name__ == '__main__':
    path = r''# 存放labelme标注好的数据(图片以及json标签)
    print(os.path.join(path, '*.json'))
    labelme_json=glob.glob(os.path.join(path, '*.json'))
    print("json data numbers: ", len(labelme_json))
    save_path = 'save.json'# coco的格式主要就是将所有数据存放在一个json文件内。
    if not os.path.exists(save_path):
        os.mkdir(save_path)
    labelme2coco((labelme_json), path, save_path)


(2)visual_coco

转换好coco 标签格式后,最好可视化检查一下标签是否正常。
这里可以通过pycocotools写一个可视化脚本。

import os
from pycocotools.coco import COCO

from skimage import io
from matplotlib import pyplot as plt

img_path = r'dataset\train_dataset'#图片路径
path_save = r'hardSamples/mult_lossSORT_biaozhu'# 输出可视化图片
json_file = r'dataset/train.json' #转换好coco格式的json文件
os.makedirs(path_save, exist_ok=True)

coco = COCO(json_file)
print(coco)

catIds = coco.getCatIds()
imgIds = coco.getImgIds()

for i in range(len(imgIds)):
    # print('11')
    img = coco.loadImgs(imgIds[i])[0]
    img_name = img['file_name']
    print('[{}/{}:{}]'.format(i+1,len(imgIds),img_name.replace('/','_')))

    img_fullpath = os.path.join(img_path,img_name)
    I = io.imread(img_fullpath)
    plt.axis('off')
    plt.imshow(I) #绘制图像,显示交给plt.show()处理
    annIds = coco.getAnnIds(imgIds=img['id'], iscrowd=None)
    anns = coco.loadAnns(annIds)
    coco.showAnns(anns)
    # plt.show() #显示图像

    img_save_fullpath = os.path.join(path_save,img_name.replace('/','_'))
    plt.savefig(img_save_fullpath)
    plt.cla()

3. 修改代码配置文件

首先是修改data/config.py ,自己写一个my_custom_dataset如下,里面放入你做好的数据集和标签。对应的yolact_base_config里面改dataset': my_custom_dataset,里面还有其他配置可以改的,
如网络:我这里使用的yolact_plus,输入尺寸用的550。里面还可以设置损失函数之类的,图形增强的一些配置,可以自己去看。因为这里面几个配置文件copy来copy去的,容易把自己绕晕。可以自己根据配置写一个Config。然后修改train.py里面的config名字就行了。

my_custom_dataset = Config({
    
    'name': 'my_custom_dataset',

    'train_images': '/data/train/',
    'train_info': '/data/tran_data.json',
    'valid_images': '/data/val/',
    'valid_info': '/data/val_data.json',

    'has_gt': True,
    'class_names': ('1', '2') # 类别
})

# ----------------------- YOLACT v1.0 CONFIGS ----------------------- #

yolact_base_config = coco_base_config.copy({
    
    'name': 'yolact_base',

    # Dataset stuff
    'dataset': my_custom_dataset,
    'num_classes': len(my_custom_dataset.class_names) + 1,

    # Image Size
    'max_size': 550,

    # Training params
    'lr_steps': (280000, 600000, 700000, 750000),
    'max_iter': 800000,

    # Backbone Settings
    'backbone': resnet101_backbone.copy({
    
        'selected_layers': list(range(1, 4)),
        'use_pixel_scales': True,
        'preapply_sqrt': False,
        'use_square_anchors': True,  # This is for backward compatability with a bug

        'pred_aspect_ratios': [[[1, 1 / 2, 2]]] * 5,
        'pred_scales': [[24], [48], [96], [192], [384]],
    }),

    # FPN Settings
    'fpn': fpn_base.copy({
    
        'use_conv_downsample': True,
        'num_downsample': 2,
    }),

    # Mask Settings
    'mask_type': mask_type.lincomb,
    'mask_alpha': 6.125,
    'mask_proto_src': 0,
    'mask_proto_net': [(256, 3, {
    'padding': 1})] * 3 + [(None, -2, {
    }), (256, 3, {
    'padding': 1})] + [(32, 1, {
    })],
    'mask_proto_normalize_emulate_roi_pooling': True,

    # Other stuff
    'share_prediction_module': True,
    'extra_head_net': [(256, 3, {
    'padding': 1})],

    'positive_iou_threshold': 0.5,
    'negative_iou_threshold': 0.4,

    'crowd_iou_threshold': 0.7,

    'use_semantic_segmentation_loss': True,
})

4. 训练

修改一下train.py里面的配置。

batch_size: 模型有点大,我用1080Ti 11G显存,一块最多只能训练14个batch_size(输入尺寸550)。
batch_alloc: 你用多块GPU的话,这里可以对应写每块GPU要多少个batchsize。(如上面batchsize为28,这里就是14,14)
config: 对应刚刚配置数据的代码。
save_folder: 保存权重的地址。
init_weights: 第一次训练使用的权重。(如果save_folder中没有权重的话)
resume: 这个为latest的时候会优先使用save_folder中的最后一个权重进行训练。

训练的优化器,这里默认使用SGD。这里要到train.py中optimizer = optim.SGD(net.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.decay)这一块去改。我也不太清楚是不是分割模型用SGD比较好,后面我也有改成Adam优化器,损失会降的比较快。

这里如果要指定那几块GPU进行训练,需要在train.py最上端加入这一串代码。

import os
os.environ['CUDA_VISIBLE_DEVICES'] = '6,7'  # 

5. 预测

训练完模型要跑预测只需要按照eval.py中的配置进行就行了。

6. 总结

实例分割模型训练起来比较慢,而且模型训练起来也比较难。我正在慢慢摸索,也尝试了很多技巧。(最近也还在忙这个项目,后面又时间再把使用过的技巧,还有一些坑总结一下。)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_41809530/article/details/111062123

智能推荐

STM32芯片--FSMC外设扩展外部SRAM-程序员宅基地

文章浏览阅读7.4k次,点赞6次,收藏49次。这里写自定义目录标题为什么要扩展外部SRAM(一)什么是SRAM简介存储器型号容量引脚配置通讯方式读写特性读取数据时序图读取数据的时序要求写入数据时序图写入数据的时序要求(二)什么是FSMC外设简介功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建..._外部sram

新手用qt控件显示本地图片_qt实现拉取一个控件显示一张照片-程序员宅基地

文章浏览阅读6.8k次,点赞4次,收藏17次。QT显示图片的方法(部分源码)这个方法太简单网上已经泛滥了,不过我还是写一下吧,无聊。1 首先,新建一个项目,使用界面文件(可以不使用,不过人生苦短,没事我就不麻烦自己了)2使用qt设计器,在界面拖入一个label(我修改其objectname为screen)与一个button。3 转到button的槽,书写相应的程序。 程序如下 需要使用并添加的头文件部分#include#_qt实现拉取一个控件显示一张照片

【YOLOv4探讨 之三】mosaic数据增强_yolo mosaic-程序员宅基地

文章浏览阅读1w次,点赞17次,收藏51次。最近太忙,久未更新,对不住大家。进入正题,我们聊一聊YOLOv4中使用的mosaic数据增强。关于mosaic数据增强相关文章不少,三个月前这个方面的学习开了个头,那时候各路诸侯都是以TensorFlow框架为主,我这里依然坚持分析研究darknet框架下的数据增强。mosaic数据增强原理YOLOv4中在载入图片数据时同步进行mosaic数据增强。mosaic数据增强基本原理就是在训练集中随机选择若干个(一般是4个)图像,经过裁剪拼接形成新的训练集元素,可以缓解训练集元素少或者增强识别能力,是cut_yolo mosaic

vue 实现主题换肤(element-ui)_--el-select-input-focus-border-color-程序员宅基地

文章浏览阅读1.4k次。vue 实现主题换肤(element-ui)一. 插件安装npm i webpack-theme-color-replacer -D二. 项目使用vue.config.jsconst webpack = require('webpack')const ThemeColorReplacer = require('webpack-theme-color-replacer')const forElementUI = require('webpack-theme-color-replacer/for_--el-select-input-focus-border-color

JVM-从熟悉到精通_jvm 精通 技术学习-程序员宅基地

文章浏览阅读600次。JVM、JMM、GC、三色标记、常见调优参数_jvm 精通 技术学习

低成本挖出电商API接口-程序员要注意那些事项-技术分享_pvwd-程序员宅基地

文章浏览阅读914次。通过了解API接口的相关信息后,您可以根据需求进行开发,从而获取天猫的购物车API接口数据,实现自己的电商平台的功能需求。在注册阿里云后,了解天猫购物车API接口的相关信息是必须的。参数校验:接口参数是影响接口调用结果的重要因素,我们需要在接口调用前对参数进行校验,确保参数合法、规范、完整。接口性能:电商业务接口需要承受巨大的访问量,所以我们需要保证接口的性能。总之,无论选择哪种方式,都需要您投入精力和时间,但是从长远考虑,低成本获取天猫购物车API接口可以为您的电商平台节约不少资金,提升您的竞争优势。_pvwd

随便推点

案例-做一个酒店预定小程序用的日期选择案例_小程序订酒店日历选择-程序员宅基地

文章浏览阅读6.6k次,点赞3次,收藏39次。做一个酒店预定用的日期选择案例不多说,先上效果图设计思路:根据年份和月份构建最近6个月的日历,获取每个月的第一天是星期几,空白用空对象填充,每个月需要的格子数 = 每月1号的星期数 + 每月对应的天数。在HTML中遍历日期数据,根据条件判断高亮显示入住时间、离店时间以及两者之间的时间。一下是全部代码(优化在最后). &amp;amp;amp;lt;!-- html代码 --&amp;amp;amp;gt; &amp;amp;amp;lt;view..._小程序订酒店日历选择

lol佐伊美图-程序员宅基地

文章浏览阅读1.6k次。  心血来潮,分享一波从各个网站上搜集到的佐伊美图,持续更新!(最近更新日期:2019/07/01)  本页面图片较多,若无法全部加载请反复刷新页面,点击图片可查看原始大图!Section1 暮光星灵2018/11/162019/02/152019/03/172019/07/01Section2 ...

(基础入门)web安全|渗透测试|网络安全------附带 子域名挖掘,exe后门程序生成之Quasar,抓包wsexplorerv,逆向工具漏了个大洞-程序员宅基地

文章浏览阅读2.7k次,点赞9次,收藏12次。(基础入门)web安全|渗透测试|网络安全------附带 子域名挖掘,exe后门程序生成之Quasar,抓包wsexplorerv,逆向工具漏了个大洞

typeAliases标签和package标签_type-aliases-package: com.hcr.account.model.entity-程序员宅基地

文章浏览阅读442次。https://blog.csdn.net/weixin_42727032/article/details/104327048?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonec_type-aliases-package: com.hcr.account.model.entity

php正则preg_match,PHP正则表达式preg_match的具体使用规则介绍-程序员宅基地

文章浏览阅读853次。PHP对于初学者来说,最难理解的内容要属于正则表达式的应用。今天我们就向大家具体介绍有关PHP正则表达式preg_match的使用规则,希望初学者们能通过本文介绍的内容对正则表达式有一个深刻的认识。PHP正则表达式preg_match的使用:利用 preg_match(),我们可以完成字符串的规则匹配。如果找到一个匹配,preg_match() 函数返回 1,否则返回 0。还有一个可选的第三参数可..._if(preg_match("/[a-za-z0-9]/",$var)){ die("nope,this is level 5"); }

Android教程-01 Android Studio创建第一个项目_android studio创建第一个项目,并写布局-程序员宅基地

文章浏览阅读2k次,点赞2次,收藏3次。最近一直使用Android Studio 简单把Android Studio介绍下1. 首先介绍下 Android Studio的快捷键映射到Eclipse_android studio创建第一个项目,并写布局