Python实例分割 YOLOv5 segment使用教程(完善中)

Posted 此彼之间白夜幻想

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python实例分割 YOLOv5 segment使用教程(完善中)相关的知识,希望对你有一定的参考价值。

目录

一、直接试用方式

1、准备工作

2、代码测试

(1)、模型训练(可以跳过)

(2)、模型预测

 二、制作自己的数据集

1、格式

2、labelme制作标签

3、json转txt

4、修改数据集参数

三、用YOLOv5跑自己的数据集

1、train.py参数修改

2、predict.py参数修改

3、txt2mask

四、遇到过的报错与解决方式

五、原理(部分)

1、图像标签转换

2、分割原理


本文是我在使用YOLOv5时,做的一些过程记录,按照步骤走应该能够跟我获得相同的结果,初次写这种类型的文章,排版之类的可能不太好看,内容也不够充分,之后混慢慢修改补充。

本文内容包含代码的直接使用方式,与在自定义数据集上的使用方式,目前未使用过其他公开数据集进行试用。

一、直接试用方式

1、准备工作

配置conda,虚拟环境与torch,这一部分有很多教程,这里就不写了。

代码下载:GitHub - ultralytics/yolov5: YOLOv5 🚀 in PyTorch > ONNX > CoreML > TFLite

环境准备:pip install -r requirements.txt。

或者按照官方教程文件:

!git clone https://github.com/ultralytics/yolov5  # clone
%cd yolov5
%pip install -qr requirements.txt  # install

import torch
import utils
display = utils.notebook_init()  # checks

2、代码测试

(1)、模型训练(可以跳过)

打开segment/train.py,直接运行,会自动下载预训练模型参数yolov5s-seg.pt与数据集coco128-seg,模型参数会下载到yolov5目录下,数据集会下载到yolov5父目录下。训练结果会保存在runs/train-seg/exp中。

或者自行下载模型参数:https://github.com/ultralytics/yolov5/releases/download/v7.0/yolov5s-seg.pt

(2)、模型预测

打开segment/predict.py,直接运行,会将/home/w/下载/yolov5-master/data/images中的两张图像进行分割预测(没有使用上面的训练参数),分割结果会保存在runs/predict-seg/exp。

 分割结果如下:

 二、制作自己的数据集

1、格式

数据集文件夹格式:

其中images文件夹下放的是原始图像,labels文件夹下放的是满足YOLOv5要求的txt标签文件。

将原始数据按照文件夹格式复制可参考:

https://blog.csdn.net/a1004550653/article/details/128329796

txt文件格式:

 第一个数字为类别,后面每两个数字代表一个点对于整张图像的相对位置。每一行代表图像中的一个mask。

2、labelme制作标签

制作自己的数据集的话,打标签是无可避免的,用labelme就足以满足基本需求。这一部分的教程也很多,也就先不写了,有机会再加。

3、json转txt

通过labelme可以得到json格式的mask标签。按照下面的代码可以将其修改为需要的格式。

https://blog.csdn.net/a1004550653/article/details/128320398

4、修改数据集参数

 

复制coco123_seg.yaml,修改名称,例如mydataset.yaml。

修改内部参数:

     path为数据集目录,train为其子目录相对路径,val可以与train相同,test不用设置。names为不同的分类与名称,改成自己的就好。

三、用YOLOv5跑自己的数据集

1、train.py参数修改

 weigeht是预训练模型,可以使用自己的,也可以下载官方的。

data是数据集格式,改成刚才创建的yaml名称。

hyp是数据增强,可以按照需求在文件内自行增改。

epochs为训练轮数,训练中会自动保存最好与最后一次的模型参数。

batch--size为一次训练的图片个数,配置够的话可以增加。

imgsz等为训练时的图像重置大小。

基本上只用动上面几个参数就够了,如果还有别的需求,可以看help里面的描述,自行修改。

例如下面还有个optimizer,优化器,默认sgd。

当参数带有action='store_true',那么默认不会运行,运行时需要用终端加上--xxx(例如Python train.py --nosave)。

2、predict.py参数修改

 weight修改为训练得到的自己的权重,默认位置在run/train-seg/exp/weight下。

source为需要预测的图像文件夹。

data设置与train相同。

imgsz建议使用默认的640,数值增大,结果可能会更精细,但是检测框可能会不够大。

conf-thres为置信阈值,只有检测框的概率高于阈值时,才会被留下。

