目录
本文是我在使用YOLOv5时,做的一些过程记录,按照步骤走应该能够跟我获得相同的结果,初次写这种类型的文章,排版之类的可能不太好看,内容也不够充分,之后混慢慢修改补充。
本文内容包含代码的直接使用方式,与在自定义数据集上的使用方式,目前未使用过其他公开数据集进行试用。
一、直接试用方式
1、准备工作
配置conda,虚拟环境与torch,这一部分有很多教程,这里就不写了。
代码下载: 打开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 打开segment/predict.py,直接运行,会将/home/w/下载/yolov5-master/data/images中的两张图像进行分割预测(没有使用上面的训练参数),分割结果会保存在runs/predict-seg/exp。 分割结果如下: 数据集文件夹格式: 其中images文件夹下放的是原始图像,labels文件夹下放的是满足YOLOv5要求的txt标签文件。 将原始数据按照文件夹格式复制可参考: https://blog.csdn.net/a1004550653/article/details/128329796 txt文件格式: 第一个数字为类别,后面每两个数字代表一个点对于整张图像的相对位置。每一行代表图像中的一个mask。 制作自己的数据集的话,打标签是无可避免的,用labelme就足以满足基本需求。这一部分的教程也很多,也就先不写了,有机会再加。 通过labelme可以得到json格式的mask标签。按照下面的代码可以将其修改为需要的格式。 https://blog.csdn.net/a1004550653/article/details/128320398 复制coco123_seg.yaml,修改名称,例如mydataset.yaml。 修改内部参数: path为数据集目录,train为其子目录相对路径,val可以与train相同,test不用设置。names为不同的分类与名称,改成自己的就好。 weigeht是预训练模型,可以使用自己的,也可以下载官方的。 data是数据集格式,改成刚才创建的yaml名称。 hyp是数据增强,可以按照需求在文件内自行增改。 epochs为训练轮数,训练中会自动保存最好与最后一次的模型参数。 batch–size为一次训练的图片个数,配置够的话可以增加。 imgsz等为训练时的图像重置大小。 基本上只用动上面几个参数就够了,如果还有别的需求,可以看help里面的描述,自行修改。 例如下面还有个optimizer,优化器,默认sgd。 当参数带有action='store_true',那么默认不会运行,运行时需要用终端加上–xxx(例如Python train.py –nosave)。 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后,会得到一张实例分割的图像。 将预测得到的txt转换为需要的mask图像。 https://blog.csdn.net/a1004550653/article/details/128313599 在yolov5-seg中,准备的txt文件中包含的是类别与seg点位,在实际训练的过程中,seg点位会被转换为检测用的box信息,即xywh。具体的转换代码如下: 在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]。!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)、模型训练(可以跳过)
(2)、模型预测
二、制作自己的数据集
1、格式
2、labelme制作标签
3、json转txt
4、修改数据集参数
三、用YOLOv5跑自己的数据集
1、train.py参数修改
2、predict.py参数修改
3、txt2mask
四、遇到过的报错与解决方式
五、原理(部分)
1、图像标签转换
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、分割原理
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
文章出处登录后可见!