From 417c2adea07697bdafc0d99693b98f4e8e1a7c98 Mon Sep 17 00:00:00 2001 From: Chilicyy Date: Mon, 28 Aug 2023 14:52:58 +0800 Subject: [PATCH] commit yolov6_seg code --- README.md | 2 +- configs/base/README.md | 26 - configs/base/README_cn.md | 25 - configs/base/yolov6l_base_finetune.py | 63 -- configs/base/yolov6m_base_finetune.py | 67 -- configs/base/yolov6n_base.py | 66 -- configs/base/yolov6n_base_finetune.py | 66 -- configs/base/yolov6s_base.py | 68 -- configs/base/yolov6s_base_finetune.py | 68 -- configs/experiment/eval_640_repro.py | 79 -- .../experiment/yolov6n_with_eval_params.py | 76 -- configs/experiment/yolov6s_csp_scaled.py | 57 -- configs/experiment/yolov6t.py | 55 -- configs/experiment/yolov6t_csp_scaled.py | 57 -- configs/experiment/yolov6t_finetune.py | 55 -- configs/mbla/README.md | 28 - configs/mbla/README_cn.md | 26 - configs/mbla/yolov6l_mbla_finetune.py | 70 -- configs/mbla/yolov6m_mbla.py | 70 -- configs/mbla/yolov6m_mbla_finetune.py | 70 -- configs/mbla/yolov6s_mbla.py | 70 -- configs/mbla/yolov6s_mbla_finetune.py | 70 -- configs/mbla/yolov6x_mbla.py | 70 -- configs/mbla/yolov6x_mbla_finetune.py | 70 -- configs/qarepvgg/README.md | 26 - configs/repopt/yolov6_tiny_hs.py | 59 -- configs/repopt/yolov6_tiny_opt.py | 59 -- configs/repopt/yolov6_tiny_opt_qat.py | 83 -- configs/repopt/yolov6n_hs.py | 59 -- configs/repopt/yolov6n_opt.py | 59 -- configs/repopt/yolov6n_opt_qat.py | 82 -- configs/repopt/yolov6s_hs.py | 59 -- configs/repopt/yolov6s_opt.py | 60 -- configs/repopt/yolov6s_opt_qat.py | 113 --- configs/{yolov6l.py => solo/yolov6l_solo.py} | 8 +- .../yolov6m_qa.py => solo/yolov6m_solo.py} | 10 +- configs/{yolov6n.py => solo/yolov6n_solo.py} | 8 +- .../yolov6s_qa.py => solo/yolov6s_solo.py} | 8 +- .../yolov6m_base.py => solo/yolov6x_solo.py} | 19 +- configs/yolov6_lite/README.md | 22 - configs/yolov6_lite/README_cn.md | 23 - configs/yolov6_lite/yolov6_lite_l.py | 54 -- configs/yolov6_lite/yolov6_lite_l_finetune.py | 54 -- configs/yolov6_lite/yolov6_lite_m.py | 54 -- configs/yolov6_lite/yolov6_lite_m_finetune.py | 54 -- configs/yolov6_lite/yolov6_lite_s.py | 54 -- configs/yolov6_lite/yolov6_lite_s_finetune.py | 54 -- configs/yolov6l6.py | 62 -- configs/yolov6l6_finetune.py | 62 -- configs/yolov6l_finetune.py | 68 -- .../{base/yolov6l_base.py => yolov6l_seg.py} | 13 +- configs/yolov6m6.py | 61 -- configs/yolov6m6_finetune.py | 61 -- configs/yolov6m_finetune.py | 66 -- configs/{yolov6m.py => yolov6m_seg.py} | 8 +- configs/yolov6n6.py | 56 -- configs/yolov6n6_finetune.py | 56 -- configs/yolov6n_finetune.py | 65 -- .../yolov6n_qa.py => yolov6n_seg.py} | 9 +- configs/yolov6s6.py | 56 -- configs/yolov6s6_finetune.py | 56 -- configs/yolov6s_finetune.py | 65 -- configs/{yolov6s.py => yolov6s_seg.py} | 8 +- .../{mbla/yolov6l_mbla.py => yolov6x_seg.py} | 22 +- data/coco.yaml | 10 +- data/images/000000056350.jpg | Bin 0 -> 139726 bytes ...ress_Conference_Press_Conference_9_946.jpg | Bin 0 -> 378790 bytes deploy/ONNX/README.md | 9 - deploy/ONNX/export_onnx.py | 2 +- tools/eval.py | 22 +- tools/infer.py | 14 +- tools/train.py | 8 +- yolov6/assigners/anchor_generator.py | 12 +- yolov6/assigners/atss_assigner.py | 18 +- yolov6/assigners/atss_assigner_seg.py | 166 ++++ yolov6/assigners/tal_assigner.py | 25 +- yolov6/assigners/tal_assigner_seg.py | 185 ++++ yolov6/assigners/tal_assigner_seg2.py | 183 ++++ yolov6/core/engine.py | 61 +- yolov6/core/evaler.py | 262 +++++- yolov6/core/inferer.py | 281 +++++- yolov6/data/data_augment.py | 25 +- yolov6/data/data_load.py | 2 +- yolov6/data/seg_data_augment.py | 298 ++++++ yolov6/data/seg_datasets.py | 859 ++++++++++++++++++ yolov6/models/efficientrep.py | 21 +- yolov6/models/effidehead_seg.py | 452 +++++++++ yolov6/models/heads/effidehead_fuseab_seg.py | 551 +++++++++++ .../heads/effidehead_fuseab_seg_solo.py | 540 +++++++++++ yolov6/models/losses/loss.py | 11 +- yolov6/models/losses/seg_loss.py | 532 +++++++++++ yolov6/models/losses/seg_loss_solo_main.py | 583 ++++++++++++ yolov6/models/reppan.py | 62 +- yolov6/models/yolo.py | 21 +- yolov6/utils/general.py | 12 - yolov6/utils/metrics.py | 390 +++++++- yolov6/utils/nms.py | 161 ++++ yolov6/utils/test1.py | 23 + yolov6/utils/test2.py | 37 + 99 files changed, 5694 insertions(+), 3393 deletions(-) delete mode 100644 configs/base/README.md delete mode 100644 configs/base/README_cn.md delete mode 100644 configs/base/yolov6l_base_finetune.py delete mode 100644 configs/base/yolov6m_base_finetune.py delete mode 100644 configs/base/yolov6n_base.py delete mode 100644 configs/base/yolov6n_base_finetune.py delete mode 100644 configs/base/yolov6s_base.py delete mode 100644 configs/base/yolov6s_base_finetune.py delete mode 100644 configs/experiment/eval_640_repro.py delete mode 100644 configs/experiment/yolov6n_with_eval_params.py delete mode 100644 configs/experiment/yolov6s_csp_scaled.py delete mode 100644 configs/experiment/yolov6t.py delete mode 100644 configs/experiment/yolov6t_csp_scaled.py delete mode 100644 configs/experiment/yolov6t_finetune.py delete mode 100644 configs/mbla/README.md delete mode 100644 configs/mbla/README_cn.md delete mode 100644 configs/mbla/yolov6l_mbla_finetune.py delete mode 100644 configs/mbla/yolov6m_mbla.py delete mode 100644 configs/mbla/yolov6m_mbla_finetune.py delete mode 100644 configs/mbla/yolov6s_mbla.py delete mode 100644 configs/mbla/yolov6s_mbla_finetune.py delete mode 100644 configs/mbla/yolov6x_mbla.py delete mode 100644 configs/mbla/yolov6x_mbla_finetune.py delete mode 100644 configs/qarepvgg/README.md delete mode 100644 configs/repopt/yolov6_tiny_hs.py delete mode 100644 configs/repopt/yolov6_tiny_opt.py delete mode 100644 configs/repopt/yolov6_tiny_opt_qat.py delete mode 100644 configs/repopt/yolov6n_hs.py delete mode 100644 configs/repopt/yolov6n_opt.py delete mode 100644 configs/repopt/yolov6n_opt_qat.py delete mode 100644 configs/repopt/yolov6s_hs.py delete mode 100644 configs/repopt/yolov6s_opt.py delete mode 100644 configs/repopt/yolov6s_opt_qat.py rename configs/{yolov6l.py => solo/yolov6l_solo.py} (92%) rename configs/{qarepvgg/yolov6m_qa.py => solo/yolov6m_solo.py} (92%) rename configs/{yolov6n.py => solo/yolov6n_solo.py} (92%) rename configs/{qarepvgg/yolov6s_qa.py => solo/yolov6s_solo.py} (94%) rename configs/{base/yolov6m_base.py => solo/yolov6x_solo.py} (81%) delete mode 100644 configs/yolov6_lite/README.md delete mode 100644 configs/yolov6_lite/README_cn.md delete mode 100644 configs/yolov6_lite/yolov6_lite_l.py delete mode 100644 configs/yolov6_lite/yolov6_lite_l_finetune.py delete mode 100644 configs/yolov6_lite/yolov6_lite_m.py delete mode 100644 configs/yolov6_lite/yolov6_lite_m_finetune.py delete mode 100644 configs/yolov6_lite/yolov6_lite_s.py delete mode 100644 configs/yolov6_lite/yolov6_lite_s_finetune.py delete mode 100644 configs/yolov6l6.py delete mode 100644 configs/yolov6l6_finetune.py delete mode 100644 configs/yolov6l_finetune.py rename configs/{base/yolov6l_base.py => yolov6l_seg.py} (85%) delete mode 100644 configs/yolov6m6.py delete mode 100644 configs/yolov6m6_finetune.py delete mode 100644 configs/yolov6m_finetune.py rename configs/{yolov6m.py => yolov6m_seg.py} (92%) delete mode 100644 configs/yolov6n6.py delete mode 100644 configs/yolov6n6_finetune.py delete mode 100644 configs/yolov6n_finetune.py rename configs/{qarepvgg/yolov6n_qa.py => yolov6n_seg.py} (92%) delete mode 100644 configs/yolov6s6.py delete mode 100644 configs/yolov6s6_finetune.py delete mode 100644 configs/yolov6s_finetune.py rename configs/{yolov6s.py => yolov6s_seg.py} (92%) rename configs/{mbla/yolov6l_mbla.py => yolov6x_seg.py} (79%) create mode 100644 data/images/000000056350.jpg create mode 100644 data/images/9_Press_Conference_Press_Conference_9_946.jpg create mode 100644 yolov6/assigners/atss_assigner_seg.py create mode 100644 yolov6/assigners/tal_assigner_seg.py create mode 100644 yolov6/assigners/tal_assigner_seg2.py create mode 100644 yolov6/data/seg_data_augment.py create mode 100644 yolov6/data/seg_datasets.py create mode 100644 yolov6/models/effidehead_seg.py create mode 100644 yolov6/models/heads/effidehead_fuseab_seg.py create mode 100644 yolov6/models/heads/effidehead_fuseab_seg_solo.py create mode 100644 yolov6/models/losses/seg_loss.py create mode 100644 yolov6/models/losses/seg_loss_solo_main.py create mode 100644 yolov6/utils/test1.py create mode 100644 yolov6/utils/test2.py diff --git a/README.md b/README.md index 92d8ee93..47d1deab 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ English | [简体中文](README_cn.md) Open In Kaggle
- +cp ## YOLOv6 Implementation of paper: diff --git a/configs/base/README.md b/configs/base/README.md deleted file mode 100644 index 77ef5a4e..00000000 --- a/configs/base/README.md +++ /dev/null @@ -1,26 +0,0 @@ -## YOLOv6 base model - -English | [简体中文](./README_cn.md) - -### Features - -- Use only regular convolution and Relu activation functions. - -- Apply CSP (1/2 channel dim) blocks in the network structure, except for Nano base model. - -Advantage: -- Adopt a unified network structure and configuration, and the accuracy loss of the PTQ 8-bit quantization model is negligible. -- Suitable for users who are just getting started or who need to apply, optimize and deploy an 8-bit quantization model quickly and frequently. - - -### Performance - -| Model | Size | mAPval
0.5:0.95 | SpeedT4
TRT FP16 b1
(FPS) | SpeedT4
TRT FP16 b32
(FPS) | SpeedT4
TRT INT8 b1
(FPS) | SpeedT4
TRT INT8 b32
(FPS) | Params
(M) | FLOPs
(G) | -| :--------------------------------------------------------------------------------------------- | --- | ----------------- | ----- | ---- | ---- | ---- | ----- | ------ | -| [**YOLOv6-N-base**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6n_base.pt) | 640 | 36.6distill | 727 | 1302 | 814 | 1805 | 4.65 | 11.46 | -| [**YOLOv6-S-base**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6s_base.pt) | 640 | 45.3distill | 346 | 525 | 487 | 908 | 13.14 | 30.6 | -| [**YOLOv6-M-base**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6m_base.pt) | 640 | 49.4distill | 179 | 245 | 284 | 439 | 28.33 | 72.30 | -| [**YOLOv6-L-base**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6l_base.pt) | 640 | 51.1distill | 116 | 157 | 196 | 288 | 59.61 | 150.89 | - -- Speed is tested with TensorRT 8.2.4.2 on T4. -- The processes of model training, evaluation, and inference are the same as the original ones. For details, please refer to [this README](https://github.com/meituan/YOLOv6#quick-start). diff --git a/configs/base/README_cn.md b/configs/base/README_cn.md deleted file mode 100644 index b6b01d14..00000000 --- a/configs/base/README_cn.md +++ /dev/null @@ -1,25 +0,0 @@ -## YOLOv6 基础版模型 - -简体中文 | [English](./README.md) - -### 模型特点 - -- 仅使用常规卷积和Relu激活函数 - -- 网络结构均采用CSP (1/2通道) block,Nano网络除外。 - -优势: -- 采用统一的网络结构和配置,且 PTQ 8位量化模型精度损失较小,适合刚入门或有快速迭代部署8位量化模型需求的用户。 - - -### 模型指标 - -| 模型 | 尺寸 | mAPval
0.5:0.95 | 速度T4
TRT FP16 b1
(FPS) | 速度T4
TRT FP16 b32
(FPS) | 速度T4
TRT INT8 b1
(FPS) | 速度T4
TRT INT8 b32
(FPS) | Params
(M) | FLOPs
(G) | -| :--------------------------------------------------------------------------------------------- | --- | ----------------- | ----- | ---- | ---- | ---- | ----- | ------ | -| [**YOLOv6-N-base**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6n_base.pt) | 640 | 36.6distill | 727 | 1302 | 814 | 1805 | 4.65 | 11.46 | -| [**YOLOv6-S-base**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6s_base.pt) | 640 | 45.3distill | 346 | 525 | 487 | 908 | 13.14 | 30.6 | -| [**YOLOv6-M-base**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6m_base.pt) | 640 | 49.4distill | 179 | 245 | 284 | 439 | 28.33 | 72.30 | -| [**YOLOv6-L-base**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6l_base.pt) | 640 | 51.1distill | 116 | 157 | 196 | 288 | 59.61 | 150.89 | - -- 速度是在 T4 上测试的,TensorRT 版本为 8.4.2.4; -- 模型训练、评估、推理流程与原来保持一致,具体可参考 [首页 README 文档](https://github.com/meituan/YOLOv6/blob/main/README_cn.md#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)。 diff --git a/configs/base/yolov6l_base_finetune.py b/configs/base/yolov6l_base_finetune.py deleted file mode 100644 index 7e8dc062..00000000 --- a/configs/base/yolov6l_base_finetune.py +++ /dev/null @@ -1,63 +0,0 @@ -# YOLOv6 large base model -model = dict( - type='YOLOv6l_base', - depth_multiple=1.0, - width_multiple=1.0, - pretrained=None, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 2.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) -training_mode = "conv_relu" diff --git a/configs/base/yolov6m_base_finetune.py b/configs/base/yolov6m_base_finetune.py deleted file mode 100644 index af5449ec..00000000 --- a/configs/base/yolov6m_base_finetune.py +++ /dev/null @@ -1,67 +0,0 @@ -# YOLOv6m medium/large base model -model = dict( - type='YOLOv6m_base', - pretrained=None, - depth_multiple=0.80, - width_multiple=0.75, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 0.8, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) -training_mode = "conv_relu" diff --git a/configs/base/yolov6n_base.py b/configs/base/yolov6n_base.py deleted file mode 100644 index 8340ca60..00000000 --- a/configs/base/yolov6n_base.py +++ /dev/null @@ -1,66 +0,0 @@ -# YOLOv6s nano base model -model = dict( - type='YOLOv6n_base', - pretrained=None, - depth_multiple=0.33, - width_multiple=0.25, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - fuse_P2=True, - cspsppf=True, - ), - neck=dict( - type='RepBiFPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, # set to True if you want to further train with distillation - reg_max=16, # set to 16 if you want to further train with distillation - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) -training_mode = "conv_relu" diff --git a/configs/base/yolov6n_base_finetune.py b/configs/base/yolov6n_base_finetune.py deleted file mode 100644 index 593c3def..00000000 --- a/configs/base/yolov6n_base_finetune.py +++ /dev/null @@ -1,66 +0,0 @@ -# YOLOv6s nanao base model -model = dict( - type='YOLOv6n_base', - pretrained=None, - depth_multiple=0.33, - width_multiple=0.25, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - fuse_P2=True, - cspsppf=True, - ), - neck=dict( - type='RepBiFPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=False, # set to True if you want to further train with distillation - reg_max=0, # set to 16 if you want to further train with distillation - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) -training_mode = "conv_relu" diff --git a/configs/base/yolov6s_base.py b/configs/base/yolov6s_base.py deleted file mode 100644 index 4e28c178..00000000 --- a/configs/base/yolov6s_base.py +++ /dev/null @@ -1,68 +0,0 @@ -# YOLOv6s small base model -model = dict( - type='YOLOv6s_base', - pretrained=None, - depth_multiple=0.70, - width_multiple=0.50, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - cspsppf=True, - ), - neck=dict( - type='CSPRepBiFPANNeck',#CSPRepPANNeck - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, # set to True if you want to further train with distillation - reg_max=16, # set to 16 if you want to further train with distillation - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) -training_mode = "conv_relu" diff --git a/configs/base/yolov6s_base_finetune.py b/configs/base/yolov6s_base_finetune.py deleted file mode 100644 index eb4d2159..00000000 --- a/configs/base/yolov6s_base_finetune.py +++ /dev/null @@ -1,68 +0,0 @@ -# YOLOv6s small base model -model = dict( - type='YOLOv6s_base', - pretrained=None, - depth_multiple=0.70, - width_multiple=0.50, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - cspsppf=True, - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=False, # set to True if you want to further train with distillation - reg_max=0, # set to 16 if you want to further train with distillation - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) -training_mode = "conv_relu" diff --git a/configs/experiment/eval_640_repro.py b/configs/experiment/eval_640_repro.py deleted file mode 100644 index 1f6a6217..00000000 --- a/configs/experiment/eval_640_repro.py +++ /dev/null @@ -1,79 +0,0 @@ -# eval param for different scale - -eval_params = dict( - default = dict( - img_size=640, - shrink_size=2, - infer_on_rect=False, - ), - yolov6n = dict( - img_size=640, - shrink_size=4, - infer_on_rect=False, - ), - yolov6t = dict( - img_size=640, - shrink_size=6, - infer_on_rect=False, - ), - yolov6s = dict( - img_size=640, - shrink_size=6, - infer_on_rect=False, - ), - yolov6m = dict( - img_size=640, - shrink_size=4, - infer_on_rect=False, - ), - yolov6l = dict( - img_size=640, - shrink_size=4, - infer_on_rect=False, - ), - yolov6l_relu = dict( - img_size=640, - shrink_size=2, - infer_on_rect=False, - ), - yolov6n6 = dict( - img_size=1280, - shrink_size=17, - infer_on_rect=False, - ), - yolov6s6 = dict( - img_size=1280, - shrink_size=8, - infer_on_rect=False, - ), - yolov6m6 = dict( - img_size=1280, - shrink_size=64, - infer_on_rect=False, - ), - yolov6l6 = dict( - img_size=1280, - shrink_size=41, - infer_on_rect=False, - ), - yolov6s_mbla = dict( - img_size=640, - shrink_size=7, - infer_on_rect=False, - ), - yolov6m_mbla = dict( - img_size=640, - shrink_size=7, - infer_on_rect=False, - ), - yolov6l_mbla = dict( - img_size=640, - shrink_size=7, - infer_on_rect=False, - ), - yolov6x_mbla = dict( - img_size=640, - shrink_size=3, - infer_on_rect=False, - ) -) diff --git a/configs/experiment/yolov6n_with_eval_params.py b/configs/experiment/yolov6n_with_eval_params.py deleted file mode 100644 index e7366b33..00000000 --- a/configs/experiment/yolov6n_with_eval_params.py +++ /dev/null @@ -1,76 +0,0 @@ -# YOLOv6n model with eval param(when traing) -model = dict( - type='YOLOv6n', - pretrained=None, - depth_multiple=0.33, - width_multiple=0.25, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.02, #0.01 # 0.02 - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) - -# Eval params when eval model. -# If eval_params item is list, eg conf_thres=[0.03, 0.03], -# first will be used in train.py and second will be used in eval.py. -eval_params = dict( - batch_size=None, #None mean will be the same as batch on one device * 2 - img_size=None, #None mean will be the same as train image size - conf_thres=0.03, - iou_thres=0.65, - - #pading and scale coord - shrink_size=None, # None mean will not shrink the image. - infer_on_rect=True, - - #metric - verbose=False, - do_coco_metric=True, - do_pr_metric=False, - plot_curve=False, - plot_confusion_matrix=False -) diff --git a/configs/experiment/yolov6s_csp_scaled.py b/configs/experiment/yolov6s_csp_scaled.py deleted file mode 100644 index ba28843a..00000000 --- a/configs/experiment/yolov6s_csp_scaled.py +++ /dev/null @@ -1,57 +0,0 @@ -# YOLOv6m model -model = dict( - type='YOLOv6s_csp', - pretrained=None, - depth_multiple=0.70, - width_multiple=0.50, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - ), - neck=dict( - type='CSPRepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - iou_type='giou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver=dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.9, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.1, -) diff --git a/configs/experiment/yolov6t.py b/configs/experiment/yolov6t.py deleted file mode 100644 index afacd436..00000000 --- a/configs/experiment/yolov6t.py +++ /dev/null @@ -1,55 +0,0 @@ -# YOLOv6t model -model = dict( - type='YOLOv6t', - pretrained=None, - depth_multiple=0.33, - width_multiple=0.375, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) diff --git a/configs/experiment/yolov6t_csp_scaled.py b/configs/experiment/yolov6t_csp_scaled.py deleted file mode 100644 index e8ba99a9..00000000 --- a/configs/experiment/yolov6t_csp_scaled.py +++ /dev/null @@ -1,57 +0,0 @@ -# YOLOv6n model -model = dict( - type='YOLOv6n_csp', - pretrained=None, - depth_multiple=0.60, - width_multiple=0.50, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - ), - neck=dict( - type='CSPRepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - iou_type='giou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver=dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.9, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.1, -) diff --git a/configs/experiment/yolov6t_finetune.py b/configs/experiment/yolov6t_finetune.py deleted file mode 100644 index 8be47416..00000000 --- a/configs/experiment/yolov6t_finetune.py +++ /dev/null @@ -1,55 +0,0 @@ -# YOLOv6t model -model = dict( - type='YOLOv6t', - pretrained='weights/yolov6t.pt', - depth_multiple=0.33, - width_multiple=0.375, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) diff --git a/configs/mbla/README.md b/configs/mbla/README.md deleted file mode 100644 index d163124d..00000000 --- a/configs/mbla/README.md +++ /dev/null @@ -1,28 +0,0 @@ -## YOLOv6 mbla model - -English | [简体中文](./README_cn.md) - -### Features - -- Apply MBLABlock(Multi Branch Layer Aggregation Block) blocks in the network structure. - -Advantage: -- Adopt a unified network structure and configuration. - -- Better performance for Small model comparing to yolov6 3.0 release. - -- Better performance comparing to yolov6 3.0 base. - - - -### Performance - -| Model | Size | mAPval
0.5:0.95 | SpeedT4
trt fp16 b1
(fps) | SpeedT4
trt fp16 b32
(fps) | Params
(M) | FLOPs
(G) | -| :----------------------------------------------------------- | -------- | :----------------------- | -------------------------------------- | --------------------------------------- | -------------------- | ------------------- | -| [**YOLOv6-S-mbla**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6s_mbla.pt) | 640 | 47.0distill | 300 | 424 | 11.6 | 29.8 | -| [**YOLOv6-M-mbla**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6m_mbla.pt) | 640 | 50.3distill | 168 | 216 | 26.1 | 66.7 | -| [**YOLOv6-L-mbla**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6l_base.pt) | 640 | 52.0distill | 129 | 154 | 46.3 | 118.2 | -| [**YOLOv6-X-base**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6x_base.pt) | 640 | 53.5distill | 78 | 94 | 78.8 | 199.0 | - -- Speed is tested with TensorRT 8.4.2.4 on T4. -- The processes of model training, evaluation, and inference are the same as the original ones. For details, please refer to [this README](https://github.com/meituan/YOLOv6#quick-start). diff --git a/configs/mbla/README_cn.md b/configs/mbla/README_cn.md deleted file mode 100644 index ad399fe0..00000000 --- a/configs/mbla/README_cn.md +++ /dev/null @@ -1,26 +0,0 @@ -## YOLOv6 MBLA版模型 - -简体中文 | [English](./README.md) - -### 模型特点 - -- 网络主体结构均采用MBLABlock(Multi Branch Layer Aggregation Block) - -优势: -- 采用统一的网络结构和配置 - -- 相比3.0版本在s尺度效果提升,相比3.0base版本各尺度效果提升 - - - -### 模型指标 - -| 模型 | 输入尺寸 | mAPval
0.5:0.95 | 速度T4
trt fp16 b1
(fps) | 速度T4
trt fp16 b32
(fps) | Params
(M) | FLOPs
(G) | -| :----------------------------------------------------------- | -------- | :----------------------- | -------------------------------------- | --------------------------------------- | -------------------- | ------------------- | -| [**YOLOv6-S-mbla**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6s_mbla.pt) | 640 | 47.0distill | 300 | 424 | 11.6 | 29.8 | -| [**YOLOv6-M-mbla**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6m_mbla.pt) | 640 | 50.3distill | 168 | 216 | 26.1 | 66.7 | -| [**YOLOv6-L-mbla**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6l_base.pt) | 640 | 52.0distill | 129 | 154 | 46.3 | 118.2 | -| [**YOLOv6-X-base**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6x_base.pt) | 640 | 53.5distill | 78 | 94 | 78.8 | 199.0 | - -- 速度是在 T4 上测试的,TensorRT 版本为 8.4.2.4; -- 模型训练、评估、推理流程与原来保持一致,具体可参考 [首页 README 文档](https://github.com/meituan/YOLOv6/blob/main/README_cn.md#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)。 diff --git a/configs/mbla/yolov6l_mbla_finetune.py b/configs/mbla/yolov6l_mbla_finetune.py deleted file mode 100644 index 6ea88967..00000000 --- a/configs/mbla/yolov6l_mbla_finetune.py +++ /dev/null @@ -1,70 +0,0 @@ -# YOLOv6l model -model = dict( - type='YOLOv6l_mbla', - pretrained=None, - depth_multiple=0.5, - width_multiple=1.0, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 4, 8, 8, 4], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - stage_block_type="MBLABlock", - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[8, 8, 8, 8], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - stage_block_type="MBLABlock", - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 2.0, - 'dfl': 1.0, - }, - ) -) - -solver=dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) - -training_mode = "conv_silu" diff --git a/configs/mbla/yolov6m_mbla.py b/configs/mbla/yolov6m_mbla.py deleted file mode 100644 index f84fc43d..00000000 --- a/configs/mbla/yolov6m_mbla.py +++ /dev/null @@ -1,70 +0,0 @@ -# YOLOv6l model -model = dict( - type='YOLOv6m_mbla', - pretrained=None, - depth_multiple=0.5, - width_multiple=0.75, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 4, 8, 8, 4], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - stage_block_type="MBLABlock", - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[8, 8, 8, 8], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - stage_block_type="MBLABlock", - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 2.0, - 'dfl': 1.0, - }, - ) -) - -solver=dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.9, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.1, -) - -training_mode = "conv_silu" diff --git a/configs/mbla/yolov6m_mbla_finetune.py b/configs/mbla/yolov6m_mbla_finetune.py deleted file mode 100644 index aa0bc816..00000000 --- a/configs/mbla/yolov6m_mbla_finetune.py +++ /dev/null @@ -1,70 +0,0 @@ -# YOLOv6l model -model = dict( - type='YOLOv6m_mbla', - pretrained=None, - depth_multiple=0.5, - width_multiple=0.75, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 4, 8, 8, 4], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - stage_block_type="MBLABlock", - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[8, 8, 8, 8], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - stage_block_type="MBLABlock", - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 2.0, - 'dfl': 1.0, - }, - ) -) - -solver=dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) - -training_mode = "conv_silu" diff --git a/configs/mbla/yolov6s_mbla.py b/configs/mbla/yolov6s_mbla.py deleted file mode 100644 index eedc76ee..00000000 --- a/configs/mbla/yolov6s_mbla.py +++ /dev/null @@ -1,70 +0,0 @@ -# YOLOv6l model -model = dict( - type='YOLOv6s_mbla', - pretrained=None, - depth_multiple=0.5, - width_multiple=0.5, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 4, 8, 8, 4], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - stage_block_type="MBLABlock", - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[8, 8, 8, 8], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - stage_block_type="MBLABlock", - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 2.0, - 'dfl': 1.0, - }, - ) -) - -solver=dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.9, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.1, -) - -training_mode = "conv_silu" diff --git a/configs/mbla/yolov6s_mbla_finetune.py b/configs/mbla/yolov6s_mbla_finetune.py deleted file mode 100644 index a9812c71..00000000 --- a/configs/mbla/yolov6s_mbla_finetune.py +++ /dev/null @@ -1,70 +0,0 @@ -# YOLOv6l model -model = dict( - type='YOLOv6s_mbla', - pretrained=None, - depth_multiple=0.5, - width_multiple=0.5, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 4, 8, 8, 4], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - stage_block_type="MBLABlock", - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[8, 8, 8, 8], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - stage_block_type="MBLABlock", - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 2.0, - 'dfl': 1.0, - }, - ) -) - -solver=dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) - -training_mode = "conv_silu" diff --git a/configs/mbla/yolov6x_mbla.py b/configs/mbla/yolov6x_mbla.py deleted file mode 100644 index b7b9703c..00000000 --- a/configs/mbla/yolov6x_mbla.py +++ /dev/null @@ -1,70 +0,0 @@ -# YOLOv6l model -model = dict( - type='YOLOv6x_mbla', - pretrained=None, - depth_multiple=1.0, - width_multiple=1.0, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 4, 8, 8, 4], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - stage_block_type="MBLABlock", - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[8, 8, 8, 8], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - stage_block_type="MBLABlock", - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 2.0, - 'dfl': 1.0, - }, - ) -) - -solver=dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.9, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.1, -) - -training_mode = "conv_silu" diff --git a/configs/mbla/yolov6x_mbla_finetune.py b/configs/mbla/yolov6x_mbla_finetune.py deleted file mode 100644 index 65c57cb2..00000000 --- a/configs/mbla/yolov6x_mbla_finetune.py +++ /dev/null @@ -1,70 +0,0 @@ -# YOLOv6l model -model = dict( - type='YOLOv6x_mbla', - pretrained=None, - depth_multiple=1.0, - width_multiple=1.0, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 4, 8, 8, 4], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - stage_block_type="MBLABlock", - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[8, 8, 8, 8], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - stage_block_type="MBLABlock", - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 2.0, - 'dfl': 1.0, - }, - ) -) - -solver=dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) - -training_mode = "conv_silu" diff --git a/configs/qarepvgg/README.md b/configs/qarepvgg/README.md deleted file mode 100644 index 81b130d2..00000000 --- a/configs/qarepvgg/README.md +++ /dev/null @@ -1,26 +0,0 @@ -## YOLOv6 base model - -English | [简体中文](./README_cn.md) - -### Features - -- This is a RepOpt-version implementation of YOLOv6 according to [QARepVGG](https://arxiv.org/abs/2212.01593). - -- The QARep version models possess slightly lower float accuracy on COCO than the RepVGG version models, but achieve highly improved quantized accuracy. - -- The INT8 accuracies listed were obtained using a simple PTQ process, as implemented in the [`onnx_to_trt.py`](../../deploy/TensorRT/onnx_to_trt.py) script. However, higher accuracies could be achieved using Quantization-Aware Training (QAT) due to the specific architecture design of the QARepVGG model. - -### Performance - -| Model | Size | Float
mAPval
0.5:0.95 | INT8
mAPval
0.5:0.95 | SpeedT4
trt fp16 b32
(fps) | SpeedT4
trt int8 b32
(fps) | Params
(M) | FLOPs
(G) | -| :----------------------------------------------------------- | -------- | :----------------------- | -------------------------------------- | --------------------------------------- | -------------------- | ------------------- | -------------------- | -| [**YOLOv6-N**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6n.pt) | 640 | 37.5 | 34.3 | 1286 | 1773 |4.7 | 11.4 | -| [**YOLOv6-N-qa**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6n_qa.pt) | 640 | 37.1 | 36.4 | 1286 | 1773 | 4.7 | 11.4 | -| [**YOLOv6-S**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6s.pt) | 640 | 45.0 | 41.3 | 513 | 1117 | 18.5 | 45.3 | -| [**YOLOv6-S-qa**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6s_qa.pt) | 640 | 44.7 | 44.0 | 513 | 1117 | 18.5 | 45.3 | -| [**YOLOv6-M**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6m.pt) | 640 | 50.0 | 48.1 | 250 | 439 | 34.9 | 85.8 | -| [**YOLOv6-M-qa**](https://github.com/meituan/YOLOv6/releases/download/0.3.0/yolov6m_qa.pt) | 640 | 49.7 | 49.4 | 250 | 439 | 34.9 | 85.8 | - -- Speed is tested with TensorRT 8.4 on T4. -- We have not conducted experiments on the YOLOv6-L model since it does not use the RepVGG architecture. -- The processes of model training, evaluation, and inference are the same as the original ones. For details, please refer to [this README](https://github.com/meituan/YOLOv6#quick-start). diff --git a/configs/repopt/yolov6_tiny_hs.py b/configs/repopt/yolov6_tiny_hs.py deleted file mode 100644 index 70a74279..00000000 --- a/configs/repopt/yolov6_tiny_hs.py +++ /dev/null @@ -1,59 +0,0 @@ -# YOLOv6t model -model = dict( - type='YOLOv6t', - pretrained=None, - depth_multiple=0.33, - width_multiple=0.375, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) - -# Choose Rep-block by the training Mode, choices=["repvgg", "hyper-search", "repopt"] -training_mode='hyper_search' diff --git a/configs/repopt/yolov6_tiny_opt.py b/configs/repopt/yolov6_tiny_opt.py deleted file mode 100644 index 95dbf317..00000000 --- a/configs/repopt/yolov6_tiny_opt.py +++ /dev/null @@ -1,59 +0,0 @@ -# YOLOv6t model -model = dict( - type='YOLOv6t', - pretrained=None, - scales='../yolov6_assert/v6t_v2_scale_last.pt', - depth_multiple=0.33, - width_multiple=0.375, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) -# Choose Rep-block by the training Mode, choices=["repvgg", "hyper-search", "repopt"] -training_mode='repopt' diff --git a/configs/repopt/yolov6_tiny_opt_qat.py b/configs/repopt/yolov6_tiny_opt_qat.py deleted file mode 100644 index 701bf4f1..00000000 --- a/configs/repopt/yolov6_tiny_opt_qat.py +++ /dev/null @@ -1,83 +0,0 @@ -# YOLOv6t model -model = dict( - type='YOLOv6t', - pretrained='./assets/v6s_t.pt', - scales='./assets/v6t_v2_scale_last.pt', - depth_multiple=0.33, - width_multiple=0.375, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='siou', - use_dfl=False, - reg_max=0, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.00001, - lrf=0.001, - momentum=0.937, - weight_decay=0.00005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) - -ptq = dict( - num_bits = 8, - calib_batches = 4, - # 'max', 'histogram' - calib_method = 'max', - # 'entropy', 'percentile', 'mse' - histogram_amax_method='entropy', - histogram_amax_percentile=99.99, - calib_output_path='./', - sensitive_layers_skip=False, - sensitive_layers_list=[], -) - -qat = dict( - calib_pt = './assets/v6s_t_calib_max.pt', - sensitive_layers_skip = False, - sensitive_layers_list=[], -) - -# Choose Rep-block by the training Mode, choices=["repvgg", "hyper-search", "repopt"] -training_mode='repopt' diff --git a/configs/repopt/yolov6n_hs.py b/configs/repopt/yolov6n_hs.py deleted file mode 100644 index 67607ba2..00000000 --- a/configs/repopt/yolov6n_hs.py +++ /dev/null @@ -1,59 +0,0 @@ -# YOLOv6n model -model = dict( - type='YOLOv6n', - pretrained=None, - depth_multiple=0.33, - width_multiple=0.25, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.02, #0.01 # 0.02 - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) - -# Choose Rep-block by the training Mode, choices=["repvgg", "hyper-search", "repopt"] -training_mode='hyper_search' diff --git a/configs/repopt/yolov6n_opt.py b/configs/repopt/yolov6n_opt.py deleted file mode 100644 index 9b3db4fb..00000000 --- a/configs/repopt/yolov6n_opt.py +++ /dev/null @@ -1,59 +0,0 @@ -# YOLOv6n model -model = dict( - type='YOLOv6n', - pretrained=None, - scales='../yolov6_assert/v6n_v2_scale_last.pt', - depth_multiple=0.33, - width_multiple=0.25, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.02, #0.01 # 0.02 - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) -# Choose Rep-block by the training Mode, choices=["repvgg", "hyper-search", "repopt"] -training_mode='repopt' diff --git a/configs/repopt/yolov6n_opt_qat.py b/configs/repopt/yolov6n_opt_qat.py deleted file mode 100644 index 4e76dfd3..00000000 --- a/configs/repopt/yolov6n_opt_qat.py +++ /dev/null @@ -1,82 +0,0 @@ -# YOLOv6n model -model = dict( - type='YOLOv6n', - pretrained='./assets/v6s_n.pt', - scales='./assets/v6n_v2_scale_last.pt', - depth_multiple=0.33, - width_multiple=0.25, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='siou', - use_dfl=False, - reg_max=0, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.00001, #0.01 # 0.02 - lrf=0.001, - momentum=0.937, - weight_decay=0.00005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) - -ptq = dict( - num_bits = 8, - calib_batches = 4, - # 'max', 'histogram' - calib_method = 'max', - # 'entropy', 'percentile', 'mse' - histogram_amax_method='entropy', - histogram_amax_percentile=99.99, - calib_output_path='./', - sensitive_layers_skip=False, - sensitive_layers_list=[], -) - -qat = dict( - calib_pt = './assets/v6s_n_calib_max.pt', - sensitive_layers_skip = False, - sensitive_layers_list=[], -) -# Choose Rep-block by the training Mode, choices=["repvgg", "hyper-search", "repopt"] -training_mode='repopt' diff --git a/configs/repopt/yolov6s_hs.py b/configs/repopt/yolov6s_hs.py deleted file mode 100644 index 60c7286a..00000000 --- a/configs/repopt/yolov6s_hs.py +++ /dev/null @@ -1,59 +0,0 @@ -# YOLOv6s model -model = dict( - type='YOLOv6s', - pretrained=None, - depth_multiple=0.33, - width_multiple=0.50, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=False, - reg_max=0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) - -# Choose Rep-block by the training Mode, choices=["repvgg", "hyper-search", "repopt"] -training_mode='hyper_search' diff --git a/configs/repopt/yolov6s_opt.py b/configs/repopt/yolov6s_opt.py deleted file mode 100644 index 2676eb4f..00000000 --- a/configs/repopt/yolov6s_opt.py +++ /dev/null @@ -1,60 +0,0 @@ -# YOLOv6s model -model = dict( - type='YOLOv6s', - pretrained=None, - scales='../yolov6_assert/v6s_v2_scale.pt', - depth_multiple=0.33, - width_multiple=0.50, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=False, - reg_max=0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) - -# Choose Rep-block by the training Mode, choices=["repvgg", "hyper-search", "repopt"] -training_mode='repopt' diff --git a/configs/repopt/yolov6s_opt_qat.py b/configs/repopt/yolov6s_opt_qat.py deleted file mode 100644 index a41ea085..00000000 --- a/configs/repopt/yolov6s_opt_qat.py +++ /dev/null @@ -1,113 +0,0 @@ -# YOLOv6s model -model = dict( - type='YOLOv6s', - pretrained='./assets/yolov6s_v2_reopt_43.1.pt', - scales='./assets/yolov6s_v2_scale.pt', - depth_multiple=0.33, - width_multiple=0.50, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - ), - neck=dict( - type='RepPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=1, - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type = 'giou', - use_dfl = False, - reg_max = 0, # if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.00001, - lrf=0.001, - momentum=0.937, - weight_decay=0.00005, - warmup_epochs=3, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) - -ptq = dict( - num_bits = 8, - calib_batches = 4, - # 'max', 'histogram' - calib_method = 'histogram', - # 'entropy', 'percentile', 'mse' - histogram_amax_method='entropy', - histogram_amax_percentile=99.99, - calib_output_path='./', - sensitive_layers_skip=False, - sensitive_layers_list=['detect.stems.0.conv', - 'detect.stems.1.conv', - 'detect.stems.2.conv', - 'detect.cls_convs.0.conv', - 'detect.cls_convs.1.conv', - 'detect.cls_convs.2.conv', - 'detect.reg_convs.0.conv', - 'detect.reg_convs.1.conv', - 'detect.reg_convs.2.conv', - 'detect.cls_preds.0', - 'detect.cls_preds.1', - 'detect.cls_preds.2', - 'detect.reg_preds.0', - 'detect.reg_preds.1', - 'detect.reg_preds.2', - ], -) - -qat = dict( - calib_pt = './assets/yolov6s_v2_reopt_43.1_calib_histogram.pt', - sensitive_layers_skip = False, - sensitive_layers_list=['detect.stems.0.conv', - 'detect.stems.1.conv', - 'detect.stems.2.conv', - 'detect.cls_convs.0.conv', - 'detect.cls_convs.1.conv', - 'detect.cls_convs.2.conv', - 'detect.reg_convs.0.conv', - 'detect.reg_convs.1.conv', - 'detect.reg_convs.2.conv', - 'detect.cls_preds.0', - 'detect.cls_preds.1', - 'detect.cls_preds.2', - 'detect.reg_preds.0', - 'detect.reg_preds.1', - 'detect.reg_preds.2', - ], -) - -# Choose Rep-block by the training Mode, choices=["repvgg", "hyper-search", "repopt"] -training_mode='repopt' diff --git a/configs/yolov6l.py b/configs/solo/yolov6l_solo.py similarity index 92% rename from configs/yolov6l.py rename to configs/solo/yolov6l_solo.py index bfa6728b..caabc1f4 100644 --- a/configs/yolov6l.py +++ b/configs/solo/yolov6l_solo.py @@ -1,4 +1,4 @@ -# YOLOv6l model +# YOLOv6l-seg model model = dict( type='YOLOv6l', pretrained=None, @@ -22,6 +22,10 @@ in_channels=[128, 256, 512], num_layers=3, begin_indices=24, + npr=256, + nm=64, + isseg=True, + issolo=True, anchors=3, anchors_init=[[10,13, 19,19, 33,23], [30,61, 59,59, 59,119], @@ -45,7 +49,7 @@ lr0=0.01, lrf=0.01, momentum=0.937, - weight_decay=0.0005, + weight_decay=0.001, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1 diff --git a/configs/qarepvgg/yolov6m_qa.py b/configs/solo/yolov6m_solo.py similarity index 92% rename from configs/qarepvgg/yolov6m_qa.py rename to configs/solo/yolov6m_solo.py index c0690f15..84e73c0f 100644 --- a/configs/qarepvgg/yolov6m_qa.py +++ b/configs/solo/yolov6m_solo.py @@ -1,4 +1,4 @@ -# YOLOv6m model +# YOLOv6m-seg model model = dict( type='YOLOv6m', pretrained=None, @@ -22,6 +22,10 @@ in_channels=[128, 256, 512], num_layers=3, begin_indices=24, + npr=256, + nm=64, + isseg=True, + issolo=True, anchors=3, anchors_init=[[10,13, 19,19, 33,23], [30,61, 59,59, 59,119], @@ -45,7 +49,7 @@ lr0=0.01, lrf=0.01, momentum=0.937, - weight_decay=0.0005, + weight_decay=0.001, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1 @@ -64,5 +68,3 @@ mosaic=1.0, mixup=0.1, ) - -training_mode='qarepvggv2' diff --git a/configs/yolov6n.py b/configs/solo/yolov6n_solo.py similarity index 92% rename from configs/yolov6n.py rename to configs/solo/yolov6n_solo.py index 74f9386d..6392ceb4 100644 --- a/configs/yolov6n.py +++ b/configs/solo/yolov6n_solo.py @@ -1,4 +1,4 @@ -# YOLOv6n model +# YOLOv6n-seg model model = dict( type='YOLOv6n', pretrained=None, @@ -21,6 +21,10 @@ in_channels=[128, 256, 512], num_layers=3, begin_indices=24, + npr=256, + nm=64, + isseg=True, + issolo=True, anchors=3, anchors_init=[[10,13, 19,19, 33,23], [30,61, 59,59, 59,119], @@ -44,7 +48,7 @@ lr0=0.02, lrf=0.01, momentum=0.937, - weight_decay=0.0005, + weight_decay=0.001, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1 diff --git a/configs/qarepvgg/yolov6s_qa.py b/configs/solo/yolov6s_solo.py similarity index 94% rename from configs/qarepvgg/yolov6s_qa.py rename to configs/solo/yolov6s_solo.py index 3051679a..c2499ba3 100644 --- a/configs/qarepvgg/yolov6s_qa.py +++ b/configs/solo/yolov6s_solo.py @@ -1,4 +1,4 @@ -# YOLOv6s model +# YOLOv6s-seg model model = dict( type='YOLOv6s', pretrained=None, @@ -21,6 +21,10 @@ in_channels=[128, 256, 512], num_layers=3, begin_indices=24, + npr=256, + nm=64, + isseg=True, + issolo=True, anchors=3, anchors_init=[[10,13, 19,19, 33,23], [30,61, 59,59, 59,119], @@ -63,5 +67,3 @@ mosaic=1.0, mixup=0.0, ) - -training_mode='qarepvggv2' diff --git a/configs/base/yolov6m_base.py b/configs/solo/yolov6x_solo.py similarity index 81% rename from configs/base/yolov6m_base.py rename to configs/solo/yolov6x_solo.py index 5670f096..57a175ab 100644 --- a/configs/base/yolov6m_base.py +++ b/configs/solo/yolov6x_solo.py @@ -1,9 +1,9 @@ -# YOLOv6m medium/large base model +# YOLOv6x-seg model model = dict( - type='YOLOv6m_base', + type='YOLOv6x', pretrained=None, - depth_multiple=0.80, - width_multiple=0.75, + depth_multiple=1.33, + width_multiple=1.25, backbone=dict( type='CSPBepBackbone', num_repeats=[1, 6, 12, 18, 6], @@ -22,6 +22,10 @@ in_channels=[128, 256, 512], num_layers=3, begin_indices=24, + npr=256, + nm=64, + isseg=True, + issolo=True, anchors=3, anchors_init=[[10,13, 19,19, 33,23], [30,61, 59,59, 59,119], @@ -33,7 +37,7 @@ use_dfl=True, reg_max=16, #if use_dfl is False, please set reg_max to 0 distill_weight={ - 'class': 0.8, + 'class': 2.0, 'dfl': 1.0, }, ) @@ -45,7 +49,7 @@ lr0=0.01, lrf=0.01, momentum=0.937, - weight_decay=0.0005, + weight_decay=0.0015, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1 @@ -64,4 +68,5 @@ mosaic=1.0, mixup=0.1, ) -training_mode = "conv_relu" +training_mode = "conv_silu" +# use normal conv to speed up training and further improve accuracy. diff --git a/configs/yolov6_lite/README.md b/configs/yolov6_lite/README.md deleted file mode 100644 index 170d12d9..00000000 --- a/configs/yolov6_lite/README.md +++ /dev/null @@ -1,22 +0,0 @@ -## YOLOv6Lite model - -English | [简体中文](./README_cn.md) - -## Mobile Benchmark -| Model | Size | mAPval
0.5:0.95 | sm8350
(ms) | mt6853
(ms) | sdm660
(ms) |Params
(M) | FLOPs
(G) | -| :----------------------------------------------------------- | ---- | -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | -| [**YOLOv6Lite-S**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6lite_s.pt) | 320*320 | 22.4 | 7.99 | 11.99 | 41.86 | 0.55 | 0.56 | -| [**YOLOv6Lite-M**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6lite_m.pt) | 320*320 | 25.1 | 9.08 | 13.27 | 47.95 | 0.79 | 0.67 | -| [**YOLOv6Lite-L**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6lite_l.pt) | 320*320 | 28.0 | 11.37 | 16.20 | 61.40 | 1.09 | 0.87 | -| [**YOLOv6Lite-L**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6lite_l.pt) | 320*192 | 25.0 | 7.02 | 9.66 | 36.13 | 1.09 | 0.52 | -| [**YOLOv6Lite-L**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6lite_l.pt) | 224*128 | 18.9 | 3.63 | 4.99 | 17.76 | 1.09 | 0.24 | - -
-Table Notes - -- From the perspective of model size and input image ratio, we have built a series of models on the mobile terminal to facilitate flexible applications in different scenarios. -- All checkpoints are trained with 400 epochs without distillation. -- Results of the mAP and speed are evaluated on [COCO val2017](https://cocodataset.org/#download) dataset, and the input resolution is the Size in the table. -- Speed is tested on MNN 2.3.0 AArch64 with 2 threads by arm82 acceleration. The inference warm-up is performed 10 times, and the cycle is performed 100 times. -- Qualcomm 888(sm8350), Dimensity 720(mt6853) and Qualcomm 660(sdm660) correspond to chips with different performances at the high, middle and low end respectively, which can be used as a reference for model capabilities under different chips. -- Refer to [Test NCNN Speed](./docs/Test_NCNN_speed.md) tutorial to reproduce the NCNN speed results of YOLOv6Lite. diff --git a/configs/yolov6_lite/README_cn.md b/configs/yolov6_lite/README_cn.md deleted file mode 100644 index 23dd715e..00000000 --- a/configs/yolov6_lite/README_cn.md +++ /dev/null @@ -1,23 +0,0 @@ -## YOLOv6 轻量级模型 - -简体中文 | [English](./README.md) - -## 移动端模型指标 - -| 模型 | 输入尺寸 | mAPval
0.5:0.95 | sm8350
(ms) | mt6853
(ms) | sdm660
(ms) |Params
(M) | FLOPs
(G) | -| :----------------------------------------------------------- | ---- | -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | -| [**YOLOv6Lite-S**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6lite_s.pt) | 320*320 | 22.4 | 7.99 | 11.99 | 41.86 | 0.55 | 0.56 | -| [**YOLOv6Lite-M**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6lite_m.pt) | 320*320 | 25.1 | 9.08 | 13.27 | 47.95 | 0.79 | 0.67 | -| [**YOLOv6Lite-L**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6lite_l.pt) | 320*320 | 28.0 | 11.37 | 16.20 | 61.40 | 1.09 | 0.87 | -| [**YOLOv6Lite-L**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6lite_l.pt) | 320*192 | 25.0 | 7.02 | 9.66 | 36.13 | 1.09 | 0.52 | -| [**YOLOv6Lite-L**](https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6lite_l.pt) | 224*128 | 18.9 | 3.63 | 4.99 | 17.76 | 1.09 | 0.24 | - -
-表格笔记 - -- 从模型尺寸和输入图片比例两种角度,在构建了移动端系列模型,方便不同场景下的灵活应用。 -- 所有权重都经过 400 个 epoch 的训练,并且没有使用蒸馏技术。 -- mAP 和速度指标是在 COCO val2017 数据集上评估的,输入分辨率为表格中对应展示的。 -- 使用 MNN 2.3.0 AArch64 进行速度测试。测速时,采用2个线程,并开启arm82加速,推理预热10次,循环100次。 -- 高通888(sm8350)、天玑720(mt6853)和高通660(sdm660)分别对应高中低端不同性能的芯片,可以作为不同芯片下机型能力的参考。 -- [NCNN 速度测试](./docs/Test_NCNN_speed.md)教程可以帮助展示及复现 YOLOv6Lite 的 NCNN 速度结果。 diff --git a/configs/yolov6_lite/yolov6_lite_l.py b/configs/yolov6_lite/yolov6_lite_l.py deleted file mode 100644 index 212c8c73..00000000 --- a/configs/yolov6_lite/yolov6_lite_l.py +++ /dev/null @@ -1,54 +0,0 @@ -# YOLOv6-lite-l model -model = dict( - type='YOLOv6-lite-l', - pretrained=None, - width_multiple=1.5, - backbone=dict( - type='Lite_EffiBackbone', - num_repeats=[1, 3, 7, 3], - out_channels=[24, 32, 64, 128, 256], - scale_size=0.5, - ), - neck=dict( - type='Lite_EffiNeck', - in_channels=[256, 128, 64], - unified_channels=96 - ), - head=dict( - type='Lite_EffideHead', - in_channels=[96, 96, 96, 96], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.1 * 4, - lrf=0.01, - momentum=0.9, - weight_decay=0.00004, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) diff --git a/configs/yolov6_lite/yolov6_lite_l_finetune.py b/configs/yolov6_lite/yolov6_lite_l_finetune.py deleted file mode 100644 index 48315c4d..00000000 --- a/configs/yolov6_lite/yolov6_lite_l_finetune.py +++ /dev/null @@ -1,54 +0,0 @@ -# YOLOv6-lite-l model -model = dict( - type='YOLOv6-lite-l', - pretrained='weights/yolov6lite_l.pt', - width_multiple=1.5, - backbone=dict( - type='Lite_EffiBackbone', - num_repeats=[1, 3, 7, 3], - out_channels=[24, 32, 64, 128, 256], - scale_size=0.5, - ), - neck=dict( - type='Lite_EffiNeck', - in_channels=[256, 128, 64], - unified_channels=96 - ), - head=dict( - type='Lite_EffideHead', - in_channels=[96, 96, 96, 96], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) diff --git a/configs/yolov6_lite/yolov6_lite_m.py b/configs/yolov6_lite/yolov6_lite_m.py deleted file mode 100644 index 8f0de368..00000000 --- a/configs/yolov6_lite/yolov6_lite_m.py +++ /dev/null @@ -1,54 +0,0 @@ -# YOLOv6-lite-m model -model = dict( - type='YOLOv6-lite-m', - pretrained=None, - width_multiple=1.1, - backbone=dict( - type='Lite_EffiBackbone', - num_repeats=[1, 3, 7, 3], - out_channels=[24, 32, 64, 128, 256], - scale_size=0.5, - ), - neck=dict( - type='Lite_EffiNeck', - in_channels=[256, 128, 64], - unified_channels=96 - ), - head=dict( - type='Lite_EffideHead', - in_channels=[96, 96, 96, 96], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.1 * 4, - lrf=0.01, - momentum=0.9, - weight_decay=0.00004, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) diff --git a/configs/yolov6_lite/yolov6_lite_m_finetune.py b/configs/yolov6_lite/yolov6_lite_m_finetune.py deleted file mode 100644 index 108adda5..00000000 --- a/configs/yolov6_lite/yolov6_lite_m_finetune.py +++ /dev/null @@ -1,54 +0,0 @@ -# YOLOv6-lite-m model -model = dict( - type='YOLOv6-lite-m', - pretrained='weights/yolov6lite_m.pt', - width_multiple=1.1, - backbone=dict( - type='Lite_EffiBackbone', - num_repeats=[1, 3, 7, 3], - out_channels=[24, 32, 64, 128, 256], - scale_size=0.5, - ), - neck=dict( - type='Lite_EffiNeck', - in_channels=[256, 128, 64], - unified_channels=96 - ), - head=dict( - type='Lite_EffideHead', - in_channels=[96, 96, 96, 96], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) diff --git a/configs/yolov6_lite/yolov6_lite_s.py b/configs/yolov6_lite/yolov6_lite_s.py deleted file mode 100644 index 42a52e37..00000000 --- a/configs/yolov6_lite/yolov6_lite_s.py +++ /dev/null @@ -1,54 +0,0 @@ -# YOLOv6-lite-s model -model = dict( - type='YOLOv6-lite-s', - pretrained=None, - width_multiple=0.7, - backbone=dict( - type='Lite_EffiBackbone', - num_repeats=[1, 3, 7, 3], - out_channels=[24, 32, 64, 128, 256], - scale_size=0.5, - ), - neck=dict( - type='Lite_EffiNeck', - in_channels=[256, 128, 64], - unified_channels=96 - ), - head=dict( - type='Lite_EffideHead', - in_channels=[96, 96, 96, 96], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.1 * 4, - lrf=0.01, - momentum=0.9, - weight_decay=0.00004, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) diff --git a/configs/yolov6_lite/yolov6_lite_s_finetune.py b/configs/yolov6_lite/yolov6_lite_s_finetune.py deleted file mode 100644 index befee2ce..00000000 --- a/configs/yolov6_lite/yolov6_lite_s_finetune.py +++ /dev/null @@ -1,54 +0,0 @@ -# YOLOv6-lite-s model -model = dict( - type='YOLOv6-lite-s', - pretrained='weights/yolov6lite_s.pt', - width_multiple=0.7, - backbone=dict( - type='Lite_EffiBackbone', - num_repeats=[1, 3, 7, 3], - out_channels=[24, 32, 64, 128, 256], - scale_size=0.5, - ), - neck=dict( - type='Lite_EffiNeck', - in_channels=[256, 128, 64], - unified_channels=96 - ), - head=dict( - type='Lite_EffideHead', - in_channels=[96, 96, 96, 96], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) diff --git a/configs/yolov6l6.py b/configs/yolov6l6.py deleted file mode 100644 index 3bb77c5f..00000000 --- a/configs/yolov6l6.py +++ /dev/null @@ -1,62 +0,0 @@ -# YOLOv6l6 model -model = dict( - type='YOLOv6l6', - pretrained=None, - depth_multiple=1.0, - width_multiple=1.0, - backbone=dict( - type='CSPBepBackbone_P6', - num_repeats=[1, 6, 12, 18, 6, 6], - out_channels=[64, 128, 256, 512, 768, 1024], - csp_e=float(1)/2, - fuse_P2=True, - ), - neck=dict( - type='CSPRepBiFPANNeck_P6', - num_repeats=[12, 12, 12, 12, 12, 12], - out_channels=[512, 256, 128, 256, 512, 1024], - csp_e=float(1)/2, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512, 1024], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.9, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.2, -) -training_mode = "conv_silu" diff --git a/configs/yolov6l6_finetune.py b/configs/yolov6l6_finetune.py deleted file mode 100644 index 2ffb8ada..00000000 --- a/configs/yolov6l6_finetune.py +++ /dev/null @@ -1,62 +0,0 @@ -# YOLOv6l6 model -model = dict( - type='YOLOv6l6', - pretrained='weights/yolov6l6.pt', - depth_multiple=1.0, - width_multiple=1.0, - backbone=dict( - type='CSPBepBackbone_P6', - num_repeats=[1, 6, 12, 18, 6, 6], - out_channels=[64, 128, 256, 512, 768, 1024], - csp_e=float(1)/2, - fuse_P2=True, - ), - neck=dict( - type='CSPRepBiFPANNeck_P6', - num_repeats=[12, 12, 12, 12, 12, 12], - out_channels=[512, 256, 128, 256, 512, 1024], - csp_e=float(1)/2, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512, 1024], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) -training_mode = "conv_silu" diff --git a/configs/yolov6l_finetune.py b/configs/yolov6l_finetune.py deleted file mode 100644 index 9b301233..00000000 --- a/configs/yolov6l_finetune.py +++ /dev/null @@ -1,68 +0,0 @@ -# YOLOv6l model -model = dict( - type='YOLOv6l', - pretrained='weights/yolov6l.pt', - depth_multiple=1.0, - width_multiple=1.0, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(1)/2, - fuse_P2=True, - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(1)/2, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 2.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) -training_mode = "conv_silu" -# use normal conv to speed up training and further improve accuracy. diff --git a/configs/base/yolov6l_base.py b/configs/yolov6l_seg.py similarity index 85% rename from configs/base/yolov6l_base.py rename to configs/yolov6l_seg.py index ef2dbbb2..2ed9211f 100644 --- a/configs/base/yolov6l_base.py +++ b/configs/yolov6l_seg.py @@ -1,6 +1,6 @@ -# YOLOv6l large base model +# YOLOv6l-seg model model = dict( - type='YOLOv6l_base', + type='YOLOv6l', pretrained=None, depth_multiple=1.0, width_multiple=1.0, @@ -22,6 +22,10 @@ in_channels=[128, 256, 512], num_layers=3, begin_indices=24, + npr=256, + nm=32, + isseg=True, + issolo=False, anchors=3, anchors_init=[[10,13, 19,19, 33,23], [30,61, 59,59, 59,119], @@ -45,7 +49,7 @@ lr0=0.01, lrf=0.01, momentum=0.937, - weight_decay=0.0005, + weight_decay=0.001, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1 @@ -64,4 +68,5 @@ mosaic=1.0, mixup=0.1, ) -training_mode = "conv_relu" +training_mode = "conv_silu" +# use normal conv to speed up training and further improve accuracy. diff --git a/configs/yolov6m6.py b/configs/yolov6m6.py deleted file mode 100644 index e741bbc0..00000000 --- a/configs/yolov6m6.py +++ /dev/null @@ -1,61 +0,0 @@ -# YOLOv6m6 model -model = dict( - type='YOLOv6m6', - pretrained=None, - depth_multiple=0.60, - width_multiple=0.75, - backbone=dict( - type='CSPBepBackbone_P6', - num_repeats=[1, 6, 12, 18, 6, 6], - out_channels=[64, 128, 256, 512, 768, 1024], - csp_e=float(2)/3, - fuse_P2=True, - ), - neck=dict( - type='CSPRepBiFPANNeck_P6', - num_repeats=[12, 12, 12, 12, 12, 12], - out_channels=[512, 256, 128, 256, 512, 1024], - csp_e=float(2)/3, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512, 1024], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.9, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.1, -) diff --git a/configs/yolov6m6_finetune.py b/configs/yolov6m6_finetune.py deleted file mode 100644 index 83760d3a..00000000 --- a/configs/yolov6m6_finetune.py +++ /dev/null @@ -1,61 +0,0 @@ -# YOLOv6m6 model -model = dict( - type='YOLOv6m6', - pretrained='weights/yolov6m6.pt', - depth_multiple=0.60, - width_multiple=0.75, - backbone=dict( - type='CSPBepBackbone_P6', - num_repeats=[1, 6, 12, 18, 6, 6], - out_channels=[64, 128, 256, 512, 768, 1024], - csp_e=float(2)/3, - fuse_P2=True, - ), - neck=dict( - type='CSPRepBiFPANNeck_P6', - num_repeats=[12, 12, 12, 12, 12, 12], - out_channels=[512, 256, 128, 256, 512, 1024], - csp_e=float(2)/3, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512, 1024], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) diff --git a/configs/yolov6m_finetune.py b/configs/yolov6m_finetune.py deleted file mode 100644 index cfe0fa93..00000000 --- a/configs/yolov6m_finetune.py +++ /dev/null @@ -1,66 +0,0 @@ -# YOLOv6m model -model = dict( - type='YOLOv6m', - pretrained='weights/yolov6m.pt', - depth_multiple=0.60, - width_multiple=0.75, - backbone=dict( - type='CSPBepBackbone', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - csp_e=float(2)/3, - fuse_P2=True, - ), - neck=dict( - type='CSPRepBiFPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - csp_e=float(2)/3, - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=True, - reg_max=16, #if use_dfl is False, please set reg_max to 0 - distill_weight={ - 'class': 0.8, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) diff --git a/configs/yolov6m.py b/configs/yolov6m_seg.py similarity index 92% rename from configs/yolov6m.py rename to configs/yolov6m_seg.py index 29fae396..d8660be3 100644 --- a/configs/yolov6m.py +++ b/configs/yolov6m_seg.py @@ -1,4 +1,4 @@ -# YOLOv6m model +# YOLOv6m-seg model model = dict( type='YOLOv6m', pretrained=None, @@ -22,6 +22,10 @@ in_channels=[128, 256, 512], num_layers=3, begin_indices=24, + npr=256, + nm=32, + isseg=True, + issolo=False, anchors=3, anchors_init=[[10,13, 19,19, 33,23], [30,61, 59,59, 59,119], @@ -45,7 +49,7 @@ lr0=0.01, lrf=0.01, momentum=0.937, - weight_decay=0.0005, + weight_decay=0.001, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1 diff --git a/configs/yolov6n6.py b/configs/yolov6n6.py deleted file mode 100644 index 0abe3a44..00000000 --- a/configs/yolov6n6.py +++ /dev/null @@ -1,56 +0,0 @@ -# YOLOv6n model -model = dict( - type='YOLOv6n6', - pretrained=None, - depth_multiple=0.33, - width_multiple=0.25, - backbone=dict( - type='EfficientRep6', - num_repeats=[1, 6, 12, 18, 6, 6], - out_channels=[64, 128, 256, 512, 768, 1024], - fuse_P2=True, # if use RepBiFPANNeck6, please set fuse_P2 to True. - cspsppf=True, - ), - neck=dict( - type='RepBiFPANNeck6', - num_repeats=[12, 12, 12, 12, 12, 12], - out_channels=[512, 256, 128, 256, 512, 1024], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512, 1024], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.02, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) diff --git a/configs/yolov6n6_finetune.py b/configs/yolov6n6_finetune.py deleted file mode 100644 index 01100f0f..00000000 --- a/configs/yolov6n6_finetune.py +++ /dev/null @@ -1,56 +0,0 @@ -# YOLOv6n model -model = dict( - type='YOLOv6n6', - pretrained='weights/yolov6n6.pt', - depth_multiple=0.33, - width_multiple=0.25, - backbone=dict( - type='EfficientRep6', - num_repeats=[1, 6, 12, 18, 6, 6], - out_channels=[64, 128, 256, 512, 768, 1024], - fuse_P2=True, # if use RepBiFPANNeck6, please set fuse_P2 to True. - cspsppf=True, - ), - neck=dict( - type='RepBiFPANNeck6', - num_repeats=[12, 12, 12, 12, 12, 12], - out_channels=[512, 256, 128, 256, 512, 1024], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512, 1024], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='siou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) diff --git a/configs/yolov6n_finetune.py b/configs/yolov6n_finetune.py deleted file mode 100644 index 03b6d1ba..00000000 --- a/configs/yolov6n_finetune.py +++ /dev/null @@ -1,65 +0,0 @@ -# YOLOv6s model -model = dict( - type='YOLOv6n', - pretrained='weights/yolov6n.pt', - depth_multiple=0.33, - width_multiple=0.25, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - fuse_P2=True, - cspsppf=True, - ), - neck=dict( - type='RepBiFPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='siou', - use_dfl=False, # set to True if you want to further train with distillation - reg_max=0, # set to 16 if you want to further train with distillation - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) diff --git a/configs/qarepvgg/yolov6n_qa.py b/configs/yolov6n_seg.py similarity index 92% rename from configs/qarepvgg/yolov6n_qa.py rename to configs/yolov6n_seg.py index b42d9ddb..94b42ed1 100644 --- a/configs/qarepvgg/yolov6n_qa.py +++ b/configs/yolov6n_seg.py @@ -1,4 +1,4 @@ -# YOLOv6s model +# YOLOv6n-seg model model = dict( type='YOLOv6n', pretrained=None, @@ -21,6 +21,10 @@ in_channels=[128, 256, 512], num_layers=3, begin_indices=24, + npr=256, + nm=32, + isseg=True, + issolo=False, anchors=3, anchors_init=[[10,13, 19,19, 33,23], [30,61, 59,59, 59,119], @@ -44,7 +48,7 @@ lr0=0.02, lrf=0.01, momentum=0.937, - weight_decay=0.0005, + weight_decay=0.001, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1 @@ -63,4 +67,3 @@ mosaic=1.0, mixup=0.0, ) -training_mode='qarepvggv2' diff --git a/configs/yolov6s6.py b/configs/yolov6s6.py deleted file mode 100644 index 091bfffc..00000000 --- a/configs/yolov6s6.py +++ /dev/null @@ -1,56 +0,0 @@ -# YOLOv6n model -model = dict( - type='YOLOv6s6', - pretrained=None, - depth_multiple=0.33, - width_multiple=0.50, - backbone=dict( - type='EfficientRep6', - num_repeats=[1, 6, 12, 18, 6, 6], - out_channels=[64, 128, 256, 512, 768, 1024], - fuse_P2=True, # if use RepBiFPANNeck6, please set fuse_P2 to True. - cspsppf=True, - ), - neck=dict( - type='RepBiFPANNeck6', - num_repeats=[12, 12, 12, 12, 12, 12], - out_channels=[512, 256, 128, 256, 512, 1024], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512, 1024], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='giou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.01, - lrf=0.01, - momentum=0.937, - weight_decay=0.0005, - warmup_epochs=3.0, - warmup_momentum=0.8, - warmup_bias_lr=0.1 -) - -data_aug = dict( - hsv_h=0.015, - hsv_s=0.7, - hsv_v=0.4, - degrees=0.0, - translate=0.1, - scale=0.5, - shear=0.0, - flipud=0.0, - fliplr=0.5, - mosaic=1.0, - mixup=0.0, -) diff --git a/configs/yolov6s6_finetune.py b/configs/yolov6s6_finetune.py deleted file mode 100644 index a22697ed..00000000 --- a/configs/yolov6s6_finetune.py +++ /dev/null @@ -1,56 +0,0 @@ -# YOLOv6n model -model = dict( - type='YOLOv6s6', - pretrained='weights/yolov6s6.pt', - depth_multiple=0.33, - width_multiple=0.50, - backbone=dict( - type='EfficientRep6', - num_repeats=[1, 6, 12, 18, 6, 6], - out_channels=[64, 128, 256, 512, 768, 1024], - fuse_P2=True, # if use RepBiFPANNeck6, please set fuse_P2 to True. - cspsppf=True, - ), - neck=dict( - type='RepBiFPANNeck6', - num_repeats=[12, 12, 12, 12, 12, 12], - out_channels=[512, 256, 128, 256, 512, 1024], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512, 1024], - num_layers=4, - anchors=1, - strides=[8, 16, 32, 64], - atss_warmup_epoch=4, - iou_type='giou', - use_dfl=False, - reg_max=0 #if use_dfl is False, please set reg_max to 0 - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) diff --git a/configs/yolov6s_finetune.py b/configs/yolov6s_finetune.py deleted file mode 100644 index d6fb27fe..00000000 --- a/configs/yolov6s_finetune.py +++ /dev/null @@ -1,65 +0,0 @@ -# YOLOv6s model -model = dict( - type='YOLOv6s', - pretrained='weights/yolov6s.pt', - depth_multiple=0.33, - width_multiple=0.50, - backbone=dict( - type='EfficientRep', - num_repeats=[1, 6, 12, 18, 6], - out_channels=[64, 128, 256, 512, 1024], - fuse_P2=True, - cspsppf=True, - ), - neck=dict( - type='RepBiFPANNeck', - num_repeats=[12, 12, 12, 12], - out_channels=[256, 128, 128, 256, 256, 512], - ), - head=dict( - type='EffiDeHead', - in_channels=[128, 256, 512], - num_layers=3, - begin_indices=24, - anchors=3, - anchors_init=[[10,13, 19,19, 33,23], - [30,61, 59,59, 59,119], - [116,90, 185,185, 373,326]], - out_indices=[17, 20, 23], - strides=[8, 16, 32], - atss_warmup_epoch=0, - iou_type='giou', - use_dfl=False, # set to True if you want to further train with distillation - reg_max=0, # set to 16 if you want to further train with distillation - distill_weight={ - 'class': 1.0, - 'dfl': 1.0, - }, - ) -) - -solver = dict( - optim='SGD', - lr_scheduler='Cosine', - lr0=0.0032, - lrf=0.12, - momentum=0.843, - weight_decay=0.00036, - warmup_epochs=2.0, - warmup_momentum=0.5, - warmup_bias_lr=0.05 -) - -data_aug = dict( - hsv_h=0.0138, - hsv_s=0.664, - hsv_v=0.464, - degrees=0.373, - translate=0.245, - scale=0.898, - shear=0.602, - flipud=0.00856, - fliplr=0.5, - mosaic=1.0, - mixup=0.243, -) diff --git a/configs/yolov6s.py b/configs/yolov6s_seg.py similarity index 92% rename from configs/yolov6s.py rename to configs/yolov6s_seg.py index 8d8b6739..c4274ccc 100644 --- a/configs/yolov6s.py +++ b/configs/yolov6s_seg.py @@ -1,4 +1,4 @@ -# YOLOv6s model +# YOLOv6s-seg model model = dict( type='YOLOv6s', pretrained=None, @@ -21,6 +21,10 @@ in_channels=[128, 256, 512], num_layers=3, begin_indices=24, + npr=256, + nm=32, + isseg=True, + issolo=False, anchors=3, anchors_init=[[10,13, 19,19, 33,23], [30,61, 59,59, 59,119], @@ -44,7 +48,7 @@ lr0=0.01, lrf=0.01, momentum=0.937, - weight_decay=0.0005, + weight_decay=0.001, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1 diff --git a/configs/mbla/yolov6l_mbla.py b/configs/yolov6x_seg.py similarity index 79% rename from configs/mbla/yolov6l_mbla.py rename to configs/yolov6x_seg.py index 7534b705..3ef53e50 100644 --- a/configs/mbla/yolov6l_mbla.py +++ b/configs/yolov6x_seg.py @@ -1,29 +1,31 @@ -# YOLOv6l model +# YOLOv6l-seg model model = dict( - type='YOLOv6l_mbla', + type='YOLOv6l', pretrained=None, - depth_multiple=0.5, - width_multiple=1.0, + depth_multiple=1.33, + width_multiple=1.25, backbone=dict( type='CSPBepBackbone', - num_repeats=[1, 4, 8, 8, 4], + num_repeats=[1, 6, 12, 18, 6], out_channels=[64, 128, 256, 512, 1024], csp_e=float(1)/2, fuse_P2=True, - stage_block_type="MBLABlock", ), neck=dict( type='CSPRepBiFPANNeck', - num_repeats=[8, 8, 8, 8], + num_repeats=[12, 12, 12, 12], out_channels=[256, 128, 128, 256, 256, 512], csp_e=float(1)/2, - stage_block_type="MBLABlock", ), head=dict( type='EffiDeHead', in_channels=[128, 256, 512], num_layers=3, begin_indices=24, + npr=256, + nm=32, + isseg=True, + issolo=False, anchors=3, anchors_init=[[10,13, 19,19, 33,23], [30,61, 59,59, 59,119], @@ -47,7 +49,7 @@ lr0=0.01, lrf=0.01, momentum=0.937, - weight_decay=0.0005, + weight_decay=0.001, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1 @@ -66,5 +68,5 @@ mosaic=1.0, mixup=0.1, ) - training_mode = "conv_silu" +# use normal conv to speed up training and further improve accuracy. diff --git a/data/coco.yaml b/data/coco.yaml index d20d411e..8ce2676d 100644 --- a/data/coco.yaml +++ b/data/coco.yaml @@ -1,13 +1,11 @@ # COCO 2017 dataset http://cocodataset.org -train: ../coco/images/train2017 # 118287 images -val: ../coco/images/val2017 # 5000 images -test: ../coco/images/test2017 -anno_path: ../coco/annotations/instances_val2017.json +train: ./data/coco/images/train2017 # 118287 images +val: ./data/coco/images/val2017 # 5000 images +# test: ./data/coco/images/val2017 # number of classes nc: 80 -# whether it is coco dataset, only coco dataset should be set to True. -is_coco: True + # class names names: [ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', diff --git a/data/images/000000056350.jpg b/data/images/000000056350.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0c95d084ec0802da88e7bbfa1517337a6e3f5c8f GIT binary patch literal 139726 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 z

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

OI}r9&%zk>N*g0&%V7%2EG?W{;Jdk&=>_Q4l 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 z%0D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 W$Nmrq)_x?4Ko*E$h8?g8pa0p{^VKQ< literal 0 HcmV?d00001 diff --git a/data/images/9_Press_Conference_Press_Conference_9_946.jpg b/data/images/9_Press_Conference_Press_Conference_9_946.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aa3426674e7f9a6e698e5329efa1a58ed9989b1e GIT binary patch literal 378790 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�fffzTf6@oJH zL5&&qx?1$=dVtWq%RKG{GS5~7CTaB^C>`oEuyw7)VY#VhuQ?OwRC=hh;=z@|>BpK~ zjCxdWJmJfhM~P15srxTi3P})TbKJ{^Y@G-TwckoDnQJXrR+PCrc`|M+DI3QNZ}do_ z2ZG^I|JNI?>P$g-n%>}A#{$tZia)V?dfUGX94et_=1!LQv78uULgp(-X$9f{BZA(? zAk3h=oX!{joERA)=c-ev&ZKM@Ruq-$8RQ4int*9yX4 zS*O)@g?U11F!e=-0C@hW==Nb}$Y$G0sWWNyLszekn*a@okg1uRTvYKDqkgJsK-W=| zlpT7C0BxKF3P9Nz(*ek|5v!`=0WF#hirjZ-hoQ0^W*$+PxR?eKh7N+(T=*8y6+4vq zh7K3+se5^$L!QeJ{TbR8#Fegw#L@_6!7<=PPdi(5XE>vuWGxT=OgW`{RWLNJ{pum~ zV}s=rbYzDgv^eY$*ZoDPpn?bHo-3Ml4@#kjVbgbGI?xQodolkyn+zGX-+lyrYit%2 zAmetbJf3tBB(TM}adf;lAsOatnd-SGC_{S*2PS=n5?px%rBM}90b^7hK;nB+?(KfA zyhJ-8XI}k!gB0AqEd;>A)*J|J>yNf^6SEkjyrN)kZzs4f>xZkPqy;o)2Uz4cRcGVj)ohcZ0r zvtdrUlMjKh41K@371~GBp_f6Al7jG=VL9yn%x534u@5z}xGJ;_YPoy>?mj84cLc2^ zjw21L;8%BXUhK_>ljQ5hZdRtbf_i19++A{O?3eruM5ECZjTdNF>}I@=G$ZOMBOE1e z%N<8QhW7i20G!(4iHq-LN1lyeNPGbfWdH&VH3&jqT1Q$KIg29|FhA~t^(M{POD)fi zj~e*+BNn|S!n(ZK?3mF<^upDgt|Y>x@!jmF&0CuUnBt!UhC zdxs@+3dlUk3P|dGRM1=9FD|)jNT6Gm!(}}-nPppym(1pz!q7JI3Gl#d8QLq7odhK? z74%y#f~U{6cdIB~6}4g%ECNjzYnglUuQo|RtvKedHj3`Q;YEFdF_1v&TCF+Ypm!-@ zdwliRNDClh@$M|x*{1+3u(9Q~h&wdSO{Ygw!_`yXcU*V6(DbgH-rPaa{V=EV^fxri{D*u=JFlE8^$6A<$LgolrkS35n%!U0J zG+&_Na!BtTSeO~CL*|4Mroo*DzCj^4nDKM(hYP&`((yA#4<6K@T;g2){KE?HG3dB_ z0_XkVCTKwT11&#K-~b*zU~nIpez0lt+1kj5~UKfF9$W22uy>Df5;VB zb=0OKvnSc0mXBMwX0S)vi(=X@Sley%r3BtTHgSleX^LaPj{@dN{SvrL(@$QgIaJmsU8!ZsT}y^~Czzg4fvnnX^a3k5?gZ z1)m->eoScXy=@qt7MfRMs{R%wKOTL+l6=#u!@0;KdqPlt1$2lo`0mYIEtGXi z;h~%hi_XbgYRIdBTo;V0128Zw4@l6&@#O3{k})C*zWb*Bk3Qx`9>gaEH11Xr{}H zOvPuWIytY@;bnEh^_;>%UBr-0BG$IW`!-jyBZ)W%F&r*HIVv!n=NbeXx^*3%EHes5 zhbTbpmU1=je_#fX`+P`Fl6BuTVNVfu1NPK(zR)kEmHg)K5ckx?Iz)e^xy_JT( zV|q-_7oMoO*&EC~QS<(MmG-i>Dj=m05Hwi76nRntN7n(L-uu^Q9BCftL&1RTp9KzV`FUrw?+!iIxpEbJQi7>XV+g z`dp$2IL2(~Lf%(nHFBdhNC)jOZu2=|XP~QIlUojT3K7DA`*J^sY~aW(M@d2cvb>^RCN%rf6%knnPWFO%>=XLUZ#lC^srThh~Ek^srRg9tYH2RhoiC9p}!UO9<3KS-}Epa)OEpUHJ@TsfXE{KHS(h5 zmvE@ZI1P>!fiC-!MnL=rifO!&@dw5BOz^Xu-x8tA+_@&&-_g47*IC*GA6AH8bChw&pwhttsF(gc~8X5Cp`5=>_Bte5PImupn`cNo** zYEQ)x*Cc}Ve$ATAuquQ{513trzV=qd42Zezhx?;%HA>Aco5R)4{{y6Dp%4`cRj2IR ze!D~Qs*#>bC2nrMb(Fa*%qe!i3PdeKHG*wyt1I`j6-32Y1;$&5aB~R#9)X9?NrSer z;cdOt_eTED*AzA?gI5K`@gRr-KRkNZ!1PWGTm(0U>|Iq&te4aeergaB+WTsG!`Ve~ zRc3FnEzvK(4#U@w>LOmx-TwA^$+K%4U<4QU?}FXO^pI|(KP+xm5;}ZJ0R~&|=k}0m5U_A(sHTp(gG2ca*Ag%KuwH|U zXJhZ9EDz`)dnDk40=8%fYg!0t3B-Kp{qW>4Gq0HXe(m9yeL&wt^!j+!jd{8ZDd z)l|Ic;!zFx5Z~kzceErX@Y=S-pT*ohjRX1)2Fhr^hudnvhCeGCuo89vZH_^F0;aeCg)Wp9y2a9xx&9}DuJ!u)0aag9u zfKQZ(=??fl>=v7%Yb59uJ#3ZmDvHL-&FJ4wQvH&S=IdtD@n$v^GF(__9~fz+MHch~ zd)T?xus&%a4UZns6vAYghSo9kyF-Pm*mUAlW@qb!ngB^4EbcM4;{g83eljt)t~eT; zgmU`#XuJK|9smLptU>X<=LbXLa?a(1bv#5{(P4GPuL7??K;c;$+Z+IVgR}Ae3(Hkp z;2>y1eRvH%F|qt(&8Cfu{oCfaPbT&ODK7Jk>=fR5dEU|f*bYs9oY4pz&9?1RyvFwZ zW$h547sE=UB926puOT<>e2{^+9QlXck?-tn$1=Ap2X<|8OWWAgML8yjsSjagGH-ivPNss$D#rbU3Vj8n-97iK0Z3Z_kPg7Jp2> zX;6vQ+zQ_Z2oMk~y%SjIRe!$;0hsrjQ~`X0QYt2A7oBg-KRB%Kr%!Qv&OkUB(xb@i zwEGZhuLy89wQH31u;x>U-zgJsip-W^o|4%P7>{P}xvRQtA z{UA!1e7ak84lT}pR~&u5Z`{uCc(AuG;Cx=au`p@Q#=Lt~_{y!4*?|lil~BP+rlvZS@Lq4t1~FUtOi}Roxz;!Li6)1)gM_@6R24;$~|`ko`A#T#8Ji%Ie)7M z3%txE!K96;x)ZBb1bqxwy zS-VQ9s+jYTMRK2O4Cd^<7JpC|%C@v>Kkje73LA7yH?xgAJ9B+!e_;Ih*FX;1@7ZR&s(m2a{#osc z1c1^qv=?e`(Z!ViE1b$&O7u?xbSVk@vE=IBT3+M`p#Cc6jJmg#(G|iM(vXq&Ix<%^ z6V9V`;7WDpGlbHj=If=SOq=BWMTjbUSj`)ht zEyCeH2DqODIB#28egSHufD{EE2^_Gw-O?@s)`cRC8!GKgdAr15Jq{c7Fs<>rKfVRw z9d2jfe-F>udz-d{h|Rtm=vP5sT|NPhdgXdCZ=qFn1&G&YFK+ky--3nrW88NyaToaV z-FCZe_!R(B;Z+AYm;I*xvsO(9n>T~?jGH#IIH!OHja6;j7;~abV58R4LNLv+{9+4u zvF2*l$()IJO;aT5hSj8T2&fF3=h)vT5S6;&#nalu;o%xqMQ{C>3tmQ!b&V3~s_L{7g-JYp9^xeT>(;6RzpYehnZ);9Qn&>kJeN>t}W@9?)23 zR1;wPQVejIe{ihiOZTD6bC1bAc0#L?Or0(h*Dc+6z#{$&Two`(>#)-E$BQHqoUTa~ z$5SjgmyQV6F_QjW@Lo&OfKQX6j2}yL4U%~^%lCt2tKw#Skz5A-2j8?{IFzpp^S?+? z#lRYr2&(Tg`}q4x>Y*G|b!w?c$B?^i$oW(N)!ZijuDO8FP}*qoF~;Kaf z{<)~kw0`P@9Ow_iIUp+f-V{?DI|4gM8*>QOzdX=b-G%<_XplUh!aVwhfF?)E>c*N@ zwyK2l{5P!AN`j5n4E( z0#lEj%9IC$hB`gd5OU|~6GQC#kM5Sr`BEL7=fV@UZl zs`2bO^I6&Yz-p|u2(29X-er*V#l(?r^il8yGyt*~x2Bk@G%@e$wW}$P{?eT|E(x-J z68r?p7;|P(jwsu<@Sf<^j19K!7Hzrnm{-VQ8ZMR+-r#G*(2(jbAT`pt8Ym&(vF_WM z28kVpT>MQi8gSKhEE{F>{kmMG6J;}YoP1&wGE1-9vwiQ-S6R05yxgPR@W>a-nEzx+UhK3rTcRe5)G5e%cjhls|7`UmTbwAp!6wjLInydm2oaObtrp{uE?RV3){ zwASwfvm3pA(-(f%q?w)PiXgZ?k2@_ctr`qZG2Q?G+1H#Cp|JMpBAGr%^&9+`)fhkX z_a8e3n>VOAVRD}CW88kp`ABvJ#QYSQS0d)^Z0UNmv$2LOa~`oLKjD!+P1e!}pA)W< zOkiEThuZ~No1DkY5;MT#cJ2;r2Yqp_U_Se0jI%#S+@C$7^nDloJIs#23Wcv^(nQKs zp?kTm{j4v_C&EmIckw(rr%$TSDwK{?b7UKFHX~2_W83dodqsI7cG0!zXh|~xkzATK zR9v1lDYrYFQE@;H&@PZHc_H|#0;0yLp2>bx8y^qaJ|F;O(*}mpzFnkZSa9~)HNz0< zO*P>Q<*l<8IZ#F!x6)c@Ruit>V~^v{iW>!Q%0R}bpSQ9e7xErx ze@>hh!d#hNEx)eUxT5)luIj6Kb~8g;{sKh#+8OJ(ezEIgj*=?(0t%eEzd2ufT=``Y zZIxYMw{})|06cp$*z)|Gch{+d48Le(XeDuK25tpDUkqUVDn_rv?c7Ndu~IQ3?K!dN zdTDOru8Hbo?bDR-dDpJeXoQzlN{=M=aq*YPLi00ce~V#M+dUTdmj+y&-VHx2c^#1W zhAv>O7DuH{|9V)k5IdPZZb(SsXz+EU>eNxq_jzt}`o^**_E=0etWFYu=z>anpTDqm zTAU{Xf3~nQ!PPu9e=T&_rIr~YN~O(4>9!2}5#aej#_{ncLQoTf%YBu`)*l938Can1 z2j=prC(w60fzbu~x zi&%4X?oGwKMeI8rkMZkRp_|q9-%vKn(tY$-rXA6Iwn>=ha-@$r?E}egVTfJaAxf~8 zj1W1kwI7LE8Q!L5uM>YHq|rYm|L} z#P96BpJIGxMJcyO)a$4ckwFX(V4#~qYRMfI^UdcPy-Ku`bol|@%}Bvz@3%2yRsPVF z^arLvKe{+!&3M9M@rMi6);~0idajmGobnDhWme>SDG@QxNo;&Q+&|9?lWq?!%L*@V zz;YGoTlQA4b*~Jz=48uX*M3BFQdmX{a0CL8fV}IriU8DIR1CK zJC4{gko|M(z{SSxoSuibg7T7TZb{$qT%hrIyg{0q5gZ->Pu8i>)vAmNEeK(FLDRrCpQ`#uCFYXmj2RO6;Z#*N!b0 z7mvtDvs-m<;rFX-DK3c(zPl<~xv`_&G*3j{xg)N-$QiMdy^6{jltLk5K7{W&^Vy66 zom0Gxb356QOa!|cNce5rxw;uzdWgoG=c#>RX}2+yIN^aEn9DiIor3l>=kNhdkqWxZ zluna&zY6jBFPl@R-q4$5+zv4Nq^ix}&w?y>&X%mT9R6CM8-~mGa4Sop>H;Z1MA<7v z4dNW1ONckI zRwNzecJfz(-W;;T69m^g-wM>%)xdkagP<^71J|td)*=_6w;&KzK$z0(Db$%GVPs;2B;@ zjbkbA(9#mdYAWg!H=@HaQ|C1ZZD-z?0|1YCTtcIQp7 z@v>v)^S4!fb^Y1w`JVIRIExOe$rda`mtugQ86ZOEOAkJE>vd# zzg$HO!A<79;b`mStB9vKaw*u)>0|ZJRw^wIlczta8<*rpW%V`<%TF*4Ahy+tk zS$pk<4^Wqn9o(5`!4`pD5R)VR)%e#ZlpqRRhF zYtct_8vfUQ@F43%j8XKZdmakfmcQcSK-cIemF&QU`>YEegm5LZa-_n+1`jFj_{e7z z{co8-EQdwKd(g+3h{~ZtHn;U-J+iX!BN%G!p2O=9kZ?~NHpVPHNW9L@<*y6? z>lXKepg3d@_fCfqm(@L@Qqs$cHIcLGz({!F9x^MFPK?s#Km1`Zm^~eZPTvdiO&Kr-_7-#rEkQ|BEpBmG_Yg+xXPKX_CH)t zke_yimfsv|^w;jhE2Irqe|}yI2IGg`2R%df!2Yy|_}zy0=Z}&XnEboo7YGs=9bz;akvgN}tU4-vyVFH9sAzlh4kf`}wRpgIPRR<%94Cx@u~9Z+&Eh zjqXWGt<8!fy+f14z)Jgb8i#c;^^psR=8Of{t_3XnfJZ2N_l9 zWzU=o6@hsuf4^5kn{SvyTS0kdezuta+~1&<;??~1!!gfFTnUZtHCxG=KUXjRJ8n|^ z?*e^HQ-2R>wQ?5B!xFNYb)Rf=zk3`ZoF0&I;d+x8^KVR%(I%K%?W0^uATV5&Oyeq1 zgGEhp`WB)5O*v*7U4<-P2(uP>0FQ_Hae%7mUAz$NMj2;OT>+5B96?4DD}9fH|17 zl<3%oR#qBa_r#9!X)qp`I^OYHiX%bds?hjN4lUWf>FV@Tv?>|=I;j6i;C)|L9BN~X z&YrR4n(lp=j8!>PcTX8_!CJ4nF%-V1O3PP5)C1<|IHeohP5fam4U5*^gpQ?D{}d|l z!_yyo=3U%VK^!!$()@_N)}zQo5IB~4eh<<)G@k>IuhH6FdDZGgc^0y&s@UL&|Nux9OE#`Wy1B<|KiC@7L5y+4NSlnp#BFAYuz z@0p1-TOu?Az!CeCF`2`71;dFm7r!1(J%*4JPzrF+##I;8ifKtP4R*NyE~vv!B6DxN zN;!q7yyVp9OuXLl(&+)t`!Lxk9}2rj8KdEs-z$_rm-g2b?kxV4o86qO3!gcAH{*r$4wwOQX6xkW z>6YU8W)H$gK3EKEW>upGE)F4q$e@qjOjiB_ED?JezN>uJyfLrnSU1?ds1EZBX*asc zj)Iv+u5W1Odz8=k1oe78TO@K!#8$*cZ?HRl7ND}n3(kF*-UwV2>wgzG=fu)?Q&NIY zf}%VNz8*-h*xxj0S2!?~$*j6UKpY=JoKRwcOE1>SRwM(i)H zC)KOh>e_Dx*Pd*1i?JP9&G_!qLSt&?2}=*5YKek9-iqyLIHGW)m)WgRsX>N7X#%_0 zUu1c#^U7G7{Kb-3ILDm_sJ2>i=Y|UQGpR`$hM0}JED~T_Iu-9}z9$dC=QouVp=;&~ z(FKzmpIkIz+3~PG_t&uSh`qeM<RQVWQqaXX%`?}7GS@Qvoj;HSZA}E2TJ*T^-b-bT_-Lvi`Siozt z!2ftS@Kt!?E?!=AQRA3~k`gXF5JfG>;$8Z^o%0(14o^FV(6wBcEH8_G**P@y^(Gm7 zg5U@$EZU7d#ptjikrTGMPrJ8Bmo-FECHvsmqoMOdUeH>eEW?{DC3VX z8+*2Q#yidKuVmjpF>LX02HSi-6`*pSmwL2r;5_#38AHLS)-@lm0KyHI5Gic69<6pO4b+i5e(#tjz*C_QEY9?)UQVM1mBUx_Nabh4vX2F2i+T zWTGNPjw#~}9iw&8C#h>TNFf5N0?j+<#c|OW1^kQg=X#HB)qOx}nexl69fBZ^WXp

=kM=tL1X_=kekGh&lrtuc5d*s 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

C>2D6h$_p70n6 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¨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 z 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%0D%E*SR=G0KawK%`~zZG0lX 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 threshold as positive + is_pos = torch.where( + iou_candidates > overlaps_thr_per_gt.repeat([1, 1, self.n_anchors]), + is_in_candidate, torch.zeros_like(is_in_candidate)) + + is_in_gts = select_candidates_in_gts(ac_points, gt_bboxes) + mask_pos = is_pos * is_in_gts * mask_gt + + target_gt_idx, fg_mask, mask_pos = select_highest_overlaps( + mask_pos, overlaps, self.n_max_boxes) + + # assigned target + target_labels, target_bboxes, target_scores, target_segmasks = self.get_targets( + gt_labels, gt_bboxes, target_gt_idx, fg_mask, gt_segmasks) + + # soft label with iou + if pd_bboxes is not None: + ious = iou_calculator(gt_bboxes, pd_bboxes) * mask_pos + ious = ious.max(axis=-2)[0].unsqueeze(-1) + target_scores *= ious + + return target_labels.long(), target_bboxes, target_scores, fg_mask.bool(), target_segmasks + + def select_topk_candidates(self, + distances, + n_level_bboxes, + mask_gt): + + mask_gt = mask_gt.repeat(1, 1, self.topk).bool() + level_distances = torch.split(distances, n_level_bboxes, dim=-1) + is_in_candidate_list = [] + candidate_idxs = [] + start_idx = 0 + for per_level_distances, per_level_boxes in zip(level_distances, n_level_bboxes): + + end_idx = start_idx + per_level_boxes + selected_k = min(self.topk, per_level_boxes) + _, per_level_topk_idxs = per_level_distances.topk(selected_k, dim=-1, largest=False) + candidate_idxs.append(per_level_topk_idxs + start_idx) + per_level_topk_idxs = torch.where(mask_gt, + per_level_topk_idxs, torch.zeros_like(per_level_topk_idxs)) + is_in_candidate = F.one_hot(per_level_topk_idxs, per_level_boxes).sum(dim=-2) + is_in_candidate = torch.where(is_in_candidate > 1, + torch.zeros_like(is_in_candidate), is_in_candidate) + is_in_candidate_list.append(is_in_candidate.to(distances.dtype)) + start_idx = end_idx + + is_in_candidate_list = torch.cat(is_in_candidate_list, dim=-1) + candidate_idxs = torch.cat(candidate_idxs, dim=-1) + + return is_in_candidate_list, candidate_idxs + + def thres_calculator(self, + is_in_candidate, + candidate_idxs, + overlaps): + + n_bs_max_boxes = self.bs * self.n_max_boxes + _candidate_overlaps = torch.where(is_in_candidate > 0, + overlaps, torch.zeros_like(overlaps)) + candidate_idxs = candidate_idxs.reshape([n_bs_max_boxes, -1]) + assist_idxs = self.n_anchors * torch.arange(n_bs_max_boxes, device=candidate_idxs.device) + assist_idxs = assist_idxs[:,None] + faltten_idxs = candidate_idxs + assist_idxs + candidate_overlaps = _candidate_overlaps.reshape(-1)[faltten_idxs] + candidate_overlaps = candidate_overlaps.reshape([self.bs, self.n_max_boxes, -1]) + + overlaps_mean_per_gt = candidate_overlaps.mean(axis=-1, keepdim=True) + overlaps_std_per_gt = candidate_overlaps.std(axis=-1, keepdim=True) + overlaps_thr_per_gt = overlaps_mean_per_gt + overlaps_std_per_gt + + return overlaps_thr_per_gt, _candidate_overlaps + + def get_targets(self, + gt_labels, + gt_bboxes, + target_gt_idx, + fg_mask, + gt_segmasks): + + # assigned target labels + batch_idx = torch.arange(self.bs, dtype=gt_labels.dtype, device=gt_labels.device) + batch_idx = batch_idx[...,None] + target_gt_idx = (target_gt_idx + batch_idx * self.n_max_boxes).long() + target_labels = gt_labels.flatten()[target_gt_idx.flatten()] + target_labels = target_labels.reshape([self.bs, self.n_anchors]) + target_labels = torch.where(fg_mask > 0, + target_labels, torch.full_like(target_labels, self.bg_idx)) + + # assigned target boxes + target_bboxes = gt_bboxes.reshape([-1, 4])[target_gt_idx.flatten()] + target_bboxes = target_bboxes.reshape([self.bs, self.n_anchors, 4]) + + # assigned target scores + target_scores = F.one_hot(target_labels.long(), self.num_classes + 1).float() + target_scores = target_scores[:, :, :self.num_classes] + + m_shape = gt_segmasks.shape[-2:] + target_segmasks = gt_segmasks.reshape([-1, m_shape[0], m_shape[1]])[target_gt_idx.flatten()] + + return target_labels, target_bboxes, target_scores, target_segmasks diff --git a/yolov6/assigners/tal_assigner.py b/yolov6/assigners/tal_assigner.py index 45008f5a..d1bd404a 100644 --- a/yolov6/assigners/tal_assigner.py +++ b/yolov6/assigners/tal_assigner.py @@ -25,7 +25,8 @@ def forward(self, anc_points, gt_labels, gt_bboxes, - mask_gt): + mask_gt, + gt_segmasks): """This code referenced to https://github.com/Nioolek/PPYOLOE_pytorch/blob/master/ppyoloe/assigner/tal_assigner.py @@ -50,10 +51,11 @@ def forward(self, return torch.full_like(pd_scores[..., 0], self.bg_idx).to(device), \ torch.zeros_like(pd_bboxes).to(device), \ torch.zeros_like(pd_scores).to(device), \ - torch.zeros_like(pd_scores[..., 0]).to(device) + torch.zeros_like(pd_scores[..., 0]).to(device), \ + torch.zeros(*pd_bboxes.shape[:2], 40, 40) cycle, step, self.bs = (1, self.bs, self.bs) if self.n_max_boxes <= 100 else (self.bs, 1, 1) - target_labels_lst, target_bboxes_lst, target_scores_lst, fg_mask_lst = [], [], [], [] + target_labels_lst, target_bboxes_lst, target_scores_lst, fg_mask_lst, target_segmasks_lst = [], [], [], [], [] # loop batch dim in case of numerous object box for i in range(cycle): start, end = i*step, (i+1)*step @@ -62,6 +64,7 @@ def forward(self, gt_labels_ = gt_labels[start:end, ...] gt_bboxes_ = gt_bboxes[start:end, ...] mask_gt_ = mask_gt[start:end, ...] + gt_segmasks_ = gt_segmasks[start:end, ...] mask_pos, align_metric, overlaps = self.get_pos_mask( pd_scores_, pd_bboxes_, gt_labels_, gt_bboxes_, anc_points, mask_gt_) @@ -70,8 +73,8 @@ def forward(self, mask_pos, overlaps, self.n_max_boxes) # assigned target - target_labels, target_bboxes, target_scores = self.get_targets( - gt_labels_, gt_bboxes_, target_gt_idx, fg_mask) + target_labels, target_bboxes, target_scores, target_segmasks = self.get_targets( + gt_labels_, gt_bboxes_, target_gt_idx, fg_mask, gt_segmasks_) # normalize align_metric *= mask_pos @@ -85,14 +88,16 @@ def forward(self, target_bboxes_lst.append(target_bboxes) target_scores_lst.append(target_scores) fg_mask_lst.append(fg_mask) + target_segmasks_lst.append(target_segmasks) # concat target_labels = torch.cat(target_labels_lst, 0) target_bboxes = torch.cat(target_bboxes_lst, 0) target_scores = torch.cat(target_scores_lst, 0) fg_mask = torch.cat(fg_mask_lst, 0) + target_segmasks = torch.cat(target_segmasks_lst, 0) - return target_labels, target_bboxes, target_scores, fg_mask.bool() + return target_labels, target_bboxes, target_scores, fg_mask.bool(), target_segmasks def get_pos_mask(self, pd_scores, @@ -153,7 +158,8 @@ def get_targets(self, gt_labels, gt_bboxes, target_gt_idx, - fg_mask): + fg_mask, + gt_segmasks): # assigned target labels batch_ind = torch.arange(end=self.bs, dtype=torch.int64, device=gt_labels.device)[...,None] @@ -169,5 +175,8 @@ def get_targets(self, fg_scores_mask = fg_mask[:, :, None].repeat(1, 1, self.num_classes) target_scores = torch.where(fg_scores_mask > 0, target_scores, torch.full_like(target_scores, 0)) + m_shape = gt_segmasks.shape[-2:] + target_segmasks = gt_segmasks.reshape([-1, m_shape[0], m_shape[1]])[target_gt_idx] - return target_labels, target_bboxes, target_scores + + return target_labels, target_bboxes, target_scores, target_segmasks diff --git a/yolov6/assigners/tal_assigner_seg.py b/yolov6/assigners/tal_assigner_seg.py new file mode 100644 index 00000000..057c718b --- /dev/null +++ b/yolov6/assigners/tal_assigner_seg.py @@ -0,0 +1,185 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from yolov6.assigners.assigner_utils import select_candidates_in_gts, select_highest_overlaps, iou_calculator, dist_calculator + +class TaskAlignedAssigner(nn.Module): + def __init__(self, + topk=13, + num_classes=80, + alpha=1.0, + beta=6.0, + eps=1e-9): + super(TaskAlignedAssigner, self).__init__() + self.topk = topk + self.num_classes = num_classes + self.bg_idx = num_classes + self.alpha = alpha + self.beta = beta + self.eps = eps + + @torch.no_grad() + def forward(self, + pd_scores, + pd_bboxes, + anc_points, + gt_labels, + gt_bboxes, + mask_gt, + gt_segmasks): + """This code referenced to + https://github.com/Nioolek/PPYOLOE_pytorch/blob/master/ppyoloe/assigner/tal_assigner.py + + Args: + pd_scores (Tensor): shape(bs, num_total_anchors, num_classes) + pd_bboxes (Tensor): shape(bs, num_total_anchors, 4) + anc_points (Tensor): shape(num_total_anchors, 2) + gt_labels (Tensor): shape(bs, n_max_boxes, 1) + gt_bboxes (Tensor): shape(bs, n_max_boxes, 4) + mask_gt (Tensor): shape(bs, n_max_boxes, 1) + Returns: + target_labels (Tensor): shape(bs, num_total_anchors) + target_bboxes (Tensor): shape(bs, num_total_anchors, 4) + target_scores (Tensor): shape(bs, num_total_anchors, num_classes) + fg_mask (Tensor): shape(bs, num_total_anchors) + """ + self.bs = pd_scores.size(0) + self.n_max_boxes = gt_bboxes.size(1) + + if self.n_max_boxes == 0: + device = gt_bboxes.device + return torch.full_like(pd_scores[..., 0], self.bg_idx).to(device), \ + torch.zeros_like(pd_bboxes).to(device), \ + torch.zeros_like(pd_scores).to(device), \ + torch.zeros_like(pd_scores[..., 0]).to(device), \ + [] + #torch.zeros(*pd_bboxes.shape[:2]).to(device) + + + # cycle, step, self.bs = (1, self.bs, self.bs) if self.n_max_boxes <= 100 else (self.bs, 1, 1) + cycle, step, self.bs = (1, self.bs, self.bs) + target_labels_lst, target_bboxes_lst, target_scores_lst, fg_mask_lst, idx_lst = [], [], [], [], [] + # loop batch dim in case of numerous object box + for i in range(cycle): + start, end = i*step, (i+1)*step + pd_scores_ = pd_scores[start:end, ...] + pd_bboxes_ = pd_bboxes[start:end, ...] + gt_labels_ = gt_labels[start:end, ...] + gt_bboxes_ = gt_bboxes[start:end, ...] + mask_gt_ = mask_gt[start:end, ...] + # gt_segmasks_ = gt_segmasks[start:end, ...] + + mask_pos, align_metric, overlaps = self.get_pos_mask( + pd_scores_, pd_bboxes_, gt_labels_, gt_bboxes_, anc_points, mask_gt_) + + target_gt_idx, fg_mask, mask_pos = select_highest_overlaps( + mask_pos, overlaps, self.n_max_boxes) + + # assigned target + target_labels, target_bboxes, target_scores, idx = self.get_targets( + gt_labels_, gt_bboxes_, target_gt_idx, fg_mask) + + # normalize + align_metric *= mask_pos + pos_align_metrics = align_metric.max(axis=-1, keepdim=True)[0] + pos_overlaps = (overlaps * mask_pos).max(axis=-1, keepdim=True)[0] + norm_align_metric = (align_metric * pos_overlaps / (pos_align_metrics + self.eps)).max(-2)[0].unsqueeze(-1) + target_scores = target_scores * norm_align_metric + + # append + target_labels_lst.append(target_labels) + idx_lst.append(idx) + target_bboxes_lst.append(target_bboxes) + target_scores_lst.append(target_scores) + fg_mask_lst.append(fg_mask) + # target_segmasks_lst.append(target_segmasks) + + # concat + target_labels = torch.cat(target_labels_lst, 0) + target_bboxes = torch.cat(target_bboxes_lst, 0) + target_scores = torch.cat(target_scores_lst, 0) + fg_mask = torch.cat(fg_mask_lst, 0) + # target_segmasks = torch.cat(target_segmasks_lst, 0) + + return target_labels, target_bboxes, target_scores, fg_mask.bool(), idx_lst + + def get_pos_mask(self, + pd_scores, + pd_bboxes, + gt_labels, + gt_bboxes, + anc_points, + mask_gt): + + # get anchor_align metric + align_metric, overlaps = self.get_box_metrics(pd_scores, pd_bboxes, gt_labels, gt_bboxes) + # get in_gts mask + mask_in_gts = select_candidates_in_gts(anc_points, gt_bboxes) + # get topk_metric mask + mask_topk = self.select_topk_candidates( + align_metric * mask_in_gts, topk_mask=mask_gt.repeat([1, 1, self.topk]).bool()) + # merge all mask to a final mask + mask_pos = mask_topk * mask_in_gts * mask_gt + + return mask_pos, align_metric, overlaps + + def get_box_metrics(self, + pd_scores, + pd_bboxes, + gt_labels, + gt_bboxes): + + pd_scores = pd_scores.permute(0, 2, 1) + gt_labels = gt_labels.to(torch.long) + ind = torch.zeros([2, self.bs, self.n_max_boxes], dtype=torch.long) + ind[0] = torch.arange(end=self.bs).view(-1, 1).repeat(1, self.n_max_boxes) + ind[1] = gt_labels.squeeze(-1) + bbox_scores = pd_scores[ind[0], ind[1]] + + overlaps = iou_calculator(gt_bboxes, pd_bboxes) + align_metric = bbox_scores.pow(self.alpha) * overlaps.pow(self.beta) + + return align_metric, overlaps + + def select_topk_candidates(self, + metrics, + largest=True, + topk_mask=None): + + num_anchors = metrics.shape[-1] + topk_metrics, topk_idxs = torch.topk( + metrics, self.topk, axis=-1, largest=largest) + if topk_mask is None: + topk_mask = (topk_metrics.max(axis=-1, keepdim=True) > self.eps).tile( + [1, 1, self.topk]) + topk_idxs = torch.where(topk_mask, topk_idxs, torch.zeros_like(topk_idxs)) + is_in_topk = F.one_hot(topk_idxs, num_anchors).sum(axis=-2) + is_in_topk = torch.where(is_in_topk > 1, + torch.zeros_like(is_in_topk), is_in_topk) + return is_in_topk.to(metrics.dtype) + + def get_targets(self, + gt_labels, + gt_bboxes, + target_gt_idx, + fg_mask): + + # assigned target labels + batch_ind = torch.arange(end=self.bs, dtype=torch.int64, device=gt_labels.device)[...,None] + target_gt_idx = target_gt_idx + batch_ind * self.n_max_boxes + target_labels = gt_labels.long().flatten()[target_gt_idx] + + # assigned target boxes + target_bboxes = gt_bboxes.reshape([-1, 4])[target_gt_idx] + + # assigned target scores + target_labels[target_labels<0] = 0 + target_scores = F.one_hot(target_labels, self.num_classes) + fg_scores_mask = fg_mask[:, :, None].repeat(1, 1, self.num_classes) + target_scores = torch.where(fg_scores_mask > 0, target_scores, + torch.full_like(target_scores, 0)) + # m_shape = gt_segmasks.shape[-2:] + # target_segmasks = gt_segmasks.reshape([-1, m_shape[0], m_shape[1]])[target_gt_idx] + + + return target_labels, target_bboxes, target_scores, target_gt_idx diff --git a/yolov6/assigners/tal_assigner_seg2.py b/yolov6/assigners/tal_assigner_seg2.py new file mode 100644 index 00000000..aa1101cd --- /dev/null +++ b/yolov6/assigners/tal_assigner_seg2.py @@ -0,0 +1,183 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from yolov6.assigners.assigner_utils import select_candidates_in_gts, select_highest_overlaps, iou_calculator, dist_calculator + +class TaskAlignedAssigner(nn.Module): + def __init__(self, + topk=13, + num_classes=80, + alpha=1.0, + beta=6.0, + eps=1e-9): + super(TaskAlignedAssigner, self).__init__() + self.topk = topk + self.num_classes = num_classes + self.bg_idx = num_classes + self.alpha = alpha + self.beta = beta + self.eps = eps + + @torch.no_grad() + def forward(self, + pd_scores, + pd_bboxes, + anc_points, + gt_labels, + gt_bboxes, + mask_gt, + gt_segmasks): + """This code referenced to + https://github.com/Nioolek/PPYOLOE_pytorch/blob/master/ppyoloe/assigner/tal_assigner.py + + Args: + pd_scores (Tensor): shape(bs, num_total_anchors, num_classes) + pd_bboxes (Tensor): shape(bs, num_total_anchors, 4) + anc_points (Tensor): shape(num_total_anchors, 2) + gt_labels (Tensor): shape(bs, n_max_boxes, 1) + gt_bboxes (Tensor): shape(bs, n_max_boxes, 4) + mask_gt (Tensor): shape(bs, n_max_boxes, 1) + Returns: + target_labels (Tensor): shape(bs, num_total_anchors) + target_bboxes (Tensor): shape(bs, num_total_anchors, 4) + target_scores (Tensor): shape(bs, num_total_anchors, num_classes) + fg_mask (Tensor): shape(bs, num_total_anchors) + """ + self.bs = pd_scores.size(0) + self.n_max_boxes = gt_bboxes.size(1) + + if self.n_max_boxes == 0: + device = gt_bboxes.device + return torch.full_like(pd_scores[..., 0], self.bg_idx).to(device), \ + torch.zeros_like(pd_bboxes).to(device), \ + torch.zeros_like(pd_scores).to(device), \ + torch.zeros_like(pd_scores[..., 0]).to(device), \ + torch.zeros(*pd_bboxes.shape[:2], 40, 40) + + cycle, step, self.bs = (1, self.bs, self.bs) if self.n_max_boxes <= 100 else (self.bs, 1, 1) + target_labels_lst, target_bboxes_lst, target_scores_lst, fg_mask_lst, target_segmasks_lst = [], [], [], [], [] + # loop batch dim in case of numerous object box + for i in range(cycle): + start, end = i*step, (i+1)*step + pd_scores_ = pd_scores[start:end, ...] + pd_bboxes_ = pd_bboxes[start:end, ...] + gt_labels_ = gt_labels[start:end, ...] + gt_bboxes_ = gt_bboxes[start:end, ...] + mask_gt_ = mask_gt[start:end, ...] + gt_segmasks_ = gt_segmasks[start:end, ...] + + mask_pos, align_metric, overlaps = self.get_pos_mask( + pd_scores_, pd_bboxes_, gt_labels_, gt_bboxes_, anc_points, mask_gt_) + + target_gt_idx, fg_mask, mask_pos = select_highest_overlaps( + mask_pos, overlaps, self.n_max_boxes) + + # assigned target + target_labels, target_bboxes, target_scores, target_segmasks = self.get_targets( + gt_labels_, gt_bboxes_, target_gt_idx, fg_mask, gt_segmasks_) + + # normalize + align_metric *= mask_pos + pos_align_metrics = align_metric.max(axis=-1, keepdim=True)[0] + pos_overlaps = (overlaps * mask_pos).max(axis=-1, keepdim=True)[0] + norm_align_metric = (align_metric * pos_overlaps / (pos_align_metrics + self.eps)).max(-2)[0].unsqueeze(-1) + target_scores = target_scores * norm_align_metric + + # append + target_labels_lst.append(target_labels) + target_bboxes_lst.append(target_bboxes) + target_scores_lst.append(target_scores) + fg_mask_lst.append(fg_mask) + target_segmasks_lst.append(target_segmasks) + + # concat + target_labels = torch.cat(target_labels_lst, 0) + target_bboxes = torch.cat(target_bboxes_lst, 0) + target_scores = torch.cat(target_scores_lst, 0) + fg_mask = torch.cat(fg_mask_lst, 0) + target_segmasks = torch.cat(target_segmasks_lst, 0) + + return target_labels, target_bboxes, target_scores, fg_mask.bool(), target_segmasks + + def get_pos_mask(self, + pd_scores, + pd_bboxes, + gt_labels, + gt_bboxes, + anc_points, + mask_gt): + + # get anchor_align metric + align_metric, overlaps = self.get_box_metrics(pd_scores, pd_bboxes, gt_labels, gt_bboxes) + # get in_gts mask + mask_in_gts = select_candidates_in_gts(anc_points, gt_bboxes) + # get topk_metric mask + mask_topk = self.select_topk_candidates( + align_metric * mask_in_gts, topk_mask=mask_gt.repeat([1, 1, self.topk]).bool()) + # merge all mask to a final mask + mask_pos = mask_topk * mask_in_gts * mask_gt + + return mask_pos, align_metric, overlaps + + def get_box_metrics(self, + pd_scores, + pd_bboxes, + gt_labels, + gt_bboxes): + + pd_scores = pd_scores.permute(0, 2, 1) + gt_labels = gt_labels.to(torch.long) + ind = torch.zeros([2, self.bs, self.n_max_boxes], dtype=torch.long) + ind[0] = torch.arange(end=self.bs).view(-1, 1).repeat(1, self.n_max_boxes) + ind[1] = gt_labels.squeeze(-1) + bbox_scores = pd_scores[ind[0], ind[1]] + + overlaps = iou_calculator(gt_bboxes, pd_bboxes) + align_metric = bbox_scores.pow(self.alpha) * overlaps.pow(self.beta) + + return align_metric, overlaps + + def select_topk_candidates(self, + metrics, + largest=True, + topk_mask=None): + + num_anchors = metrics.shape[-1] + topk_metrics, topk_idxs = torch.topk( + metrics, self.topk, axis=-1, largest=largest) + if topk_mask is None: + topk_mask = (topk_metrics.max(axis=-1, keepdim=True) > self.eps).tile( + [1, 1, self.topk]) + topk_idxs = torch.where(topk_mask, topk_idxs, torch.zeros_like(topk_idxs)) + is_in_topk = F.one_hot(topk_idxs, num_anchors).sum(axis=-2) + is_in_topk = torch.where(is_in_topk > 1, + torch.zeros_like(is_in_topk), is_in_topk) + return is_in_topk.to(metrics.dtype) + + def get_targets(self, + gt_labels, + gt_bboxes, + target_gt_idx, + fg_mask, + gt_segmasks): + + # assigned target labels + batch_ind = torch.arange(end=self.bs, dtype=torch.int64, device=gt_labels.device)[...,None] + target_gt_idx = target_gt_idx + batch_ind * self.n_max_boxes + target_labels = gt_labels.long().flatten()[target_gt_idx] + + # assigned target boxes + target_bboxes = gt_bboxes.reshape([-1, 4])[target_gt_idx] + + # assigned target scores + target_labels[target_labels<0] = 0 + target_scores = F.one_hot(target_labels, self.num_classes) + fg_scores_mask = fg_mask[:, :, None].repeat(1, 1, self.num_classes) + target_scores = torch.where(fg_scores_mask > 0, target_scores, + torch.full_like(target_scores, 0)) + m_shape = gt_segmasks.shape[-2:] + target_segmasks = gt_segmasks.reshape([-1, m_shape[0], m_shape[1]])[target_gt_idx] + print(target_gt_idx.shape, fg_mask.shape) + + + return target_labels, target_bboxes, target_scores, target_segmasks diff --git a/yolov6/core/engine.py b/yolov6/core/engine.py index 10545135..663a0812 100644 --- a/yolov6/core/engine.py +++ b/yolov6/core/engine.py @@ -21,7 +21,6 @@ from yolov6.models.yolo import build_model from yolov6.models.yolo_lite import build_model as build_lite_model -from yolov6.models.losses.loss import ComputeLoss as ComputeLoss from yolov6.models.losses.loss_fuseab import ComputeLoss as ComputeLoss_ab from yolov6.models.losses.loss_distill import ComputeLoss as ComputeLoss_distill from yolov6.models.losses.loss_distill_ns import ComputeLoss as ComputeLoss_distill_ns @@ -35,6 +34,8 @@ from yolov6.utils.general import download_ckpt + + class Trainer: def __init__(self, args, cfg, device): self.args = args @@ -42,6 +43,8 @@ def __init__(self, args, cfg, device): self.device = device self.max_epoch = args.epochs + + if args.resume: self.ckpt = torch.load(args.resume, map_location='cpu') @@ -105,8 +108,8 @@ def __init__(self, args, cfg, device): self.height = args.height self.width = args.width - self.loss_num = 3 - self.loss_info = ['Epoch', 'lr', 'iou_loss', 'dfl_loss', 'cls_loss'] + self.loss_num = 4 + self.loss_info = ['Epoch', 'lr', 'iou_loss', 'dfl_loss', 'cls_loss', "seg_loss"] if self.args.distill: self.loss_num += 1 self.loss_info += ['cwd_loss'] @@ -140,7 +143,9 @@ def train_one_epoch(self, epoch_num): # Training one batch data. def train_in_steps(self, epoch_num, step_num): - images, targets = self.prepro_data(self.batch_data, self.device) + # torch.cuda.synchronize() + # qq1 = time.time() + images, targets, segmasks = self.prepro_data(self.batch_data, self.device) # plot train_batch and save to tensorboard once an epoch if self.write_trainbatch_tb and self.main_process and self.step == 0: self.plot_train_batch(images, targets) @@ -149,7 +154,11 @@ def train_in_steps(self, epoch_num, step_num): # forward with amp.autocast(enabled=self.device != 'cpu'): _, _, batch_height, batch_width = images.shape + # torch.cuda.synchronize() + # qq2 = time.time() preds, s_featmaps = self.model(images) + # torch.cuda.synchronize() + # qq3 = time.time() if self.args.distill: with torch.no_grad(): t_preds, t_featmaps = self.teacher_model(images) @@ -159,18 +168,21 @@ def train_in_steps(self, epoch_num, step_num): batch_height, batch_width) elif self.args.fuse_ab: - total_loss, loss_items = self.compute_loss((preds[0],preds[3],preds[4]), targets, epoch_num, - step_num, batch_height, batch_width) # YOLOv6_af - total_loss_ab, loss_items_ab = self.compute_loss_ab(preds[:3], targets, epoch_num, step_num, - batch_height, batch_width) # YOLOv6_ab + total_loss, loss_items = self.compute_loss((preds[0],preds[3],preds[4], preds[5]), targets, epoch_num, + step_num, batch_height, batch_width, segmasks) # YOLOv6_af + total_loss_ab, loss_items_ab = self.compute_loss_ab((preds[0],preds[1],preds[2], preds[6]), targets, epoch_num, step_num, + batch_height, batch_width, segmasks) # YOLOv6_ab total_loss += total_loss_ab loss_items += loss_items_ab else: - total_loss, loss_items = self.compute_loss(preds, targets, epoch_num, step_num, - batch_height, batch_width) # YOLOv6_af + total_loss, loss_items = self.compute_loss((preds[0],preds[3],preds[4], preds[5]), targets, epoch_num, step_num, + batch_height, batch_width, segmasks, img=images) # YOLOv6_af if self.rank != -1: total_loss *= self.world_size + # torch.cuda.synchronize() + # qq4 = time.time() # backward + # print("prepare : {}s | model : {}s | loss : {}s".format(qq2 - qq1, qq3 - qq2, qq4 - qq3)) self.scaler.scale(total_loss).backward() self.loss_items = loss_items self.update_optimizer() @@ -186,12 +198,12 @@ def after_epoch(self): is_val_epoch = (remaining_epochs == 0) or ((not self.args.eval_final_only) and ((self.epoch + 1) % eval_interval == 0)) if is_val_epoch: self.eval_model() - self.ap = self.evaluate_results[1] + self.ap = self.evaluate_results[3] self.best_ap = max(self.ap, self.best_ap) # save ckpt ckpt = { - 'model': deepcopy(de_parallel(self.model)).half(), - 'ema': deepcopy(self.ema.ema).half(), + 'model': deepcopy(de_parallel(self.model)), + 'ema': deepcopy(self.ema.ema), 'updates': self.ema.updates, 'optimizer': self.optimizer.state_dict(), 'scheduler': self.scheduler.state_dict(), @@ -231,7 +243,10 @@ def eval_model(self): task='train', specific_shape=self.specific_shape, height=self.height, - width=self.width + width=self.width, + do_pr_metric=True, + do_coco_metric=False, + issolo=self.cfg.model.head.issolo ) else: def get_cfg_value(cfg_dict, value_str, default_value): @@ -263,10 +278,10 @@ def get_cfg_value(cfg_dict, value_str, default_value): width=self.width ) - LOGGER.info(f"Epoch: {self.epoch} | mAP@0.5: {results[0]} | mAP@0.50:0.95: {results[1]}") - self.evaluate_results = results[:2] + LOGGER.info(f"Epoch: {self.epoch} | box_mAP@0.5: {results[0]} | box_mAP@0.50:0.95: {results[1]} | mask_mAP@0.5: {results[2]} | mask_mAP@0.50:0.95: {results[3]}") + self.evaluate_results = [results[1], results[3]] # plot validation predictions - self.plot_val_pred(vis_outputs, vis_paths) + # self.plot_val_pred(vis_outputs, vis_paths) def before_train_loop(self): @@ -286,6 +301,10 @@ def before_train_loop(self): self.best_ap = self.evaluate_results[1] self.best_stop_strong_aug_ap = self.evaluate_results[1] + if self.cfg.model.head.issolo: + from yolov6.models.losses.seg_loss_solo_main import ComputeLoss as ComputeLoss + else: + from yolov6.models.losses.seg_loss import ComputeLoss as ComputeLoss self.compute_loss = ComputeLoss(num_classes=self.data_dict['nc'], ori_img_size=self.img_size, @@ -293,6 +312,7 @@ def before_train_loop(self): use_dfl=self.cfg.model.head.use_dfl, reg_max=self.cfg.model.head.reg_max, iou_type=self.cfg.model.head.iou_type, + nm=self.cfg.model.head.nm, fpn_strides=self.cfg.model.head.strides) if self.args.fuse_ab: @@ -305,7 +325,7 @@ def before_train_loop(self): fpn_strides=self.cfg.model.head.strides, ) if self.args.distill : - if self.cfg.model.type in ['YOLOv6n','YOLOv6s']: + if self.cfg.model.type in ['YOLOv6n','YOLOv6s']: Loss_distill_func = ComputeLoss_distill_ns else: Loss_distill_func = ComputeLoss_distill @@ -404,7 +424,8 @@ def get_data_loader(args, cfg, data_dict): def prepro_data(batch_data, device): images = batch_data[0].to(device, non_blocking=True).float() / 255 targets = batch_data[1].to(device) - return images, targets + segmask = batch_data[4].to(device) + return images, targets, segmask def get_model(self, args, cfg, nc, device): if 'YOLOv6-lite' in cfg.model.type: @@ -588,4 +609,4 @@ def quant_setup(self, model, cfg, device): # QAT flow load calibrated model assert cfg.qat.calib_pt is not None, 'Please provide calibrated model' model.load_state_dict(torch.load(cfg.qat.calib_pt)['model'].float().state_dict()) - model.to(device) + model.to(device) \ No newline at end of file diff --git a/yolov6/core/evaler.py b/yolov6/core/evaler.py index e79f51be..15c8bc76 100644 --- a/yolov6/core/evaler.py +++ b/yolov6/core/evaler.py @@ -7,13 +7,19 @@ import torch import yaml from pathlib import Path +import cv2 +from multiprocessing.pool import ThreadPool + + from pycocotools.coco import COCO from pycocotools.cocoeval import COCOeval +import torch.nn.functional as F + from yolov6.data.data_load import create_dataloader from yolov6.utils.events import LOGGER, NCOLS -from yolov6.utils.nms import non_max_suppression +from yolov6.utils.nms import non_max_suppression_seg, non_max_suppression_seg_solo from yolov6.utils.general import download_ckpt from yolov6.utils.checkpoint import load_checkpoint from yolov6.utils.torch_utils import time_sync, get_model_info @@ -87,24 +93,25 @@ def init_data(self, dataloader, task): self.is_coco = self.data.get("is_coco", False) self.ids = self.coco80_to_coco91_class() if self.is_coco else list(range(1000)) if task != 'train': + pad = 0.0 eval_hyp = { "shrink_size":self.shrink_size, } rect = self.infer_on_rect - pad = 0.5 if rect else 0.0 dataloader = create_dataloader(self.data[task if task in ('train', 'val', 'test') else 'val'], - self.img_size, self.batch_size, self.stride, hyp=eval_hyp, check_labels=True, pad=pad, rect=rect, + self.img_size, self.batch_size, self.stride, hyp=eval_hyp, check_labels=True, pad=0.5, rect=True, data_dict=self.data, task=task, specific_shape=self.specific_shape, height=self.height, width=self.width)[0] return dataloader - def predict_model(self, model, dataloader, task): + def predict_model(self, model, dataloader, task, issolo=False, weight_nums=66, bias_nums=1, dyconv_channels=66): '''Model prediction Predicts the whole dataset and gets the prediced results and inference time. ''' self.speed_result = torch.zeros(4, device=self.device) pred_results = [] pbar = tqdm(dataloader, desc=f"Inferencing model in {task} datasets.", ncols=NCOLS) - + weight_nums = [weight_nums] + bias_nums = [bias_nums] # whether to compute metric and plot PR curve and P、R、F1 curve under iou50 match rule if self.do_pr_metric: stats, ap = [], [] @@ -115,7 +122,7 @@ def predict_model(self, model, dataloader, task): from yolov6.utils.metrics import ConfusionMatrix confusion_matrix = ConfusionMatrix(nc=model.nc) - for i, (imgs, targets, paths, shapes) in enumerate(pbar): + for i, (imgs, targets, paths, shapes, masks) in enumerate(pbar): # pre-process t1 = time_sync() imgs = imgs.to(self.device, non_blocking=True) @@ -125,12 +132,23 @@ def predict_model(self, model, dataloader, task): # Inference t2 = time_sync() - outputs, _ = model(imgs) + toutputs, _ = model(imgs) self.speed_result[2] += time_sync() - t2 # inference time # post-process t3 = time_sync() - outputs = non_max_suppression(outputs, self.conf_thres, self.iou_thres, multi_label=True) + if not issolo: + loutputs = non_max_suppression_seg(toutputs, self.conf_thres, self.iou_thres, multi_label=True) + else: + loutputs = non_max_suppression_seg_solo(toutputs, self.conf_thres, self.iou_thres, multi_label=True) + protos = toutputs[1][0] + segments = [] + segconf = [loutputs[li][..., 0:] for li in range(len(loutputs))] + outputs = [loutputs[li][..., :6] for li in range(len(loutputs))] + if not issolo: + segments = [self.handle_proto_test([protos[li].reshape(1, *(protos[li].shape[-3:]))], segconf[li], imgs.shape[-2:]) for li in range(len(loutputs))] + else: + segments = [self.handle_proto_solo([protos[li].reshape(1, *(protos[li].shape[-3:]))], segconf[li], imgs.shape[-2:], weight_sums=weight_nums, bias_sums=bias_nums, dyconv=dyconv_channels) for li in range(len(loutputs))] self.speed_result[3] += time_sync() - t3 # post-process time self.speed_result[0] += len(outputs) @@ -139,7 +157,7 @@ def predict_model(self, model, dataloader, task): eval_outputs = copy.deepcopy([x.detach().cpu() for x in outputs]) # save result - pred_results.extend(self.convert_to_coco_format(outputs, imgs, paths, shapes, self.ids)) + # pred_results.extend(self.convert_to_coco_format_seg(outputs, imgs, paths, shapes, self.ids, segments)) # for tensorboard visualization, maximum images to show: 8 if i == 0: @@ -153,25 +171,29 @@ def predict_model(self, model, dataloader, task): # Statistics per image # This code is based on # https://github.com/ultralytics/yolov5/blob/master/val.py - for si, pred in enumerate(eval_outputs): + for si, (pred, pred_masks) in enumerate(zip(eval_outputs, segments)): labels = targets[targets[:, 0] == si, 1:] nl = len(labels) tcls = labels[:, 0].tolist() if nl else [] # target class seen += 1 + correct_masks = torch.zeros(len(pred), niou, dtype=torch.bool) # init + correct = torch.zeros(len(pred), niou, dtype=torch.bool) # init if len(pred) == 0: if nl: - stats.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls)) + stats.append((correct_masks, correct, torch.Tensor(), torch.Tensor(), tcls)) continue + # Masks + midx = targets[:, 0] == si + gt_masks = masks[midx] # Predictions predn = pred.clone() self.scale_coords(imgs[si].shape[1:], predn[:, :4], shapes[si][0], shapes[si][1]) # native-space pred # Assign all predictions as incorrect - correct = torch.zeros(pred.shape[0], niou, dtype=torch.bool) + if nl: - from yolov6.utils.nms import xywh2xyxy # target boxes @@ -183,49 +205,122 @@ def predict_model(self, model, dataloader, task): labelsn = torch.cat((labels[:, 0:1], tbox), 1) # native-space labels - from yolov6.utils.metrics import process_batch + from yolov6.utils.metrics import process_batch correct = process_batch(predn, labelsn, iouv) + correct_masks = process_batch(predn, labelsn, iouv, pred_masks, gt_masks, overlap=False, masks=True) if self.plot_confusion_matrix: confusion_matrix.process_batch(predn, labelsn) # Append statistics (correct, conf, pcls, tcls) - stats.append((correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls)) + + + stats.append((correct_masks.cpu(), correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls)) if self.do_pr_metric: # Compute statistics stats = [np.concatenate(x, 0) for x in zip(*stats)] # to numpy if len(stats) and stats[0].any(): - - from yolov6.utils.metrics import ap_per_class - p, r, ap, f1, ap_class = ap_per_class(*stats, plot=self.plot_curve, save_dir=self.save_dir, names=model.names) - AP50_F1_max_idx = len(f1.mean(0)) - f1.mean(0)[::-1].argmax() -1 - LOGGER.info(f"IOU 50 best mF1 thershold near {AP50_F1_max_idx/1000.0}.") - ap50, ap = ap[:, 0], ap.mean(1) # AP@0.5, AP@0.5:0.95 - mp, mr, map50, map = p[:, AP50_F1_max_idx].mean(), r[:, AP50_F1_max_idx].mean(), ap50.mean(), ap.mean() - nt = np.bincount(stats[3].astype(np.int64), minlength=model.nc) # number of targets per class + from yolov6.utils.metrics import ap_per_class_box_and_mask, Metrics + metrics = Metrics() + # v5 method + results = ap_per_class_box_and_mask(*stats, plot=self.plot_curve, save_dir=self.save_dir, names=model.names) + metrics.update(results) + nt = np.bincount(stats[4].astype(np.int64), minlength=model.nc) # number of targets per class # Print results - s = ('%-16s' + '%12s' * 7) % ('Class', 'Images', 'Labels', 'P@.5iou', 'R@.5iou', 'F1@.5iou', 'mAP@.5', 'mAP@.5:.95') + s = ('%22s' + '%15s' * 10) % ('Class', 'Images', 'Instances', 'Box(P', 'R', 'mAP50', 'mAP50-95)', 'Mask(P', 'R', + 'mAP50', 'mAP50-95)') LOGGER.info(s) - pf = '%-16s' + '%12i' * 2 + '%12.3g' * 5 # print format - LOGGER.info(pf % ('all', seen, nt.sum(), mp, mr, f1.mean(0)[AP50_F1_max_idx], map50, map)) - - self.pr_metric_result = (map50, map) - - # Print results per class - if self.verbose and model.nc > 1: - for i, c in enumerate(ap_class): - LOGGER.info(pf % (model.names[c], seen, nt[c], p[i, AP50_F1_max_idx], r[i, AP50_F1_max_idx], - f1[i, AP50_F1_max_idx], ap50[i], ap[i])) + pf = '%22s' + '%15i' * 2 + '%11.5g' * 8 # print format + mr = metrics.mean_results() + LOGGER.info(pf % ('all', seen, nt.sum(), *mr)) + return [mr[2], mr[3], mr[6], mr[7]], [], [] if self.plot_confusion_matrix: confusion_matrix.plot(save_dir=self.save_dir, names=list(model.names)) else: - LOGGER.info("Calculate metric failed, might check dataset.") - self.pr_metric_result = (0.0, 0.0) + return [0, 0, 0, 0], [], [] + + return pred_results - return pred_results, vis_outputs, vis_paths + def parse_dynamic_params(self, flatten_kernels, weight_nums, bias_nums, dyconv_channels): + """split kernel head prediction to conv weight and bias.""" + n_inst = flatten_kernels.size(0) + n_layers = len(weight_nums) + params_splits = list( + torch.split_with_sizes( + flatten_kernels, weight_nums + bias_nums, dim=1)) + weight_splits = params_splits[:n_layers] + bias_splits = params_splits[n_layers:] + for i in range(n_layers): + if i < n_layers - 1: + weight_splits[i] = weight_splits[i].reshape( + n_inst * dyconv_channels, -1, 1, 1) + bias_splits[i] = bias_splits[i].reshape(n_inst * + dyconv_channels) + else: + weight_splits[i] = weight_splits[i].reshape(n_inst, -1, 1, 1) + bias_splits[i] = bias_splits[i].reshape(n_inst) + + return weight_splits, bias_splits + + def handle_proto_solo(self, proto_list, oconfs, imgshape, weight_sums=66, bias_sums=1, dyconv=66, img_orishape=None): + ''' + proto_list: [(bs, 32, w, h), ...] + conf: (bs, l, 33) -> which_proto, 32 + ''' + def handle_proto_coord(proto): + _ = proto.shape[-2:] + x = torch.arange(0, 1, step = 1 / _[1]).unsqueeze(0).unsqueeze(0).repeat(1, _[0], 1).to(proto.dtype).to(proto.device) + y = torch.arange(0, 1, step = 1 / _[0]).unsqueeze(0).T.unsqueeze(0).repeat(1, 1, _[1]).to(proto.dtype).to(proto.device) + return torch.cat([proto, x, y]).reshape(1, -1, *_) + + 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 [n, h, w] 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)) + + conf = oconfs[..., 6:] + if conf.shape[0] == 0: + return None + + xyxy = oconfs[..., :4] + confs = conf[..., 1:] + proto = proto_list[0][0] + proto = handle_proto_coord(proto) + s = proto.shape[-2:] + num_inst = confs.shape[0] + proto = proto.reshape(1, -1, *proto.shape[-2:]) + weights, biases = self.parse_dynamic_params(confs, weight_nums=weight_sums, bias_nums=bias_sums, dyconv_channels=dyconv) + n_layers = len(weights) + for i, (weight, bias) in enumerate(zip(weights, biases)): + x = F.conv2d( + proto, weight, bias=bias, stride=1, padding=0, groups=1) + if i < n_layers - 1: + x = F.relu(x) + x = x.reshape(num_inst, *proto.shape[-2:]).unsqueeze(0) + seg = x.sigmoid() + masks = F.interpolate(seg, imgshape, mode='bilinear', align_corners=False)[0] + if img_orishape: + masks_ori = F.interpolate(seg, img_orishape, mode='nearest')[0] + else: + masks_ori = None + masks = crop_mask(masks, xyxy).gt_(0.5) + masks = masks.gt_(0.5) + return masks + def eval_model(self, pred_results, model, dataloader, task): @@ -282,7 +377,8 @@ def eval_model(self, pred_results, model, dataloader, task): label_count_dicts[nc_i]["images"].add(ann_i["image_id"]) label_count_dicts[nc_i]["anns"] += 1 - s = ('%-16s' + '%12s' * 7) % ('Class', 'Labeled_images', 'Labels', 'P@.5iou', 'R@.5iou', 'F1@.5iou', 'mAP@.5', 'mAP@.5:.95') + s = ('%22s' + '%11s' * 10) % ('Class', 'Images', 'Instances', 'Box(P', 'R', 'mAP50', 'mAP50-95)', 'Mask(P', 'R', + 'mAP50', 'mAP50-95)') LOGGER.info(s) #IOU , all p, all cats, all gt, maxdet 100 coco_p = cocoEval.eval['precision'] @@ -383,6 +479,51 @@ def convert_to_coco_format(self, outputs, imgs, paths, shapes, ids): pred_results.append(pred_data) return pred_results + def convert_to_coco_format_seg(self, outputs, imgs, paths, shapes, ids, masks): + + from pycocotools.mask import encode + import time + + def single_encode(x): + rle = encode(np.asarray(x[:, :, None], order='F', dtype='uint8'))[0] + rle['counts'] = rle['counts'].decode('utf-8') + return rle + + + pred_results = [] + for i, pred in enumerate(outputs): + if len(pred) == 0: + continue + pred_masks = masks[i].cpu().numpy() + pred_masks = np.transpose(pred_masks, (2, 0, 1)) + a = time.time() + with ThreadPool(64) as pool: + rles = pool.map(single_encode, pred_masks) + print("rle time") + b = time.time() + path, shape = Path(paths[i]), shapes[i][0] + self.scale_coords(imgs[i].shape[1:], pred[:, :4], shape, shapes[i][1]) + image_id = int(path.stem) if self.is_coco else path.stem + bboxes = self.box_convert(pred[:, 0:4]) + bboxes[:, :2] -= bboxes[:, 2:] / 2 + cls = pred[:, 5] + scores = pred[:, 4] + for ind in range(pred.shape[0]): + category_id = ids[int(cls[ind])] + bbox = [round(x, 3) for x in bboxes[ind].tolist()] + score = round(scores[ind].item(), 5) + pred_data = { + "image_id": image_id, + "category_id": category_id, + "bbox": bbox, + "score": score, + 'segmentation': rles[i] + } + pred_results.append(pred_data) + c = time.time() + print(b-a, c-b) + return pred_results + @staticmethod def check_task(task): if task not in ['train', 'val', 'test', 'speed']: @@ -543,3 +684,48 @@ def convert_to_coco_format_trt(nums, boxes, scores, classes, paths, shapes, ids) pred_results.extend(convert_to_coco_format_trt(nums, boxes, scores, classes, paths, shapes, self.ids)) self.speed_result[0] += self.batch_size return dataloader, pred_results + + + + @staticmethod + def handle_proto_test(proto_list, oconfs, imgshape, img_orishape=None): + ''' + proto_list: [(bs, 32, w, h), ...] + conf: (bs, l, 33) -> which_proto, 32 + ''' + + + 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 [n, h, w] 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)) + + conf = oconfs[..., 6:] + if conf.shape[0] == 0: + return None + + xyxy = oconfs[..., :4] + confs = conf[..., 1:] + proto = proto_list[0] + + s = proto.shape[-2:] + seg = ((confs@proto.reshape(proto.shape[0], proto.shape[1], -1)).reshape(proto.shape[0], confs.shape[0], *s)) + seg = seg.sigmoid() + masks = F.interpolate(seg, imgshape, mode='bilinear', align_corners=False)[0] + if img_orishape: + masks_ori = F.interpolate(seg, img_orishape, mode='nearest')[0] + else: + masks_ori = None + masks = crop_mask(masks, xyxy).gt_(0.5) + return masks diff --git a/yolov6/core/inferer.py b/yolov6/core/inferer.py index cea6586d..3fef6b35 100644 --- a/yolov6/core/inferer.py +++ b/yolov6/core/inferer.py @@ -13,11 +13,13 @@ from PIL import ImageFont from collections import deque +import torch.nn.functional as F + from yolov6.utils.events import LOGGER, load_yaml from yolov6.layers.common import DetectBackend from yolov6.data.data_augment import letterbox from yolov6.data.datasets import LoadData -from yolov6.utils.nms import non_max_suppression +from yolov6.utils.nms import non_max_suppression_seg, non_max_suppression_seg_solo from yolov6.utils.torch_utils import get_model_info class Inferer: @@ -67,10 +69,13 @@ def model_switch(self, model, img_size): LOGGER.info("Switch model to deploy modality.") - def infer(self, conf_thres, iou_thres, classes, agnostic_nms, max_det, save_dir, save_txt, save_img, hide_labels, hide_conf, view_img=True): + def infer(self, conf_thres, iou_thres, classes, agnostic_nms, max_det, save_dir, save_txt, save_img, hide_labels, hide_conf, view_img=True, issolo=True, weight_nums=66, bias_nums=1, dyconv_channels=66): ''' Model Inference and results visualization ''' vid_path, vid_writer, windows = None, None, [] + print(issolo) fps_calculator = CalcFPS() + weight_nums = [weight_nums] + bias_nums = [bias_nums] for img_src, img_path, vid_cap in tqdm(self.files): img, img_src = self.process_image(img_src, self.img_size, self.stride, self.half) img = img.to(self.device) @@ -79,15 +84,31 @@ def infer(self, conf_thres, iou_thres, classes, agnostic_nms, max_det, save_dir, # expand for batch dim t1 = time.time() pred_results = self.model(img) - det = non_max_suppression(pred_results, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)[0] + if not issolo: + loutputs = non_max_suppression_seg(pred_results, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det) + else: + loutputs = non_max_suppression_seg_solo(pred_results, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det) + protos = pred_results[1][0] + segments = [] + print(len(loutputs)) + segconf = [loutputs[li][..., 0:] for li in range(len(loutputs))] + det = [loutputs[li][..., :6] for li in range(len(loutputs))][0] + if not issolo: + segments = [self.handle_proto_test([protos[li].reshape(1, *(protos[li].shape[-3:]))], segconf[li], img.shape[-2:]) for li in range(len(loutputs))][0] + else: + segments = [self.handle_proto_solo([protos[li].reshape(1, *(protos[li].shape[-3:]))], segconf[li], img.shape[-2:], weight_sums=weight_nums, bias_sums=bias_nums, dyconv=dyconv_channels) for li in range(len(loutputs))][0] t2 = time.time() + + if self.webcam: save_path = osp.join(save_dir, self.webcam_addr) txt_path = osp.join(save_dir, self.webcam_addr) else: # Create output files in nested dirs that mirrors the structure of the images' dirs - rel_path = osp.relpath(osp.dirname(img_path), osp.dirname(self.source)) + print(osp.dirname(img_path)) + print(osp.dirname(self.source)) + rel_path = "test" save_path = osp.join(save_dir, rel_path, osp.basename(img_path)) # im.jpg txt_path = osp.join(save_dir, rel_path, 'labels', osp.splitext(osp.basename(img_path))[0]) os.makedirs(osp.join(save_dir, rel_path), exist_ok=True) @@ -98,9 +119,14 @@ def infer(self, conf_thres, iou_thres, classes, agnostic_nms, max_det, save_dir, # check image and font assert img_ori.data.contiguous, 'Image needs to be contiguous. Please apply to input images with np.ascontiguousarray(im).' self.font_check() - if len(det): det[:, :4] = self.rescale(img.shape[2:], det[:, :4], img_src.shape).round() + + + ii = 0 + segments = self.rescale_mask(img.shape[2:], segments.cpu().numpy(), img_src.shape) + print(segments.shape) + segments = segments.transpose(2, 0, 1) for *xyxy, conf, cls in reversed(det): if save_txt: # Write to file xywh = (self.box_convert(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh @@ -109,13 +135,16 @@ def infer(self, conf_thres, iou_thres, classes, agnostic_nms, max_det, save_dir, f.write(('%g ' * len(line)).rstrip() % line + '\n') if save_img: + print(cls) class_num = int(cls) # integer class label = None if hide_labels else (self.class_names[class_num] if hide_conf else f'{self.class_names[class_num]} {conf:.2f}') - self.plot_box_and_label(img_ori, max(round(sum(img_ori.shape) / 2 * 0.003), 2), xyxy, label, color=self.generate_colors(class_num, True)) + img_ori = self.plot_box_and_label(img_ori, max(round(sum(img_ori.shape) / 2 * 0.003), 2), xyxy, label, color=self.generate_colors(class_num, True), segment=segments[ii]) + ii += 1 img_src = np.asarray(img_ori) + # FPS counter fps_calculator.update(1.0 / (t2 - t1)) avg_fps = fps_calculator.accumulate() @@ -187,6 +216,21 @@ def rescale(ori_shape, boxes, target_shape): return boxes + @staticmethod + def rescale_mask(ori_shape, masks, target_shape): + '''Rescale the output to the original image shape''' + ratio = min(ori_shape[0] / target_shape[0], ori_shape[1] / target_shape[1]) + padding = int((ori_shape[1] - target_shape[1] * ratio) / 2), int((ori_shape[0] - target_shape[0] * ratio) / 2) + + + masks = masks[:, padding[1]: ori_shape[0]- padding[1], padding[0]: ori_shape[1] - padding[0]] + masks = masks.transpose(1, 2, 0) + masks = cv2.resize(masks, target_shape[:2][::-1]) + if len(masks.shape) == 2: + masks = masks.reshape(*masks.shape, 1) + + return masks + def check_img_size(self, img_size, s=32, floor=0): """Make sure image size is a multiple of stride s in each dimension, and return a new shape list of image.""" if isinstance(img_size, int): # integer i.e. img_size=640 @@ -204,6 +248,200 @@ def make_divisible(self, x, divisor): # Upward revision the value x to make it evenly divisible by the divisor. return math.ceil(x / divisor) * divisor + @staticmethod + def handle_proto(proto_list, oconfs, imgshape, det): + ''' + proto_list: [(bs, 32, w, h), ...] + conf: (bs, l, 33) -> which_proto, 32 + ''' + 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 [n, h, w] 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)) + + conf = oconfs[..., 6:] + + xyxy = oconfs[..., :4] + which_proto = conf[..., 0] + confs = conf[..., 1:] + res = [] + protos = proto_list[0] + for i, proto in enumerate([protos, protos, protos]): + s = proto.shape[-2:] + tconfs = confs[which_proto[..., 0] == i] + if tconfs.shape[0] == 0: + continue + tseg = ((tconfs@proto.reshape(proto.shape[0], proto.shape[1], -1)).reshape(proto.shape[0], tconfs.shape[1], *s)) + print("a:") + print(which_proto[..., 0] == i) + tseg=tseg.sigmoid() + masks = F.interpolate(tseg, imgshape, mode='nearest')[0] + #return masks + print(xyxy[which_proto[..., 0] == i][0].shape) + masks = crop_mask(masks, xyxy[which_proto[..., 0] == i][0])[0] + res.append(masks.gt_(0.5)) + return torch.cat(res, dim = 0), xyxy[which_proto[..., 0] == i][0] + + + @staticmethod + def handle_proto_test(proto_list, oconfs, imgshape, img_orishape=None): + ''' + proto_list: [(bs, 32, w, h), ...] + conf: (bs, l, 33) -> which_proto, 32 + ''' + 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 [n, h, w] 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)) + + conf = oconfs[..., 6:] + if conf.shape[0] == 0: + return None + + xyxy = oconfs[..., :4] + confs = conf[..., 1:] + proto = proto_list[0] + s = proto.shape[-2:] + seg = ((confs@proto.reshape(proto.shape[0], proto.shape[1], -1)).reshape(proto.shape[0], confs.shape[0], *s)) + seg = seg.sigmoid() + masks = F.interpolate(seg, imgshape, mode='bilinear', align_corners=False)[0] + if img_orishape: + masks_ori = F.interpolate(seg, img_orishape, mode='nearest')[0] + else: + masks_ori = None + masks = crop_mask(masks, xyxy).gt_(0.5) + return masks + + # def handle_proto_solo(self, proto_list, oconfs, imgshape, weight_sums=66, bias_sums=66, dyconv=66, img_orishape=None): + # ''' + # proto_list: [(bs, 32, w, h), ...] + # conf: (bs, l, 33) -> which_proto, 32 + # ''' + # 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 [n, h, w] 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)) + + # conf = oconfs[..., 6:] + # if conf.shape[0] == 0: + # return None + + # xyxy = oconfs[..., :4] + # confs = conf[..., 1:] + # proto = proto_list[0] + # s = proto.shape[-2:] + # num_inst = confs.shape[0] + # proto = proto.reshape(1, -1, *proto.shape[-2:]) + # proto = proto.repeat(num_inst, 1, 1, 1) + # weights, biases = self.parse_dynamic_params(confs, weight_nums=weight_sums, bias_nums=bias_sums, dyconv_channels=dyconv) + # n_layers = len(weights) + # for i, (weight, bias) in enumerate(zip(weights, biases)): + # x = F.conv2d( + # proto, weight, bias=bias, stride=1, padding=0, groups=num_inst) + # if i < n_layers - 1: + # x = F.relu(x) + # x = x.reshape(num_inst, *proto.shape[-2:]) + # seg = x.sigmoid() + # masks = F.interpolate(seg, imgshape, mode='bilinear', align_corners=False)[0] + # if img_orishape: + # masks_ori = F.interpolate(seg, img_orishape, mode='nearest')[0] + # else: + # masks_ori = None + # masks = crop_mask(masks, xyxy).gt_(0.5) + # return masks + def handle_proto_solo(self, proto_list, oconfs, imgshape, weight_sums=66, bias_sums=1, dyconv=66, img_orishape=None): + ''' + proto_list: [(bs, 32, w, h), ...] + conf: (bs, l, 33) -> which_proto, 32 + ''' + def handle_proto_coord(proto): + _ = proto.shape[-2:] + x = torch.arange(0, 1, step = 1 / _[1]).unsqueeze(0).unsqueeze(0).repeat(1, _[0], 1).to(proto.dtype).to(proto.device) + y = torch.arange(0, 1, step = 1 / _[0]).unsqueeze(0).T.unsqueeze(0).repeat(1, 1, _[1]).to(proto.dtype).to(proto.device) + return torch.cat([proto, x, y]).reshape(1, -1, *_) + + 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 [n, h, w] 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)) + + conf = oconfs[..., 6:] + if conf.shape[0] == 0: + return None + + xyxy = oconfs[..., :4] + confs = conf[..., 1:] + proto = proto_list[0][0] + proto = handle_proto_coord(proto) + s = proto.shape[-2:] + num_inst = confs.shape[0] + proto = proto.reshape(1, -1, *proto.shape[-2:]) + weights, biases = self.parse_dynamic_params(confs, weight_nums=weight_sums, bias_nums=bias_sums, dyconv_channels=dyconv) + n_layers = len(weights) + for i, (weight, bias) in enumerate(zip(weights, biases)): + x = F.conv2d( + proto, weight, bias=bias, stride=1, padding=0, groups=1) + if i < n_layers - 1: + x = F.relu(x) + x = x.reshape(num_inst, *proto.shape[-2:]).unsqueeze(0) + seg = x.sigmoid() + masks = F.interpolate(seg, imgshape, mode='bilinear', align_corners=False)[0] + if img_orishape: + masks_ori = F.interpolate(seg, img_orishape, mode='nearest')[0] + else: + masks_ori = None + masks = crop_mask(masks, xyxy).gt_(0.5) + masks = masks.gt_(0.5) + return masks + + + + + @staticmethod def draw_text( img, @@ -237,9 +475,10 @@ def draw_text( return text_size @staticmethod - def plot_box_and_label(image, lw, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255), font=cv2.FONT_HERSHEY_COMPLEX): + def plot_box_and_label(image, lw, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255), font=cv2.FONT_HERSHEY_COMPLEX, segment=None): # Add one xyxy box to image with label p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3])) + common_color = [[128,0,0], [255,0,0],[255,0,255],[255,102,0],[51,51,0],[0,51,0],[51,204,204],[0,128,128],[0,204,255]] cv2.rectangle(image, p1, p2, color, thickness=lw, lineType=cv2.LINE_AA) if label: tf = max(lw - 1, 1) # font thickness @@ -249,6 +488,13 @@ def plot_box_and_label(image, lw, box, label='', color=(128, 128, 128), txt_colo cv2.rectangle(image, p1, p2, color, -1, cv2.LINE_AA) # filled cv2.putText(image, label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2), font, lw / 3, txt_color, thickness=tf, lineType=cv2.LINE_AA) + if segment is not None: + import random + ii=random.randint(0, len(common_color)-1) + colr = np.asarray(common_color[ii]) + colr = colr.reshape(1,3).repeat((image.shape[0] * image.shape[1]), axis = 0).reshape(image.shape[0], image.shape[1], 3) + image = cv2.addWeighted(image, 1, (colr * segment.reshape(*segment.shape[:2], 1)).astype(image.dtype), 0.8, 1) + return image @staticmethod def font_check(font='./yolov6/utils/Arial.ttf', size=10): @@ -280,6 +526,27 @@ def generate_colors(i, bgr=False): num = len(palette) color = palette[int(i) % num] return (color[2], color[1], color[0]) if bgr else color + + def parse_dynamic_params(self, flatten_kernels, weight_nums, bias_nums, dyconv_channels): + """split kernel head prediction to conv weight and bias.""" + n_inst = flatten_kernels.size(0) + n_layers = len(weight_nums) + params_splits = list( + torch.split_with_sizes( + flatten_kernels, weight_nums + bias_nums, dim=1)) + weight_splits = params_splits[:n_layers] + bias_splits = params_splits[n_layers:] + for i in range(n_layers): + if i < n_layers - 1: + weight_splits[i] = weight_splits[i].reshape( + n_inst * dyconv_channels, -1, 1, 1) + bias_splits[i] = bias_splits[i].reshape(n_inst * + dyconv_channels) + else: + weight_splits[i] = weight_splits[i].reshape(n_inst, -1, 1, 1) + bias_splits[i] = bias_splits[i].reshape(n_inst) + + return weight_splits, bias_splits class CalcFPS: def __init__(self, nsamples: int = 50): diff --git a/yolov6/data/data_augment.py b/yolov6/data/data_augment.py index 45df88e6..e21c3873 100644 --- a/yolov6/data/data_augment.py +++ b/yolov6/data/data_augment.py @@ -26,7 +26,7 @@ def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5): cv2.cvtColor(im_hsv, cv2.COLOR_HSV2BGR, dst=im) # no return needed -def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32): +def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=False, scaleup=True, stride=32): '''Resize and pad image while meeting stride-multiple constraints.''' shape = im.shape[:2] # current shape [height, width] if isinstance(new_shape, int): @@ -51,19 +51,22 @@ def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleu if shape[::-1] != new_unpad: # resize im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR) + top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border + return im, r, (left, top) -def mixup(im, labels, im2, labels2): +def mixup(im, labels, segments, im2, labels2, segments2): '''Applies MixUp augmentation https://arxiv.org/pdf/1710.09412.pdf.''' r = np.random.beta(32.0, 32.0) # mixup ratio, alpha=beta=32.0 im = (im * r + im2 * (1 - r)).astype(np.uint8) labels = np.concatenate((labels, labels2), 0) - return im, labels + segments = np.concatenate((segments, segments2), 0) + return im, labels, segments def box_candidates(box1, box2, wh_thr=2, ar_thr=20, area_thr=0.1, eps=1e-16): # box1(4,n), box2(4,n) @@ -78,19 +81,17 @@ def random_affine(img, labels=(), degrees=10, translate=.1, scale=.1, shear=10, new_shape=(640, 640)): '''Applies Random affine transformation.''' n = len(labels) - if isinstance(new_shape, int): - height = width = new_shape - else: - height, width = new_shape + height, width = new_shape M, s = get_transform_matrix(img.shape[:2], (height, width), degrees, scale, shear, translate) if (M != np.eye(3)).any(): # image changed img = cv2.warpAffine(img, M[:2], dsize=(width, height), borderValue=(114, 114, 114)) # Transform label coordinates + new_segments = [] if n: new = np.zeros((n, 4)) - + xy = np.ones((n * 4, 3)) xy[:, :2] = labels[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2) # x1y1, x2y2, x1y2, x2y1 xy = xy @ M.T # transform @@ -113,6 +114,7 @@ def random_affine(img, labels=(), degrees=10, translate=.1, scale=.1, shear=10, return img, labels + def get_transform_matrix(img_shape, new_shape, degrees, scale, shear, translate): new_height, new_width = new_shape # Center @@ -147,6 +149,7 @@ def mosaic_augmentation(shape, imgs, hs, ws, labels, hyp, specific_shape = False '''Applies Mosaic augmentation.''' assert len(imgs) == 4, "Mosaic augmentation of current version only supports 4 images." labels4 = [] + if not specific_shape: if isinstance(shape, list) or isinstance(shape, np.ndarray): target_height, target_width = shape @@ -180,15 +183,18 @@ def mosaic_augmentation(shape, imgs, hs, ws, labels, hyp, specific_shape = False # Labels labels_per_img = labels[i].copy() + if labels_per_img.size: boxes = np.copy(labels_per_img[:, 1:]) boxes[:, 0] = w * (labels_per_img[:, 1] - labels_per_img[:, 3] / 2) + padw # top left x boxes[:, 1] = h * (labels_per_img[:, 2] - labels_per_img[:, 4] / 2) + padh # top left y boxes[:, 2] = w * (labels_per_img[:, 1] + labels_per_img[:, 3] / 2) + padw # bottom right x boxes[:, 3] = h * (labels_per_img[:, 2] + labels_per_img[:, 4] / 2) + padh # bottom right y + labels_per_img[:, 1:] = boxes labels4.append(labels_per_img) + # Concat/clip labels labels4 = np.concatenate(labels4, 0) @@ -196,6 +202,7 @@ def mosaic_augmentation(shape, imgs, hs, ws, labels, hyp, specific_shape = False # np.clip(x, 0, 2 * s, out=x) labels4[:, 1::2] = np.clip(labels4[:, 1::2], 0, 2 * target_width) labels4[:, 2::2] = np.clip(labels4[:, 2::2], 0, 2 * target_height) + # Augment img4, labels4 = random_affine(img4, labels4, @@ -205,4 +212,4 @@ def mosaic_augmentation(shape, imgs, hs, ws, labels, hyp, specific_shape = False shear=hyp['shear'], new_shape=(target_height, target_width)) - return img4, labels4 + return img4, labels4 \ No newline at end of file diff --git a/yolov6/data/data_load.py b/yolov6/data/data_load.py index e68e8d71..923ab1f2 100644 --- a/yolov6/data/data_load.py +++ b/yolov6/data/data_load.py @@ -7,7 +7,7 @@ import torch.distributed as dist from torch.utils.data import dataloader, distributed -from .datasets import TrainValDataset +from .seg_datasets import TrainValDataset from yolov6.utils.events import LOGGER from yolov6.utils.torch_utils import torch_distributed_zero_first diff --git a/yolov6/data/seg_data_augment.py b/yolov6/data/seg_data_augment.py new file mode 100644 index 00000000..6a2c87b6 --- /dev/null +++ b/yolov6/data/seg_data_augment.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- +# This code is based on +# https://github.com/ultralytics/yolov5/blob/master/utils/dataloaders.py + +import math +import random + +import cv2 +import numpy as np + + +def augment_hsv(im, hgain=0.5, sgain=0.5, vgain=0.5): + '''HSV color-space augmentation.''' + if hgain or sgain or vgain: + r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1 # random gains + hue, sat, val = cv2.split(cv2.cvtColor(im, cv2.COLOR_BGR2HSV)) + dtype = im.dtype # uint8 + + x = np.arange(0, 256, dtype=r.dtype) + lut_hue = ((x * r[0]) % 180).astype(dtype) + lut_sat = np.clip(x * r[1], 0, 255).astype(dtype) + lut_val = np.clip(x * r[2], 0, 255).astype(dtype) + + im_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))) + cv2.cvtColor(im_hsv, cv2.COLOR_HSV2BGR, dst=im) # no return needed + + +def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32): + '''Resize and pad image while meeting stride-multiple constraints.''' + shape = im.shape[:2] # current shape [height, width] + if isinstance(new_shape, int): + new_shape = (new_shape, new_shape) + elif isinstance(new_shape, list) and len(new_shape) == 1: + new_shape = (new_shape[0], new_shape[0]) + + # Scale ratio (new / old) + r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) + if not scaleup: # only scale down, do not scale up (for better val mAP) + r = min(r, 1.0) + + # Compute padding + new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) + dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding + + if auto: # minimum rectangle + dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding + + dw /= 2 # divide padding into 2 sides + dh /= 2 + + if shape[::-1] != new_unpad: # resize + im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR) + top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) + left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) + im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border + + return im, r, (left, top) + + +def mixup(im, labels, segments, im2, labels2, segments2): + # Applies MixUp augmentation https://arxiv.org/pdf/1710.09412.pdf + r = np.random.beta(32.0, 32.0) # mixup ratio, alpha=beta=32.0 + im = (im * r + im2 * (1 - r)).astype(np.uint8) + labels = np.concatenate((labels, labels2), 0) + segments = np.concatenate((segments, segments2), 0) + return im, labels, segments + + +def box_candidates(box1, box2, wh_thr=2, ar_thr=20, area_thr=0.1, eps=1e-16): # box1(4,n), box2(4,n) + '''Compute candidate boxes: box1 before augment, box2 after augment, wh_thr (pixels), aspect_ratio_thr, area_ratio.''' + w1, h1 = box1[2] - box1[0], box1[3] - box1[1] + w2, h2 = box2[2] - box2[0], box2[3] - box2[1] + ar = np.maximum(w2 / (h2 + eps), h2 / (w2 + eps)) # aspect ratio + return (w2 > wh_thr) & (h2 > wh_thr) & (ar < ar_thr) # candidates + + +def random_affine(img, labels=(), segments=(), degrees=10, translate=.1, scale=.1, shear=10, + new_shape=(640, 640), task=""): + '''Applies Random affine transformation.''' + n = len(labels) + if isinstance(new_shape, int): + new_shape = (new_shape, new_shape) + height, width = new_shape + # print(height, width, (height, width)) + + M, s = get_transform_matrix(img.shape[:2], (height, width), degrees, scale, shear, translate) + if (M != np.eye(3)).any(): # image changed + img = cv2.warpAffine(img, M[:2], dsize=(width, height), borderValue=(114, 114, 114)) + + new_segments = [] + # Transform label coordinates + if n: + new = np.zeros((n, 4)) + segments = resample_segments(segments) + for i, segment in enumerate(segments): + xy = np.ones((len(segment), 3)) + xy[:, :2] = segment + xy = xy @ M.T # transform + xy = (xy[:, :2]) + + # clip + new[i] = segment2box(xy, width, height) + new_segments.append(xy) + i = box_candidates(box1=labels[:, 1:5].T * s, box2=new.T, area_thr=0.01) + if task!="val": + labels = labels[i] + labels[:, 1:5] = new[i] + new_segments = np.array(new_segments)[i] + else: + labels[:, 1:5] = new + new_segments = np.array(new_segments) + return img, labels, new_segments + +def copy_paste(im, labels, segments, p=0.5): + # Implement Copy-Paste augmentation https://arxiv.org/abs/2012.07177, labels as nx5 np.array(cls, xyxy) + n = len(segments) + if p and n: + h, w, c = im.shape # height, width, channels + im_new = np.zeros(im.shape, np.uint8) + for j in random.sample(range(n), k=round(p * n)): + l, s = labels[j], segments[j] + box = w - l[3], l[2], w - l[1], l[4] + ioa = bbox_ioa(box, labels[:, 1:5]) # intersection over area + if (ioa < 0.30).all(): # allow 30% obscuration of existing labels + labels = np.concatenate((labels, [[l[0], *box]]), 0) + segments.append(np.concatenate((w - s[:, 0:1], s[:, 1:2]), 1)) + cv2.drawContours(im_new, [segments[j].astype(np.int32)], -1, (1, 1, 1), cv2.FILLED) + result = cv2.flip(im, 1) # augment segments (flip left-right) + i = cv2.flip(im_new, 1).astype(bool) + im[i] = result[i] # cv2.imwrite('debug.jpg', im) # debug + + return im, labels, segments + +def bbox_ioa(box1, box2, eps=1e-7): + """ Returns the intersection over box2 area given box1, box2. Boxes are x1y1x2y2 + box1: np.array of shape(4) + box2: np.array of shape(nx4) + returns: np.array of shape(n) + """ + + # Get the coordinates of bounding boxes + b1_x1, b1_y1, b1_x2, b1_y2 = box1 + b2_x1, b2_y1, b2_x2, b2_y2 = box2.T + + # Intersection area + inter_area = (np.minimum(b1_x2, b2_x2) - np.maximum(b1_x1, b2_x1)).clip(0) * \ + (np.minimum(b1_y2, b2_y2) - np.maximum(b1_y1, b2_y1)).clip(0) + + # box2 area + box2_area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1) + eps + + # Intersection over box2 area + return inter_area / box2_area + + +def regen_labels(labels=None, segments=None, new_shape=(640, 640)): + '''Applies Random affine transformation.''' + n = len(segments) + if isinstance(new_shape, int): + new_shape = (new_shape, new_shape) + height, width = new_shape + + new_segments = [] + # Transform label coordinates + if n: + new = np.zeros((n, 4)) + segments = resample_segments(segments) + for i, segment in enumerate(segments): + new[i] = segment2box(segment, width, height) + new_segments.append(segment) + labels[:, 1:5] = new[i] + new_segments = np.array(new_segments)[i] + + return labels, new_segments + +def resample_segments(segments, n=1000): + # Up-sample an (n,2) segment + for i, s in enumerate(segments): + s = np.concatenate((s, s[0:1, :]), axis=0) + x = np.linspace(0, len(s) - 1, n) + xp = np.arange(len(s)) + segments[i] = np.concatenate([np.interp(x, xp, s[:, i]) for i in range(2)]).reshape(2, -1).T # segment xy + return segments + + +def get_transform_matrix(img_shape, new_shape, degrees, scale, shear, translate): + new_height, new_width = new_shape + # print(new_height, new_width) + # Center + C = np.eye(3) + C[0, 2] = -img_shape[1] / 2 # x translation (pixels) + C[1, 2] = -img_shape[0] / 2 # y translation (pixels) + + # Rotation and Scale + R = np.eye(3) + a = random.uniform(-degrees, degrees) + # a += random.choice([-180, -90, 0, 90]) # add 90deg rotations to small rotations + s = random.uniform(1 - scale, 1 + scale) + # s = 2 ** random.uniform(-scale, scale) + R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s) + + # Shear + S = np.eye(3) + S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # x shear (deg) + S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # y shear (deg) + + # Translation + T = np.eye(3) + T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * new_width # x translation (pixels) + T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * new_height # y transla ion (pixels) + + # Combined rotation matrix + M = T @ S @ R @ C # order of operations (right to left) is IMPORTANT + return M, s + + +def mosaic_augmentation(shape, imgs, hs, ws, labels, segments, hyp, specific_shape = False, target_height=640, target_width=640): + '''Applies Mosaic augmentation.''' + assert len(imgs) == 4, "Mosaic augmentation of current version only supports 4 images." + labels4 = [] + segments4 = [] + if not specific_shape: + if isinstance(shape, list) or isinstance(shape, np.ndarray): + target_height, target_width = shape + else: + target_height = target_width = shape + + yc, xc = (int(random.uniform(x//2, 3*x//2)) for x in (target_height, target_width) ) # mosaic center x, y + + for i in range(len(imgs)): + # Load image + img, h, w = imgs[i], hs[i], ws[i] + # place img in img4 + if i == 0: # top left + img4 = np.full((target_height * 2, target_width * 2, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles + + x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc # xmin, ymin, xmax, ymax (large image) + x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h # xmin, ymin, xmax, ymax (small image) + elif i == 1: # top right + x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, target_width * 2), yc + x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h + elif i == 2: # bottom left + x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(target_height * 2, yc + h) + x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, w, min(y2a - y1a, h) + elif i == 3: # bottom right + x1a, y1a, x2a, y2a = xc, yc, min(xc + w, target_width * 2), min(target_height * 2, yc + h) + x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h) + + img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b] # img4[ymin:ymax, xmin:xmax] + padw = x1a - x1b + padh = y1a - y1b + + # Labels + labels_per_img = labels[i].copy() + segments_per_img = segments[i].copy() + if labels_per_img.size: + boxes = np.copy(labels_per_img[:, 1:]) + boxes[:, 0] = w * (labels_per_img[:, 1] - labels_per_img[:, 3] / 2) + padw # top left x + boxes[:, 1] = h * (labels_per_img[:, 2] - labels_per_img[:, 4] / 2) + padh # top left y + boxes[:, 2] = w * (labels_per_img[:, 1] + labels_per_img[:, 3] / 2) + padw # bottom right x + boxes[:, 3] = h * (labels_per_img[:, 2] + labels_per_img[:, 4] / 2) + padh # bottom right y + for __ in range(len(segments_per_img)): + segments_per_img[__][:, 0] = w * segments_per_img[__][:, 0] + padw + segments_per_img[__][:, 1] = h * segments_per_img[__][:, 1] + padh + labels_per_img[:, 1:] = boxes + + labels4.append(labels_per_img) + segments4.extend(segments_per_img) + + # Concat/clip labels + labels4 = np.concatenate(labels4, 0) + # for x in (labels4[:, 1:]): + # np.clip(x, 0, 2 * s, out=x) + labels4[:, 1::2] = np.clip(labels4[:, 1::2], 0, 2 * target_width) + labels4[:, 2::2] = np.clip(labels4[:, 2::2], 0, 2 * target_height) + for __ in range(len(segments4)): + segments4[__][:, 0] = np.clip(segments4[__][:, 0], 0, 2 * target_width) + segments4[__][:, 1] = np.clip(segments4[__][:, 1], 0, 2 * target_height) + + # Augment + return img4, labels4, segments4 + img4, labels4, segments4 = random_affine(img4, labels4, segments4, + degrees=hyp['degrees'], + translate=hyp['translate'], + scale=hyp['scale'], + shear=hyp['shear'], + new_shape=(target_height, target_width)) + + return img4, labels4, segments4 + +def segment2box(segment, width=640, height=640): + # Convert 1 segment label to 1 box label, applying inside-image constraint, i.e. (xy1, xy2, ...) to (xyxy) + x, y = segment.T # segment xy + inside = (x >= 0) & (y >= 0) & (x <= width) & (y <= height) + x, y, = x[inside], y[inside] + return np.array([x.min(), y.min(), x.max(), y.max()]) if any(x) else np.zeros((1, 4)) # xyxy + diff --git a/yolov6/data/seg_datasets.py b/yolov6/data/seg_datasets.py new file mode 100644 index 00000000..8cca6513 --- /dev/null +++ b/yolov6/data/seg_datasets.py @@ -0,0 +1,859 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- + +import glob +from io import UnsupportedOperation +import os +import os.path as osp +import random +import json +import time +import hashlib +from pathlib import Path +import copy + +from multiprocessing.pool import Pool + +import cv2 +import numpy as np +from tqdm import tqdm +from PIL import ExifTags, Image, ImageOps + +import torch +from torch.utils.data import Dataset +import torch.distributed as dist + +from .seg_data_augment import ( + augment_hsv, + letterbox, + mixup, + random_affine, + mosaic_augmentation, + copy_paste +) +from yolov6.utils.events import LOGGER +import pickle + + +# Parameters +IMG_FORMATS = ["bmp", "jpg", "jpeg", "png", "tif", "tiff", "dng", "webp", "mpo"] +VID_FORMATS = ["mp4", "mov", "avi", "mkv"] +IMG_FORMATS.extend([f.upper() for f in IMG_FORMATS]) +VID_FORMATS.extend([f.upper() for f in VID_FORMATS]) +# Get orientation exif tag +for k, v in ExifTags.TAGS.items(): + if v == "Orientation": + ORIENTATION = k + break + +def img2label_paths(img_paths): + # Define label paths as a function of image paths + sa, sb = f'{os.sep}images{os.sep}', f'{os.sep}labels{os.sep}' # /images/, /labels/ substrings + return [sb.join(x.rsplit(sa, 1)).rsplit('.', 1)[0] + '.txt' for x in img_paths] + +class TrainValDataset(Dataset): + '''YOLOv6 train_loader/val_loader, loads images and labels for training and validation.''' + def __init__( + self, + img_dir, + img_size=640, + batch_size=16, + augment=False, + hyp=None, + rect=False, + check_images=False, + check_labels=False, + stride=32, + pad=0.0, + rank=-1, + data_dict=None, + task="train", + specific_shape = False, + height=1088, + width=1920, + downsample_ratio=4, + overlap=False + ): + assert task.lower() in ("train", "val", "test", "speed"), f"Not supported task: {task}" + + t1 = time.time() + self.__dict__.update(locals()) + if task.lower()!="train": + self.downsample_ratio = 1 + self.main_process = self.rank in (-1, 0) + self.task = self.task.capitalize() + self.class_names = data_dict["names"] + self.img_paths, self.labels = self.get_imgs_labels(self.img_dir) + self.labels, self.segments = self.get_segment(self.labels) + + self.rect = rect + self.specific_shape = specific_shape + self.target_height = height + self.target_width = width + if self.rect: + shapes = [self.img_info[p]["shape"] for p in self.img_paths] + self.shapes = np.array(shapes, dtype=np.float64) + if dist.is_initialized(): + # in DDP mode, we need to make sure all images within batch_size * gpu_num + # will resized and padded to same shape. + sample_batch_size = self.batch_size * dist.get_world_size() + else: + sample_batch_size = self.batch_size + self.batch_indices = np.floor( + np.arange(len(shapes)) / sample_batch_size + ).astype( + np.int_ + ) # batch indices of each image + + self.sort_files_shapes() + + t2 = time.time() + if self.main_process: + LOGGER.info(f"%.1fs for dataset initialization." % (t2 - t1)) + + def __len__(self): + """Get the length of dataset""" + return len(self.img_paths) + + def __getitem__(self, index): + """Fetching a data sample for a given key. + This function applies mosaic and mixup augments during training. + During validation, letterbox augment is applied. + """ + target_shape = ( + (self.target_height, self.target_width) if self.specific_shape else + self.batch_shapes[self.batch_indices[index]] if self.rect + else self.img_size + ) + + # Mosaic Augmentation + if self.augment and random.random() < self.hyp["mosaic"]: + img, labels, segments = self.get_mosaic(index, target_shape) + shapes = None + + + # MixUp augmentation + if random.random() < self.hyp["mixup"]: + img_other, labels_other, segments_other = self.get_mosaic( + random.randint(0, len(self.img_paths) - 1), target_shape + ) + img, labels, segments = mixup(img, labels, segments, img_other, labels_other, segments_other) # To Change + + else: + # Load image + if self.hyp and "shrink_size" in self.hyp: + img, (h0, w0), (h, w) = self.load_image(index, self.hyp["shrink_size"]) + else: + img, (h0, w0), (h, w) = self.load_image(index) + + # letterbox + img, ratio, pad = letterbox(img, target_shape, auto=False, scaleup=self.augment) + shapes = (h0, w0), ((h * ratio / h0, w * ratio / w0), pad) # for COCO mAP rescaling + labels = copy.deepcopy(self.labels[index]) + segments = copy.deepcopy(self.segments[index]) + + if labels.size: + w *= ratio + h *= ratio + # new boxes + boxes = np.copy(labels[:, 1:5]) + boxes[:, 0] = ( + w * (labels[:, 1] - labels[:, 3] / 2) + pad[0] + ) # top left x + boxes[:, 1] = ( + h * (labels[:, 2] - labels[:, 4] / 2) + pad[1] + ) # top left y + boxes[:, 2] = ( + w * (labels[:, 1] + labels[:, 3] / 2) + pad[0] + ) # bottom right x + boxes[:, 3] = ( + h * (labels[:, 2] + labels[:, 4] / 2) + pad[1] + ) # bottom right y + labels[:, 1:] = boxes + + if len(segments): + for i_s in range(len(segments)): + segments[i_s][:, 0] = segments[i_s][:, 0] * ratio * w + pad[0] + segments[i_s][:, 1] = segments[i_s][:, 1] * ratio * h + pad[1] + + if self.augment: + img, labels, segments = random_affine( + img, + labels, + segments, + degrees=self.hyp["degrees"], + translate=self.hyp["translate"], + scale=self.hyp["scale"], + shear=self.hyp["shear"], + new_shape=target_shape, + ) + else: + img, labels, segments = random_affine( + img, + labels, + segments, + degrees=0, + translate=0, + scale=0, + shear=0, + new_shape=target_shape, + task="val" + ) + + + if len(labels): + h, w = img.shape[:2] + + labels[:, [1, 3]] = labels[:, [1, 3]].clip(0, w - 1e-3) # x1, x2 + labels[:, [2, 4]] = labels[:, [2, 4]].clip(0, h - 1e-3) # y1, y2 + + boxes = np.copy(labels[:, 1:]) + boxes[:, 0] = ((labels[:, 1] + labels[:, 3]) / 2) / w # x center + boxes[:, 1] = ((labels[:, 2] + labels[:, 4]) / 2) / h # y center + boxes[:, 2] = (labels[:, 3] - labels[:, 1]) / w # width + boxes[:, 3] = (labels[:, 4] - labels[:, 2]) / h # height + labels[:, 1:] = boxes + lindex = labels[:, 0] >= 0 + masks = self.polygons2masks(img.shape[:2], segments, color=1, downsample_ratio=self.downsample_ratio) + labels = labels[lindex] + masks = masks[lindex] + + else: + masks = np.asarray([]) + + if self.augment: + img, labels, masks = self.general_augment(img, labels, masks.transpose(1, 2, 0) if masks.shape[0]!=0 else masks) + + #? + + masks_out = (torch.from_numpy(masks.copy()) if len(masks) else torch.zeros(1 if self.overlap else len(labels), img.shape[0] // + self.downsample_ratio, img.shape[1] // + self.downsample_ratio)) + + labels_out = torch.zeros((len(labels), 6)) + if len(labels): + labels_out[:, 1:] = torch.from_numpy(labels) + + # Convert + # self.drawit(img, labels, masks, self.img_paths[index], self.task) + img = img.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB + img = np.ascontiguousarray(img) + return torch.from_numpy(img), labels_out, self.img_paths[index], shapes, masks_out + + def load_image(self, index, shrink_size=None): + """Load image. + This function loads image by cv2, resize original image to target shape(img_size) with keeping ratio. + + Returns: + Image, original shape of image, resized image shape + """ + path = self.img_paths[index] + try: + im = cv2.imread(path) + assert im is not None, f"opencv cannot read image correctly or {path} not exists" + except: + im = cv2.cvtColor(np.asarray(Image.open(path)), cv2.COLOR_RGB2BGR) + assert im is not None, f"Image Not Found {path}, workdir: {os.getcwd()}" + + h0, w0 = im.shape[:2] # origin shape + if self.specific_shape: + # keep ratio resize + ratio = min(self.target_width / w0, self.target_height / h0) + + elif shrink_size: + ratio = (self.img_size - shrink_size) / max(h0, w0) + + else: + ratio = self.img_size / max(h0, w0) + + if ratio != 1: + im = cv2.resize( + im, + (int(w0 * ratio), int(h0 * ratio)), + interpolation=cv2.INTER_AREA + if ratio < 1 and not self.augment + else cv2.INTER_LINEAR, + ) + return im, (h0, w0), im.shape[:2] + + @staticmethod + def collate_fn(batch): + """Merges a list of samples to form a mini-batch of Tensor(s)""" + img, label, path, shapes, masks = zip(*batch) + for i, l in enumerate(label): + l[:, 0] = i # add target image index for build_targets() + return torch.stack(img, 0), torch.cat(label, 0), path, shapes, torch.cat(masks, 0) + + @staticmethod + def get_segment(labels): + rlabels = [] + segments = [] + if len(labels) == 0: + return np.asarray([]) + for label in labels: + z1 = []#labels + z2 = []#seg + for l in label: + z1.append(np.asarray(l[:5]).reshape(1, 5).astype(np.float32)) + z2.append(np.asarray(l[1:]).reshape(-1, 2).astype(np.float32)) + if z1: + rlabels.append(np.concatenate(z1, axis = 0)) + segments.append(z2) + else: + t = np.zeros((1, 5), dtype = np.float32) + t[..., 0]= -1 + rlabels.append(t) + segments.append([np.zeros((2, 2), dtype = np.float32)]) + return rlabels, segments + + + + + def get_imgs_labels(self, img_dirs): + if not isinstance(img_dirs, list): + img_dirs = [img_dirs] + # we store the cache img file in the first directory of img_dirs + valid_img_record = osp.join( + osp.dirname(img_dirs[0]), "." + osp.basename(img_dirs[0]) + "_cache.json" + ) + NUM_THREADS = min(8, os.cpu_count()) + img_paths = [] + for img_dir in img_dirs: + assert osp.exists(img_dir), f"{img_dir} is an invalid directory path!" + img_paths += glob.glob(osp.join(img_dir, "**/*"), recursive=True) + + img_paths = sorted( + p for p in img_paths if p.split(".")[-1].lower() in IMG_FORMATS and os.path.isfile(p) + ) + + assert img_paths, f"No images found in {img_dir}." + img_hash = self.get_hash(img_paths) + LOGGER.info(f'img record infomation path is:{valid_img_record}') + if osp.exists(valid_img_record): + with open(valid_img_record, "r") as f: + cache_info = json.load(f) + if "image_hash" in cache_info and cache_info["image_hash"] == img_hash: + img_info = cache_info["information"] + else: + self.check_images = True + else: + self.check_images = True + + # check images + if self.check_images and self.main_process: + img_info = {} + nc, msgs = 0, [] # number corrupt, messages + LOGGER.info( + f"{self.task}: Checking formats of images with {NUM_THREADS} process(es): " + ) + with Pool(NUM_THREADS) as pool: + pbar = tqdm( + pool.imap(TrainValDataset.check_image, img_paths), + total=len(img_paths), + ) + for img_path, shape_per_img, nc_per_img, msg in pbar: + if nc_per_img == 0: # not corrupted + img_info[img_path] = {"shape": shape_per_img} + nc += nc_per_img + if msg: + msgs.append(msg) + pbar.desc = f"{nc} image(s) corrupted" + pbar.close() + if msgs: + LOGGER.info("\n".join(msgs)) + + cache_info = {"information": img_info, "image_hash": img_hash} + # save valid image paths. + with open(valid_img_record, "w") as f: + json.dump(cache_info, f) + + # check and load anns + + img_paths = list(img_info.keys()) + label_paths = img2label_paths(img_paths) + assert label_paths, f"No labels found." + label_hash = self.get_hash(label_paths) + if "label_hash" not in cache_info or cache_info["label_hash"] != label_hash: + self.check_labels = True + + if self.check_labels: + cache_info["label_hash"] = label_hash + nm, nf, ne, nc, msgs = 0, 0, 0, 0, [] # number corrupt, messages + LOGGER.info( + f"{self.task}: Checking formats of labels with {NUM_THREADS} process(es): " + ) + with Pool(NUM_THREADS) as pool: + pbar = pool.imap( + TrainValDataset.check_label_files, zip(img_paths, label_paths) + ) + pbar = tqdm(pbar, total=len(label_paths)) if self.main_process else pbar + for ( + img_path, + labels_per_file, + nc_per_file, + nm_per_file, + nf_per_file, + ne_per_file, + msg, + ) in pbar: + if nc_per_file == 0: + img_info[img_path]["labels"] = labels_per_file + else: + img_info.pop(img_path) + nc += nc_per_file + nm += nm_per_file + nf += nf_per_file + ne += ne_per_file + if msg: + msgs.append(msg) + if self.main_process: + pbar.desc = f"{nf} label(s) found, {nm} label(s) missing, {ne} label(s) empty, {nc} invalid label files" + if self.main_process: + pbar.close() + with open(valid_img_record, "w") as f: + json.dump(cache_info, f) + if msgs: + LOGGER.info("\n".join(msgs)) + if nf == 0: + LOGGER.warning( + f"WARNING: No labels found in {osp.dirname(img_paths[0])}. " + ) + + if self.task.lower() == "val": + if self.data_dict.get("is_coco", False): # use original json file when evaluating on coco dataset. + assert osp.exists(self.data_dict["anno_path"]), "Eval on coco dataset must provide valid path of the annotation file in config file: data/coco.yaml" + else: + assert ( + self.class_names + ), "Class names is required when converting labels to coco format for evaluating." + save_dir = osp.join(osp.dirname(osp.dirname(img_dirs[0])), "annotations") + if not osp.exists(save_dir): + os.mkdir(save_dir) + save_path = osp.join( + save_dir, "instances_" + osp.basename(img_dirs[0]) + ".json" + ) + TrainValDataset.generate_coco_format_labels( + img_info, self.class_names, save_path + ) + + # img_paths, labels = list( + # zip( + # *[ + # ( + # img_path, + # np.array(info["labels"], dtype=np.float32) + # if info["labels"] + # else np.zeros((0, 5), dtype=np.float32), + # ) + # for img_path, info in img_info.items() + # ] + # ) + # ) + img_paths, labels = list( + zip( + *[ + ( + img_path, + info["labels"] + if info["labels"] + else [], + ) + for img_path, info in img_info.items() + ] + ) + ) + self.img_info = img_info + LOGGER.info( + f"{self.task}: Final numbers of valid images: {len(img_paths)}/ labels: {len(labels)}. " + ) + return img_paths, labels + + def get_mosaic(self, index, shape): + """Gets images and labels after mosaic augments""" + indices = [index] + random.choices( + range(0, len(self.img_paths)), k=3 + ) # 3 additional image indices + random.shuffle(indices) + imgs, hs, ws, labels, segments = [], [], [], [], [] + for index in indices: + img, _, (h, w) = self.load_image(index) + labels_per_img = self.labels[index] + segments_per_img = copy.deepcopy(self.segments[index]) + imgs.append(img) + hs.append(h) + ws.append(w) + labels.append(labels_per_img) + segments.append(segments_per_img) + img, labels, segments = mosaic_augmentation(shape, imgs, hs, ws, labels, segments, self.hyp, self.specific_shape, self.target_height, self.target_width) + img, labels, segments = copy_paste(img, labels, segments, 0) + img, labels, segments = random_affine(img, labels, segments, + degrees=self.hyp['degrees'], + translate=self.hyp['translate'], + scale=self.hyp['scale'], + shear=self.hyp['shear'], + new_shape=shape if not self.specific_shape else (self.target_height, self.target_width)) + return img, labels, segments + + def general_augment(self, img, labels, segments): + """Gets images and labels after general augment + This function applies hsv, random ud-flip and random lr-flips augments. + """ + nl = len(labels) + + # HSV color-space + augment_hsv( + img, + hgain=self.hyp["hsv_h"], + sgain=self.hyp["hsv_s"], + vgain=self.hyp["hsv_v"], + ) + + # Flip up-down + if random.random() < self.hyp["flipud"]: + img = np.flipud(img) + if nl: + segments = np.flipud(segments) + labels[:, 2] = 1 - labels[:, 2] + + # Flip left-right + if random.random() < self.hyp["fliplr"]: + img = np.fliplr(img) + if nl: + segments = np.fliplr(segments) + labels[:, 1] = 1 - labels[:, 1] + + return img, labels, segments.transpose(2, 0, 1) if segments.shape[0]!=0 else segments + + def sort_files_shapes(self): + '''Sort by aspect ratio.''' + batch_num = self.batch_indices[-1] + 1 + s = self.shapes # [height, width] + ar = s[:, 1] / s[:, 0] # aspect ratio + irect = ar.argsort() + self.img_paths = [self.img_paths[i] for i in irect] + self.labels = [self.labels[i] for i in irect] + self.segments = [self.segments[i] for i in irect] + self.shapes = s[irect] # wh + ar = ar[irect] + + # Set training image shapes + shapes = [[1, 1]] * batch_num + for i in range(batch_num): + ari = ar[self.batch_indices == i] + mini, maxi = ari.min(), ari.max() + if maxi < 1: + shapes[i] = [1, maxi] + elif mini > 1: + shapes[i] = [1 / mini, 1] + self.batch_shapes = ( + np.ceil(np.array(shapes) * self.img_size / self.stride + self.pad).astype( + np.int_ + ) + * self.stride + ) + + @staticmethod + def check_image(im_file): + '''Verify an image.''' + nc, msg = 0, "" + try: + im = Image.open(im_file) + im.verify() # PIL verify + im = Image.open(im_file) # need to reload the image after using verify() + shape = (im.height, im.width) # (height, width) + try: + im_exif = im._getexif() + if im_exif and ORIENTATION in im_exif: + rotation = im_exif[ORIENTATION] + if rotation in (6, 8): + shape = (shape[1], shape[0]) + except: + im_exif = None + + assert (shape[0] > 9) & (shape[1] > 9), f"image size {shape} <10 pixels" + assert im.format.lower() in IMG_FORMATS, f"invalid image format {im.format}" + if im.format.lower() in ("jpg", "jpeg"): + with open(im_file, "rb") as f: + f.seek(-2, 2) + if f.read() != b"\xff\xd9": # corrupt JPEG + ImageOps.exif_transpose(Image.open(im_file)).save( + im_file, "JPEG", subsampling=0, quality=100 + ) + msg += f"WARNING: {im_file}: corrupt JPEG restored and saved" + return im_file, shape, nc, msg + except Exception as e: + nc = 1 + msg = f"WARNING: {im_file}: ignoring corrupt image: {e}" + return im_file, None, nc, msg + + @staticmethod + def xyn2xy(x, w=640, h=640, padw=0, padh=0): + # Convert normalized segments into pixel segments, shape (n,2) + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[..., 0] = w * x[..., 0] + padw # top left x + y[..., 1] = h * x[..., 1] + padh # top left y + return y + + @staticmethod + def drawit(img, labels, masks, imgname = "", task = ""): + # Convert normalized segments into pixel segments, shape (n,2) + # There are some bugs in Val! + if task == "Val": + return 0 + import copy + + spsp = copy.deepcopy(img) + for label in labels: + xy = label[1:3] * np.asarray(img.shape[:2])[::-1] + wh = label[3:5] * np.asarray(img.shape[:2])[::-1] + pt1 = (xy - wh / 2).astype(np.int_) + pt2 = (xy + wh / 2).astype(np.int_) + cv2.rectangle(spsp, pt1, pt2, (0,255,255), 1) + ssss = random.randint(0,100000000) + for mask in masks: + if mask.shape[:2]!=(img.shape[0], img.shape[1]): + m = cv2.resize(mask,(img.shape[0], img.shape[1])) + else: + m = mask + m = m.reshape(img.shape[0], img.shape[1], 1) + q = np.ones((img.shape[0], img.shape[1], 1), dtype = np.int_) * 255 * m + q = q * m + s = np.zeros((img.shape[0], img.shape[1], 2)) + s = np.concatenate([s, q], axis = 2) + spsp = cv2.addWeighted(spsp, 1, s.astype(np.int_), 0.5, 0, dtype=cv2.CV_8U) + print(img.shape, labels.shape, masks.shape) + try: + print(cv2.imwrite("/home/hadoop-seccv/ssd/wangzhaonian/yolov6_seg/test_img/{}.jpg".format(ssss), spsp)) + print(imgname, ssss, len(labels), len(masks)) + except: + print("?") + + + @staticmethod + def check_label_files(args): + img_path, lb_path = args + nm, nf, ne, nc, msg = 0, 0, 0, 0, "" # number (missing, found, empty, message + try: + if osp.exists(lb_path): + nf = 1 # label found + with open(lb_path, "r") as f: + labels = [ + x.split() for x in f.read().strip().splitlines() if len(x) > 5 # get which has seg + ] + # labels = np.array(labels, dtype=np.float32) + if len(labels): + # assert all( + # len(l) >= 5 for l in labels + # ), f"{lb_path}: wrong label format." + # assert ( + # labels >= 0 + # ).all(), f"{lb_path}: Label values error: all values in label file must > 0" + # assert ( + # labels[:, 1:] <= 1 + # ).all(), f"{lb_path}: Label values error: all coordinates must be normalized" + + # _, indices = np.unique(labels, axis=0, return_index=True) + # if len(indices) < len(labels): # duplicate row check + # labels = labels[indices] # remove duplicates + # msg += f"WARNING: {lb_path}: {len(labels) - len(indices)} duplicate labels removed" + # labels = labels.tolist() + _t = 0 + else: + ne = 1 # label empty + labels = [] + else: + nm = 1 # label missing + labels = [] + return img_path, labels, nc, nm, nf, ne, msg + except Exception as e: + nc = 1 + msg = f"WARNING: {lb_path}: ignoring invalid labels: {e}" + return img_path, None, nc, nm, nf, ne, msg + + @staticmethod + def polygon2mask(img_size, polygons, color=1, downsample_ratio=1): + mask = np.zeros(img_size, dtype=np.uint8) + polygons = np.asarray(polygons) + polygons = polygons.astype(np.int32) + shape = polygons.shape + polygons = polygons.reshape(shape[0], -1, 2) + cv2.fillPoly(mask, polygons, color=color) + nh, nw = (img_size[0] // downsample_ratio, img_size[1] // downsample_ratio) + # NOTE: fillPoly firstly then resize is trying the keep the same way + # of loss calculation when mask-ratio=1. + mask = cv2.resize(mask, (nw, nh)) + return mask + + def polygons2masks(self, img_size, polygons, color, downsample_ratio=1): + """ + Args: + img_size (tuple): The image size. + polygons (list[np.ndarray]): each polygon is [N, M], + N is the number of polygons, + M is the number of points(Be divided by 2). + """ + masks = [] + for si in range(len(polygons)): + mask = self.polygon2mask(img_size, [polygons[si].reshape(-1)], color, downsample_ratio) + masks.append(mask) + return np.array(masks) + + + def polygons2masks_overlap(self, img_size, segments, downsample_ratio=1): + """Return a (640, 640) overlap mask.""" + masks = np.zeros((img_size[0] // downsample_ratio, img_size[1] // downsample_ratio), + dtype=np.int32 if len(segments) > 255 else np.uint8) + areas = [] + ms = [] + for si in range(len(segments)): + mask = self.polygon2mask( + img_size, + [segments[si].reshape(-1)], + downsample_ratio=downsample_ratio, + color=1, + ) + ms.append(mask) + areas.append(mask.sum()) + areas = np.asarray(areas) + index = np.argsort(-areas) + ms = np.array(ms)[index] + for i in range(len(segments)): + mask = ms[i] * (i + 1) + masks = masks + mask + masks = np.clip(masks, a_min=0, a_max=i + 1) + return masks, index + + @staticmethod + def generate_coco_format_labels(img_info, class_names, save_path): + # for evaluation with pycocotools + dataset = {"categories": [], "annotations": [], "images": []} + for i, class_name in enumerate(class_names): + dataset["categories"].append( + {"id": i, "name": class_name, "supercategory": ""} + ) + + ann_id = 0 + LOGGER.info(f"Convert to COCO format") + for i, (img_path, info) in enumerate(tqdm(img_info.items())): + labels = info["labels"] if info["labels"] else [] + img_id = osp.splitext(osp.basename(img_path))[0] + img_h, img_w = info["shape"] + dataset["images"].append( + { + "file_name": os.path.basename(img_path), + "id": img_id, + "width": img_w, + "height": img_h, + } + ) + if labels: + for label in labels: + c, x, y, w, h = label[:5] + c, x, y, w, h = float(c), float(x), float(y), float(w), float(h) + seg = np.asarray(label[5:]).astype(np.float32) + seg = seg.reshape(-1, 2) + #breakpoint() + seg = seg * np.asarray([img_w, img_h]) + seg = seg.reshape(-1) + # convert x,y,w,h to x1,y1,x2,y2 + x1 = (x - w / 2) * img_w + y1 = (y - h / 2) * img_h + x2 = (x + w / 2) * img_w + y2 = (y + h / 2) * img_h + # cls_id starts from 0 + cls_id = int(c) + w = max(0, x2 - x1) + h = max(0, y2 - y1) + dataset["annotations"].append( + { + "area": h * w, + "bbox": [x1, y1, w, h], + "category_id": cls_id, + "id": ann_id, + "image_id": img_id, + "iscrowd": 0, + # mask + "segmentation": list(seg), + } + ) + ann_id += 1 + + with open(save_path, "w") as f: + json.dump(dataset, f) + LOGGER.info( + f"Convert to COCO format finished. Resutls saved in {save_path}" + ) + + @staticmethod + def get_hash(paths): + """Get the hash value of paths""" + assert isinstance(paths, list), "Only support list currently." + h = hashlib.md5("".join(paths).encode()) + return h.hexdigest() + + +class LoadData: + def __init__(self, path, webcam, webcam_addr): + self.webcam = webcam + self.webcam_addr = webcam_addr + if webcam: # if use web camera + imgp = [] + vidp = [int(webcam_addr) if webcam_addr.isdigit() else webcam_addr] + else: + p = str(Path(path).resolve()) # os-agnostic absolute path + if os.path.isdir(p): + files = sorted(glob.glob(os.path.join(p, '**/*.*'), recursive=True)) # dir + elif os.path.isfile(p): + files = [p] # files + else: + raise FileNotFoundError(f'Invalid path {p}') + imgp = [i for i in files if i.split('.')[-1] in IMG_FORMATS] + vidp = [v for v in files if v.split('.')[-1] in VID_FORMATS] + self.files = imgp + vidp + self.nf = len(self.files) + self.type = 'image' + if len(vidp) > 0: + self.add_video(vidp[0]) # new video + else: + self.cap = None + + # @staticmethod + def checkext(self, path): + if self.webcam: + file_type = 'video' + else: + file_type = 'image' if path.split('.')[-1].lower() in IMG_FORMATS else 'video' + return file_type + + def __iter__(self): + self.count = 0 + return self + + def __next__(self): + if self.count == self.nf: + raise StopIteration + path = self.files[self.count] + if self.checkext(path) == 'video': + self.type = 'video' + ret_val, img = self.cap.read() + while not ret_val: + self.count += 1 + self.cap.release() + if self.count == self.nf: # last video + raise StopIteration + path = self.files[self.count] + self.add_video(path) + ret_val, img = self.cap.read() + else: + # Read image + self.count += 1 + img = cv2.imread(path) # BGR + return img, path, self.cap + + def add_video(self, path): + self.frame = 0 + self.cap = cv2.VideoCapture(path) + self.frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) + + def __len__(self): + return self.nf # number of files diff --git a/yolov6/models/efficientrep.py b/yolov6/models/efficientrep.py index 5d0de7ce..4ca75083 100644 --- a/yolov6/models/efficientrep.py +++ b/yolov6/models/efficientrep.py @@ -387,20 +387,11 @@ def __init__( block=RepVGGBlock, csp_e=float(1)/2, fuse_P2=False, - cspsppf=False, - stage_block_type="BepC3" + cspsppf=False ): super().__init__() assert channels_list is not None assert num_repeats is not None - - if stage_block_type == "BepC3": - stage_block = BepC3 - elif stage_block_type == "MBLABlock": - stage_block = MBLABlock - else: - raise NotImplementedError - self.fuse_P2 = fuse_P2 self.stem = block( @@ -417,7 +408,7 @@ def __init__( kernel_size=3, stride=2 ), - stage_block( + BepC3( in_channels=channels_list[1], out_channels=channels_list[1], n=num_repeats[1], @@ -433,7 +424,7 @@ def __init__( kernel_size=3, stride=2 ), - stage_block( + BepC3( in_channels=channels_list[2], out_channels=channels_list[2], n=num_repeats[2], @@ -449,7 +440,7 @@ def __init__( kernel_size=3, stride=2 ), - stage_block( + BepC3( in_channels=channels_list[3], out_channels=channels_list[3], n=num_repeats[3], @@ -469,7 +460,7 @@ def __init__( kernel_size=3, stride=2, ), - stage_block( + BepC3( in_channels=channels_list[4], out_channels=channels_list[4], n=num_repeats[4], @@ -484,7 +475,7 @@ def __init__( kernel_size=3, stride=2, ), - stage_block( + BepC3( in_channels=channels_list[5], out_channels=channels_list[5], n=num_repeats[5], diff --git a/yolov6/models/effidehead_seg.py b/yolov6/models/effidehead_seg.py new file mode 100644 index 00000000..2bfe9843 --- /dev/null +++ b/yolov6/models/effidehead_seg.py @@ -0,0 +1,452 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +import math +from yolov6.layers.common import * +from yolov6.assigners.anchor_generator import generate_anchors +from yolov6.utils.general import dist2bbox + + +class Detect(nn.Module): + export = False + '''Efficient Decoupled Head + With hardware-aware degisn, the decoupled head is optimized with + hybridchannels methods. + ''' + def __init__(self, num_classes=80, num_layers=3, inplace=True, head_layers=None, reg_mask=None, use_dfl=True, reg_max=16, nm=32): # detection layer + # nm: number of masks + super().__init__() + assert head_layers is not None + assert reg_mask is not None + self.nc = num_classes # number of classes + self.no = num_classes + 5 + nm # number of outputs per anchor + self.nl = num_layers # number of detection layers + self.nm = nm + self.grid = [torch.zeros(1)] * num_layers + self.prior_prob = 1e-2 + self.inplace = inplace + stride = [8, 16, 32] if num_layers == 3 else [8, 16, 32, 64] # strides computed during build + self.stride = torch.tensor(stride) + self.use_dfl = use_dfl + self.reg_max = reg_max + self.proj_conv = nn.Conv2d(self.reg_max + 1, 1, 1, bias=False) + self.grid_cell_offset = 0.5 + self.grid_cell_size = 5.0 + + # Init decouple head + self.stems = nn.ModuleList() + self.cls_convs = nn.ModuleList() + self.reg_convs = nn.ModuleList() + self.cls_preds = nn.ModuleList() + self.reg_preds = nn.ModuleList() + + # Efficient decoupled head layers + for i in range(num_layers): + idx = i*5 + self.stems.append(head_layers[idx]) + self.cls_convs.append(head_layers[idx+1]) + self.reg_convs.append(head_layers[idx+2]) + self.cls_preds.append(head_layers[idx+3]) + self.reg_preds.append(head_layers[idx+4]) + + def initialize_biases(self): + + for conv in self.cls_preds: + b = conv.bias.view(-1, ) + b.data.fill_(-math.log((1 - self.prior_prob) / self.prior_prob)) + conv.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + w = conv.weight + w.data.fill_(0.) + conv.weight = torch.nn.Parameter(w, requires_grad=True) + + for conv in self.reg_preds: + b = conv.bias.view(-1, ) + b.data.fill_(1.0) + conv.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + w = conv.weight + w.data.fill_(0.) + conv.weight = torch.nn.Parameter(w, requires_grad=True) + + self.proj = nn.Parameter(torch.linspace(0, self.reg_max, self.reg_max + 1), requires_grad=False) + self.proj_conv.weight = nn.Parameter(self.proj.view([1, self.reg_max + 1, 1, 1]).clone().detach(), + requires_grad=False) + + def forward(self, x): + if self.training: + cls_score_list = [] + reg_distri_list = [] + + for i in range(self.nl): + x[i] = self.stems[i](x[i]) + cls_x = x[i] + reg_x = x[i] + cls_feat = self.cls_convs[i](cls_x) + cls_output = self.cls_preds[i](cls_feat) + reg_feat = self.reg_convs[i](reg_x) + reg_output = self.reg_preds[i](reg_feat) + + cls_output = torch.sigmoid(cls_output) + cls_score_list.append(cls_output.flatten(2).permute((0, 2, 1))) + reg_distri_list.append(reg_output.flatten(2).permute((0, 2, 1))) + + cls_score_list = torch.cat(cls_score_list, axis=1) + reg_distri_list = torch.cat(reg_distri_list, axis=1) + + return x, cls_score_list, reg_distri_list + else: + cls_score_list = [] + reg_dist_list = [] + + for i in range(self.nl): + b, _, h, w = x[i].shape + l = h * w + x[i] = self.stems[i](x[i]) + cls_x = x[i] + reg_x = x[i] + cls_feat = self.cls_convs[i](cls_x) + cls_output = self.cls_preds[i](cls_feat) + reg_feat = self.reg_convs[i](reg_x) + reg_output = self.reg_preds[i](reg_feat) + + if self.use_dfl: + reg_output = reg_output.reshape([-1, 4, self.reg_max + 1, l]).permute(0, 2, 1, 3) + reg_output = self.proj_conv(F.softmax(reg_output, dim=1)) + + cls_output = torch.sigmoid(cls_output) + + if self.export: + cls_score_list.append(cls_output) + reg_dist_list.append(reg_output) + else: + cls_score_list.append(cls_output.reshape([b, self.nc, l])) + reg_dist_list.append(reg_output.reshape([b, 4, l])) + + if self.export: + return tuple(torch.cat([cls, reg], 1) for cls, reg in zip(cls_score_list, reg_dist_list)) + + cls_score_list = torch.cat(cls_score_list, axis=-1).permute(0, 2, 1) + reg_dist_list = torch.cat(reg_dist_list, axis=-1).permute(0, 2, 1) + + + anchor_points, stride_tensor = generate_anchors( + x, self.stride, self.grid_cell_size, self.grid_cell_offset, device=x[0].device, is_eval=True, mode='af') + + pred_bboxes = dist2bbox(reg_dist_list, anchor_points, box_format='xywh') + pred_bboxes *= stride_tensor + return torch.cat( + [ + pred_bboxes, + torch.ones((b, pred_bboxes.shape[1], 1), device=pred_bboxes.device, dtype=pred_bboxes.dtype), + cls_score_list + ], + axis=-1) + +def build_seg_layer(channels_list, num_anchors, num_classes, reg_max=16, num_layers=3): + + chx = [6, 8, 10] if num_layers == 3 else [8, 9, 10, 11] + + head_layers = nn.Sequential( + # stem0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=1, + stride=1 + ), + # cls_conv0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=3, + stride=1 + ), + # reg_conv0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=3, + stride=1 + ), + # cls_pred0 + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred0 + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=4 * (reg_max + num_anchors), + kernel_size=1 + ), + # stem1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=1, + stride=1 + ), + # cls_conv1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=3, + stride=1 + ), + # reg_conv1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=3, + stride=1 + ), + # cls_pred1 + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred1 + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=4 * (reg_max + num_anchors), + kernel_size=1 + ), + # stem2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=1, + stride=1 + ), + # cls_conv2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=3, + stride=1 + ), + # reg_conv2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=3, + stride=1 + ), + # cls_pred2 + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred2 + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=4 * (reg_max + num_anchors), + kernel_size=1 + ) + ) + + if num_layers == 4: + head_layers.add_module('stem3', + # stem3 + ConvBNSiLU( + in_channels=channels_list[chx[3]], + out_channels=channels_list[chx[3]], + kernel_size=1, + stride=1 + ) + ) + head_layers.add_module('cls_conv3', + # cls_conv3 + ConvBNSiLU( + in_channels=channels_list[chx[3]], + out_channels=channels_list[chx[3]], + kernel_size=3, + stride=1 + ) + ) + head_layers.add_module('reg_conv3', + # reg_conv3 + ConvBNSiLU( + in_channels=channels_list[chx[3]], + out_channels=channels_list[chx[3]], + kernel_size=3, + stride=1 + ) + ) + head_layers.add_module('cls_pred3', + # cls_pred3 + nn.Conv2d( + in_channels=channels_list[chx[3]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ) + ) + head_layers.add_module('reg_pred3', + # reg_pred3 + nn.Conv2d( + in_channels=channels_list[chx[3]], + out_channels=4 * (reg_max + num_anchors), + kernel_size=1 + ) + ) + + return head_layers + + +###### + + +def build_effidehead_layer(channels_list, num_anchors, num_classes, reg_max=16, num_layers=3): + + chx = [6, 8, 10] if num_layers == 3 else [8, 9, 10, 11] + + head_layers = nn.Sequential( + # stem0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=1, + stride=1 + ), + # cls_conv0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=3, + stride=1 + ), + # reg_conv0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=3, + stride=1 + ), + # cls_pred0 + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred0 + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=4 * (reg_max + num_anchors), + kernel_size=1 + ), + # stem1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=1, + stride=1 + ), + # cls_conv1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=3, + stride=1 + ), + # reg_conv1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=3, + stride=1 + ), + # cls_pred1 + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred1 + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=4 * (reg_max + num_anchors), + kernel_size=1 + ), + # stem2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=1, + stride=1 + ), + # cls_conv2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=3, + stride=1 + ), + # reg_conv2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=3, + stride=1 + ), + # cls_pred2 + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred2 + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=4 * (reg_max + num_anchors), + kernel_size=1 + ) + ) + + if num_layers == 4: + head_layers.add_module('stem3', + # stem3 + ConvBNSiLU( + in_channels=channels_list[chx[3]], + out_channels=channels_list[chx[3]], + kernel_size=1, + stride=1 + ) + ) + head_layers.add_module('cls_conv3', + # cls_conv3 + ConvBNSiLU( + in_channels=channels_list[chx[3]], + out_channels=channels_list[chx[3]], + kernel_size=3, + stride=1 + ) + ) + head_layers.add_module('reg_conv3', + # reg_conv3 + ConvBNSiLU( + in_channels=channels_list[chx[3]], + out_channels=channels_list[chx[3]], + kernel_size=3, + stride=1 + ) + ) + head_layers.add_module('cls_pred3', + # cls_pred3 + nn.Conv2d( + in_channels=channels_list[chx[3]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ) + ) + head_layers.add_module('reg_pred3', + # reg_pred3 + nn.Conv2d( + in_channels=channels_list[chx[3]], + out_channels=4 * (reg_max + num_anchors), + kernel_size=1 + ) + ) + + return head_layers diff --git a/yolov6/models/heads/effidehead_fuseab_seg.py b/yolov6/models/heads/effidehead_fuseab_seg.py new file mode 100644 index 00000000..80272928 --- /dev/null +++ b/yolov6/models/heads/effidehead_fuseab_seg.py @@ -0,0 +1,551 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +import math +from yolov6.layers.common import * +from yolov6.assigners.anchor_generator import generate_anchors +from yolov6.utils.general import dist2bbox + + +class Detect(nn.Module): + export = False + '''Efficient Decoupled Head for fusing anchor-base branches. + ''' + def __init__(self, num_classes=80, anchors=None, num_layers=3, inplace=True, head_layers=None, reg_mask=None, use_dfl=True, reg_max=16, nm=32, fuse_ab=False): # detection layer + super().__init__() + assert head_layers is not None + assert reg_mask is not None + self.nc = num_classes # number of classes + self.no = num_classes + 5 + nm # number of outputs per anchor + self.nl = num_layers # number of detection layers + self.nm = nm # number of masks + if isinstance(anchors, (list, tuple)): + self.na = len(anchors[0]) // 2 + else: + self.na = anchors + self.grid = [torch.zeros(1)] * num_layers + self.fuse_ab = fuse_ab + self.prior_prob = 1e-2 + self.inplace = inplace + stride = [8, 16, 32] if num_layers == 3 else [8, 16, 32, 64] # strides computed during build + self.stride = torch.tensor(stride) + self.use_dfl = use_dfl + self.reg_max = reg_max + self.proj_conv = nn.Conv2d(self.reg_max + 1, 1, 1, bias=False) + self.grid_cell_offset = 0.5 + self.grid_cell_size = 5.0 + self.anchors_init= ((torch.tensor(anchors) / self.stride[:,None])).reshape(self.nl, self.na, 2) + self.reg_mask = reg_mask + + # Init decouple head + self.stems = nn.ModuleList() + self.cls_convs = nn.ModuleList() + self.reg_convs = nn.ModuleList() + self.seg_convs = nn.ModuleList() + self.cls_preds = nn.ModuleList() + self.reg_preds = nn.ModuleList() + self.seg_preds = nn.ModuleList() + self.cls_preds_ab = nn.ModuleList() + self.reg_preds_ab = nn.ModuleList() + self.seg_preds_ab = nn.ModuleList() + self.seg_proto = nn.ModuleList() + self.seg_proto.append(reg_mask[0]) + + + # Efficient decoupled head layers + for i in range(num_layers): + idx = i*10 + self.stems.append(head_layers[idx]) + self.cls_convs.append(head_layers[idx+1]) + self.reg_convs.append(head_layers[idx+2]) + self.seg_convs.append(head_layers[idx+3]) + self.cls_preds.append(head_layers[idx+4]) + self.reg_preds.append(head_layers[idx+5]) + self.seg_preds.append(head_layers[idx+6]) + if self.fuse_ab: + self.cls_preds_ab.append(head_layers[idx+7]) + self.reg_preds_ab.append(head_layers[idx+8]) + self.seg_preds_ab.append(head_layers[idx+9]) + + + def initialize_biases(self): + + for conv in self.cls_preds: + b = conv.bias.view(-1, ) + b.data.fill_(-math.log((1 - self.prior_prob) / self.prior_prob)) + conv.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + w = conv.weight + w.data.fill_(0.) + conv.weight = torch.nn.Parameter(w, requires_grad=True) + + if self.fuse_ab: + for conv in self.cls_preds_ab: + b = conv.bias.view(-1, ) + b.data.fill_(-math.log((1 - self.prior_prob) / self.prior_prob)) + conv.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + w = conv.weight + w.data.fill_(0.) + conv.weight = torch.nn.Parameter(w, requires_grad=True) + + for conv in self.reg_preds: + b = conv.bias.view(-1, ) + b.data.fill_(1.0) + conv.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + w = conv.weight + w.data.fill_(0.) + conv.weight = torch.nn.Parameter(w, requires_grad=True) + + if self.fuse_ab: + for conv in self.reg_preds_ab: + b = conv.bias.view(-1, ) + b.data.fill_(1.0) + conv.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + w = conv.weight + w.data.fill_(0.) + conv.weight = torch.nn.Parameter(w, requires_grad=True) + + self.proj = nn.Parameter(torch.linspace(0, self.reg_max, self.reg_max + 1), requires_grad=False) + self.proj_conv.weight = nn.Parameter(self.proj.view([1, self.reg_max + 1, 1, 1]).clone().detach(), + requires_grad=False) + + def handleseg_af(self, sgot_lst, sg_msk_lst): + ''' + sg_msk_lst --> lst sg_msk: segmask: Shape(bs, 32, w, h) + sgot_lst --> lst sgot: seg_output_conf: Shape(bs, n, 32) + ''' + mask_res = [] + for i in range(len(sgot_lst)): + sgot = sgot_lst[i] + sg_msk = sg_msk_lst[i] + t_mask_res = [] + for j in range(sgot.shape[0]): + sgot_t = sgot[j] # (n, 32) + sg_msk_t = sg_msk[j] # (32, w, h) + m_t = (sgot_t@sg_msk_t.reshape(32, -1)).reshape(-1, *sg_msk_t.shape[1:]) + m_t = m_t.unsqueeze(0) + t_mask_res.append(m_t) + mask_res.append(torch.cat(t_mask_res, 0).flatten(0,1)) + return mask_res + + def handleseg_ab(self, sgot_lst, sg_msk_lst): + ''' + sg_msk_lst --> lst sg_msk: segmask: Shape(bs, 32, w, h) + sgot_lst --> lst sgot: seg_output_conf: Shape(bs, num_of_anchors, h, w, 32) + sgot.flatten(1, 3) -> Shape(bs, n*num_of_anchors, 32) + for j in range(bs) -> ((n*num_of_anchor, 32)@(32, w0, h0) = (n*num_of_anchor, 32)@(32, w0, h0)) + ''' + mask_res = [] + for i in range(len(sgot_lst)): + sgot = sgot_lst[i] + sg_msk = sg_msk_lst[i] + s_shape = sgot.shape[1:4] + sgot = sgot.flatten(1, 3) + t_mask_res = [] + for j in range(sgot.shape[0]): + sgot_t = sgot[j] # (n, 32) + sg_msk_t = sg_msk[j] # (32, w, h) + m_t = (sgot_t@sg_msk_t.reshape(32, -1)).reshape(-1, *sg_msk_t.shape[1:]) + m_t = m_t.unsqueeze(0) + t_mask_res.append(m_t) + mask_res.append(torch.cat(t_mask_res, 0).flatten(0,1)) + return mask_res + + + + + + def forward(self, x): + if self.training: + device = x[0].device + cls_score_list_af = [] + reg_dist_list_af = [] + cls_score_list_ab = [] + reg_dist_list_ab = [] + seg_conf_list_af = [] + seg_conf_list_ab = [] + seg_list = [] + af_seg_list = [] + ab_seg_list = [] + + seg_mask = self.seg_proto[0](x[0]) + seg_list.append(seg_mask) + + + + for i in range(self.nl): + b, _, h, w = x[i].shape + l = h * w + + + x[i] = self.stems[i](x[i]) + + + cls_x = x[i] + reg_x = x[i] + seg_x = x[i] + + cls_feat = self.cls_convs[i](cls_x) + reg_feat = self.reg_convs[i](reg_x) + seg_feat = self.seg_convs[i](seg_x) + + #anchor_base + if self.fuse_ab: + cls_output_ab = self.cls_preds_ab[i](cls_feat) + reg_output_ab = self.reg_preds_ab[i](reg_feat) + seg_output_ab = self.seg_preds_ab[i](seg_feat) + + cls_output_ab = torch.sigmoid(cls_output_ab) + seg_output_ab = torch.sigmoid(seg_output_ab) + if self.fuse_ab: + seg_conf_list_ab.append(seg_output_ab.reshape(b, self.na, -1, h, w).permute(0,1,3,4,2)) + cls_output_ab = cls_output_ab.reshape(b, self.na, -1, h, w).permute(0,1,3,4,2) + cls_score_list_ab.append(cls_output_ab.flatten(1,3)) + + + reg_output_ab = reg_output_ab.reshape(b, self.na, -1, h, w).permute(0,1,3,4,2) + reg_output_ab[..., 2:4] = ((reg_output_ab[..., 2:4].sigmoid() * 2) ** 2 ) * (self.anchors_init[i].reshape(1, self.na, 1, 1, 2).to(device)) + reg_dist_list_ab.append(reg_output_ab.flatten(1,3)) + + #anchor_free + cls_output_af = self.cls_preds[i](cls_feat) + reg_output_af = self.reg_preds[i](reg_feat) + seg_output_af = self.seg_preds[i](seg_feat) + + cls_output_af = torch.sigmoid(cls_output_af) + # seg_output_af = torch.sigmoid(seg_output_af) + seg_conf_list_af.append(seg_output_af.flatten(2).permute((0, 2, 1))) + + cls_score_list_af.append(cls_output_af.flatten(2).permute((0, 2, 1))) + reg_dist_list_af.append(reg_output_af.flatten(2).permute((0, 2, 1))) + + #Not support fuseab now + if False: + ab_seg_list = self.handleseg_ab(seg_conf_list_ab, seg_list) if self.fuse_ab else [] + cls_score_list_ab = torch.cat(cls_score_list_ab, axis=1) + reg_dist_list_ab = torch.cat(reg_dist_list_ab, axis=1) + cls_score_list_af = torch.cat(cls_score_list_af, axis=1) + reg_dist_list_af = torch.cat(reg_dist_list_af, axis=1) + + return x, cls_score_list_ab, reg_dist_list_ab, cls_score_list_af, reg_dist_list_af, [seg_conf_list_af, seg_list], ab_seg_list + + else: + device = x[0].device + cls_score_list_af = [] + reg_dist_list_af = [] + seg_list = [] + seg_conf_list_af = [] + seg_mask = self.seg_proto[0](x[0]) + seg_list.append(seg_mask) + + for i in range(self.nl): + b, _, h, w = x[i].shape + l = h * w + + + x[i] = self.stems[i](x[i]) + + cls_x = x[i] + reg_x = x[i] + seg_x = x[i] + + cls_feat = self.cls_convs[i](cls_x) + reg_feat = self.reg_convs[i](reg_x) + seg_feat = self.seg_convs[i](seg_x) + + #anchor_free + cls_output_af = self.cls_preds[i](cls_feat) + reg_output_af = self.reg_preds[i](reg_feat) + seg_output_af = self.seg_preds[i](seg_feat) + + if self.use_dfl: + reg_output_af = reg_output_af.reshape([-1, 4, self.reg_max + 1, l]).permute(0, 2, 1, 3) + reg_output_af = self.proj_conv(F.softmax(reg_output_af, dim=1)) + + cls_output_af = torch.sigmoid(cls_output_af) + # seg_output_af = torch.sigmoid(seg_output_af) + proto_no = (torch.ones(b, 1, l) * i).cuda() + + + if self.export: + cls_score_list_af.append(cls_output_af) + reg_dist_list_af.append(reg_output_af) + seg_conf_list_af.append(seg_output_af) + else: + cls_score_list_af.append(cls_output_af.reshape([b, self.nc, l])) + reg_dist_list_af.append(reg_output_af.reshape([b, 4, l])) + seg_conf_list_af.append(torch.cat([proto_no, seg_output_af.reshape([b, 32, l])], axis = 1)) #[which_proto, (32...)] + + if self.export: + return tuple(torch.cat([cls, reg, seg], 1) for cls, reg, seg in zip(cls_score_list_af, reg_dist_list_af, seg_conf_list_af)), seg_list[0] + + cls_score_list_af = torch.cat(cls_score_list_af, axis=-1).permute(0, 2, 1) + reg_dist_list_af = torch.cat(reg_dist_list_af, axis=-1).permute(0, 2, 1) + seg_conf_list_af = torch.cat(seg_conf_list_af, axis=-1).permute(0, 2, 1) + + + + #anchor_free + anchor_points_af, stride_tensor_af = generate_anchors( + x, self.stride, self.grid_cell_size, self.grid_cell_offset, device=x[0].device, is_eval=True, mode='af') + + pred_bboxes_af = dist2bbox(reg_dist_list_af, anchor_points_af, box_format='xywh') + pred_bboxes_af *= stride_tensor_af + + pred_bboxes = pred_bboxes_af + cls_score_list = cls_score_list_af + + return torch.cat( + [ + pred_bboxes, + torch.ones((b, pred_bboxes.shape[1], 1), device=pred_bboxes.device, dtype=pred_bboxes.dtype), + cls_score_list + ], + axis=-1), seg_list, seg_conf_list_af + +class Proto(nn.Module): + # Borrow from YOLOv5 + def __init__(self, num_layers, channels_list, pos, c_=256, c2=32): # ch_in, number of protos, number of masks + super().__init__() + chx = [6, 8, 10] if num_layers == 3 else [8, 9, 10, 11] + c1 = channels_list[chx[pos]] + self.cv1 = Conv(c1, c_, k=3) + self.upsample = nn.Upsample(scale_factor=2, mode='nearest') + self.cv2 = Conv(c_, c_, k=3) + self.cv3 = Conv(c_, c2) + + def forward(self, x): + return self.cv3(self.cv2(self.upsample(self.cv1(x)))) + +def autopad(k, p=None, d=1): # kernel, padding, dilation + # Pad to 'same' shape outputs + if d > 1: + k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size + if p is None: + p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad + return p + + +class Conv(nn.Module): + # Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation) + default_act = nn.SiLU() # default activation + + def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True): + super().__init__() + self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False) + self.bn = nn.BatchNorm2d(c2) + self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity() + + def forward(self, x): + return self.act(self.bn(self.conv(x))) + + def forward_fuse(self, x): + return self.act(self.conv(x)) + + +def build_effidehead_layer(channels_list, num_anchors, num_classes, reg_max=16, num_layers=3, num_masks=32, fuse_ab=False): + + chx = [6, 8, 10] if num_layers == 3 else [8, 9, 10, 11] + + head_layers = nn.Sequential( + # stem0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=1, + stride=1 + ), + # cls_conv0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=3, + stride=1 + ), + # reg_conv0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=3, + stride=1 + ), + # seg_conv0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=3, + stride=1 + ), + # cls_pred0_af + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=num_classes, + kernel_size=1 + ), + # reg_pred0_af + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=4 * (reg_max + 1), + kernel_size=1 + ), + # seg_pred0_af + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=num_masks, + kernel_size=1 + ), + # cls_pred0_3ab + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred0_3ab + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=4 * num_anchors, + kernel_size=1 + ), + # seg_pred0_3ab + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=num_masks * num_anchors, + kernel_size=1 + ), + # stem1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=1, + stride=1 + ), + # cls_conv1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=3, + stride=1 + ), + # reg_conv1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=3, + stride=1 + ), + # seg_conv1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=3, + stride=1 + ), + # cls_pred1_af + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=num_classes, + kernel_size=1 + ), + # reg_pred1_af + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=4 * (reg_max + 1), + kernel_size=1 + ), + # seg_pred1_af + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=num_masks, + kernel_size=1 + ), + # cls_pred1_3ab + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred1_3ab + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=4 * num_anchors, + kernel_size=1 + ), + # seg_pred1_3ab + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=num_masks * num_anchors, + kernel_size=1 + ), + # stem2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=1, + stride=1 + ), + # cls_conv2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=3, + stride=1 + ), + # reg_conv2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=3, + stride=1 + ), + # seg_conv2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=3, + stride=1 + ), + # cls_pred2_af + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=num_classes, + kernel_size=1 + ), + # reg_pred2_af + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=4 * (reg_max + 1), + kernel_size=1 + ), + # seg_pred2_af + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=num_masks, + kernel_size=1 + ), + # cls_pred2_3ab + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred2_3ab + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=4 * num_anchors, + kernel_size=1 + ), + # seg_pred2_3ab + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=num_masks * num_anchors, + kernel_size=1 + ), + ) + + return head_layers + + + + + + + diff --git a/yolov6/models/heads/effidehead_fuseab_seg_solo.py b/yolov6/models/heads/effidehead_fuseab_seg_solo.py new file mode 100644 index 00000000..61bd1328 --- /dev/null +++ b/yolov6/models/heads/effidehead_fuseab_seg_solo.py @@ -0,0 +1,540 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +import math +from yolov6.layers.common import * +from yolov6.assigners.anchor_generator import generate_anchors +from yolov6.utils.general import dist2bbox + + +class Detect(nn.Module): + export = False + '''Efficient Decoupled Head for fusing anchor-base branches. + ''' + def __init__(self, num_classes=80, anchors=None, num_layers=3, inplace=True, head_layers=None, reg_mask=None, use_dfl=True, reg_max=16, nm=32, fuse_ab=False): # detection layer + super().__init__() + assert head_layers is not None + assert reg_mask is not None + self.nc = num_classes # number of classes + self.no = num_classes + 5 + nm # number of outputs per anchor + self.nl = num_layers # number of detection layers + self.nm = nm # number of masks + if isinstance(anchors, (list, tuple)): + self.na = len(anchors[0]) // 2 + else: + self.na = anchors + self.grid = [torch.zeros(1)] * num_layers + self.fuse_ab = fuse_ab + self.prior_prob = 1e-2 + self.inplace = inplace + stride = [8, 16, 32] if num_layers == 3 else [8, 16, 32, 64] # strides computed during build + self.stride = torch.tensor(stride) + self.use_dfl = use_dfl + self.reg_max = reg_max + self.proj_conv = nn.Conv2d(self.reg_max + 1, 1, 1, bias=False) + self.grid_cell_offset = 0.5 + self.grid_cell_size = 5.0 + self.anchors_init= ((torch.tensor(anchors) / self.stride[:,None])).reshape(self.nl, self.na, 2) + self.reg_mask = reg_mask + + # Init decouple head + self.stems = nn.ModuleList() + self.cls_convs = nn.ModuleList() + self.reg_convs = nn.ModuleList() + self.seg_convs = nn.ModuleList() + self.cls_preds = nn.ModuleList() + self.reg_preds = nn.ModuleList() + self.seg_preds = nn.ModuleList() + self.cls_preds_ab = nn.ModuleList() + self.reg_preds_ab = nn.ModuleList() + self.seg_preds_ab = nn.ModuleList() + self.seg_proto = nn.ModuleList() + self.seg_proto.append(reg_mask[0]) + self.seg_proto.append(reg_mask[1]) + self.seg_proto.append(reg_mask[2]) + + + # Efficient decoupled head layers + for i in range(num_layers): + idx = i*10 + self.stems.append(head_layers[idx]) + self.cls_convs.append(head_layers[idx+1]) + self.reg_convs.append(head_layers[idx+2]) + self.seg_convs.append(head_layers[idx+3]) + self.cls_preds.append(head_layers[idx+4]) + self.reg_preds.append(head_layers[idx+5]) + self.seg_preds.append(head_layers[idx+6]) + if self.fuse_ab: + self.cls_preds_ab.append(head_layers[idx+7]) + self.reg_preds_ab.append(head_layers[idx+8]) + self.seg_preds_ab.append(head_layers[idx+9]) + + + def initialize_biases(self): + + for conv in self.cls_preds: + b = conv.bias.view(-1, ) + b.data.fill_(-math.log((1 - self.prior_prob) / self.prior_prob)) + conv.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + w = conv.weight + w.data.fill_(0.) + conv.weight = torch.nn.Parameter(w, requires_grad=True) + + if self.fuse_ab: + for conv in self.cls_preds_ab: + b = conv.bias.view(-1, ) + b.data.fill_(-math.log((1 - self.prior_prob) / self.prior_prob)) + conv.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + w = conv.weight + w.data.fill_(0.) + conv.weight = torch.nn.Parameter(w, requires_grad=True) + + for conv in self.reg_preds: + b = conv.bias.view(-1, ) + b.data.fill_(1.0) + conv.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + w = conv.weight + w.data.fill_(0.) + conv.weight = torch.nn.Parameter(w, requires_grad=True) + + if self.fuse_ab: + for conv in self.reg_preds_ab: + b = conv.bias.view(-1, ) + b.data.fill_(1.0) + conv.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + w = conv.weight + w.data.fill_(0.) + conv.weight = torch.nn.Parameter(w, requires_grad=True) + + self.proj = nn.Parameter(torch.linspace(0, self.reg_max, self.reg_max + 1), requires_grad=False) + self.proj_conv.weight = nn.Parameter(self.proj.view([1, self.reg_max + 1, 1, 1]).clone().detach(), + requires_grad=False) + + + def handleseg_ab(self, sgot_lst, sg_msk_lst): + ''' + sg_msk_lst --> lst sg_msk: segmask: Shape(bs, 32, w, h) + sgot_lst --> lst sgot: seg_output_conf: Shape(bs, num_of_anchors, h, w, 32) + sgot.flatten(1, 3) -> Shape(bs, n*num_of_anchors, 32) + for j in range(bs) -> ((n*num_of_anchor, 32)@(32, w0, h0) = (n*num_of_anchor, 32)@(32, w0, h0)) + ''' + mask_res = [] + for i in range(len(sgot_lst)): + sgot = sgot_lst[i] + sg_msk = sg_msk_lst[i] + s_shape = sgot.shape[1:4] + sgot = sgot.flatten(1, 3) + t_mask_res = [] + for j in range(sgot.shape[0]): + sgot_t = sgot[j] # (n, 32) + sg_msk_t = sg_msk[j] # (32, w, h) + m_t = (sgot_t@sg_msk_t.reshape(self.nm, -1)).reshape(-1, *sg_msk_t.shape[1:]) + m_t = m_t.unsqueeze(0) + t_mask_res.append(m_t) + mask_res.append(torch.cat(t_mask_res, 0).flatten(0,1)) + return mask_res + + + + + + def forward(self, x): + if self.training: + device = x[0].device + cls_score_list_af = [] + reg_dist_list_af = [] + cls_score_list_ab = [] + reg_dist_list_ab = [] + seg_conf_list_af = [] + seg_conf_list_ab = [] + seg_list = [] + af_seg_list = [] + ab_seg_list = [] + + s1 = self.seg_proto[0](x[0]) + s2 = self.seg_proto[1](x[1]) + s3 = self.seg_proto[2](x[2]) + seg_mask = s1 + s2 + s3 + seg_list.append(seg_mask) + + + + for i in range(self.nl): + b, _, h, w = x[i].shape + l = h * w + + + x[i] = self.stems[i](x[i]) + + + cls_x = x[i] + reg_x = x[i] + seg_x = x[i] + + cls_feat = self.cls_convs[i](cls_x) + reg_feat = self.reg_convs[i](reg_x) + seg_feat = self.seg_convs[i](seg_x) + + #anchor_base + if self.fuse_ab: + cls_output_ab = self.cls_preds_ab[i](cls_feat) + reg_output_ab = self.reg_preds_ab[i](reg_feat) + seg_output_ab = self.seg_preds_ab[i](seg_feat) + + cls_output_ab = torch.sigmoid(cls_output_ab) + seg_output_ab = torch.sigmoid(seg_output_ab) + if self.fuse_ab: + seg_conf_list_ab.append(seg_output_ab.reshape(b, self.na, -1, h, w).permute(0,1,3,4,2)) + cls_output_ab = cls_output_ab.reshape(b, self.na, -1, h, w).permute(0,1,3,4,2) + cls_score_list_ab.append(cls_output_ab.flatten(1,3)) + + + reg_output_ab = reg_output_ab.reshape(b, self.na, -1, h, w).permute(0,1,3,4,2) + reg_output_ab[..., 2:4] = ((reg_output_ab[..., 2:4].sigmoid() * 2) ** 2 ) * (self.anchors_init[i].reshape(1, self.na, 1, 1, 2).to(device)) + reg_dist_list_ab.append(reg_output_ab.flatten(1,3)) + + #anchor_free + cls_output_af = self.cls_preds[i](cls_feat) + reg_output_af = self.reg_preds[i](reg_feat) + seg_output_af = self.seg_preds[i](seg_feat) + + cls_output_af = torch.sigmoid(cls_output_af) + # seg_output_af = torch.sigmoid(seg_output_af) + seg_conf_list_af.append(seg_output_af.flatten(2).permute((0, 2, 1))) + + cls_score_list_af.append(cls_output_af.flatten(2).permute((0, 2, 1))) + reg_dist_list_af.append(reg_output_af.flatten(2).permute((0, 2, 1))) + + #Not support fuseab now + if False: + ab_seg_list = self.handleseg_ab(seg_conf_list_ab, seg_list) if self.fuse_ab else [] + cls_score_list_ab = torch.cat(cls_score_list_ab, axis=1) + reg_dist_list_ab = torch.cat(reg_dist_list_ab, axis=1) + cls_score_list_af = torch.cat(cls_score_list_af, axis=1) + reg_dist_list_af = torch.cat(reg_dist_list_af, axis=1) + + return x, cls_score_list_ab, reg_dist_list_ab, cls_score_list_af, reg_dist_list_af, [seg_conf_list_af, seg_list], ab_seg_list + + else: + device = x[0].device + cls_score_list_af = [] + reg_dist_list_af = [] + seg_list = [] + seg_conf_list_af = [] + s1 = self.seg_proto[0](x[0]) + s2 = self.seg_proto[1](x[1]) + s3 = self.seg_proto[2](x[2]) + seg_mask = s1 + s2 + s3 + seg_list.append(seg_mask) + + for i in range(self.nl): + b, _, h, w = x[i].shape + l = h * w + + + x[i] = self.stems[i](x[i]) + + cls_x = x[i] + reg_x = x[i] + seg_x = x[i] + + cls_feat = self.cls_convs[i](cls_x) + reg_feat = self.reg_convs[i](reg_x) + seg_feat = self.seg_convs[i](seg_x) + + #anchor_free + cls_output_af = self.cls_preds[i](cls_feat) + reg_output_af = self.reg_preds[i](reg_feat) + seg_output_af = self.seg_preds[i](seg_feat) + + if self.use_dfl: + reg_output_af = reg_output_af.reshape([-1, 4, self.reg_max + 1, l]).permute(0, 2, 1, 3) + reg_output_af = self.proj_conv(F.softmax(reg_output_af, dim=1)) + + cls_output_af = torch.sigmoid(cls_output_af) + proto_no = (torch.ones(b, 1, l) * i).cuda() + + + if self.export: + cls_score_list_af.append(cls_output_af) + reg_dist_list_af.append(reg_output_af) + seg_conf_list_af.append(seg_output_af) + else: + cls_score_list_af.append(cls_output_af.reshape([b, self.nc, l])) + reg_dist_list_af.append(reg_output_af.reshape([b, 4, l])) + seg_conf_list_af.append(torch.cat([proto_no, seg_output_af.reshape([b, 67, l])], axis = 1)) #[which_proto, (32...)] + + if self.export: + return tuple(torch.cat([cls, reg, seg], 1) for cls, reg, seg in zip(cls_score_list_af, reg_dist_list_af, seg_conf_list_af)), seg_list[0] + + cls_score_list_af = torch.cat(cls_score_list_af, axis=-1).permute(0, 2, 1) + reg_dist_list_af = torch.cat(reg_dist_list_af, axis=-1).permute(0, 2, 1) + seg_conf_list_af = torch.cat(seg_conf_list_af, axis=-1).permute(0, 2, 1) + + + + #anchor_free + anchor_points_af, stride_tensor_af = generate_anchors( + x, self.stride, self.grid_cell_size, self.grid_cell_offset, device=x[0].device, is_eval=True, mode='af') + + pred_bboxes_af = dist2bbox(reg_dist_list_af, anchor_points_af, box_format='xywh') + pred_bboxes_af *= stride_tensor_af + + pred_bboxes = pred_bboxes_af + cls_score_list = cls_score_list_af + + return torch.cat( + [ + pred_bboxes, + torch.ones((b, pred_bboxes.shape[1], 1), device=pred_bboxes.device, dtype=pred_bboxes.dtype), + cls_score_list + ], + axis=-1), seg_list, seg_conf_list_af + +class Proto(nn.Module): + # Borrowed from YOLOv5 + def __init__(self, num_layers, channels_list, pos, c_=256, c2=64, scale_factor=2): # ch_in, number of protos, number of masks + super().__init__() + chx = [6, 8, 10] if num_layers == 3 else [8, 9, 10, 11] + c1 = channels_list[chx[pos]] + self.cv1 = Conv(c1, c_, k=3) + self.upsample = nn.Upsample(scale_factor=scale_factor, mode='nearest') + self.cv2 = Conv(c_, c_, k=3) + self.cv3 = Conv(c_, c2) + + def forward(self, x): + return self.cv3(self.cv2(self.upsample(self.cv1(x)))) + +def autopad(k, p=None, d=1): # kernel, padding, dilation + # Pad to 'same' shape outputs + if d > 1: + k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size + if p is None: + p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad + return p + + +class Conv(nn.Module): + # Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation) + default_act = nn.SiLU() # default activation + + def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True): + super().__init__() + self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False) + self.bn = nn.BatchNorm2d(c2) + self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity() + + def forward(self, x): + return self.act(self.bn(self.conv(x))) + + def forward_fuse(self, x): + return self.act(self.conv(x)) + + +def build_effidehead_layer(channels_list, num_anchors, num_classes, reg_max=16, num_layers=3, num_masks=67, fuse_ab=False): + + chx = [6, 8, 10] if num_layers == 3 else [8, 9, 10, 11] + + head_layers = nn.Sequential( + # stem0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=1, + stride=1 + ), + # cls_conv0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=3, + stride=1 + ), + # reg_conv0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=3, + stride=1 + ), + # seg_conv0 + ConvBNSiLU( + in_channels=channels_list[chx[0]], + out_channels=channels_list[chx[0]], + kernel_size=3, + stride=1 + ), + # cls_pred0_af + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=num_classes, + kernel_size=1 + ), + # reg_pred0_af + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=4 * (reg_max + 1), + kernel_size=1 + ), + # seg_pred0_af + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=num_masks, + kernel_size=1 + ), + # cls_pred0_3ab + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred0_3ab + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=4 * num_anchors, + kernel_size=1 + ), + # seg_pred0_3ab + nn.Conv2d( + in_channels=channels_list[chx[0]], + out_channels=num_masks * num_anchors, + kernel_size=1 + ), + # stem1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=1, + stride=1 + ), + # cls_conv1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=3, + stride=1 + ), + # reg_conv1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=3, + stride=1 + ), + # seg_conv1 + ConvBNSiLU( + in_channels=channels_list[chx[1]], + out_channels=channels_list[chx[1]], + kernel_size=3, + stride=1 + ), + # cls_pred1_af + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=num_classes, + kernel_size=1 + ), + # reg_pred1_af + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=4 * (reg_max + 1), + kernel_size=1 + ), + # seg_pred1_af + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=num_masks, + kernel_size=1 + ), + # cls_pred1_3ab + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred1_3ab + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=4 * num_anchors, + kernel_size=1 + ), + # seg_pred1_3ab + nn.Conv2d( + in_channels=channels_list[chx[1]], + out_channels=num_masks * num_anchors, + kernel_size=1 + ), + # stem2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=1, + stride=1 + ), + # cls_conv2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=3, + stride=1 + ), + # reg_conv2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=3, + stride=1 + ), + # seg_conv2 + ConvBNSiLU( + in_channels=channels_list[chx[2]], + out_channels=channels_list[chx[2]], + kernel_size=3, + stride=1 + ), + # cls_pred2_af + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=num_classes, + kernel_size=1 + ), + # reg_pred2_af + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=4 * (reg_max + 1), + kernel_size=1 + ), + # seg_pred2_af + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=num_masks, + kernel_size=1 + ), + # cls_pred2_3ab + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=num_classes * num_anchors, + kernel_size=1 + ), + # reg_pred2_3ab + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=4 * num_anchors, + kernel_size=1 + ), + # seg_pred2_3ab + nn.Conv2d( + in_channels=channels_list[chx[2]], + out_channels=num_masks * num_anchors, + kernel_size=1 + ), + ) + + return head_layers + + + + + + + diff --git a/yolov6/models/losses/loss.py b/yolov6/models/losses/loss.py index ec534923..c4fe8d87 100644 --- a/yolov6/models/losses/loss.py +++ b/yolov6/models/losses/loss.py @@ -30,8 +30,6 @@ def __init__(self, ): self.fpn_strides = fpn_strides - self.cached_feat_sizes = [torch.Size([0, 0]) for _ in fpn_strides] - self.cached_anchors = None self.grid_cell_size = grid_cell_size self.grid_cell_offset = grid_cell_offset self.num_classes = num_classes @@ -60,13 +58,8 @@ def __call__( ): feats, pred_scores, pred_distri = outputs - if all(feat.shape[2:] == cfsize for feat, cfsize in zip(feats, self.cached_feat_sizes)): - anchors, anchor_points, n_anchors_list, stride_tensor = self.cached_anchors - else: - self.cached_feat_sizes = [feat.shape[2:] for feat in feats] - anchors, anchor_points, n_anchors_list, stride_tensor = \ - generate_anchors(feats, self.fpn_strides, self.grid_cell_size, self.grid_cell_offset, device=feats[0].device) - self.cached_anchors = anchors, anchor_points, n_anchors_list, stride_tensor + anchors, anchor_points, n_anchors_list, stride_tensor = \ + generate_anchors(feats, self.fpn_strides, self.grid_cell_size, self.grid_cell_offset, device=feats[0].device) assert pred_scores.type() == pred_distri.type() gt_bboxes_scale = torch.tensor([batch_width, batch_height, batch_width, batch_height]).type_as(pred_scores) diff --git a/yolov6/models/losses/seg_loss.py b/yolov6/models/losses/seg_loss.py new file mode 100644 index 00000000..04a25ecd --- /dev/null +++ b/yolov6/models/losses/seg_loss.py @@ -0,0 +1,532 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- + +import torch +import torch.nn as nn +import numpy as np +import torch.nn.functional as F +from yolov6.assigners.anchor_generator import generate_anchors +from yolov6.utils.general import dist2bbox, bbox2dist, xywh2xyxy, box_iou +from yolov6.utils.figure_iou import IOUloss +from yolov6.assigners.atss_assigner_seg import ATSSAssigner +from yolov6.assigners.tal_assigner_seg import TaskAlignedAssigner +import time +import pickle + +class ComputeLoss: + '''Loss computation func.''' + def __init__(self, + fpn_strides=[8, 16, 32], + grid_cell_size=5.0, + grid_cell_offset=0.5, + num_classes=80, + ori_img_size=640, + warmup_epoch=4, + use_dfl=True, + reg_max=16, + nm=32, + iou_type='giou', + loss_weight={ + 'class': 1.0, + 'iou': 2.5, + 'dfl': 0.5, + 'seg': 2.5}, + ): + + self.fpn_strides = fpn_strides + self.grid_cell_size = grid_cell_size + self.grid_cell_offset = grid_cell_offset + self.num_classes = num_classes + self.ori_img_size = ori_img_size + self.nm = nm + self.tt = nm + self.warmup_epoch = warmup_epoch + self.warmup_assigner = ATSSAssigner(9, num_classes=self.num_classes) + self.formal_assigner = TaskAlignedAssigner(topk=13, num_classes=self.num_classes, alpha=1.0, beta=6.0) + + self.use_dfl = use_dfl + self.reg_max = reg_max + self.proj = nn.Parameter(torch.linspace(0, self.reg_max, self.reg_max + 1), requires_grad=False) + self.iou_type = iou_type + self.varifocal_loss = VarifocalLoss().cuda() + self.bbox_loss = BboxLoss(self.num_classes, self.reg_max, self.use_dfl, self.iou_type).cuda() + self.loss_weight = loss_weight + + def __call__( + self, + outputs, + targets, + epoch_num, + step_num, + batch_height, + batch_width, + segmasks, + img=None, + ): + + feats, pred_scores, pred_distri, pred_seg = outputs # seg_list:shape(3)(b, nm, mw, mh) seg_conf_list:shape(3):(b, l ,nm) + seg_cf, seg_proto = pred_seg + anchors, anchor_points, n_anchors_list, stride_tensor = \ + generate_anchors(feats, self.fpn_strides, self.grid_cell_size, self.grid_cell_offset, device=feats[0].device) + + assert pred_scores.type() == pred_distri.type() + gt_bboxes_scale = torch.tensor([batch_width, batch_height, batch_width, batch_height]).type_as(pred_scores) + batch_size = pred_scores.shape[0] + + targets, gt_segmasks =self.preprocess(targets, batch_size, gt_bboxes_scale, segmasks) + gt_labels = targets[:, :, :1] + gt_bboxes = targets[:, :, 1:] #xyxy + mask_gt = (gt_bboxes.sum(-1, keepdim=True) > 0).float() + + anchor_points_s = anchor_points / stride_tensor + pred_bboxes = self.bbox_decode(anchor_points_s, pred_distri) #xyxy + try: + if epoch_num < self.warmup_epoch: + target_labels, target_bboxes, target_scores, fg_mask, target_segmasks = \ + self.warmup_assigner( + anchors, + n_anchors_list, + gt_labels, + gt_bboxes, + mask_gt, + pred_bboxes.detach() * stride_tensor, + gt_segmasks) + else: + target_labels, target_bboxes, target_scores, fg_mask, idx_lst = \ + self.formal_assigner( + pred_scores.detach(), + pred_bboxes.detach() * stride_tensor, + anchor_points, + gt_labels, + gt_bboxes, + mask_gt, + gt_segmasks) + + except RuntimeError: + print( + "OOM RuntimeError is raised due to the huge memory cost during label assignment. \ + CPU mode is applied in this batch. If you want to avoid this issue, \ + try to reduce the batch size or image size." + ) + torch.cuda.empty_cache() + print("------------CPU Mode for This Batch-------------") + if epoch_num < self.warmup_epoch: + _anchors = anchors.cpu().float() + _n_anchors_list = n_anchors_list + _gt_labels = gt_labels.cpu().float() + _gt_bboxes = gt_bboxes.cpu().float() + _mask_gt = mask_gt.cpu().float() + _pred_bboxes = pred_bboxes.detach().cpu().float() + _stride_tensor = stride_tensor.cpu().float() + _segmasks = gt_segmasks.cpu().float() + + target_labels, target_bboxes, target_scores, fg_mask, target_segmasks = \ + self.warmup_assigner( + _anchors, + _n_anchors_list, + _gt_labels, + _gt_bboxes, + _mask_gt, + _pred_bboxes * _stride_tensor, + _segmasks) + + else: + _pred_scores = pred_scores.detach().cpu().float() + _pred_bboxes = pred_bboxes.detach().cpu().float() + _anchor_points = anchor_points.cpu().float() + _gt_labels = gt_labels.cpu().float() + _gt_bboxes = gt_bboxes.cpu().float() + _mask_gt = mask_gt.cpu().float() + _stride_tensor = stride_tensor.cpu().float() + _segmasks = gt_segmasks.cpu().float() + + target_labels, target_bboxes, target_scores, fg_mask, idx_lst = \ + self.formal_assigner( + _pred_scores, + _pred_bboxes * _stride_tensor, + _anchor_points, + _gt_labels, + _gt_bboxes, + _mask_gt, + _segmasks) + + target_labels = target_labels.cuda() + target_bboxes = target_bboxes.cuda() + target_scores = target_scores.cuda() + fg_mask = fg_mask.cuda() + for _ in idx_lst: + _ = _.cuda() + + + if step_num % 10 == 0: + torch.cuda.empty_cache() + + # rescale bbox + target_bboxes /= stride_tensor + + # cls loss + target_labels = torch.where(fg_mask > 0, target_labels, torch.full_like(target_labels, self.num_classes)) + one_hot_label = F.one_hot(target_labels.long(), self.num_classes + 1)[..., :-1] + loss_cls = self.varifocal_loss(pred_scores, target_scores, one_hot_label) + + + target_scores_sum = target_scores.sum() + + # avoid devide zero error, devide by zero will cause loss to be inf or nan. + # if target_scores_sum is 0, loss_cls equals to 0 alson + if target_scores_sum > 1: + loss_cls /= target_scores_sum + + # bbox loss + loss_iou, loss_dfl = self.bbox_loss(pred_distri, pred_bboxes, anchor_points_s, target_bboxes, + target_scores, target_scores_sum, fg_mask) + + loss_seg = self.mask_loss(gt_segmasks, seg_cf, seg_proto, target_bboxes, fg_mask, idx_lst, target_scores, target_scores_sum) + + loss = self.loss_weight['class'] * loss_cls + \ + self.loss_weight['iou'] * loss_iou + \ + self.loss_weight['dfl'] * loss_dfl + \ + self.loss_weight['seg'] * loss_seg + + + return loss, \ + torch.cat(((self.loss_weight['iou'] * loss_iou).unsqueeze(0), + (self.loss_weight['dfl'] * loss_dfl).unsqueeze(0), + (self.loss_weight['class'] * loss_cls).unsqueeze(0), + (self.loss_weight['seg'] * loss_seg).unsqueeze(0))).detach() + + def preprocess(self, targets, batch_size, scale_tensor, segmask): + targets_list = np.zeros((batch_size, 1, 5)).tolist() + cu = [] + already = [] + # seg_list = np.zeros((batch_size, 1, *segmask.shape[1:])).tolist() + for i, item in enumerate(targets.cpu().numpy().tolist()): + index = int(item[0]) + targets_list[index].append(item[1:]) + if index not in already: + already.append(index) + cu.append(i) + cu.append(segmask.shape[0]) + max_len = max((len(l) for l in targets_list)) + segmasks = torch.zeros(batch_size, max_len - 1, segmask.shape[-2], segmask.shape[-1]).cuda() + if len(already) != 0: + for i in range(len(already)): + j = already[i] + start = cu[i] + end = cu[i+1] + segmasks[j, : end - start] = segmask[start: end].clone() + targets = torch.from_numpy(np.array(list(map(lambda l:l + [[-1,0,0,0,0]]*(max_len - len(l)), targets_list)))[:,1:,:]).to(targets.device) + + batch_target = targets[:, :, 1:5].mul_(scale_tensor) + targets[..., 1:] = xywh2xyxy(batch_target) + return targets, segmasks + + def bbox_decode(self, anchor_points, pred_dist): + if self.use_dfl: + batch_size, n_anchors, _ = pred_dist.shape + pred_dist = F.softmax(pred_dist.view(batch_size, n_anchors, 4, self.reg_max + 1), dim=-1).matmul(self.proj.to(pred_dist.device)) + return dist2bbox(pred_dist, anchor_points) + + def mask_loss(self, gt_segmasks, seg_cf, seg_proto, txyxy_ori, fg_mask, idx_lst, target_scores=None, target_scores_sum=None): + # pred_mask_lst -> list + ''' + pred_mask -> Shape(n1, w, h) + gt_mask -> Shape(n, img_w, img_h) + xyxy -> Shape(n, 4) + sum(n1, n2, n3, ...) = n + torch.abs((xyxy[..., 3] - xyxy[..., 1]) * (xyxy[..., 4] - xyxy[..., 2])) -> area + fg_mask --> (bs, tsize) + idx -> (bs, tsize) + gt_segmasks -> (bs, labelsize, w, h) + ''' + sl = 0 + sl2 = 0 + bl = [2, 4, 8] + num_pos = fg_mask.sum() + tloss = torch.zeros(1).float().cuda() + if num_pos<=0: + for ipred in seg_proto: + tloss += (ipred.sum() * 0.) + for ipred in seg_cf: + tloss += (ipred.sum() * 0.) + return tloss[0] + + + xyxy_mask = fg_mask.unsqueeze(-1).repeat([1, 1, 4]) + mtarget_scores = target_scores.sum(-1) # (bs, nl, 1) + + sl = 0 + qf = len(idx_lst) == 1 and len(idx_lst[0].shape) == 2 + if qf: + idx_lst = idx_lst[0] + for j in range(len(seg_cf)): + ishape = 0 + pshape = 0 + + iseg_proto = seg_proto[0] # (bs, 32, h, w) + bs = iseg_proto.shape[0] + iseg_cf = seg_cf[j] # (bs, part_n, 32) + + pshape = iseg_proto.shape[-1] + ishape = iseg_cf.shape[1] # (1) = part_n + idx = idx_lst[:, sl: sl + ishape] # (bs, part_n) + + ifg_mask = fg_mask[:, sl: sl + ishape] # (n) --> (bs, part_n) + itarget_scores = mtarget_scores[:, sl: sl + ishape] + if ifg_mask.sum() <= 0: + tloss += (iseg_proto.sum() * 0.) + tloss += (iseg_cf.sum() * 0.) + continue + target_sg = [] + pred_sg = [] + ixyxy_lst = [] + mask_weight = [] + for i in range(bs): + idx_thisbatch = torch.masked_select(idx[i], ifg_mask[i]) #(casize) + igt_segmasks = gt_segmasks.reshape(-1, *gt_segmasks.shape[-2:])[idx_thisbatch] # (?1, h?, w?) --> (?2, h?, w?) + imask_weight = torch.masked_select(itarget_scores[i], ifg_mask[i]).unsqueeze(-1) + mask_weight.append(imask_weight) + target_sg.append(igt_segmasks) + tiseg_cf = torch.masked_select(iseg_cf[i], ifg_mask[i].unsqueeze(-1).repeat(1, self.tt)) # (?2, 32) + tiseg_cf = tiseg_cf.reshape(-1, self.tt) + ipred_seg = (tiseg_cf@iseg_proto[i].reshape(self.tt, -1)).reshape(-1, pshape, pshape) # (?2, h, w) + ixyxy = torch.masked_select(txyxy_ori[i, sl: sl + ishape], xyxy_mask[i, sl: sl + ishape, :]).reshape(-1, 4) # (n, 4) --> (part_n, 4) --> (?2, 4) + ixyxy_lst.append(ixyxy) + pred_sg.append(ipred_seg) + + + + + bxyxy = torch.cat(ixyxy_lst, dim = 0) * bl[j] + bpred_seg = torch.cat(pred_sg, dim = 0) + bgt_seg = torch.cat(target_sg, dim = 0) + masks_weight = torch.cat(mask_weight, dim = 0).reshape(-1) + if tuple(bgt_seg.shape[-2:]) != (pshape, pshape): # downsample + bgt_seg = F.interpolate(bgt_seg[None], (pshape, pshape), mode='nearest')[0] + area = torch.abs((bxyxy[..., 2] - bxyxy[..., 0]) * (bxyxy[..., 3] - bxyxy[..., 1])) + area = area / (pshape) + area = area / (pshape) + + + + + + sl += ishape + loss = F.binary_cross_entropy_with_logits(bpred_seg, bgt_seg, reduction='none') + + loss = (self.crop_mask(loss, bxyxy).mean(dim=(1, 2)) / area) * masks_weight + loss = loss.sum() + tloss += loss + if target_scores_sum > 1: + tloss[0] = tloss[0] / target_scores_sum + return tloss[0] / len(seg_cf) + + + @staticmethod + 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 [n, h, w] 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)) + + +class VarifocalLoss(nn.Module): + def __init__(self): + super(VarifocalLoss, self).__init__() + + def forward(self, pred_score,gt_score, label, alpha=0.75, gamma=2.0): + + weight = alpha * pred_score.pow(gamma) * (1 - label) + gt_score * label + with torch.cuda.amp.autocast(enabled=False): + loss = (F.binary_cross_entropy(pred_score.float(), gt_score.float(), reduction='none') * weight).sum() + + return loss + + +class BboxLoss(nn.Module): + def __init__(self, num_classes, reg_max, use_dfl=False, iou_type='giou'): + super(BboxLoss, self).__init__() + self.num_classes = num_classes + self.iou_loss = IOUloss(box_format='xyxy', iou_type=iou_type, eps=1e-10) + self.reg_max = reg_max + self.use_dfl = use_dfl + + def forward(self, pred_dist, pred_bboxes, anchor_points, + target_bboxes, target_scores, target_scores_sum, fg_mask): + + # select positive samples mask + num_pos = fg_mask.sum() + if num_pos > 0: + # iou loss + bbox_mask = fg_mask.unsqueeze(-1).repeat([1, 1, 4]) + pred_bboxes_pos = torch.masked_select(pred_bboxes, + bbox_mask).reshape([-1, 4]) + target_bboxes_pos = torch.masked_select( + target_bboxes, bbox_mask).reshape([-1, 4]) + bbox_weight = torch.masked_select( + target_scores.sum(-1), fg_mask).unsqueeze(-1) + loss_iou = self.iou_loss(pred_bboxes_pos, + target_bboxes_pos) * bbox_weight + if target_scores_sum > 1: + loss_iou = loss_iou.sum() / target_scores_sum + else: + loss_iou = loss_iou.sum() + + # dfl loss + if self.use_dfl: + dist_mask = fg_mask.unsqueeze(-1).repeat( + [1, 1, (self.reg_max + 1) * 4]) + pred_dist_pos = torch.masked_select( + pred_dist, dist_mask).reshape([-1, 4, self.reg_max + 1]) + target_ltrb = bbox2dist(anchor_points, target_bboxes, self.reg_max) + target_ltrb_pos = torch.masked_select( + target_ltrb, bbox_mask).reshape([-1, 4]) + loss_dfl = self._df_loss(pred_dist_pos, + target_ltrb_pos) * bbox_weight + if target_scores_sum > 1: + loss_dfl = loss_dfl.sum() / target_scores_sum + else: + loss_dfl = loss_dfl.sum() + else: + loss_dfl = pred_dist.sum() * 0. + + else: + loss_iou = pred_dist.sum() * 0. + loss_dfl = pred_dist.sum() * 0. + + return loss_iou, loss_dfl + + def _df_loss(self, pred_dist, target): + target_left = target.to(torch.long) + target_right = target_left + 1 + weight_left = target_right.to(torch.float) - target + weight_right = 1 - weight_left + loss_left = F.cross_entropy( + pred_dist.view(-1, self.reg_max + 1), target_left.view(-1), reduction='none').view( + target_left.shape) * weight_left + loss_right = F.cross_entropy( + pred_dist.view(-1, self.reg_max + 1), target_right.view(-1), reduction='none').view( + target_left.shape) * weight_right + return (loss_left + loss_right).mean(-1, keepdim=True) + +def dice_loss(pred, + target, + weight=None, + eps=1e-3, + reduction='mean', + naive_dice=False, + avg_factor=None): + """Calculate dice loss, there are two forms of dice loss is supported: + + - the one proposed in `V-Net: Fully Convolutional Neural + Networks for Volumetric Medical Image Segmentation + `_. + - the dice loss in which the power of the number in the + denominator is the first power instead of the second + power. + + Args: + pred (torch.Tensor): The prediction, has a shape (n, *) + target (torch.Tensor): The learning label of the prediction, + shape (n, *), same shape of pred. + weight (torch.Tensor, optional): The weight of loss for each + prediction, has a shape (n,). Defaults to None. + eps (float): Avoid dividing by zero. Default: 1e-3. + reduction (str, optional): The method used to reduce the loss into + a scalar. Defaults to 'mean'. + Options are "none", "mean" and "sum". + naive_dice (bool, optional): If false, use the dice + loss defined in the V-Net paper, otherwise, use the + naive dice loss in which the power of the number in the + denominator is the first power instead of the second + power.Defaults to False. + avg_factor (int, optional): Average factor that is used to average + the loss. Defaults to None. + """ + + input = pred.flatten(1) + target = target.flatten(1).float() + + a = torch.sum(input * target, 1) + if naive_dice: + b = torch.sum(input, 1) + c = torch.sum(target, 1) + d = (2 * a + eps) / (b + c + eps) + else: + b = torch.sum(input * input, 1) + eps + c = torch.sum(target * target, 1) + eps + d = (2 * a) / (b + c) + + loss = 1 - d + if weight is not None: + assert weight.ndim == loss.ndim + assert len(weight) == len(pred) + loss = weight_reduce_loss(loss, weight, reduction, avg_factor) + return loss + +def weight_reduce_loss(loss, + weight=None, + reduction='mean', + avg_factor=None): + """Apply element-wise weight and reduce loss. + + Args: + loss (Tensor): Element-wise loss. + weight (Optional[Tensor], optional): Element-wise weights. + Defaults to None. + reduction (str, optional): Same as built-in losses of PyTorch. + Defaults to 'mean'. + avg_factor (Optional[float], optional): Average factor when + computing the mean of losses. Defaults to None. + + Returns: + Tensor: Processed loss values. + """ + # if weight is specified, apply element-wise weight + if weight is not None: + loss = loss * weight + + # if avg_factor is not specified, just reduce the loss + if avg_factor is None: + loss = reduce_loss(loss, reduction) + else: + # if reduction is mean, then average the loss by avg_factor + if reduction == 'mean': + # Avoid causing ZeroDivisionError when avg_factor is 0.0, + # i.e., all labels of an image belong to ignore index. + eps = torch.finfo(torch.float32).eps + loss = loss.sum() / (avg_factor + eps) + # if reduction is 'none', then do nothing, otherwise raise an error + elif reduction != 'none': + raise ValueError('avg_factor can not be used with reduction="sum"') + return loss + +def reduce_loss(loss, reduction): + """Reduce loss as specified. + + Args: + loss (Tensor): Elementwise loss tensor. + reduction (str): Options are "none", "mean" and "sum". + + Return: + Tensor: Reduced loss tensor. + """ + reduction_enum = F._Reduction.get_enum(reduction) + # none: 0, elementwise_mean:1, sum: 2 + if reduction_enum == 0: + return loss + elif reduction_enum == 1: + return loss.mean() + elif reduction_enum == 2: + return loss.sum() \ No newline at end of file diff --git a/yolov6/models/losses/seg_loss_solo_main.py b/yolov6/models/losses/seg_loss_solo_main.py new file mode 100644 index 00000000..3a329beb --- /dev/null +++ b/yolov6/models/losses/seg_loss_solo_main.py @@ -0,0 +1,583 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- + +import torch +import torch.nn as nn +import numpy as np +import torch.nn.functional as F +from yolov6.assigners.anchor_generator import generate_anchors +from yolov6.utils.general import dist2bbox, bbox2dist, xywh2xyxy, box_iou +from yolov6.utils.figure_iou import IOUloss +from yolov6.assigners.atss_assigner_seg import ATSSAssigner +from yolov6.assigners.tal_assigner_seg import TaskAlignedAssigner +import time +import pickle + +class ComputeLoss: + '''Loss computation func.''' + def __init__(self, + fpn_strides=[8, 16, 32], + grid_cell_size=5.0, + grid_cell_offset=0.5, + num_classes=80, + ori_img_size=640, + warmup_epoch=4, + use_dfl=True, + reg_max=16, + weight_nums = 66, + bias_nums = 1, + nm = 64, + dyconv_channels = 66, + iou_type='giou', + loss_weight={ + 'class': 1.0, + 'iou': 2.5, + 'dfl': 0.5, + 'seg': 2.5}, + ): + + self.fpn_strides = fpn_strides + self.grid_cell_size = grid_cell_size + self.grid_cell_offset = grid_cell_offset + self.num_classes = num_classes + self.ori_img_size = ori_img_size + self.nm = nm + self.tt = nm + bias_nums + 2 + self.weight_nums = [nm + 2] + self.bias_nums = [bias_nums] + self.dyconv_channels = dyconv_channels + + self.warmup_epoch = warmup_epoch + self.warmup_assigner = ATSSAssigner(9, num_classes=self.num_classes) + self.formal_assigner = TaskAlignedAssigner(topk=13, num_classes=self.num_classes, alpha=1.0, beta=6.0) + + self.use_dfl = use_dfl + self.reg_max = reg_max + self.proj = nn.Parameter(torch.linspace(0, self.reg_max, self.reg_max + 1), requires_grad=False) + self.iou_type = iou_type + self.varifocal_loss = VarifocalLoss().cuda() + self.bbox_loss = BboxLoss(self.num_classes, self.reg_max, self.use_dfl, self.iou_type).cuda() + self.loss_weight = loss_weight + self.dice = True + + def parse_dynamic_params(self, flatten_kernels): + """split kernel head prediction to conv weight and bias.""" + n_inst = flatten_kernels.size(0) + n_layers = len(self.weight_nums) + params_splits = list( + torch.split_with_sizes( + flatten_kernels, self.weight_nums + self.bias_nums, dim=1)) + weight_splits = params_splits[:n_layers] + bias_splits = params_splits[n_layers:] + for i in range(n_layers): + if i < n_layers - 1: + weight_splits[i] = weight_splits[i].reshape( + n_inst * self.dyconv_channels, -1, 1, 1) + bias_splits[i] = bias_splits[i].reshape(n_inst * + self.dyconv_channels) + else: + weight_splits[i] = weight_splits[i].reshape(n_inst, -1, 1, 1) + bias_splits[i] = bias_splits[i].reshape(n_inst) + + return weight_splits, bias_splits + + def handle_proto_coord(self, proto): + _ = proto.shape[-1] + x = torch.arange(0, 1, step = 1 / _).unsqueeze(0).unsqueeze(0).repeat(1, _, 1).to(proto.dtype).to(proto.device) + y = torch.arange(0, 1, step = 1 / _).unsqueeze(0).T.unsqueeze(0).repeat(1, 1, _).to(proto.dtype).to(proto.device) + return torch.cat([proto, x, y]).reshape(1, -1, _, _) + + def __call__( + self, + outputs, + targets, + epoch_num, + step_num, + batch_height, + batch_width, + segmasks, + img=None, + ): + + + feats, pred_scores, pred_distri, pred_seg = outputs # seg_list:shape(3)(b, nm, mw, mh) seg_conf_list:shape(3):(b, l ,nm) + seg_cf, seg_proto = pred_seg + anchors, anchor_points, n_anchors_list, stride_tensor = \ + generate_anchors(feats, self.fpn_strides, self.grid_cell_size, self.grid_cell_offset, device=feats[0].device) + + assert pred_scores.type() == pred_distri.type() + gt_bboxes_scale = torch.tensor([batch_width, batch_height, batch_width, batch_height]).type_as(pred_scores) + batch_size = pred_scores.shape[0] + + targets, gt_segmasks =self.preprocess(targets, batch_size, gt_bboxes_scale, segmasks) + gt_labels = targets[:, :, :1] + gt_bboxes = targets[:, :, 1:] #xyxy + mask_gt = (gt_bboxes.sum(-1, keepdim=True) > 0).float() + + + # pboxes + anchor_points_s = anchor_points / stride_tensor + pred_bboxes = self.bbox_decode(anchor_points_s, pred_distri) #xyxy + + + try: + if epoch_num < self.warmup_epoch: + target_labels, target_bboxes, target_scores, fg_mask, target_segmasks = \ + self.warmup_assigner( + anchors, + n_anchors_list, + gt_labels, + gt_bboxes, + mask_gt, + pred_bboxes.detach() * stride_tensor, + gt_segmasks) + else: + target_labels, target_bboxes, target_scores, fg_mask, idx_lst = \ + self.formal_assigner( + pred_scores.detach(), + pred_bboxes.detach() * stride_tensor, + anchor_points, + gt_labels, + gt_bboxes, + mask_gt, + gt_segmasks) + + except RuntimeError: + print( + "OOM RuntimeError is raised due to the huge memory cost during label assignment. \ + CPU mode is applied in this batch. If you want to avoid this issue, \ + try to reduce the batch size or image size." + ) + torch.cuda.empty_cache() + print("------------CPU Mode for This Batch-------------") + if epoch_num < self.warmup_epoch: + _anchors = anchors.cpu().float() + _n_anchors_list = n_anchors_list + _gt_labels = gt_labels.cpu().float() + _gt_bboxes = gt_bboxes.cpu().float() + _mask_gt = mask_gt.cpu().float() + _pred_bboxes = pred_bboxes.detach().cpu().float() + _stride_tensor = stride_tensor.cpu().float() + _segmasks = gt_segmasks.cpu().float() + + target_labels, target_bboxes, target_scores, fg_mask, target_segmasks = \ + self.warmup_assigner( + _anchors, + _n_anchors_list, + _gt_labels, + _gt_bboxes, + _mask_gt, + _pred_bboxes * _stride_tensor, + _segmasks) + + else: + _pred_scores = pred_scores.detach().cpu().float() + _pred_bboxes = pred_bboxes.detach().cpu().float() + _anchor_points = anchor_points.cpu().float() + _gt_labels = gt_labels.cpu().float() + _gt_bboxes = gt_bboxes.cpu().float() + _mask_gt = mask_gt.cpu().float() + _stride_tensor = stride_tensor.cpu().float() + _segmasks = gt_segmasks.cpu().float() + + target_labels, target_bboxes, target_scores, fg_mask, idx_lst = \ + self.formal_assigner( + _pred_scores, + _pred_bboxes * _stride_tensor, + _anchor_points, + _gt_labels, + _gt_bboxes, + _mask_gt, + _segmasks) + + target_labels = target_labels.cuda() + target_bboxes = target_bboxes.cuda() + target_scores = target_scores.cuda() + fg_mask = fg_mask.cuda() + for _ in idx_lst: + _ = _.cuda() + + if step_num % 10 == 0: + torch.cuda.empty_cache() + + # rescale bbox + target_bboxes /= stride_tensor + + # cls loss + target_labels = torch.where(fg_mask > 0, target_labels, torch.full_like(target_labels, self.num_classes)) + one_hot_label = F.one_hot(target_labels.long(), self.num_classes + 1)[..., :-1] + loss_cls = self.varifocal_loss(pred_scores, target_scores, one_hot_label) + + + target_scores_sum = target_scores.sum() + + + if target_scores_sum > 1: + loss_cls /= target_scores_sum + + # bbox loss + loss_iou, loss_dfl = self.bbox_loss(pred_distri, pred_bboxes, anchor_points_s, target_bboxes, + target_scores, target_scores_sum, fg_mask) + + loss_seg = self.mask_loss(gt_segmasks, seg_cf, seg_proto, target_bboxes, fg_mask, idx_lst, target_scores, target_scores_sum, epoch=0) + + loss = self.loss_weight['class'] * loss_cls + \ + self.loss_weight['iou'] * loss_iou + \ + self.loss_weight['dfl'] * loss_dfl + \ + self.loss_weight['seg'] * loss_seg + + + return loss, \ + torch.cat(((self.loss_weight['iou'] * loss_iou).unsqueeze(0), + (self.loss_weight['dfl'] * loss_dfl).unsqueeze(0), + (self.loss_weight['class'] * loss_cls).unsqueeze(0), + (self.loss_weight['seg'] * loss_seg).unsqueeze(0))).detach() + + def preprocess(self, targets, batch_size, scale_tensor, segmask): + targets_list = np.zeros((batch_size, 1, 5)).tolist() + cu = [] + already = [] + for i, item in enumerate(targets.cpu().numpy().tolist()): + index = int(item[0]) + targets_list[index].append(item[1:]) + if index not in already: + already.append(index) + cu.append(i) + cu.append(segmask.shape[0]) + max_len = max((len(l) for l in targets_list)) + segmasks = torch.zeros(batch_size, max_len - 1, segmask.shape[-2], segmask.shape[-1]).cuda() + if len(already) != 0: + for i in range(len(already)): + j = already[i] + start = cu[i] + end = cu[i+1] + segmasks[j, : end - start] = segmask[start: end].clone() + targets = torch.from_numpy(np.array(list(map(lambda l:l + [[-1,0,0,0,0]]*(max_len - len(l)), targets_list)))[:,1:,:]).to(targets.device) + + batch_target = targets[:, :, 1:5].mul_(scale_tensor) + targets[..., 1:] = xywh2xyxy(batch_target) + return targets, segmasks + + def bbox_decode(self, anchor_points, pred_dist): + if self.use_dfl: + batch_size, n_anchors, _ = pred_dist.shape + pred_dist = F.softmax(pred_dist.view(batch_size, n_anchors, 4, self.reg_max + 1), dim=-1).matmul(self.proj.to(pred_dist.device)) + return dist2bbox(pred_dist, anchor_points) + + def mask_loss(self, gt_segmasks, seg_cf, seg_proto, txyxy_ori_s, fg_mask, idx_lst, target_scores=None, target_scores_sum=None, epoch=0): + # pred_mask_lst -> list + ''' + pred_mask -> Shape(n1, w, h) + gt_mask -> Shape(n, img_w, img_h) + xyxy -> Shape(n, 4) + sum(n1, n2, n3, ...) = n + torch.abs((xyxy[..., 3] - xyxy[..., 1]) * (xyxy[..., 4] - xyxy[..., 2])) -> area + fg_mask --> (bs, tsize) + idx -> (bs, tsize) + gt_segmasks -> (bs, labelsize, w, h) + ''' + sl = 0 + sl2 = 0 + bl = [2, 4, 8] + num_pos = fg_mask.sum() + tloss = torch.zeros(1).float().cuda() + if num_pos<=0: + for ipred in seg_proto: + tloss += (ipred.sum() * 0.) + for ipred in seg_cf: + tloss += (ipred.sum() * 0.) + return tloss[0] + + + xyxy_mask = fg_mask.unsqueeze(-1).repeat([1, 1, 4]) + mtarget_scores = target_scores.sum(-1) # (bs, nl, 1) + + sl = 0 + qf = len(idx_lst) == 1 and len(idx_lst[0].shape) == 2 + if qf: + idx_lst = idx_lst[0] + _ = [_i.shape[1] for _i in seg_cf] + sp = [2, 4, 8] + fpn = [] + for i in range(0, 3): + fpn.extend([sp[i]] * _[i]) + fpn = torch.Tensor(fpn).unsqueeze(-1).cuda() + txyxy_ori = txyxy_ori_s * fpn.unsqueeze(0).repeat(seg_cf[0].shape[0], 1, 1) + iseg_cf = torch.cat(seg_cf, axis = 1) + iseg_proto = seg_proto[0] # (bs, 32, h, w) + bs = iseg_proto.shape[0] + if fg_mask.sum()<=0: + tloss += (iseg_proto.sum() * 0.) + tloss += (iseg_cf.sum() * 0.) + return tloss[0] + + pshape = iseg_proto.shape[-1] + ishape = iseg_cf.shape[1] # (1) = part_n + idx = idx_lst[:, :] # (bs, part_n) + + ifg_mask = fg_mask[:, :] # (n) --> (bs, part_n) + itarget_scores = mtarget_scores[:, :] + target_sg = [] + pred_sg = [] + ixyxy_lst = [] + mask_weight = [] + for i in range(bs): + siproto = self.handle_proto_coord(iseg_proto[i]) + iproto = siproto.reshape(1, -1, *siproto.shape[-2:]) + idx_thisbatch = torch.masked_select(idx[i], ifg_mask[i]) #(casize) + igt_segmasks = gt_segmasks.reshape(-1, *gt_segmasks.shape[-2:])[idx_thisbatch] # (?1, h?, w?) --> (?2, h?, w?) + imask_weight = torch.masked_select(itarget_scores[i], ifg_mask[i]).unsqueeze(-1) + tiseg_cf = torch.masked_select(iseg_cf[i], ifg_mask[i].unsqueeze(-1).repeat(1, self.tt)) # (?2, 32) + tiseg_cf = tiseg_cf.reshape(-1, self.tt) + num_inst = tiseg_cf.shape[0] + if num_inst == 0: + tloss[0] += (tiseg_cf.sum() * 0.) + continue + mask_weight.append(imask_weight) + target_sg.append(igt_segmasks) + weights, biases = self.parse_dynamic_params(tiseg_cf) + n_layers = len(weights) + for _i, (weight, bias) in enumerate(zip(weights, biases)): + x = F.conv2d( + iproto, weight, bias=bias, stride=1, padding=0, groups=1) + if _i < n_layers - 1: + x = F.relu(x) + x = x.reshape(num_inst, *iproto.shape[-2:]) + ixyxy = torch.masked_select(txyxy_ori[i, :], xyxy_mask[i, :, :]).reshape(-1, 4) # (n, 4) --> (part_n, 4) --> (?2, 4) + ixyxy_lst.append(ixyxy) + pred_sg.append(x) + bxyxy = torch.cat(ixyxy_lst, dim = 0) + bpred_seg = torch.cat(pred_sg, dim = 0) + bgt_seg = torch.cat(target_sg, dim = 0) + masks_weight = torch.cat(mask_weight, dim = 0).reshape(-1) + if tuple(bgt_seg.shape[-2:]) != (pshape, pshape): # downsample + bgt_seg = F.interpolate(bgt_seg[None], (pshape, pshape), mode='nearest')[0] + area = torch.abs((bxyxy[..., 2] - bxyxy[..., 0]) * (bxyxy[..., 3] - bxyxy[..., 1])) + area = area / (pshape) + area = area / (pshape) + + if not self.dice: + loss = F.binary_cross_entropy_with_logits(bpred_seg, bgt_seg, reduction='none') + loss = (self.crop_mask(loss, bxyxy).mean(dim=(1, 2)) / area) * masks_weight + loss = loss.sum() + tloss += loss + if target_scores_sum > 1: + tloss[0] = tloss[0] / target_scores_sum + return tloss[0] / len(seg_cf) + else: + bpred_seg = bpred_seg.sigmoid() + if epoch <= 160: + loss = dice_loss(bpred_seg, bgt_seg, masks_weight, reduction='mean', avg_factor=target_scores_sum if target_scores_sum > 1 else 1) + else: + loss = dice_loss(bpred_seg, bgt_seg, reduction='mean') + tloss += loss + return tloss[0] + + @staticmethod + 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 [n, h, w] 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)) + + +class VarifocalLoss(nn.Module): + def __init__(self): + super(VarifocalLoss, self).__init__() + + def forward(self, pred_score,gt_score, label, alpha=0.75, gamma=2.0): + + weight = alpha * pred_score.pow(gamma) * (1 - label) + gt_score * label + with torch.cuda.amp.autocast(enabled=False): + loss = (F.binary_cross_entropy(pred_score.float(), gt_score.float(), reduction='none') * weight).sum() + + return loss + + +class BboxLoss(nn.Module): + def __init__(self, num_classes, reg_max, use_dfl=False, iou_type='giou'): + super(BboxLoss, self).__init__() + self.num_classes = num_classes + self.iou_loss = IOUloss(box_format='xyxy', iou_type=iou_type, eps=1e-10) + self.reg_max = reg_max + self.use_dfl = use_dfl + + def forward(self, pred_dist, pred_bboxes, anchor_points, + target_bboxes, target_scores, target_scores_sum, fg_mask): + + # select positive samples mask + num_pos = fg_mask.sum() + if num_pos > 0: + # iou loss + bbox_mask = fg_mask.unsqueeze(-1).repeat([1, 1, 4]) + pred_bboxes_pos = torch.masked_select(pred_bboxes, + bbox_mask).reshape([-1, 4]) + target_bboxes_pos = torch.masked_select( + target_bboxes, bbox_mask).reshape([-1, 4]) + bbox_weight = torch.masked_select( + target_scores.sum(-1), fg_mask).unsqueeze(-1) + loss_iou = self.iou_loss(pred_bboxes_pos, + target_bboxes_pos) * bbox_weight + if target_scores_sum > 1: + loss_iou = loss_iou.sum() / target_scores_sum + else: + loss_iou = loss_iou.sum() + + # dfl loss + if self.use_dfl: + dist_mask = fg_mask.unsqueeze(-1).repeat( + [1, 1, (self.reg_max + 1) * 4]) + pred_dist_pos = torch.masked_select( + pred_dist, dist_mask).reshape([-1, 4, self.reg_max + 1]) + target_ltrb = bbox2dist(anchor_points, target_bboxes, self.reg_max) + target_ltrb_pos = torch.masked_select( + target_ltrb, bbox_mask).reshape([-1, 4]) + loss_dfl = self._df_loss(pred_dist_pos, + target_ltrb_pos) * bbox_weight + if target_scores_sum > 1: + loss_dfl = loss_dfl.sum() / target_scores_sum + else: + loss_dfl = loss_dfl.sum() + else: + loss_dfl = pred_dist.sum() * 0. + + else: + loss_iou = pred_dist.sum() * 0. + loss_dfl = pred_dist.sum() * 0. + + return loss_iou, loss_dfl + + def _df_loss(self, pred_dist, target): + target_left = target.to(torch.long) + target_right = target_left + 1 + weight_left = target_right.to(torch.float) - target + weight_right = 1 - weight_left + loss_left = F.cross_entropy( + pred_dist.view(-1, self.reg_max + 1), target_left.view(-1), reduction='none').view( + target_left.shape) * weight_left + loss_right = F.cross_entropy( + pred_dist.view(-1, self.reg_max + 1), target_right.view(-1), reduction='none').view( + target_left.shape) * weight_right + return (loss_left + loss_right).mean(-1, keepdim=True) + +def dice_loss(pred, + target, + weight=None, + eps=1e-3, + reduction='mean', + naive_dice=False, + avg_factor=None): + """Calculate dice loss, there are two forms of dice loss is supported: + Borrowed from MMDetection + - the one proposed in `V-Net: Fully Convolutional Neural + Networks for Volumetric Medical Image Segmentation + `_. + - the dice loss in which the power of the number in the + denominator is the first power instead of the second + power. + + Args: + pred (torch.Tensor): The prediction, has a shape (n, *) + target (torch.Tensor): The learning label of the prediction, + shape (n, *), same shape of pred. + weight (torch.Tensor, optional): The weight of loss for each + prediction, has a shape (n,). Defaults to None. + eps (float): Avoid dividing by zero. Default: 1e-3. + reduction (str, optional): The method used to reduce the loss into + a scalar. Defaults to 'mean'. + Options are "none", "mean" and "sum". + naive_dice (bool, optional): If false, use the dice + loss defined in the V-Net paper, otherwise, use the + naive dice loss in which the power of the number in the + denominator is the first power instead of the second + power.Defaults to False. + avg_factor (int, optional): Average factor that is used to average + the loss. Defaults to None. + """ + + input = pred.flatten(1) + target = target.flatten(1).float() + + a = torch.sum(input * target, 1) + if naive_dice: + b = torch.sum(input, 1) + c = torch.sum(target, 1) + d = (2 * a + eps) / (b + c + eps) + else: + b = torch.sum(input * input, 1) + eps + c = torch.sum(target * target, 1) + eps + d = (2 * a) / (b + c) + + loss = 1 - d + if weight is not None: + assert weight.ndim == loss.ndim + assert len(weight) == len(pred) + loss = weight_reduce_loss(loss, weight, reduction, avg_factor) + return loss + +def weight_reduce_loss(loss, + weight=None, + reduction='none', + avg_factor=None): + """Apply element-wise weight and reduce loss. + + Args: + loss (Tensor): Element-wise loss. + weight (Optional[Tensor], optional): Element-wise weights. + Defaults to None. + reduction (str, optional): Same as built-in losses of PyTorch. + Defaults to 'mean'. + avg_factor (Optional[float], optional): Average factor when + computing the mean of losses. Defaults to None. + + Returns: + Tensor: Processed loss values. + """ + # if weight is specified, apply element-wise weight + if weight is not None: + loss = loss * weight + + # if avg_factor is not specified, just reduce the loss + if avg_factor is None: + loss = reduce_loss(loss, reduction) + else: + # if reduction is mean, then average the loss by avg_factor + if reduction == 'mean': + # Avoid causing ZeroDivisionError when avg_factor is 0.0, + # i.e., all labels of an image belong to ignore index. + eps = torch.finfo(torch.float32).eps + loss = loss.sum() / (avg_factor + eps) + # if reduction is 'none', then do nothing, otherwise raise an error + elif reduction != 'none': + raise ValueError('avg_factor can not be used with reduction="sum"') + return loss + +def reduce_loss(loss, reduction): + """Reduce loss as specified. + + Args: + loss (Tensor): Elementwise loss tensor. + reduction (str): Options are "none", "mean" and "sum". + + Return: + Tensor: Reduced loss tensor. + """ + reduction_enum = F._Reduction.get_enum(reduction) + # none: 0, elementwise_mean:1, sum: 2 + if reduction_enum == 0: + return loss + elif reduction_enum == 1: + return loss.mean() + elif reduction_enum == 2: + return loss.sum() \ No newline at end of file diff --git a/yolov6/models/reppan.py b/yolov6/models/reppan.py index 2114f521..820f4211 100644 --- a/yolov6/models/reppan.py +++ b/yolov6/models/reppan.py @@ -551,22 +551,14 @@ def __init__( channels_list=None, num_repeats=None, block=BottleRep, - csp_e=float(1)/2, - stage_block_type="BepC3" + csp_e=float(1)/2 ): super().__init__() - if stage_block_type == "BepC3": - stage_block = BepC3 - elif stage_block_type == "MBLABlock": - stage_block = MBLABlock - else: - raise NotImplementedError - assert channels_list is not None assert num_repeats is not None - self.Rep_p4 = stage_block( + self.Rep_p4 = BepC3( in_channels=channels_list[3] + channels_list[5], # 512 + 256 out_channels=channels_list[5], # 256 n=num_repeats[5], @@ -574,7 +566,7 @@ def __init__( block=block ) - self.Rep_p3 = stage_block( + self.Rep_p3 = BepC3( in_channels=channels_list[2] + channels_list[6], # 256 + 128 out_channels=channels_list[6], # 128 n=num_repeats[6], @@ -582,7 +574,7 @@ def __init__( block=block ) - self.Rep_n3 = stage_block( + self.Rep_n3 = BepC3( in_channels=channels_list[6] + channels_list[7], # 128 + 128 out_channels=channels_list[8], # 256 n=num_repeats[7], @@ -590,7 +582,7 @@ def __init__( block=block ) - self.Rep_n4 = stage_block( + self.Rep_n4 = BepC3( in_channels=channels_list[5] + channels_list[9], # 256 + 256 out_channels=channels_list[10], # 512 n=num_repeats[8], @@ -795,21 +787,13 @@ def __init__( channels_list=None, num_repeats=None, block=BottleRep, - csp_e=float(1)/2, - stage_block_type="BepC3" + csp_e=float(1)/2 ): super().__init__() assert channels_list is not None assert num_repeats is not None - if stage_block_type == "BepC3": - stage_block = BepC3 - elif stage_block_type == "MBLABlock": - stage_block = MBLABlock - else: - raise NotImplementedError - self.reduce_layer0 = ConvBNReLU( in_channels=channels_list[5], # 1024 out_channels=channels_list[6], # 512 @@ -822,7 +806,7 @@ def __init__( out_channels=channels_list[6], # 512 ) - self.Rep_p5 = stage_block( + self.Rep_p5 = BepC3( in_channels=channels_list[4] + channels_list[6], # 768 + 512 out_channels=channels_list[6], # 512 n=num_repeats[6], @@ -842,7 +826,7 @@ def __init__( out_channels=channels_list[7] # 256 ) - self.Rep_p4 = stage_block( + self.Rep_p4 = BepC3( in_channels=channels_list[3] + channels_list[7], # 512 + 256 out_channels=channels_list[7], # 256 n=num_repeats[7], @@ -862,7 +846,7 @@ def __init__( out_channels=channels_list[8] # 128 ) - self.Rep_p3 = stage_block( + self.Rep_p3 = BepC3( in_channels=channels_list[2] + channels_list[8], # 256 + 128 out_channels=channels_list[8], # 128 n=num_repeats[8], @@ -877,7 +861,7 @@ def __init__( stride=2 ) - self.Rep_n4 = stage_block( + self.Rep_n4 = BepC3( in_channels=channels_list[8] + channels_list[8], # 128 + 128 out_channels=channels_list[9], # 256 n=num_repeats[9], @@ -892,7 +876,7 @@ def __init__( stride=2 ) - self.Rep_n5 = stage_block( + self.Rep_n5 = BepC3( in_channels=channels_list[7] + channels_list[9], # 256 + 256 out_channels=channels_list[10], # 512 n=num_repeats[10], @@ -907,7 +891,7 @@ def __init__( stride=2 ) - self.Rep_n6 = stage_block( + self.Rep_n6 = BepC3( in_channels=channels_list[6] + channels_list[10], # 512 + 512 out_channels=channels_list[11], # 1024 n=num_repeats[11], @@ -962,21 +946,13 @@ def __init__( channels_list=None, num_repeats=None, block=BottleRep, - csp_e=float(1)/2, - stage_block_type="BepC3" + csp_e=float(1)/2 ): super().__init__() assert channels_list is not None assert num_repeats is not None - if stage_block_type == "BepC3": - stage_block = BepC3 - elif stage_block_type == "MBLABlock": - stage_block = MBLABlock - else: - raise NotImplementedError - self.reduce_layer0 = ConvBNReLU( in_channels=channels_list[5], # 1024 out_channels=channels_list[6], # 512 @@ -989,7 +965,7 @@ def __init__( out_channels=channels_list[6], # 512 ) - self.Rep_p5 = stage_block( + self.Rep_p5 = BepC3( in_channels=channels_list[6], # 512 out_channels=channels_list[6], # 512 n=num_repeats[6], @@ -1009,7 +985,7 @@ def __init__( out_channels=channels_list[7], # 256 ) - self.Rep_p4 = stage_block( + self.Rep_p4 = BepC3( in_channels=channels_list[7], # 256 out_channels=channels_list[7], # 256 n=num_repeats[7], @@ -1029,7 +1005,7 @@ def __init__( out_channels=channels_list[8], # 128 ) - self.Rep_p3 = stage_block( + self.Rep_p3 = BepC3( in_channels=channels_list[8], # 128 out_channels=channels_list[8], # 128 n=num_repeats[8], @@ -1044,7 +1020,7 @@ def __init__( stride=2 ) - self.Rep_n4 = stage_block( + self.Rep_n4 = BepC3( in_channels=channels_list[8] + channels_list[8], # 128 + 128 out_channels=channels_list[9], # 256 n=num_repeats[9], @@ -1059,7 +1035,7 @@ def __init__( stride=2 ) - self.Rep_n5 = stage_block( + self.Rep_n5 = BepC3( in_channels=channels_list[7] + channels_list[9], # 256 + 256 out_channels=channels_list[10], # 512 n=num_repeats[10], @@ -1074,7 +1050,7 @@ def __init__( stride=2 ) - self.Rep_n6 = stage_block( + self.Rep_n6 = BepC3( in_channels=channels_list[6] + channels_list[10], # 512 + 512 out_channels=channels_list[11], # 1024 n=num_repeats[11], diff --git a/yolov6/models/yolo.py b/yolov6/models/yolo.py index 2f37f1b1..5e121b79 100644 --- a/yolov6/models/yolo.py +++ b/yolov6/models/yolo.py @@ -63,6 +63,11 @@ def build_network(config, channels, num_classes, num_layers, fuse_ab=False, dist channels_list_neck = config.model.neck.out_channels use_dfl = config.model.head.use_dfl reg_max = config.model.head.reg_max + issolo = config.model.head.issolo + isseg = config.model.head.isseg + npr = config.model.head.npr + npr = make_divisible(npr * width_mul, 8) + nm = config.model.head.nm num_repeat = [(max(round(i * depth_mul), 1) if i > 1 else i) for i in (num_repeat_backbone + num_repeat_neck)] channels_list = [make_divisible(i * width_mul, 8) for i in (channels_list_backbone + channels_list_neck)] @@ -110,8 +115,20 @@ def build_network(config, channels, num_classes, num_layers, fuse_ab=False, dist num_repeats=num_repeat, block=block ) - - if distill_ns: + if isseg: + if issolo: + from yolov6.models.heads.effidehead_fuseab_seg_solo import Detect, build_effidehead_layer, Proto + anchors_init = config.model.head.anchors_init + head_layers = build_effidehead_layer(channels_list, 3, num_classes, reg_max=reg_max, num_layers=num_layers, num_masks=nm + 2 + 1, fuse_ab=fuse_ab) + reg_masks = [Proto(num_layers, channels_list, 0, npr, nm, scale_factor=2), Proto(num_layers, channels_list, 1, npr, nm, scale_factor=4), Proto(num_layers, channels_list, 2, npr, nm, scale_factor=8)] + head = Detect(num_classes, anchors_init, num_layers, head_layers=head_layers, use_dfl=use_dfl, reg_mask=reg_masks, fuse_ab=fuse_ab, nm=nm + 2 + 1) + else: + from yolov6.models.heads.effidehead_fuseab_seg import Detect, build_effidehead_layer, Proto + anchors_init = config.model.head.anchors_init + head_layers = build_effidehead_layer(channels_list, 3, num_classes, reg_max=reg_max, num_layers=num_layers, num_masks=nm, fuse_ab=fuse_ab) + reg_masks = [Proto(num_layers, channels_list, 0, npr, nm)] + head = Detect(num_classes, anchors_init, num_layers, head_layers=head_layers, use_dfl=use_dfl, reg_mask=reg_masks, fuse_ab=fuse_ab) + elif distill_ns: from yolov6.models.heads.effidehead_distill_ns import Detect, build_effidehead_layer if num_layers != 3: LOGGER.error('ERROR in: Distill mode not fit on n/s models with P6 head.\n') diff --git a/yolov6/utils/general.py b/yolov6/utils/general.py index cb4418cd..e144f95d 100644 --- a/yolov6/utils/general.py +++ b/yolov6/utils/general.py @@ -5,7 +5,6 @@ import math import torch import requests -import pkg_resources as pkg from pathlib import Path from yolov6.utils.events import LOGGER @@ -94,7 +93,6 @@ def download_ckpt(path): LOGGER.info(f"checkpoint {basename} not exist, try to downloaded it from github.") # need to update the link with every release url = f"https://github.com/meituan/YOLOv6/releases/download/0.4.0/{basename}" - LOGGER.warning(f"downloading url is: {url}, pealse make sure the version of the downloading model is correspoing to the code version!") r = requests.get(url, allow_redirects=True) assert r.status_code == 200, "Unable to download checkpoints, manually download it" open(path, 'wb').write(r.content) @@ -115,13 +113,3 @@ def check_img_size(imgsz, s=32, floor=0): if new_size != imgsz: LOGGER.warning(f'--img-size {imgsz} must be multiple of max stride {s}, updating to {new_size}') return new_size - - -def check_version(current='0.0.0', minimum='0.0.0', name='version ', pinned=False, hard=False, verbose=False): - # Check whether the package's version is match the required version. - current, minimum = (pkg.parse_version(x) for x in (current, minimum)) - result = (current == minimum) if pinned else (current >= minimum) # bool - if hard: - info = f'⚠️ {name}{minimum} is required by YOLOv6, but {name}{current} is currently installed' - assert result, info # assert minimum version requirement - return result diff --git a/yolov6/utils/metrics.py b/yolov6/utils/metrics.py index cbfa130e..c54b4f8a 100644 --- a/yolov6/utils/metrics.py +++ b/yolov6/utils/metrics.py @@ -9,8 +9,9 @@ import torch import warnings from . import general +import torch.nn.functional as F -def ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names=()): +def ap_per_class_v6(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names=(), prefix = ''): """ Compute the average precision, given the recall and precision curves. Source: https://github.com/rafaelpadilla/Object-Detection-Metrics. # Arguments @@ -57,7 +58,7 @@ def ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names # AP from recall-precision curve for j in range(tp.shape[1]): - ap[ci, j], mpre, mrec = compute_ap(recall[:, j], precision[:, j]) + ap[ci, j], mpre, mrec = compute_ap_v6(recall[:, j], precision[:, j]) if plot and j == 0: py.append(np.interp(px, mrec, mpre)) # precision at mAP@0.5 @@ -71,8 +72,112 @@ def ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names # i = f1.mean(0).argmax() # max F1 index # return p[:, i], r[:, i], ap, f1[:, i], unique_classes.astype('int32') - return p, r, ap, f1, unique_classes.astype('int32') + AP50_F1_max_idx = len(f1.mean(0)) - f1.mean(0)[::-1].argmax() -1 + ap50, ap = ap[:, 0], ap.mean(1) + mp, mr, map50, map = p[:, AP50_F1_max_idx].mean(), r[:, AP50_F1_max_idx].mean(), ap50.mean(), ap.mean() + return mp, mr, map50, map, AP50_F1_max_idx +def ap_per_class(tp, conf, pred_cls, target_cls, plot=False, save_dir='.', names=(), eps=1e-16, prefix=''): + """ Compute the average precision, given the recall and precision curves. + Source: https://github.com/rafaelpadilla/Object-Detection-Metrics. + # Arguments + tp: True positives (nparray, nx1 or nx10). + conf: Objectness value from 0-1 (nparray). + pred_cls: Predicted object classes (nparray). + target_cls: True object classes (nparray). + plot: Plot precision-recall curve at mAP@0.5 + save_dir: Plot save directory + # Returns + The average precision as computed in py-faster-rcnn. + """ + + # Sort by objectness + i = np.argsort(-conf) + tp, conf, pred_cls = tp[i], conf[i], pred_cls[i] + + # Find unique classes + unique_classes, nt = np.unique(target_cls, return_counts=True) + nc = unique_classes.shape[0] # number of classes, number of detections + + # Create Precision-Recall curve and compute AP for each class + px, py = np.linspace(0, 1, 1000), [] # for plotting + ap, p, r = np.zeros((nc, tp.shape[1])), np.zeros((nc, 1000)), np.zeros((nc, 1000)) + for ci, c in enumerate(unique_classes): + i = pred_cls == c + n_l = nt[ci] # number of labels + n_p = i.sum() # number of predictions + if n_p == 0 or n_l == 0: + continue + + # Accumulate FPs and TPs + fpc = (1 - tp[i]).cumsum(0) + tpc = tp[i].cumsum(0) + + # Recall + recall = tpc / (n_l + eps) # recall curve + r[ci] = np.interp(-px, -conf[i], recall[:, 0], left=0) # negative x, xp because xp decreases + + # Precision + precision = tpc / (tpc + fpc) # precision curve + p[ci] = np.interp(-px, -conf[i], precision[:, 0], left=1) # p at pr_score + + # AP from recall-precision curve + for j in range(tp.shape[1]): + ap[ci, j], mpre, mrec = compute_ap(recall[:, j], precision[:, j]) + if plot and j == 0: + py.append(np.interp(px, mrec, mpre)) # precision at mAP@0.5 + + # Compute F1 (harmonic mean of precision and recall) + f1 = 2 * p * r / (p + r + eps) + plot = False + if plot: + names = [v for k, v in names.items() if k in unique_classes] # list: only classes that have data + names = dict(enumerate(names)) # to dict + plot_pr_curve(px, py, ap, Path(save_dir) / f'{prefix}PR_curve.png', names) + plot_mc_curve(px, f1, Path(save_dir) / f'{prefix}F1_curve.png', names, ylabel='F1') + plot_mc_curve(px, p, Path(save_dir) / f'{prefix}P_curve.png', names, ylabel='Precision') + plot_mc_curve(px, r, Path(save_dir) / f'{prefix}R_curve.png', names, ylabel='Recall') + + i = smooth(f1.mean(0), 0.1).argmax() # max F1 index + p, r, f1 = p[:, i], r[:, i], f1[:, i] + tp = (r * nt).round() # true positives + fp = (tp / (p + eps) - tp).round() # false positives + return tp, fp, p, r, f1, ap, unique_classes.astype(int) + +def smooth(y, f=0.05): + # Box filter of fraction f + nf = round(len(y) * f * 2) // 2 + 1 # number of filter elements (must be odd) + p = np.ones(nf // 2) # ones padding + yp = np.concatenate((p * y[0], y, p * y[-1]), 0) # y padded + return np.convolve(yp, np.ones(nf) / nf, mode='valid') # y-smoothed + + +def compute_ap_v6(recall, precision): + """ Compute the average precision, given the recall and precision curves + # Arguments + recall: The recall curve (list) + precision: The precision curve (list) + # Returns + Average precision, precision curve, recall curve + """ + + # Append sentinel values to beginning and end + mrec = np.concatenate(([0.0], recall, [1.0])) + mpre = np.concatenate(([1.0], precision, [0.0])) + + # Compute the precision envelope + mpre = np.flip(np.maximum.accumulate(np.flip(mpre))) + + # Integrate area under curve + method = 'interp' # methods: 'continuous', 'interp' + if method == 'interp': + x = np.linspace(0, 1, 101) # 101-point interp (COCO) + ap = np.trapz(np.interp(x, mrec, mpre), x) # integrate + else: # 'continuous' + i = np.where(mrec[1:] != mrec[:-1])[0] # points where x axis (recall) changes + ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1]) # area under curve + + return ap, mpre, mrec def compute_ap(recall, precision): """ Compute the average precision, given the recall and precision curves @@ -101,7 +206,6 @@ def compute_ap(recall, precision): return ap, mpre, mrec -# Plots ---------------------------------------------------------------------------------------------------------------- def plot_pr_curve(px, py, ap, save_dir='pr_curve.png', names=()): # Precision-recall curve @@ -142,17 +246,54 @@ def plot_mc_curve(px, py, save_dir='mc_curve.png', names=(), xlabel='Confidence' plt.legend(bbox_to_anchor=(1.04, 1), loc="upper left") fig.savefig(Path(save_dir), dpi=250) -def process_batch(detections, labels, iouv): +# def process_batch(detections, labels, iouv): +# """ +# Return correct predictions matrix. Both sets of boxes are in (x1, y1, x2, y2) format. +# Arguments: +# detections (Array[N, 6]), x1, y1, x2, y2, conf, class +# labels (Array[M, 5]), class, x1, y1, x2, y2 +# Returns: +# correct (Array[N, 10]), for 10 IoU levels +# """ +# correct = np.zeros((detections.shape[0], iouv.shape[0])).astype(bool) +# iou = general.box_iou(labels[:, 1:], detections[:, :4]) +# correct_class = labels[:, 0:1] == detections[:, 5] +# for i in range(len(iouv)): +# x = torch.where((iou >= iouv[i]) & correct_class) # IoU > threshold and classes match +# if x[0].shape[0]: +# matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy() # [label, detect, iou] +# if x[0].shape[0] > 1: +# matches = matches[matches[:, 2].argsort()[::-1]] +# matches = matches[np.unique(matches[:, 1], return_index=True)[1]] +# # matches = matches[matches[:, 2].argsort()[::-1]] +# matches = matches[np.unique(matches[:, 0], return_index=True)[1]] +# correct[matches[:, 1].astype(int), i] = True +# return torch.tensor(correct, dtype=torch.bool, device=iouv.device) + +def process_batch(detections, labels, iouv, pred_masks=None, gt_masks=None, overlap=False, masks=False): """ - Return correct predictions matrix. Both sets of boxes are in (x1, y1, x2, y2) format. + Return correct prediction matrix Arguments: - detections (Array[N, 6]), x1, y1, x2, y2, conf, class - labels (Array[M, 5]), class, x1, y1, x2, y2 + detections (array[N, 6]), x1, y1, x2, y2, conf, class + labels (array[M, 5]), class, x1, y1, x2, y2 Returns: - correct (Array[N, 10]), for 10 IoU levels + correct (array[N, 10]), for 10 IoU levels """ + if masks: + gt_masks = gt_masks.to(pred_masks.device) + if overlap: + nl = len(labels) + index = torch.arange(nl, device=gt_masks.device).view(nl, 1, 1) + 1 + gt_masks = gt_masks.repeat(nl, 1, 1) # shape(1,640,640) -> (n,640,640) + gt_masks = torch.where(gt_masks == index, 1.0, 0.0) + if gt_masks.shape[1:] != pred_masks.shape[1:]: + gt_masks = F.interpolate(gt_masks[None].to(torch.float32), pred_masks.shape[1:], mode='bilinear', align_corners=False)[0] + gt_masks = gt_masks.gt_(0.5) + iou = mask_iou(gt_masks.view(gt_masks.shape[0], -1).float(), pred_masks.view(pred_masks.shape[0], -1)).to(iouv.device) + else: # boxes + iou = box_iou(labels[:, 1:], detections[:, :4]).to(iouv.device) + correct = np.zeros((detections.shape[0], iouv.shape[0])).astype(bool) - iou = general.box_iou(labels[:, 1:], detections[:, :4]) correct_class = labels[:, 0:1] == detections[:, 5] for i in range(len(iouv)): x = torch.where((iou >= iouv[i]) & correct_class) # IoU > threshold and classes match @@ -256,3 +397,232 @@ def plot(self, normalize=True, save_dir='', names=()): def print(self): for i in range(self.nc + 1): print(' '.join(map(str, self.matrix[i]))) + + +def ap_per_class_box_and_mask( + tp_m, + tp_b, + conf, + pred_cls, + target_cls, + plot=False, + save_dir='.', + names=(), + is_v6=False +): + """ + Args: + tp_b: tp of boxes. + tp_m: tp of masks. + other arguments see `func: ap_per_class`. + #return p, r, ap, f1, unique_classes.astype('int32') + """ + if not is_v6: + results_boxes = ap_per_class(tp_b, + conf, + pred_cls, + target_cls, + plot=plot, + save_dir=save_dir, + names=names, + prefix='Box')[2:] + results_masks = ap_per_class(tp_m, + conf, + pred_cls, + target_cls, + plot=plot, + save_dir=save_dir, + names=names, + prefix='Mask')[2:] + + results = { + 'boxes': { + 'p': results_boxes[0], + 'r': results_boxes[1], + 'ap': results_boxes[3], + 'f1': results_boxes[2], + 'ap_class': results_boxes[4]}, + 'masks': { + 'p': results_masks[0], + 'r': results_masks[1], + 'ap': results_masks[3], + 'f1': results_masks[2], + 'ap_class': results_masks[4]}} + return results + else: + results_boxes = ap_per_class_v6(tp_b, + conf, + pred_cls, + target_cls, + plot=plot, + save_dir=save_dir, + names=names, + prefix='Box') + results_masks = ap_per_class(tp_m, + conf, + pred_cls, + target_cls, + plot=plot, + save_dir=save_dir, + names=names, + prefix='Mask') + return results_boxes, results_masks + +class Metric: + + def __init__(self) -> None: + self.p = [] # (nc, ) + self.r = [] # (nc, ) + self.f1 = [] # (nc, ) + self.all_ap = [] # (nc, 10) + self.ap_class_index = [] # (nc, ) + + @property + def ap50(self): + """AP@0.5 of all classes. + Return: + (nc, ) or []. + """ + return self.all_ap[:, 0] if len(self.all_ap) else [] + + @property + def ap(self): + """AP@0.5:0.95 + Return: + (nc, ) or []. + """ + return self.all_ap.mean(1) if len(self.all_ap) else [] + + @property + def mp(self): + """mean precision of all classes. + Return: + float. + """ + return self.p.mean() if len(self.p) else 0.0 + + @property + def mr(self): + """mean recall of all classes. + Return: + float. + """ + return self.r.mean() if len(self.r) else 0.0 + + @property + def map50(self): + """Mean AP@0.5 of all classes. + Return: + float. + """ + return self.all_ap[:, 0].mean() if len(self.all_ap) else 0.0 + + @property + def map(self): + """Mean AP@0.5:0.95 of all classes. + Return: + float. + """ + return self.all_ap.mean() if len(self.all_ap) else 0.0 + + def mean_results(self): + """Mean of results, return mp, mr, map50, map""" + return (self.mp, self.mr, self.map50, self.map) + + def class_result(self, i): + """class-aware result, return p[i], r[i], ap50[i], ap[i]""" + return (self.p[i], self.r[i], self.ap50[i], self.ap[i]) + + def get_maps(self, nc): + maps = np.zeros(nc) + self.map + for i, c in enumerate(self.ap_class_index): + maps[c] = self.ap[i] + return maps + + def update(self, results): + """ + Args: + results: tuple(p, r, ap, f1, ap_class) + """ + p, r, all_ap, f1, ap_class_index = results + self.p = p + self.r = r + self.all_ap = all_ap + self.f1 = f1 + self.ap_class_index = ap_class_index + + +class Metrics: + """Metric for boxes and masks.""" + + def __init__(self) -> None: + self.metric_box = Metric() + self.metric_mask = Metric() + + def update(self, results): + """ + Args: + results: Dict{'boxes': Dict{}, 'masks': Dict{}} + """ + self.metric_box.update(list(results['boxes'].values())) + self.metric_mask.update(list(results['masks'].values())) + + def mean_results(self): + return self.metric_box.mean_results() + self.metric_mask.mean_results() + + def class_result(self, i): + return self.metric_box.class_result(i) + self.metric_mask.class_result(i) + + def get_maps(self, nc): + return self.metric_box.get_maps(nc) + self.metric_mask.get_maps(nc) + + @property + def ap_class_index(self): + # boxes and masks have the same ap_class_index + return self.metric_box.ap_class_index + +def mask_iou(mask1, mask2, eps=1e-7): + """ + mask1: [N, n] m1 means number of predicted objects + mask2: [M, n] m2 means number of gt objects + Note: n means image_w x image_h + + return: masks iou, [N, M] + """ + mask1 = mask1.float() + intersection = torch.matmul(mask1, mask2.t()).clamp(0) + union = (mask1.sum(1)[:, None] + mask2.sum(1)[None]) - intersection # (area1 + area2) - intersection + return intersection / (union + eps) + + +def masks_iou(mask1, mask2, eps=1e-7): + """ + mask1: [N, n] m1 means number of predicted objects + mask2: [N, n] m2 means number of gt objects + Note: n means image_w x image_h + + return: masks iou, (N, ) + """ + intersection = (mask1 * mask2).sum(1).clamp(0) # (N, ) + union = (mask1.sum(1) + mask2.sum(1))[None] - intersection # (area1 + area2) - intersection + return intersection / (union + eps) + +def box_iou(box1, box2, eps=1e-7): + # https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py + """ + Return intersection-over-union (Jaccard index) of boxes. + Both sets of boxes are expected to be in (x1, y1, x2, y2) format. + Arguments: + box1 (Tensor[N, 4]) + box2 (Tensor[M, 4]) + Returns: + iou (Tensor[N, M]): the NxM matrix containing the pairwise + IoU values for every element in boxes1 and boxes2 + """ + + # inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2) + (a1, a2), (b1, b2) = box1.unsqueeze(1).chunk(2, 2), box2.unsqueeze(0).chunk(2, 2) + inter = (torch.min(a2, b2) - torch.max(a1, b1)).clamp(0).prod(2) + + # IoU = inter / (area1 + area2 - inter) + return inter / ((a2 - a1).prod(2) + (b2 - b1).prod(2) - inter + eps) \ No newline at end of file diff --git a/yolov6/utils/nms.py b/yolov6/utils/nms.py index 0f812642..c7369ba0 100644 --- a/yolov6/utils/nms.py +++ b/yolov6/utils/nms.py @@ -103,3 +103,164 @@ def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=Non break # time limit exceeded return output + + +def non_max_suppression_seg(predictions, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False, max_det=300): + """Runs Non-Maximum Suppression (NMS) on inference results. + This code is borrowed from: https://github.com/ultralytics/yolov5/blob/47233e1698b89fc437a4fb9463c815e9171be955/utils/general.py#L775 + Args: + prediction: (tensor), with shape [N, 5 + num_classes], N is the number of bboxes. + conf_thres: (float) confidence threshold. + iou_thres: (float) iou threshold. + classes: (None or list[int]), if a list is provided, nms only keep the classes you provide. + agnostic: (bool), when it is set to True, we do class-independent nms, otherwise, different class would do nms respectively. + multi_label: (bool), when it is set to True, one box can have multi labels, otherwise, one box only huave one label. + max_det:(int), max number of output bboxes. + + Returns: + list of detections, echo item is one tensor with shape (num_boxes, 6), 6 is for [xyxy, conf, cls]. + """ + prediction = predictions[0] + confs = predictions[2] # (bs, which_proto, fs) + prediction = torch.cat([prediction, confs], axis=2)# (bs, l ,5 + num_classes + 33) + + num_classes = prediction.shape[2] - 5 - 33 # number of classes + pred_candidates = torch.logical_and(prediction[..., 4] > conf_thres, torch.max(prediction[..., 5: 5 + num_classes], axis=-1)[0] > conf_thres) # candidates + # Check the parameters. + assert 0 <= conf_thres <= 1, f'conf_thresh must be in 0.0 to 1.0, however {conf_thres} is provided.' + assert 0 <= iou_thres <= 1, f'iou_thres must be in 0.0 to 1.0, however {iou_thres} is provided.' + + # Function settings. + max_wh = 4096 # maximum box width and height + max_nms = 30000 # maximum number of boxes put into torchvision.ops.nms() + time_limit = 10.0 # quit the function when nms cost time exceed the limit time. + multi_label &= num_classes > 1 # multiple labels per box + + tik = time.time() + output = [torch.zeros((0, 6 + 33), device=prediction.device)] * prediction.shape[0] + for img_idx, x in enumerate(prediction): # image index, image inference + x = x[pred_candidates[img_idx]] # confidence + + # If no box remains, skip the next process. + if not x.shape[0]: + continue + + # confidence multiply the objectness + x[:, 5: 5 + num_classes] *= x[:, 4:5] # conf = obj_conf * cls_conf + + # (center x, center y, width, height) to (x1, y1, x2, y2) + box = xywh2xyxy(x[:, :4]) + segconf = x[:, 5 + num_classes: ] + + # Detections matrix's shape is (n,6), each row represents (xyxy, conf, cls) + if multi_label: + box_idx, class_idx = (x[:, 5: 5 + num_classes] > conf_thres).nonzero(as_tuple=False).T + x = torch.cat((box[box_idx], x[box_idx, class_idx + 5, None], class_idx[:, None].float(), segconf[box_idx]), 1) + else: # Only keep the class with highest scores. + conf, class_idx = x[:, 5: 5 + num_classes].max(1, keepdim=True) + x = torch.cat((box, conf, class_idx.float(), segconf), 1)[conf.view(-1) > conf_thres] + + # Filter by class, only keep boxes whose category is in classes. + if classes is not None: + x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)] + + # Check shape + num_box = x.shape[0] # number of boxes + if not num_box: # no boxes kept. + continue + elif num_box > max_nms: # excess max boxes' number. + x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence + + # Batched NMS + class_offset = x[:, 5:6] * (0 if agnostic else max_wh) # classes + boxes, scores = x[:, :4] + class_offset, x[:, 4] # boxes (offset by class), scores + keep_box_idx = torchvision.ops.nms(boxes, scores, iou_thres) # NMS + if keep_box_idx.shape[0] > max_det: # limit detections + keep_box_idx = keep_box_idx[:max_det] + + output[img_idx] = x[keep_box_idx] + if (time.time() - tik) > time_limit: + print(f'WARNING: NMS cost time exceed the limited {time_limit}s.') + break # time limit exceeded + + return output + +def non_max_suppression_seg_solo(predictions, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False, max_det=300): + """Runs Non-Maximum Suppression (NMS) on inference results. + This code is borrowed from: https://github.com/ultralytics/yolov5/blob/47233e1698b89fc437a4fb9463c815e9171be955/utils/general.py#L775 + Args: + prediction: (tensor), with shape [N, 5 + num_classes], N is the number of bboxes. + conf_thres: (float) confidence threshold. + iou_thres: (float) iou threshold. + classes: (None or list[int]), if a list is provided, nms only keep the classes you provide. + agnostic: (bool), when it is set to True, we do class-independent nms, otherwise, different class would do nms respectively. + multi_label: (bool), when it is set to True, one box can have multi labels, otherwise, one box only huave one label. + max_det:(int), max number of output bboxes. + + Returns: + list of detections, echo item is one tensor with shape (num_boxes, 6), 6 is for [xyxy, conf, cls]. + """ + prediction = predictions[0] + confs = predictions[2] # (bs, which_proto, fs) + prediction = torch.cat([prediction, confs], axis=2)# (bs, l ,5 + num_classes + 68) + + num_classes = prediction.shape[2] - 5 - 68 # number of classes + pred_candidates = torch.logical_and(prediction[..., 4] > conf_thres, torch.max(prediction[..., 5: 5 + num_classes], axis=-1)[0] > conf_thres) # candidates + # Check the parameters. + assert 0 <= conf_thres <= 1, f'conf_thresh must be in 0.0 to 1.0, however {conf_thres} is provided.' + assert 0 <= iou_thres <= 1, f'iou_thres must be in 0.0 to 1.0, however {iou_thres} is provided.' + + # Function settings. + max_wh = 4096 # maximum box width and height + max_nms = 30000 # maximum number of boxes put into torchvision.ops.nms() + time_limit = 10.0 # quit the function when nms cost time exceed the limit time. + multi_label &= num_classes > 1 # multiple labels per box + + tik = time.time() + output = [torch.zeros((0, 6 + 68), device=prediction.device)] * prediction.shape[0] + for img_idx, x in enumerate(prediction): # image index, image inference + x = x[pred_candidates[img_idx]] # confidence + + # If no box remains, skip the next process. + if not x.shape[0]: + continue + + # confidence multiply the objectness + x[:, 5: 5 + num_classes] *= x[:, 4:5] # conf = obj_conf * cls_conf + + # (center x, center y, width, height) to (x1, y1, x2, y2) + box = xywh2xyxy(x[:, :4]) + segconf = x[:, 5 + num_classes: ] + + # Detections matrix's shape is (n,6), each row represents (xyxy, conf, cls) + if multi_label: + box_idx, class_idx = (x[:, 5: 5 + num_classes] > conf_thres).nonzero(as_tuple=False).T + x = torch.cat((box[box_idx], x[box_idx, class_idx + 5, None], class_idx[:, None].float(), segconf[box_idx]), 1) + else: # Only keep the class with highest scores. + conf, class_idx = x[:, 5: 5 + num_classes].max(1, keepdim=True) + x = torch.cat((box, conf, class_idx.float(), segconf), 1)[conf.view(-1) > conf_thres] + + # Filter by class, only keep boxes whose category is in classes. + if classes is not None: + x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)] + + # Check shape + num_box = x.shape[0] # number of boxes + if not num_box: # no boxes kept. + continue + elif num_box > max_nms: # excess max boxes' number. + x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence + + # Batched NMS + class_offset = x[:, 5:6] * (0 if agnostic else max_wh) # classes + boxes, scores = x[:, :4] + class_offset, x[:, 4] # boxes (offset by class), scores + keep_box_idx = torchvision.ops.nms(boxes, scores, iou_thres) # NMS + if keep_box_idx.shape[0] > max_det: # limit detections + keep_box_idx = keep_box_idx[:max_det] + + output[img_idx] = x[keep_box_idx] + if (time.time() - tik) > time_limit: + print(f'WARNING: NMS cost time exceed the limited {time_limit}s.') + break # time limit exceeded + + return output diff --git a/yolov6/utils/test1.py b/yolov6/utils/test1.py new file mode 100644 index 00000000..246494f2 --- /dev/null +++ b/yolov6/utils/test1.py @@ -0,0 +1,23 @@ +def process_batch(detections, labels, iouv): + """ + Return correct predictions matrix. Both sets of boxes are in (x1, y1, x2, y2) format. + Arguments: + detections (Array[N, 6]), x1, y1, x2, y2, conf, class + labels (Array[M, 5]), class, x1, y1, x2, y2 + Returns: + correct (Array[N, 10]), for 10 IoU levels + """ + correct = np.zeros((detections.shape[0], iouv.shape[0])).astype(bool) + iou = general.box_iou(labels[:, 1:], detections[:, :4]) + correct_class = labels[:, 0:1] == detections[:, 5] + for i in range(len(iouv)): + x = torch.where((iou >= iouv[i]) & correct_class) # IoU > threshold and classes match + if x[0].shape[0]: + matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy() # [label, detect, iou] + if x[0].shape[0] > 1: + matches = matches[matches[:, 2].argsort()[::-1]] + matches = matches[np.unique(matches[:, 1], return_index=True)[1]] + # matches = matches[matches[:, 2].argsort()[::-1]] + matches = matches[np.unique(matches[:, 0], return_index=True)[1]] + correct[matches[:, 1].astype(int), i] = True + return torch.tensor(correct, dtype=torch.bool, device=iouv.device) \ No newline at end of file diff --git a/yolov6/utils/test2.py b/yolov6/utils/test2.py new file mode 100644 index 00000000..f21ad021 --- /dev/null +++ b/yolov6/utils/test2.py @@ -0,0 +1,37 @@ +def process_batch(detections, labels, iouv, pred_masks=None, gt_masks=None, overlap=False, masks=False): + """ + Return correct prediction matrix + Arguments: + detections (array[N, 6]), x1, y1, x2, y2, conf, class + labels (array[M, 5]), class, x1, y1, x2, y2 + Returns: + correct (array[N, 10]), for 10 IoU levels + """ + #breakpoint() + if masks: + gt_masks = gt_masks.to(pred_masks.device) + if overlap: + nl = len(labels) + index = torch.arange(nl, device=gt_masks.device).view(nl, 1, 1) + 1 + gt_masks = gt_masks.repeat(nl, 1, 1) # shape(1,640,640) -> (n,640,640) + gt_masks = torch.where(gt_masks == index, 1.0, 0.0) + if gt_masks.shape[1:] != pred_masks.shape[1:]: + gt_masks = F.interpolate(gt_masks[None].to(torch.float32), pred_masks.shape[1:], mode='bilinear', align_corners=False)[0] + gt_masks = gt_masks.gt_(0.5) + iou = mask_iou(gt_masks.view(gt_masks.shape[0], -1), pred_masks.view(pred_masks.shape[0], -1)).to(iouv.device) + else: # boxes + iou = box_iou(labels[:, 1:], detections[:, :4]).to(iouv.device) + + correct = np.zeros((detections.shape[0], iouv.shape[0])).astype(bool) + correct_class = labels[:, 0:1] == detections[:, 5] + for i in range(len(iouv)): + x = torch.where((iou >= iouv[i]) & correct_class) # IoU > threshold and classes match + if x[0].shape[0]: + matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy() # [label, detect, iou] + if x[0].shape[0] > 1: + matches = matches[matches[:, 2].argsort()[::-1]] + matches = matches[np.unique(matches[:, 1], return_index=True)[1]] + # matches = matches[matches[:, 2].argsort()[::-1]] + matches = matches[np.unique(matches[:, 0], return_index=True)[1]] + correct[matches[:, 1].astype(int), i] = True + return torch.tensor(correct, dtype=torch.bool, device=iouv.device) \ No newline at end of file