iou-thres为交并比阈值,在进行NMS(非极大值抑制)时,超过阈值的检测框会被删除。

max-det为一张图中检测框存在的最大数量,因为个人需要,我会设置为1。

save-txt会将分割结果按照YOLOv5的格式保存为txt文件,可以通过txt文件再转换为需要的mask。

save-crop会将检测框内部图像截图保存。

如果不加别的处理,原始图像经过predict.py后,会得到一张实例分割的图像。

3、txt2mask

将预测得到的txt转换为需要的mask图像。

https://blog.csdn.net/a1004550653/article/details/128313599

四、遇到过的报错与解决方式

urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed_知·味的博客-CSDN博客

五、原理(部分)

1、图像标签转换

在yolov5-seg中,准备的txt文件中包含的是类别与seg点位,在实际训练的过程中,seg点位会被转换为检测用的box信息,即xywh。具体的转换代码如下:

def xyxy2xywhn(x, w=640, h=640, clip=False, eps=0.0):
    # Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] normalized where xy1=top-left, xy2=bottom-right
    if clip:
        clip_boxes(x, (h - eps, w - eps))  # warning: inplace clip
    y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
    y[:, 0] = ((x[:, 0] + x[:, 2]) / 2) / w  # x center
    y[:, 1] = ((x[:, 1] + x[:, 3]) / 2) / h  # y center
    y[:, 2] = (x[:, 2] - x[:, 0]) / w  # width
    y[:, 3] = (x[:, 3] - x[:, 1]) / h  # height
    return y



def segments2boxes(segments):
    # Convert segment labels to box labels, i.e. (cls, xy1, xy2, ...) to (cls, xywh)
    boxes = []
    for s in segments:
        x, y = s.T  # segment xy
        boxes.append([x.min(), y.min(), x.max(), y.max()])  # cls, xyxy
    return xyxy2xywh(np.array(boxes))  # cls, xywh

2、分割原理

在yolov5s-seg模型下,图像重置大小设置为640,将图片与标签输入模型后得到pred与proto。

其中pred形状为[1, 22680, 38],经过nms后得到检测框信息,每个检测框形状为[1, 38],向量中0-3为检测框位置,4为检测框的置信度,5为分类,6-37为mask协方差系数。

proto的形状为[1, 32, 160, 144],(160,144)是输入图像下采样两次后的大小。

mask的求取方法为,用pred中的mask的协方差系数,与proto做矩阵乘法,得到mask的具体输出,大小为[1, 160, 144],再经过crop_mask,只保留检测框范围内的数据,最后经过上采样,大小为[1, 640, 576]。

def crop_mask(masks, boxes):
    """
    "Crop" predicted masks by zeroing out everything not in the predicted bbox.
    Vectorized by Chong (thanks Chong).

    Args:
        - masks should be a size [h, w, n] tensor of masks
        - boxes should be a size [n, 4] tensor of bbox coords in relative point form
    """

    n, h, w = masks.shape
    x1, y1, x2, y2 = torch.chunk(boxes[:, :, None], 4, 1)  # x1 shape(1,1,n)
    r = torch.arange(w, device=masks.device, dtype=x1.dtype)[None, None, :]  # rows shape(1,w,1)
    c = torch.arange(h, device=masks.device, dtype=x1.dtype)[None, :, None]  # cols shape(h,1,1)

    return masks * ((r >= x1) * (r < x2) * (c >= y1) * (c < y2))

def process_mask(protos, masks_in, bboxes, shape, upsample=False):
    """
    Crop before upsample.
    proto_out: [mask_dim, mask_h, mask_w]
    out_masks: [n, mask_dim], n is number of masks after nms
    bboxes: [n, 4], n is number of masks after nms
    shape:input_image_size, (h, w)

    return: h, w, n
    """

    c, mh, mw = protos.shape  # CHW
    ih, iw = shape
    masks = (masks_in @ protos.float().view(c, -1)).sigmoid().view(-1, mh, mw)  # CHW

    downsampled_bboxes = bboxes.clone()
    downsampled_bboxes[:, 0] *= mw / iw
    downsampled_bboxes[:, 2] *= mw / iw
    downsampled_bboxes[:, 3] *= mh / ih
    downsampled_bboxes[:, 1] *= mh / ih

    masks = crop_mask(masks, downsampled_bboxes)  # CHW
    if upsample:
        masks = F.interpolate(masks[None], shape, mode='bilinear', align_corners=False)[0]  # CHW
    return masks.gt_(0.5)

for i, det in enumerate(pred): 
     masks = process_mask(proto[i], det[:, 6:], det[:, :4], im.shape[2:],     upsample=True)  # HWC

图像分割:Semantic/Instance/Panoramic Segmentation

一. 背景介绍

语义分割(Semantic Segmentation):对一张图片上的所有像素点进行分类,同一物体的不同实例不需要单独分割出来。

实例分割(Instance Segmentation):目标检测(比b-box更精确到边缘)和语义分割(标出同类不同个体)的结合。

全景分割(Panoramic Segmentation):语义分割和实例分割的结合,背景也要检测和分割。

图像分割是图像理解的重要基石,在自动驾驶、无人机、工业质检等应用中都有着举足轻重的地位。缺陷检测论文现在好多都是借助语义分割方法做的迁移应用到实际的工业现场等,比如国外知名的VIDI软件、国内一些检测软件。

技术图片

二. 语义分割

 

1. 【UNet】

结构:

Unet主要针对生物医学图像分割。继承FCN的思想。整体结构就是先编码(下采样),对图像的低级局域像素值进行归类与分析,从而获得高阶语义信息; 再解码(上采样),收集这些语义信息,并将同一物体对应到相应的像素点上,回归到跟原始图像一样大小的像素点的分类。

技术图片

 

 

encoder的基本单元是:两个valid卷积层(图像size会减小)接一个max pooling(2x2)下采样(1/2);

decoder的基本单元是:[up-conv(2x2)+skip connection]接两个valid卷积层。

U-Net的skip connection不同于FCN的对应像素求和,是对channel的concat(拼接)过程。Unet上采样部分可以用上采样或转置卷积,这里详细解释下up-conv转置卷积。

技术图片实际在计算机中,并不是逐像素滑动计算,效率太低。而是将卷积核转换成等效的矩阵,通过输入向量和卷积核矩阵相乘获得输出向量。如图,input:4x4,Kernel:3x3,Padding/Stride:0,output:2x2,卷积核要在输入的不同位置卷积4次,通过补零将卷积核分别置于一个4x4矩阵的四个角落,这样输入可以直接和这四个4x4的矩阵进行卷积,取代了滑窗操作。将输入展开为[16,1]向量X,输出矩阵记作Y([4,1]),四个4x4卷积核分别展开并拼接成卷积矩阵C([16,4])。普通的卷积运算可表示为矩阵运算:XT * C = YT

技术图片

技术图片

 

我们将一个1x16的行向量乘以16x4的矩阵,得到了1x4的行向量。反过来,在需要输入一个小的特征,输出更大尺寸的特征时,将一个1x4的向量乘以一个4x16的矩阵就能得到一个1x16的行向量,这就是转置卷积的思想。根据普通卷积,公式改写为:YT * CT = XT

技术图片普通卷积和转置卷积这两个操作不可逆,同一个卷积核在转置卷积操作之后不能恢复原始数值,只恢复形状。相同的形状就足够了,在训练中我们可以学习卷积核对应的权值来还原图像

关键策略:

(1).Deep Supervision。
4次上采样,并在各阶段使用skip connection,而不是直接在high-level特征上训练,保证了最后恢复出来的特征图融合了更多的low-level特征,也使不同尺度特征得到了融合,从而可以进行多尺度预测。4次上采样也使得分割图恢复边缘等信息更加精细。
(2).分类策略。
两层conv 3x3得到heatmap,再用conv 1x1分成两类,得到对应2个类得分(每个像素点针对两类都有一个得分)的最后两张heatmap, 然后作为softmax函数的输入,算出概率比较大的softmax类,输入给交叉熵进行BP。
(3).Overlap-tile策略。
为了预测框中图像,为了预测黄色区域的分割,需要蓝色区域作为输入,缺失区域通过镜像输入图像扩张。这对于应用网络到大图像很重要,避免gpu内存限制问题。
技术图片
(4).数据增加。
少量样本情况下,让网络获得不变性和鲁棒性,数据增加必不可少。处理显微镜图片时,我们需要平移与旋转不变性,并且对形变和灰度变化鲁棒。将训练样本进行随机弹性形变是训练分割网络的关键。使用随机位移矢量(random displacement vectors)在粗糙的3*3网格上产生平滑形变(smooth deformations),位移是从10像素标准偏差的高斯分布中采样的,然后使用双三次插值计算每个像素的位移。在contracting path的末尾采用drop-out层更进一步增加数据。
技术图片      技术图片
(6).加权loss。
采用SGD训练,最后一层使用交叉熵函数与softmax。为了使某些像素点更加重要,引入了w(x)。对每一张标注图像预计算了一个权重图,来补偿训练集中每类像素的不同频率,使网络更注重学习相互接触的细胞之间小的分割边界。使用形态学操作计算分割边界。权重图计算公式如下:
技术图片
wc是用于平衡类别频率的权重图,d1代表到最近细胞的边界的距离,d2代表到第二近的细胞的边界的距离。基于经验我们设定w0=10,σ≈5像素。网络的权重由高斯分布初始化,分布的标准差为(N/2)^0.5, N为每个神经元的输入节点数量。例如,对于一个上一层是64通道的3*3卷积核来说,N=9*64。
UNet系列,或基于UNet衍生出来的其他网络还包括:3D U-Net、UNet++、TernausNet、Res-UNet、Dense U-Net、MultiResUNet、R2U-Net、Attention UNet等。
 
2.【SegNet】
结构:
SegNet是Cambridge提出旨在解决自动驾驶或者智能机器人的图像语义分割深度网络,和FCN思路十分相似,只是Encoder,Decoder(upsampling)使用的技术不一致。
技术图片

encoder的基本单元是:2-3个same卷积层(图像size不变,BN对训练图像的分布归一化,加速学习)接一个max pooling(2x2)下采样;

decoder的基本单元是:upsampling接2-3个same卷积层,卷积为上采样放大的图像丰富信息,使池化过程丢失的信息被重建。

关键策略:
(1).Pooling Indices。

SegNet在pooling上有个创新,引入了index功能。这里详细解释下。

技术图片

如上图,每次pooling,都会保存通过max选出的权值在2x2 filter中的相对位置,6在粉色2x2 filter中的位置为(1,1)(index从0开始),黄色的3的index为(0,0)。同时,从网络结构图可以看到绿色的pooling与红色的upsampling通过pooling indices相连,表示pooling后的indices输出到对应的upsampling层。upsampling中先对输入的特征图放大两倍,然后把输入特征图的数据根据pooling indices放入,如下图所示。 

技术图片

2x2的输入变成4x4的图,除了被记住位置的pooling indices,其他位置的权值为0,因为数据已经被pooling走了。因此,SegNet使用卷积来填充缺失的内容。

(2).分类策略。

在网络框架中,SegNet最后一个卷积层会输出所有的类别(包括其他类),网络最后加上一个softmax层,由于是端到端,所以softmax需要求出每一个像素在所有类别最大的概率,作为该像素的label,最终完成图像像素级别的分类。

4.【Deeplabv3++】
结构:
 
技术图片
 
主干网络(DCNN): 更深的Xception结构。
技术图片
Xception: 带有残差连接的深度可分卷积层的线性堆叠,基于Inception。

encoder: 原DeepLabv3作为encoder,ASPP有四个不同的rate,额外一个全局平均池化;

decoder: 先把encoder的结果上采样4倍,然后与resnet中下采样前的特征(先1x1卷积降通道数)concat,再进行3x3的卷积,最后上采样4倍输出结果。

关键策略:

(1).深度可分离卷积(Depthwise separable convolution)。

 

encoder的主干网络Xception中引入的深度可分离卷积: depthwise_separable_convolution = depthwise_convolution(先每个通道上独自进行空间卷积) + pointwise_convolution(再用1x1卷积核组合前面dep_conv得到的特征),在保持网络性能的同时,大幅减少参数量。

 

技术图片

(2).空洞卷积(dilated convolution)。

 

又叫膨胀卷积,是通过增加一个超参数dilation rate(kernel的间隔数量),在标准的卷积图里注入空洞来增加感受野,有效避免了upsampling和pooling设计的缺陷:参数不可学习、内部数据结构及空间层级化信息丢失、小物体信息无法重建。 

技术图片

但是,Gridding Effect(kernel不连续,不是所有的pixel都用来计算)会使膨胀卷积损失信息的连续性,另外,单凭大dilation rate获得的信息可能对小物体分割没效果。通向标准化设计,Hybrid Dilated Convolution(HDC)因此被引入。HDC有三个特征:叠加卷积的dilation rate不能有大于1的公约数;将dilation rate设计成锯齿状结构,如[1, 2, 5, 1, 2, 5]循环结构;第2层的最大dilation rate M2 <= kernel size(k),至少可以用dilation rate 1(标准卷积)来覆盖掉所有洞。 

(3).ASPP(astrous spatial pyramid pooling)。

ASPP通过不同的rate构建不同感受野的卷积核,捕获多尺度信息,并联(串联)不同膨胀率的空洞卷积,来获取更多上下文信息。

技术图片

(4).encoder-decoder结构。

把ASPP模块和encoder-decoder结合在一起,encoder-decoder结构可以更好的地恢复物体的边缘信息。

技术图片

 

三. 实例分割
 
1.【Mask-RCNN】
结构:
Mask-RCNN是一个通用的实例分割架构,以Faster-RCNN为原型(速度5fps,更慢)的二阶段模型,增加了一个用于分割的Mask预测分支,可用于人的姿态估计等任务。
技术图片
 
步骤:首先是找出RPN,然后对RPN找到的每个ROI进行分类、定位、并找到binary mask。与其他先找到mask再进行分类的网络不同。因为没有采用全连接层并且使用了RoIAlign,可以实现输出与输入的像素一一对应。
技术图片
主干网络:标准的卷积神经网络(VGG/ResNet,可+FPN提升性能)作为特征提取器。底层检测的是低级特征(边缘和角等),较高层检测的是更高级的特征。
RPN网络:同Faster-RCNN,基于滑动窗口扫描图像,寻找目标区域,输出anchor box类别(FG/BG)并根据偏移量精调b-box拟合目标,过滤掉一部分候选的ROI。
ROIAlign:对这些剩下的ROI进行ROIAlign操作(先将原图和特征图的像素对应起来,然后将特征图和固定的feature对应起来)。
Final分类回归:对这些ROI进行分类(N类别分类)、b-box回归和mask生成。
损失函数:L = Lcls + Lbox + LmaskLcls和Lbox与Faster-RCNN的定义相同,表示b-box的分类和回归损失值,Lmask表示mask部分损失值。
关键策略:
(1).ROIAlign。
Faster-RCNN存在的问题是:特征图与原始图像是不对准的,所以会影响检测精度。而Mask-RCNN提出了RoIAlign的方法来取代ROI pooling,可以保留大致的空间位置。
技术图片
为了得到为了得到固定大小(7X7)的feature map,ROIAlign技术没有使用量化操作,取而代之的是双线性插值
技术图片
黑色实线框表示ROI feature,最后输出是2x2,利用双线性插值来估计这些蓝点(虚拟坐标点,又称双线性插值的网格点)处所对应的像素值,最后得到相应的输出。双线性插值本质上就是在两个方向上做线性插值。
(2).FCN on ROI。
生成mask时,在每个ROI里面进行FCN操作。
技术图片
(3).Sigmoid Loss。
对于预测的二值mask输出,我们对每个像素点应用sigmoid函数,整体Loss定义为平均二值交叉损失熵。引入预测K个输出的机制,允许每个类都生成独立的掩膜,避免类间竞争。这样做解耦了掩膜和种类预测。不像FCN的做法,在每个像素点上应用softmax函数,整体采用的多任务交叉熵,这样会导致类间竞争,最终导致分割效果差。
技术图片
四. 代码实现
https://github.com/mrgloom/awesome-semantic-segmentation 这里面非常全,语义分割和实例分割典型网络的实现都有,包括Keras、Pytorch和Tensorflow等主流架构。
五. reference
https://blog.csdn.net/github_37973614/article/details/84559861
https://zhuanlan.zhihu.com/p/44958351
https://blog.csdn.net/zhuzemin45/article/details/79709874
https://www.jianshu.com/p/755b001bfe38
https://www.cnblogs.com/hellcat/p/9749538.html#_label0
https://www.jianshu.com/p/9176bb2a8b86
 
本文借鉴了上述优秀的文章,部分图片出自相应段落,感谢作者!

以上是关于Python实例分割 YOLOv5 segment使用教程(完善中)的主要内容,如果未能解决你的问题,请参考以下文章

目标检测YOLOv5-7.0:加入实例分割

Yolov5实例分割Tensorrt部署实战

深度学习yolov5 tag7.0 实例分割 从0到1的体会,从模型训练,到量化完成

[软件工具][原创]将labelme数据集一键转换成yolov5和yolov7实例分割数据集工具使用教程

图像分割:Semantic/Instance/Panoramic Segmentation

弱监督实例分割 Box-supervised Instance Segmentation with Level Set Evolution 论文笔记