From 9fea321af39d59b012fe5e740da73c89a10bdf84 Mon Sep 17 00:00:00 2001 From: tanzk Date: Tue, 18 Jun 2024 10:59:34 +0800 Subject: [PATCH] init --- README.md | 126 + .../VOC2007/ImageSets/Segmentation/README.md | 2 + .../VOC2007/ImageSets/Segmentation/test.txt | 0 .../VOC2007/ImageSets/Segmentation/train.txt | 295 +++ .../ImageSets/Segmentation/trainval.txt | 328 +++ .../VOC2007/ImageSets/Segmentation/val.txt | 33 + .../VOC2007/ImageSets/Segmentation/README.md | 2 + .../VOC2007/ImageSets/Segmentation/test.txt | 0 .../VOC2007/ImageSets/Segmentation/train.txt | 2105 +++++++++++++++ .../ImageSets/Segmentation/trainval.txt | 2339 +++++++++++++++++ .../VOC2007/ImageSets/Segmentation/val.txt | 234 ++ deeplab.py | 397 +++ get_miou.py | 62 + json_to_dataset.py | 73 + logs/loss_2023_04_16_14_40_55/epoch_miou.txt | 1 + logs/loss_2023_04_16_15_03_17/epoch_miou.txt | 1 + logs/loss_2023_05_05_11_03_08/epoch_miou.txt | 1 + logs/loss_2023_05_06_10_18_49/epoch_miou.txt | 1 + logs/loss_2023_05_06_10_21_47/epoch_miou.txt | 1 + logs/loss_2023_05_06_10_22_54/epoch_miou.txt | 1 + logs/loss_2023_05_06_10_23_46/epoch_loss.txt | 300 +++ logs/loss_2023_05_06_10_23_46/epoch_miou.txt | 61 + .../epoch_val_loss.txt | 300 +++ nets/__init__.py | 1 + nets/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 144 bytes nets/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 161 bytes .../__pycache__/deeplabv3_plus.cpython-38.pyc | Bin 0 -> 4707 bytes .../__pycache__/deeplabv3_plus.cpython-39.pyc | Bin 0 -> 4728 bytes .../deeplabv3_training.cpython-38.pyc | Bin 0 -> 4304 bytes .../deeplabv3_training.cpython-39.pyc | Bin 0 -> 4321 bytes nets/__pycache__/mobilenetv2.cpython-38.pyc | Bin 0 -> 4241 bytes nets/__pycache__/mobilenetv2.cpython-39.pyc | Bin 0 -> 4323 bytes nets/__pycache__/xception.cpython-38.pyc | Bin 0 -> 6032 bytes nets/__pycache__/xception.cpython-39.pyc | Bin 0 -> 6079 bytes nets/deeplabv3_plus.py | 257 ++ nets/deeplabv3_training.py | 113 + nets/img.png | Bin 0 -> 60969 bytes nets/img_1.png | Bin 0 -> 137145 bytes nets/mobilenetv2.py | 164 ++ nets/test_net.py | 30 + nets/xception.py | 298 +++ new_predict.py | 82 + predict.py | 165 ++ requirements.txt | 9 + summary.py | 30 + train.py | 522 ++++ utils/__init__.py | 1 + utils/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 145 bytes utils/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 162 bytes utils/__pycache__/callbacks.cpython-38.pyc | Bin 0 -> 5481 bytes utils/__pycache__/callbacks.cpython-39.pyc | Bin 0 -> 5592 bytes utils/__pycache__/dataloader.cpython-38.pyc | Bin 0 -> 4111 bytes utils/__pycache__/dataloader.cpython-39.pyc | Bin 0 -> 4138 bytes utils/__pycache__/utils.cpython-38.pyc | Bin 0 -> 1995 bytes utils/__pycache__/utils.cpython-39.pyc | Bin 0 -> 2012 bytes utils/__pycache__/utils_fit.cpython-38.pyc | Bin 0 -> 2943 bytes utils/__pycache__/utils_fit.cpython-39.pyc | Bin 0 -> 3076 bytes .../__pycache__/utils_metrics.cpython-38.pyc | Bin 0 -> 5993 bytes .../__pycache__/utils_metrics.cpython-39.pyc | Bin 0 -> 5996 bytes utils/callbacks.py | 200 ++ utils/dataloader.py | 169 ++ utils/utils.py | 64 + utils/utils_fit.py | 174 ++ utils/utils_metrics.py | 182 ++ voc_annotation.py | 100 + 常见问题汇总.md | 554 ++++ 66 files changed, 9778 insertions(+) create mode 100644 README.md create mode 100644 VOCdevkit/VOC2007/ImageSets/Segmentation/README.md create mode 100644 VOCdevkit/VOC2007/ImageSets/Segmentation/test.txt create mode 100644 VOCdevkit/VOC2007/ImageSets/Segmentation/train.txt create mode 100644 VOCdevkit/VOC2007/ImageSets/Segmentation/trainval.txt create mode 100644 VOCdevkit/VOC2007/ImageSets/Segmentation/val.txt create mode 100644 VOCdevkit1/VOC2007/ImageSets/Segmentation/README.md create mode 100644 VOCdevkit1/VOC2007/ImageSets/Segmentation/test.txt create mode 100644 VOCdevkit1/VOC2007/ImageSets/Segmentation/train.txt create mode 100644 VOCdevkit1/VOC2007/ImageSets/Segmentation/trainval.txt create mode 100644 VOCdevkit1/VOC2007/ImageSets/Segmentation/val.txt create mode 100644 deeplab.py create mode 100644 get_miou.py create mode 100644 json_to_dataset.py create mode 100644 logs/loss_2023_04_16_14_40_55/epoch_miou.txt create mode 100644 logs/loss_2023_04_16_15_03_17/epoch_miou.txt create mode 100644 logs/loss_2023_05_05_11_03_08/epoch_miou.txt create mode 100644 logs/loss_2023_05_06_10_18_49/epoch_miou.txt create mode 100644 logs/loss_2023_05_06_10_21_47/epoch_miou.txt create mode 100644 logs/loss_2023_05_06_10_22_54/epoch_miou.txt create mode 100644 logs/loss_2023_05_06_10_23_46/epoch_loss.txt create mode 100644 logs/loss_2023_05_06_10_23_46/epoch_miou.txt create mode 100644 logs/loss_2023_05_06_10_23_46/epoch_val_loss.txt create mode 100644 nets/__init__.py create mode 100644 nets/__pycache__/__init__.cpython-38.pyc create mode 100644 nets/__pycache__/__init__.cpython-39.pyc create mode 100644 nets/__pycache__/deeplabv3_plus.cpython-38.pyc create mode 100644 nets/__pycache__/deeplabv3_plus.cpython-39.pyc create mode 100644 nets/__pycache__/deeplabv3_training.cpython-38.pyc create mode 100644 nets/__pycache__/deeplabv3_training.cpython-39.pyc create mode 100644 nets/__pycache__/mobilenetv2.cpython-38.pyc create mode 100644 nets/__pycache__/mobilenetv2.cpython-39.pyc create mode 100644 nets/__pycache__/xception.cpython-38.pyc create mode 100644 nets/__pycache__/xception.cpython-39.pyc create mode 100644 nets/deeplabv3_plus.py create mode 100644 nets/deeplabv3_training.py create mode 100644 nets/img.png create mode 100644 nets/img_1.png create mode 100644 nets/mobilenetv2.py create mode 100644 nets/test_net.py create mode 100644 nets/xception.py create mode 100644 new_predict.py create mode 100644 predict.py create mode 100644 requirements.txt create mode 100644 summary.py create mode 100644 train.py create mode 100644 utils/__init__.py create mode 100644 utils/__pycache__/__init__.cpython-38.pyc create mode 100644 utils/__pycache__/__init__.cpython-39.pyc create mode 100644 utils/__pycache__/callbacks.cpython-38.pyc create mode 100644 utils/__pycache__/callbacks.cpython-39.pyc create mode 100644 utils/__pycache__/dataloader.cpython-38.pyc create mode 100644 utils/__pycache__/dataloader.cpython-39.pyc create mode 100644 utils/__pycache__/utils.cpython-38.pyc create mode 100644 utils/__pycache__/utils.cpython-39.pyc create mode 100644 utils/__pycache__/utils_fit.cpython-38.pyc create mode 100644 utils/__pycache__/utils_fit.cpython-39.pyc create mode 100644 utils/__pycache__/utils_metrics.cpython-38.pyc create mode 100644 utils/__pycache__/utils_metrics.cpython-39.pyc create mode 100644 utils/callbacks.py create mode 100644 utils/dataloader.py create mode 100644 utils/utils.py create mode 100644 utils/utils_fit.py create mode 100644 utils/utils_metrics.py create mode 100644 voc_annotation.py create mode 100644 常见问题汇总.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..751e57c --- /dev/null +++ b/README.md @@ -0,0 +1,126 @@ +## DeepLabv3+:Encoder-Decoder with Atrous Separable Convolution语义分割模型在Pytorch当中的实现 +--- + +### 目录 +1. [仓库更新 Top News](#仓库更新) +2. [相关仓库 Related code](#相关仓库) +3. [性能情况 Performance](#性能情况) +4. [所需环境 Environment](#所需环境) +5. [文件下载 Download](#文件下载) +6. [训练步骤 How2train](#训练步骤) +7. [预测步骤 How2predict](#预测步骤) +8. [评估步骤 miou](#评估步骤) +9. [参考资料 Reference](#Reference) + +## Top News +**`2022-04`**:**支持多GPU训练。** + +**`2022-03`**:**进行大幅度更新、支持step、cos学习率下降法、支持adam、sgd优化器选择、支持学习率根据batch_size自适应调整。** +BiliBili视频中的原仓库地址为:https://github.com/bubbliiiing/deeplabv3-plus-pytorch/tree/bilibili + +**`2020-08`**:**创建仓库、支持多backbone、支持数据miou评估、标注数据处理、大量注释等。** + +## 相关仓库 +| 模型 | 路径 | +| :----- | :----- | +Unet | https://github.com/bubbliiiing/unet-pytorch +PSPnet | https://github.com/bubbliiiing/pspnet-pytorch +deeplabv3+ | https://github.com/bubbliiiing/deeplabv3-plus-pytorch +hrnet | https://github.com/bubbliiiing/hrnet-pytorch + +### 性能情况 +| 训练数据集 | 权值文件名称 | 测试数据集 | 输入图片大小 | mIOU | +| :-----: | :-----: | :------: | :------: | :------: | +| VOC12+SBD | [deeplab_mobilenetv2.pth](https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/deeplab_mobilenetv2.pth) | VOC-Val12 | 512x512| 72.59 | +| VOC12+SBD | [deeplab_xception.pth](https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/deeplab_xception.pth) | VOC-Val12 | 512x512| 76.95 | + +### 所需环境 +torch==1.2.0 + +### 注意事项 +代码中的deeplab_mobilenetv2.pth和deeplab_xception.pth是基于VOC拓展数据集训练的。训练和预测时注意修改backbone。 + +### 文件下载 +训练所需的deeplab_mobilenetv2.pth和deeplab_xception.pth可在百度网盘中下载。 +链接: https://pan.baidu.com/s/1IQ3XYW-yRWQAy7jxCUHq8Q 提取码: qqq4 + +VOC拓展数据集的百度网盘如下: +链接: https://pan.baidu.com/s/1vkk3lMheUm6IjTXznlg7Ng 提取码: 44mk + +### 训练步骤 +#### a、训练voc数据集 +1、将我提供的voc数据集放入VOCdevkit中(无需运行voc_annotation.py)。 +2、在train.py中设置对应参数,默认参数已经对应voc数据集所需要的参数了,所以只要修改backbone和model_path即可。 +3、运行train.py进行训练。 + +#### b、训练自己的数据集 +1、本文使用VOC格式进行训练。 +2、训练前将标签文件放在VOCdevkit文件夹下的VOC2007文件夹下的SegmentationClass中。 +3、训练前将图片文件放在VOCdevkit文件夹下的VOC2007文件夹下的JPEGImages中。 +4、在训练前利用voc_annotation.py文件生成对应的txt。 +5、在train.py文件夹下面,选择自己要使用的主干模型和下采样因子。本文提供的主干模型有mobilenet和xception。下采样因子可以在8和16中选择。需要注意的是,预训练模型需要和主干模型相对应。 +6、注意修改train.py的num_classes为分类个数+1。 +7、运行train.py即可开始训练。 + +### 预测步骤 +#### a、使用预训练权重 +1、下载完库后解压,如果想用backbone为mobilenet的进行预测,直接运行predict.py就可以了;如果想要利用backbone为xception的进行预测,在百度网盘下载deeplab_xception.pth,放入model_data,修改deeplab.py的backbone和model_path之后再运行predict.py,输入。 +```python +img/street.jpg +``` +可完成预测。 +2、在predict.py里面进行设置可以进行fps测试、整个文件夹的测试和video视频检测。 + +#### b、使用自己训练的权重 +1、按照训练步骤训练。 +2、在deeplab.py文件里面,在如下部分修改model_path、num_classes、backbone使其对应训练好的文件;**model_path对应logs文件夹下面的权值文件,num_classes代表要预测的类的数量加1,backbone是所使用的主干特征提取网络**。 +```python +_defaults = { + #----------------------------------------# + # model_path指向logs文件夹下的权值文件 + #----------------------------------------# + "model_path" : 'model_data/deeplab_mobilenetv2.pth', + #----------------------------------------# + # 所需要区分的类的个数+1 + #----------------------------------------# + "num_classes" : 21, + #----------------------------------------# + # 所使用的的主干网络 + #----------------------------------------# + "backbone" : "mobilenet", + #----------------------------------------# + # 输入图片的大小 + #----------------------------------------# + "input_shape" : [512, 512], + #----------------------------------------# + # 下采样的倍数,一般可选的为8和16 + # 与训练时设置的一样即可 + #----------------------------------------# + "downsample_factor" : 16, + #--------------------------------# + # blend参数用于控制是否 + # 让识别结果和原图混合 + #--------------------------------# + "blend" : True, + #-------------------------------# + # 是否使用Cuda + # 没有GPU可以设置成False + #-------------------------------# + "cuda" : True, +} +``` +3、运行predict.py,输入 +```python +img/street.jpg +``` +可完成预测。 +4、在predict.py里面进行设置可以进行fps测试、整个文件夹的测试和video视频检测。 + +### 评估步骤 +1、设置get_miou.py里面的num_classes为预测的类的数量加1。 +2、设置get_miou.py里面的name_classes为需要去区分的类别。 +3、运行get_miou.py即可获得miou大小。 + +### Reference +https://github.com/ggyyzm/pytorch_segmentation +https://github.com/bonlime/keras-deeplab-v3-plus diff --git a/VOCdevkit/VOC2007/ImageSets/Segmentation/README.md b/VOCdevkit/VOC2007/ImageSets/Segmentation/README.md new file mode 100644 index 0000000..9042c5f --- /dev/null +++ b/VOCdevkit/VOC2007/ImageSets/Segmentation/README.md @@ -0,0 +1,2 @@ +存放的是指向文件名称的txt + diff --git a/VOCdevkit/VOC2007/ImageSets/Segmentation/test.txt b/VOCdevkit/VOC2007/ImageSets/Segmentation/test.txt new file mode 100644 index 0000000..e69de29 diff --git a/VOCdevkit/VOC2007/ImageSets/Segmentation/train.txt b/VOCdevkit/VOC2007/ImageSets/Segmentation/train.txt new file mode 100644 index 0000000..9b2dc1d --- /dev/null +++ b/VOCdevkit/VOC2007/ImageSets/Segmentation/train.txt @@ -0,0 +1,295 @@ +01-1 +01-2 +01-3 +01-4 +02-1 +02-2 +03-1 +03-2 +03-3 +04-1 +04-2 +04-3 +05-1 +05-2 +05-3 +06-1 +06-2 +06-3 +07-1 +07-2 +08-1 +08-2 +09-1 +10-1 +100-1 +11-1 +116_01_01 +117_01_01 +118_01_01 +119_01_01 +11_01_01 +12-1 +120_01_01 +121_01_01 +122_01_01 +123_01_01 +124_01_01 +125_01_01 +126_01_01 +127_01_01 +128_01_01 +129_01_01 +12_01_01 +13-1 +13-3 +130_01_01 +132_01_01 +134_01_01 +138_01_01 +139_01_01 +14-1 +140_01_01 +141_01_01 +142_01_01 +143_01_01 +144_01_01 +145_01_01 +146_01_01 +147_01_01 +148_01_01 +149_01_01 +14_01_01 +15-1 +150_01_01 +151_01_01 +152_01_01 +153_01_01 +154_01_01 +155_01_01 +156_01_01 +157_01_01 +158_01_01 +159_01_01 +15_01_01 +16-1 +16-3 +160_01_01 +161_01_01 +162_01_01 +163_01_01 +164_01_01 +165_01_01 +166_01_01 +168_01_01 +16_01_01 +17-1 +171_01_01 +172_01_01 +173_01_01 +174_01_01 +175_01_01 +176_01_01 +177_01_01 +178_01_01 +179_01_01 +18-1 +180_01_01 +181_01_01 +182_01_01 +183_01_01 +184_01_01 +185_01_01 +187_01_01 +188_01_01 +189_01_01 +19-1 +190_01_01 +191_01_01 +192_01_01 +194_01_01 +195_01_01 +196_01_01 +198_01_01 +199_01_01 +19_01_01 +19_01_02 +1_01_02 +1_01_03 +1_01_04 +1_02_01 +1_02_02 +1_02_03 +1_02_04 +1_03_01 +1_03_02 +1_03_03 +1_03_04 +20-1 +200_01_01 +201_01_01 +202_01_01 +203_01_01 +204_01_01 +206_01_01 +207_01_01 +208_01_01 +21-1 +21_01_01 +22-1 +22-3 +22_02_01 +23-1 +239_01_01 +23_01_01 +24-1 +240_01_01 +241_01_01 +242_01_01 +243_01_01 +244_01_01 +245_01_01 +246_01_01 +247_01_01 +248_01_01 +249_01_01 +24_02_01 +25-1 +25-2 +250_01_01 +251_01_01 +25_01_01 +27-3 +27_01_01 +28-1 +28_01_01 +29-1 +2_01_01 +2_01_02 +2_01_03 +2_01_04 +2_02_01 +2_02_02 +2_02_03 +2_02_04 +2_03_01 +2_03_02 +2_03_03 +2_03_04 +30-1 +31-1 +32-1 +34-1 +35-1 +35-3 +36-1 +36-3 +37-1 +37-2 +38-1 +39-1 +39-2 +3_01_01 +3_01_03 +3_01_04 +3_02_01 +3_02_02 +3_02_03 +3_02_04 +3_03_02 +3_03_03 +3_03_04 +40-1 +40-3 +41-1 +41-2 +41_02_01 +42_02_01 +43-1 +44-1 +45-1 +47-1 +49-1 +4_01_01 +4_01_02 +4_01_03 +4_01_04 +4_02_01 +4_02_02 +4_02_03 +4_02_04 +4_03_02 +4_03_03 +50-1 +51-1 +52-1 +53-1 +54-1 +56-1 +57-1 +58-1 +59-1 +5_01_01 +5_01_02 +5_01_03 +5_01_04 +5_02_01 +5_02_02 +5_02_03 +5_02_04 +5_03_03 +5_03_04 +60-1 +61-1 +62-1 +64-1 +65-1 +66-1 +67-1 +68-1 +69-1 +6_01_01 +6_01_02 +6_01_03 +6_01_04 +6_02_02 +6_02_03 +6_02_04 +6_03_01 +6_03_02 +6_03_03 +6_03_04 +70-1 +71-1 +72-1 +73-1 +74-1 +75-1 +75-2 +76-1 +77-1 +78-1 +79-1 +7_01_01 +80-1 +81-1 +82-1 +83-1 +84-1 +85-1 +86-1 +87-1 +88-1 +89-1 +8_01_01 +91-1 +92-1 +93-1 +94-1 +95-1 +96-1 +97-1 +97-4 +98-1 +98-2 +99-1 diff --git a/VOCdevkit/VOC2007/ImageSets/Segmentation/trainval.txt b/VOCdevkit/VOC2007/ImageSets/Segmentation/trainval.txt new file mode 100644 index 0000000..ff632fb --- /dev/null +++ b/VOCdevkit/VOC2007/ImageSets/Segmentation/trainval.txt @@ -0,0 +1,328 @@ +01-1 +01-2 +01-3 +01-4 +02-1 +02-2 +03-1 +03-2 +03-3 +04-1 +04-2 +04-3 +05-1 +05-2 +05-3 +06-1 +06-2 +06-3 +07-1 +07-2 +08-1 +08-2 +09-1 +10-1 +100-1 +10_01_01 +11-1 +116_01_01 +117_01_01 +118_01_01 +119_01_01 +11_01_01 +12-1 +120_01_01 +121_01_01 +122_01_01 +123_01_01 +124_01_01 +125_01_01 +126_01_01 +127_01_01 +128_01_01 +129_01_01 +12_01_01 +13-1 +13-3 +130_01_01 +131_01_01 +132_01_01 +133_01_01 +134_01_01 +135_01_01 +136_01_01 +137_01_01 +138_01_01 +139_01_01 +14-1 +140_01_01 +141_01_01 +142_01_01 +143_01_01 +144_01_01 +145_01_01 +146_01_01 +147_01_01 +148_01_01 +149_01_01 +14_01_01 +15-1 +150_01_01 +151_01_01 +152_01_01 +153_01_01 +154_01_01 +155_01_01 +156_01_01 +157_01_01 +158_01_01 +159_01_01 +15_01_01 +16-1 +16-3 +160_01_01 +161_01_01 +162_01_01 +163_01_01 +164_01_01 +165_01_01 +166_01_01 +167_01_01 +168_01_01 +169_01_01 +16_01_01 +17-1 +170_01_01 +171_01_01 +172_01_01 +173_01_01 +174_01_01 +175_01_01 +176_01_01 +177_01_01 +178_01_01 +179_01_01 +18-1 +180_01_01 +181_01_01 +182_01_01 +183_01_01 +184_01_01 +185_01_01 +186_01_01 +187_01_01 +188_01_01 +189_01_01 +19-1 +190_01_01 +191_01_01 +192_01_01 +193_01_01 +194_01_01 +195_01_01 +196_01_01 +197_01_01 +198_01_01 +199_01_01 +19_01_01 +19_01_02 +1_01_01 +1_01_02 +1_01_03 +1_01_04 +1_02_01 +1_02_02 +1_02_03 +1_02_04 +1_03_01 +1_03_02 +1_03_03 +1_03_04 +20-1 +200_01_01 +201_01_01 +202_01_01 +203_01_01 +204_01_01 +205_01_01 +206_01_01 +207_01_01 +208_01_01 +20_01_02 +21-1 +21_01_01 +22-1 +22-3 +22_02_01 +23-1 +239_01_01 +23_01_01 +24-1 +240_01_01 +241_01_01 +242_01_01 +243_01_01 +244_01_01 +245_01_01 +246_01_01 +247_01_01 +248_01_01 +249_01_01 +24_02_01 +25-1 +25-2 +250_01_01 +251_01_01 +252_01_01 +25_01_01 +26-1 +27-3 +27_01_01 +28-1 +28_01_01 +29-1 +2_01_01 +2_01_02 +2_01_03 +2_01_04 +2_02_01 +2_02_02 +2_02_03 +2_02_04 +2_03_01 +2_03_02 +2_03_03 +2_03_04 +30-1 +31-1 +32-1 +33-1 +34-1 +35-1 +35-3 +36-1 +36-3 +37-1 +37-2 +38-1 +39-1 +39-2 +3_01_01 +3_01_02 +3_01_03 +3_01_04 +3_02_01 +3_02_02 +3_02_03 +3_02_04 +3_03_01 +3_03_02 +3_03_03 +3_03_04 +40-1 +40-3 +41-1 +41-2 +41_02_01 +42-1 +42_02_01 +43-1 +44-1 +45-1 +46-1 +47-1 +48-1 +49-1 +4_01_01 +4_01_02 +4_01_03 +4_01_04 +4_02_01 +4_02_02 +4_02_03 +4_02_04 +4_03_01 +4_03_02 +4_03_03 +4_03_04 +50-1 +51-1 +52-1 +53-1 +53-2 +54-1 +55-1 +56-1 +57-1 +58-1 +59-1 +5_01_01 +5_01_02 +5_01_03 +5_01_04 +5_02_01 +5_02_02 +5_02_03 +5_02_04 +5_03_01 +5_03_02 +5_03_03 +5_03_04 +60-1 +61-1 +62-1 +63-1 +64-1 +65-1 +66-1 +67-1 +68-1 +69-1 +6_01_01 +6_01_02 +6_01_03 +6_01_04 +6_02_01 +6_02_02 +6_02_03 +6_02_04 +6_03_01 +6_03_02 +6_03_03 +6_03_04 +70-1 +71-1 +72-1 +73-1 +74-1 +75-1 +75-2 +76-1 +77-1 +78-1 +79-1 +7_01_01 +80-1 +81-1 +82-1 +83-1 +84-1 +85-1 +86-1 +87-1 +88-1 +89-1 +8_01_01 +90-1 +91-1 +92-1 +93-1 +94-1 +95-1 +96-1 +97-1 +97-4 +98-1 +98-2 +99-1 +9_01_01 diff --git a/VOCdevkit/VOC2007/ImageSets/Segmentation/val.txt b/VOCdevkit/VOC2007/ImageSets/Segmentation/val.txt new file mode 100644 index 0000000..ebf9121 --- /dev/null +++ b/VOCdevkit/VOC2007/ImageSets/Segmentation/val.txt @@ -0,0 +1,33 @@ +10_01_01 +131_01_01 +133_01_01 +135_01_01 +136_01_01 +137_01_01 +167_01_01 +169_01_01 +170_01_01 +186_01_01 +193_01_01 +197_01_01 +1_01_01 +205_01_01 +20_01_02 +252_01_01 +26-1 +33-1 +3_01_02 +3_03_01 +42-1 +46-1 +48-1 +4_03_01 +4_03_04 +53-2 +55-1 +5_03_01 +5_03_02 +63-1 +6_02_01 +90-1 +9_01_01 diff --git a/VOCdevkit1/VOC2007/ImageSets/Segmentation/README.md b/VOCdevkit1/VOC2007/ImageSets/Segmentation/README.md new file mode 100644 index 0000000..9042c5f --- /dev/null +++ b/VOCdevkit1/VOC2007/ImageSets/Segmentation/README.md @@ -0,0 +1,2 @@ +存放的是指向文件名称的txt + diff --git a/VOCdevkit1/VOC2007/ImageSets/Segmentation/test.txt b/VOCdevkit1/VOC2007/ImageSets/Segmentation/test.txt new file mode 100644 index 0000000..e69de29 diff --git a/VOCdevkit1/VOC2007/ImageSets/Segmentation/train.txt b/VOCdevkit1/VOC2007/ImageSets/Segmentation/train.txt new file mode 100644 index 0000000..6c0e27c --- /dev/null +++ b/VOCdevkit1/VOC2007/ImageSets/Segmentation/train.txt @@ -0,0 +1,2105 @@ +00001 +00002 +00003 +00004 +00005 +00006 +00007 +00008 +00010 +00011 +00012 +00013 +00014 +00015 +00016 +00018 +00019 +00020 +00021 +00022 +00023 +00024 +00025 +00026 +00028 +00029 +00030 +00031 +00032 +00034 +00035 +00036 +00037 +00038 +00039 +00040 +00041 +00043 +00044 +00045 +00046 +00047 +00048 +00049 +00050 +00052 +00053 +00054 +00055 +00056 +00057 +00058 +00059 +00060 +00061 +00062 +00063 +00064 +00065 +00066 +00067 +00068 +00069 +00071 +00072 +00073 +00074 +00075 +00076 +00077 +00078 +00079 +00080 +00081 +00083 +00084 +00085 +00086 +00087 +00088 +00090 +00091 +00093 +00094 +00095 +00096 +00097 +00098 +00099 +00100 +00101 +00103 +00104 +00105 +00106 +00107 +00108 +00109 +00110 +00112 +00113 +00114 +00115 +00116 +00118 +00119 +00120 +00121 +00122 +00123 +00124 +00125 +00126 +00127 +00128 +00129 +00132 +00134 +00135 +00136 +00139 +00140 +00141 +00142 +00143 +00144 +00146 +00147 +00148 +00149 +00150 +00151 +00152 +00153 +00154 +00155 +00156 +00157 +00158 +00159 +00160 +00161 +00163 +00164 +00165 +00166 +00167 +00168 +00169 +00170 +00172 +00173 +00174 +00175 +00176 +00177 +00178 +00179 +00180 +00181 +00182 +00183 +00184 +00186 +00187 +00188 +00190 +00192 +00193 +00194 +00195 +00196 +00197 +00198 +00199 +00200 +00201 +00202 +00204 +00205 +00206 +00207 +00209 +00210 +00212 +00213 +00214 +00215 +00216 +00217 +00219 +00220 +00221 +00222 +00224 +00225 +00226 +00227 +00228 +00229 +00230 +00231 +00232 +00235 +00236 +00237 +00238 +00239 +00240 +00241 +00242 +00243 +00244 +00245 +00246 +00247 +00248 +00250 +00251 +00252 +00253 +00254 +00256 +00257 +00258 +00259 +00260 +00261 +00262 +00264 +00265 +00266 +00267 +00268 +00269 +00270 +00271 +00272 +00273 +00274 +00275 +00276 +00277 +00279 +00280 +00281 +00282 +00283 +00284 +00285 +00286 +00287 +00288 +00289 +00290 +00291 +00292 +00293 +00294 +00295 +00296 +00298 +00299 +00300 +00301 +00302 +00303 +00304 +00305 +00306 +00307 +00308 +00309 +00310 +00311 +00312 +00313 +00314 +00315 +00316 +00317 +00318 +00321 +00323 +00324 +00325 +00326 +00328 +00330 +00331 +00332 +00333 +00334 +00335 +00336 +00337 +00338 +00339 +00340 +00341 +00342 +00343 +00344 +00345 +00346 +00347 +00348 +00349 +00350 +00351 +00352 +00353 +00354 +00355 +00356 +00357 +00358 +00359 +00360 +00361 +00362 +00363 +00364 +00365 +00366 +00367 +00368 +00369 +00370 +00371 +00372 +00374 +00375 +00376 +00377 +00378 +00379 +00380 +00381 +00382 +00383 +00384 +00386 +00387 +00388 +00389 +00390 +00391 +00392 +00393 +00394 +00396 +00397 +00398 +00399 +00400 +00401 +00402 +00403 +00404 +00405 +00406 +00407 +00408 +00409 +00410 +00412 +00413 +00414 +00415 +00416 +00418 +00419 +00420 +00421 +00423 +00424 +00425 +00426 +00427 +00428 +00430 +00431 +00432 +00433 +00434 +00435 +00436 +00437 +00439 +00440 +00441 +00442 +00443 +00444 +00445 +00446 +00447 +00448 +00449 +00450 +00451 +00452 +00454 +00455 +00456 +00458 +00459 +00460 +00461 +00462 +00463 +00464 +00465 +00466 +00467 +00468 +00469 +00471 +00472 +00473 +00474 +00475 +00476 +00477 +00478 +00479 +00480 +00481 +00482 +00483 +00484 +00485 +00486 +00487 +00488 +00489 +00490 +00491 +00492 +00494 +00495 +00496 +00499 +00500 +00501 +00502 +00503 +00504 +00505 +00506 +00507 +00508 +00509 +00510 +00511 +00512 +00513 +00514 +00515 +00517 +00518 +00519 +00520 +00521 +00522 +00523 +00524 +00525 +00526 +00527 +00528 +00529 +00531 +00532 +00533 +00534 +00535 +00536 +00537 +00538 +00539 +00540 +00541 +00542 +00543 +00544 +00545 +00546 +00547 +00548 +00549 +00550 +00551 +00552 +00553 +00554 +00555 +00556 +00557 +00558 +00559 +00560 +00561 +00562 +00563 +00564 +00565 +00566 +00567 +00568 +00569 +00570 +00571 +00572 +00573 +00574 +00575 +00576 +00577 +00578 +00579 +00580 +00581 +00582 +00583 +00584 +00585 +00586 +00587 +00589 +00590 +00591 +00592 +00593 +00594 +00596 +00597 +00598 +00599 +00600 +00602 +00603 +00605 +00606 +00607 +00608 +00609 +00610 +00611 +00612 +00613 +00614 +00615 +00617 +00618 +00619 +00620 +00621 +00622 +00623 +00624 +00625 +00626 +00627 +00628 +00629 +00630 +00631 +00632 +00633 +00634 +00637 +00638 +00639 +00640 +00641 +00642 +00643 +00644 +00645 +00646 +00647 +00648 +00649 +00650 +00651 +00652 +00653 +00654 +00655 +00656 +00657 +00658 +00659 +00660 +00661 +00662 +00663 +00664 +00665 +00666 +00667 +00668 +00670 +00671 +00672 +00673 +00674 +00675 +00676 +00677 +00678 +00679 +00680 +00681 +00682 +00683 +00684 +00685 +00686 +00687 +00688 +00689 +00690 +00691 +00692 +00693 +00694 +00695 +00697 +00698 +00699 +00700 +00702 +00703 +00704 +00705 +00706 +00707 +00708 +00710 +00711 +00712 +00713 +00714 +00715 +00717 +00718 +00719 +00720 +00721 +00722 +00723 +00725 +00726 +00727 +00728 +00729 +00730 +00731 +00733 +00734 +00735 +00736 +00737 +00738 +00739 +00740 +00742 +00743 +00744 +00745 +00746 +00747 +00749 +00750 +00751 +00752 +00753 +00754 +00755 +00758 +00759 +00760 +00761 +00762 +00763 +00764 +00765 +00766 +00767 +00768 +00769 +00770 +00771 +00772 +00773 +00774 +00775 +00776 +00777 +00778 +00779 +00781 +00782 +00783 +00784 +00785 +00786 +00787 +00788 +00790 +00791 +00792 +00793 +00794 +00795 +00796 +00797 +00798 +00799 +00800 +00801 +00802 +00803 +00804 +00805 +00806 +00807 +00808 +00809 +00810 +00811 +00813 +00814 +00815 +00816 +00817 +00818 +00819 +00820 +00821 +00822 +00823 +00824 +00825 +00826 +00827 +00828 +00829 +00830 +00831 +00832 +00833 +00834 +00835 +00836 +00838 +00839 +00840 +00841 +00842 +00843 +00844 +00845 +00846 +00847 +00848 +00849 +00851 +00852 +00853 +00854 +00855 +00856 +00857 +00859 +00860 +00861 +00862 +00863 +00864 +00865 +00866 +00868 +00870 +00871 +00872 +00873 +00874 +00875 +00877 +00878 +00880 +00881 +00882 +00883 +00885 +00886 +00888 +00889 +00890 +00891 +00892 +00893 +00894 +00895 +00896 +00897 +00898 +00899 +00900 +00902 +00903 +00904 +00905 +00907 +00908 +00909 +00912 +00913 +00914 +00915 +00916 +00917 +00918 +00919 +00920 +00921 +00922 +00923 +00924 +00925 +00927 +00928 +00929 +00930 +00931 +00932 +00933 +00934 +00935 +00936 +00937 +00938 +00939 +00940 +00941 +00942 +00943 +00944 +00945 +00946 +00947 +00948 +00949 +00950 +00951 +00952 +00953 +00954 +00955 +00956 +00957 +00958 +00959 +00960 +00961 +00962 +00963 +00964 +00966 +00967 +00968 +00969 +00970 +00971 +00972 +00973 +00974 +00975 +00976 +00977 +00978 +00980 +00981 +00982 +00983 +00984 +00985 +00988 +00989 +00990 +00992 +00993 +00994 +00995 +00996 +00997 +00998 +00999 +01000 +01002 +01003 +01004 +01005 +01006 +01007 +01008 +01009 +01010 +01011 +01012 +01013 +01015 +01016 +01017 +01018 +01019 +01020 +01021 +01022 +01023 +01024 +01025 +01027 +01028 +01029 +01030 +01031 +01032 +01033 +01034 +01036 +01037 +01038 +01039 +01040 +01041 +01042 +01043 +01044 +01045 +01046 +01047 +01049 +01051 +01052 +01053 +01054 +01055 +01056 +01057 +01058 +01059 +01060 +01061 +01062 +01063 +01064 +01065 +01066 +01067 +01068 +01070 +01071 +01072 +01073 +01074 +01075 +01077 +01078 +01079 +01080 +01081 +01082 +01084 +01085 +01086 +01087 +01088 +01089 +01090 +01091 +01094 +01095 +01096 +01097 +01098 +01099 +01100 +01101 +01102 +01103 +01104 +01105 +01106 +01107 +01108 +01109 +01110 +01111 +01112 +01114 +01115 +01116 +01117 +01118 +01119 +01120 +01121 +01122 +01123 +01125 +01127 +01128 +01129 +01130 +01132 +01133 +01134 +01135 +01136 +01137 +01138 +01139 +01141 +01143 +01144 +01145 +01146 +01147 +01148 +01149 +01150 +01151 +01152 +01153 +01154 +01155 +01156 +01157 +01158 +01160 +01162 +01163 +01164 +01165 +01166 +01167 +01169 +01170 +01171 +01172 +01173 +01174 +01175 +01176 +01177 +01179 +01180 +01181 +01182 +01183 +01184 +01186 +01187 +01188 +01189 +01191 +01192 +01194 +01195 +01196 +01197 +01198 +01199 +01200 +01201 +01202 +01204 +01205 +01206 +01207 +01208 +01209 +01210 +01211 +01212 +01213 +01214 +01215 +01216 +01217 +01218 +01220 +01221 +01222 +01223 +01225 +01226 +01227 +01228 +01229 +01230 +01231 +01232 +01233 +01234 +01235 +01236 +01237 +01238 +01239 +01240 +01241 +01242 +01243 +01244 +01245 +01246 +01247 +01248 +01249 +01250 +01251 +01252 +01253 +01255 +01256 +01257 +01260 +01262 +01263 +01264 +01265 +01266 +01268 +01269 +01270 +01272 +01273 +01274 +01275 +01276 +01277 +01278 +01280 +01281 +01282 +01283 +01285 +01286 +01287 +01288 +01289 +01290 +01292 +01293 +01294 +01295 +01296 +01297 +01298 +01299 +01300 +01301 +01302 +01303 +01304 +01305 +01306 +01307 +01308 +01309 +01310 +01311 +01312 +01315 +01316 +01317 +01318 +01319 +01321 +01322 +01323 +01326 +01327 +01328 +01329 +01330 +01331 +01332 +01333 +01334 +01335 +01336 +01337 +01338 +01339 +01340 +01341 +01342 +01343 +01344 +01345 +01346 +01347 +01348 +01349 +01350 +01351 +01353 +01354 +01355 +01356 +01357 +01358 +01359 +01360 +01361 +01362 +01363 +01365 +01366 +01367 +01368 +01369 +01370 +01371 +01372 +01373 +01374 +01375 +01376 +01377 +01378 +01379 +01380 +01381 +01382 +01383 +01384 +01385 +01386 +01387 +01388 +01389 +01390 +01391 +01392 +01393 +01394 +01395 +01397 +01398 +01399 +01400 +01401 +01402 +01403 +01404 +01405 +01406 +01407 +01408 +01409 +01410 +01411 +01412 +01414 +01415 +01416 +01417 +01418 +01419 +01420 +01422 +01424 +01425 +01426 +01427 +01428 +01429 +01430 +01431 +01432 +01433 +01435 +01437 +01438 +01439 +01440 +01441 +01442 +01443 +01444 +01445 +01446 +01448 +01449 +01450 +01451 +01452 +01453 +01455 +01456 +01457 +01458 +01459 +01460 +01461 +01462 +01463 +01464 +01466 +01467 +01468 +01470 +01471 +01473 +01474 +01475 +01476 +01477 +01478 +01479 +01481 +01482 +01483 +01484 +01485 +01486 +01487 +01488 +01490 +01491 +01492 +01493 +01494 +01496 +01497 +01498 +01499 +01500 +01501 +01502 +01503 +01504 +01505 +01506 +01507 +01508 +01509 +01510 +01512 +01513 +01514 +01515 +01516 +01518 +01519 +01520 +01521 +01522 +01523 +01524 +01525 +01526 +01527 +01528 +01530 +01531 +01532 +01533 +01535 +01536 +01537 +01538 +01539 +01540 +01542 +01543 +01544 +01546 +01547 +01548 +01549 +01550 +01551 +01552 +01555 +01556 +01557 +01558 +01559 +01560 +01561 +01562 +01563 +01564 +01565 +01566 +01567 +01568 +01569 +01570 +01571 +01572 +01573 +01574 +01575 +01576 +01577 +01578 +01579 +01580 +01581 +01582 +01583 +01584 +01585 +01586 +01587 +01588 +01589 +01590 +01591 +01592 +01593 +01594 +01596 +01598 +01599 +01600 +01601 +01602 +01603 +01604 +01605 +01606 +01607 +01608 +01609 +01610 +01611 +01612 +01613 +01614 +01615 +01616 +01617 +01619 +01620 +01621 +01622 +01623 +01624 +01626 +01627 +01628 +01632 +01633 +01634 +01635 +01636 +01637 +01638 +01639 +01642 +01643 +01644 +01645 +01646 +01647 +01648 +01649 +01650 +01651 +01652 +01653 +01654 +01655 +01656 +01657 +01658 +01659 +01660 +01661 +01662 +01663 +01664 +01665 +01666 +01667 +01668 +01669 +01671 +01672 +01673 +01674 +01675 +01676 +01677 +01678 +01679 +01680 +01681 +01682 +01683 +01685 +01686 +01687 +01688 +01689 +01690 +01692 +01693 +01694 +01695 +01696 +01697 +01698 +01699 +01700 +01701 +01702 +01703 +01704 +01705 +01706 +01707 +01708 +01709 +01710 +01711 +01712 +01713 +01714 +01715 +01716 +01717 +01718 +01719 +01720 +01721 +01722 +01723 +01724 +01725 +01726 +01727 +01728 +01729 +01730 +01731 +01732 +01733 +01734 +01735 +01736 +01737 +01738 +01739 +01740 +01741 +01742 +01743 +01744 +01745 +01746 +01747 +01748 +01749 +01751 +01752 +01753 +01754 +01755 +01756 +01758 +01759 +01760 +01761 +01762 +01763 +01764 +01765 +01766 +01767 +01768 +01769 +01770 +01771 +01772 +01774 +01775 +01776 +01778 +01780 +01781 +01783 +01784 +01785 +01786 +01789 +01790 +01791 +01792 +01794 +01796 +01797 +01799 +01800 +01801 +01802 +01803 +01804 +01805 +01806 +01807 +01808 +01809 +01810 +01811 +01812 +01813 +01814 +01815 +01817 +01818 +01820 +01822 +01823 +01824 +01825 +01826 +01827 +01828 +01829 +01830 +01831 +01832 +01834 +01835 +01836 +01837 +01838 +01839 +01840 +01841 +01842 +01843 +01844 +01845 +01846 +01847 +01848 +01849 +01850 +01851 +01852 +01853 +01854 +01855 +01856 +01857 +01858 +01859 +01860 +01861 +01863 +01864 +01865 +01866 +01867 +01868 +01869 +01870 +01871 +01872 +01873 +01874 +01875 +01876 +01877 +01878 +01879 +01880 +01882 +01883 +01884 +01885 +01886 +01887 +01888 +01889 +01890 +01891 +01893 +01894 +01895 +01896 +01897 +01898 +01899 +01900 +01901 +01903 +01904 +01905 +01906 +01907 +01908 +01909 +01910 +01911 +01912 +01913 +01914 +01915 +01916 +01917 +01918 +01919 +01920 +01921 +01922 +01923 +01924 +01925 +01926 +01927 +01928 +01929 +01930 +01931 +01932 +01933 +01934 +01935 +01936 +01937 +01941 +01942 +01943 +01944 +01945 +01946 +01947 +01949 +01951 +01952 +01953 +01954 +01956 +01957 +01958 +01959 +01960 +01961 +01962 +01963 +01965 +01966 +01967 +01968 +01969 +01970 +01971 +01972 +01973 +01974 +01975 +01976 +01977 +01978 +01979 +01980 +01981 +01982 +01983 +01984 +01985 +01986 +01987 +01988 +01989 +01991 +01992 +01993 +01994 +01995 +01996 +01997 +01998 +01999 +02000 +02001 +02002 +02003 +02004 +02005 +02006 +02007 +02008 +02009 +02010 +02011 +02012 +02013 +02015 +02016 +02017 +02018 +02020 +02021 +02022 +02023 +02024 +02025 +02026 +02027 +02028 +02029 +02030 +02031 +02032 +02033 +02034 +02035 +02036 +02037 +02038 +02039 +02040 +02041 +02042 +02043 +02044 +02045 +02046 +02047 +02048 +02050 +02051 +02052 +02053 +02054 +02055 +02056 +02057 +02058 +02059 +02060 +02062 +02063 +02064 +02065 +02067 +02069 +02070 +02071 +02072 +02073 +02074 +02075 +02076 +02077 +02078 +02079 +02080 +02081 +02083 +02085 +02086 +02087 +02088 +02089 +02092 +02094 +02096 +02097 +02098 +02099 +02100 +02101 +02102 +02103 +02104 +02105 +02106 +02107 +02108 +02109 +02110 +02111 +02112 +02113 +02114 +02115 +02116 +02117 +02119 +02120 +02122 +02123 +02124 +02125 +02126 +02127 +02128 +02129 +02130 +02131 +02132 +02134 +02135 +02136 +02137 +02138 +02139 +02140 +02141 +02142 +02143 +02144 +02145 +02146 +02147 +02148 +02149 +02150 +02151 +02152 +02153 +02154 +02155 +02156 +02158 +02159 +02160 +02162 +02163 +02164 +02165 +02166 +02167 +02168 +02169 +02170 +02171 +02172 +02174 +02175 +02176 +02177 +02178 +02179 +02180 +02181 +02182 +02184 +02185 +02186 +02187 +02188 +02189 +02190 +02191 +02192 +02193 +02194 +02195 +02196 +02197 +02198 +02199 +02201 +02202 +02205 +02206 +02207 +02208 +02209 +02210 +02211 +02213 +02214 +02215 +02216 +02217 +02218 +02219 +02220 +02223 +02224 +02225 +02226 +02227 +02228 +02231 +02232 +02233 +02234 +02235 +02236 +02237 +02238 +02239 +02240 +02242 +02243 +02244 +02245 +02246 +02247 +02248 +02249 +02250 +02251 +02252 +02254 +02255 +02256 +02257 +02258 +02259 +02260 +02261 +02262 +02264 +02265 +02266 +02267 +02268 +02269 +02270 +02271 +02272 +02273 +02274 +02275 +02276 +02277 +02278 +02280 +02281 +02282 +02283 +02284 +02285 +02286 +02287 +02288 +02289 +02290 +02291 +02292 +02293 +02294 +02295 +02296 +02297 +02298 +02299 +02300 +02301 +02302 +02303 +02304 +02305 +02306 +02307 +02308 +02309 +02310 +02311 +02312 +02313 +02314 +02315 +02316 +02317 +02318 +02320 +02321 +02322 +02323 +02324 +02325 +02326 +02327 +02328 +02329 +02330 +02331 +02333 +02334 +02335 +02336 +02337 +02338 +02339 diff --git a/VOCdevkit1/VOC2007/ImageSets/Segmentation/trainval.txt b/VOCdevkit1/VOC2007/ImageSets/Segmentation/trainval.txt new file mode 100644 index 0000000..ec1d75b --- /dev/null +++ b/VOCdevkit1/VOC2007/ImageSets/Segmentation/trainval.txt @@ -0,0 +1,2339 @@ +00001 +00002 +00003 +00004 +00005 +00006 +00007 +00008 +00009 +00010 +00011 +00012 +00013 +00014 +00015 +00016 +00017 +00018 +00019 +00020 +00021 +00022 +00023 +00024 +00025 +00026 +00027 +00028 +00029 +00030 +00031 +00032 +00033 +00034 +00035 +00036 +00037 +00038 +00039 +00040 +00041 +00042 +00043 +00044 +00045 +00046 +00047 +00048 +00049 +00050 +00051 +00052 +00053 +00054 +00055 +00056 +00057 +00058 +00059 +00060 +00061 +00062 +00063 +00064 +00065 +00066 +00067 +00068 +00069 +00070 +00071 +00072 +00073 +00074 +00075 +00076 +00077 +00078 +00079 +00080 +00081 +00082 +00083 +00084 +00085 +00086 +00087 +00088 +00089 +00090 +00091 +00092 +00093 +00094 +00095 +00096 +00097 +00098 +00099 +00100 +00101 +00102 +00103 +00104 +00105 +00106 +00107 +00108 +00109 +00110 +00111 +00112 +00113 +00114 +00115 +00116 +00117 +00118 +00119 +00120 +00121 +00122 +00123 +00124 +00125 +00126 +00127 +00128 +00129 +00130 +00131 +00132 +00133 +00134 +00135 +00136 +00137 +00138 +00139 +00140 +00141 +00142 +00143 +00144 +00145 +00146 +00147 +00148 +00149 +00150 +00151 +00152 +00153 +00154 +00155 +00156 +00157 +00158 +00159 +00160 +00161 +00162 +00163 +00164 +00165 +00166 +00167 +00168 +00169 +00170 +00171 +00172 +00173 +00174 +00175 +00176 +00177 +00178 +00179 +00180 +00181 +00182 +00183 +00184 +00185 +00186 +00187 +00188 +00189 +00190 +00191 +00192 +00193 +00194 +00195 +00196 +00197 +00198 +00199 +00200 +00201 +00202 +00203 +00204 +00205 +00206 +00207 +00208 +00209 +00210 +00211 +00212 +00213 +00214 +00215 +00216 +00217 +00218 +00219 +00220 +00221 +00222 +00223 +00224 +00225 +00226 +00227 +00228 +00229 +00230 +00231 +00232 +00233 +00234 +00235 +00236 +00237 +00238 +00239 +00240 +00241 +00242 +00243 +00244 +00245 +00246 +00247 +00248 +00249 +00250 +00251 +00252 +00253 +00254 +00255 +00256 +00257 +00258 +00259 +00260 +00261 +00262 +00263 +00264 +00265 +00266 +00267 +00268 +00269 +00270 +00271 +00272 +00273 +00274 +00275 +00276 +00277 +00278 +00279 +00280 +00281 +00282 +00283 +00284 +00285 +00286 +00287 +00288 +00289 +00290 +00291 +00292 +00293 +00294 +00295 +00296 +00297 +00298 +00299 +00300 +00301 +00302 +00303 +00304 +00305 +00306 +00307 +00308 +00309 +00310 +00311 +00312 +00313 +00314 +00315 +00316 +00317 +00318 +00319 +00320 +00321 +00322 +00323 +00324 +00325 +00326 +00327 +00328 +00329 +00330 +00331 +00332 +00333 +00334 +00335 +00336 +00337 +00338 +00339 +00340 +00341 +00342 +00343 +00344 +00345 +00346 +00347 +00348 +00349 +00350 +00351 +00352 +00353 +00354 +00355 +00356 +00357 +00358 +00359 +00360 +00361 +00362 +00363 +00364 +00365 +00366 +00367 +00368 +00369 +00370 +00371 +00372 +00373 +00374 +00375 +00376 +00377 +00378 +00379 +00380 +00381 +00382 +00383 +00384 +00385 +00386 +00387 +00388 +00389 +00390 +00391 +00392 +00393 +00394 +00395 +00396 +00397 +00398 +00399 +00400 +00401 +00402 +00403 +00404 +00405 +00406 +00407 +00408 +00409 +00410 +00411 +00412 +00413 +00414 +00415 +00416 +00417 +00418 +00419 +00420 +00421 +00422 +00423 +00424 +00425 +00426 +00427 +00428 +00429 +00430 +00431 +00432 +00433 +00434 +00435 +00436 +00437 +00438 +00439 +00440 +00441 +00442 +00443 +00444 +00445 +00446 +00447 +00448 +00449 +00450 +00451 +00452 +00453 +00454 +00455 +00456 +00457 +00458 +00459 +00460 +00461 +00462 +00463 +00464 +00465 +00466 +00467 +00468 +00469 +00470 +00471 +00472 +00473 +00474 +00475 +00476 +00477 +00478 +00479 +00480 +00481 +00482 +00483 +00484 +00485 +00486 +00487 +00488 +00489 +00490 +00491 +00492 +00493 +00494 +00495 +00496 +00497 +00498 +00499 +00500 +00501 +00502 +00503 +00504 +00505 +00506 +00507 +00508 +00509 +00510 +00511 +00512 +00513 +00514 +00515 +00516 +00517 +00518 +00519 +00520 +00521 +00522 +00523 +00524 +00525 +00526 +00527 +00528 +00529 +00530 +00531 +00532 +00533 +00534 +00535 +00536 +00537 +00538 +00539 +00540 +00541 +00542 +00543 +00544 +00545 +00546 +00547 +00548 +00549 +00550 +00551 +00552 +00553 +00554 +00555 +00556 +00557 +00558 +00559 +00560 +00561 +00562 +00563 +00564 +00565 +00566 +00567 +00568 +00569 +00570 +00571 +00572 +00573 +00574 +00575 +00576 +00577 +00578 +00579 +00580 +00581 +00582 +00583 +00584 +00585 +00586 +00587 +00588 +00589 +00590 +00591 +00592 +00593 +00594 +00595 +00596 +00597 +00598 +00599 +00600 +00601 +00602 +00603 +00604 +00605 +00606 +00607 +00608 +00609 +00610 +00611 +00612 +00613 +00614 +00615 +00616 +00617 +00618 +00619 +00620 +00621 +00622 +00623 +00624 +00625 +00626 +00627 +00628 +00629 +00630 +00631 +00632 +00633 +00634 +00635 +00636 +00637 +00638 +00639 +00640 +00641 +00642 +00643 +00644 +00645 +00646 +00647 +00648 +00649 +00650 +00651 +00652 +00653 +00654 +00655 +00656 +00657 +00658 +00659 +00660 +00661 +00662 +00663 +00664 +00665 +00666 +00667 +00668 +00669 +00670 +00671 +00672 +00673 +00674 +00675 +00676 +00677 +00678 +00679 +00680 +00681 +00682 +00683 +00684 +00685 +00686 +00687 +00688 +00689 +00690 +00691 +00692 +00693 +00694 +00695 +00696 +00697 +00698 +00699 +00700 +00701 +00702 +00703 +00704 +00705 +00706 +00707 +00708 +00709 +00710 +00711 +00712 +00713 +00714 +00715 +00716 +00717 +00718 +00719 +00720 +00721 +00722 +00723 +00724 +00725 +00726 +00727 +00728 +00729 +00730 +00731 +00732 +00733 +00734 +00735 +00736 +00737 +00738 +00739 +00740 +00741 +00742 +00743 +00744 +00745 +00746 +00747 +00748 +00749 +00750 +00751 +00752 +00753 +00754 +00755 +00756 +00757 +00758 +00759 +00760 +00761 +00762 +00763 +00764 +00765 +00766 +00767 +00768 +00769 +00770 +00771 +00772 +00773 +00774 +00775 +00776 +00777 +00778 +00779 +00780 +00781 +00782 +00783 +00784 +00785 +00786 +00787 +00788 +00789 +00790 +00791 +00792 +00793 +00794 +00795 +00796 +00797 +00798 +00799 +00800 +00801 +00802 +00803 +00804 +00805 +00806 +00807 +00808 +00809 +00810 +00811 +00812 +00813 +00814 +00815 +00816 +00817 +00818 +00819 +00820 +00821 +00822 +00823 +00824 +00825 +00826 +00827 +00828 +00829 +00830 +00831 +00832 +00833 +00834 +00835 +00836 +00837 +00838 +00839 +00840 +00841 +00842 +00843 +00844 +00845 +00846 +00847 +00848 +00849 +00850 +00851 +00852 +00853 +00854 +00855 +00856 +00857 +00858 +00859 +00860 +00861 +00862 +00863 +00864 +00865 +00866 +00867 +00868 +00869 +00870 +00871 +00872 +00873 +00874 +00875 +00876 +00877 +00878 +00879 +00880 +00881 +00882 +00883 +00884 +00885 +00886 +00887 +00888 +00889 +00890 +00891 +00892 +00893 +00894 +00895 +00896 +00897 +00898 +00899 +00900 +00901 +00902 +00903 +00904 +00905 +00906 +00907 +00908 +00909 +00910 +00911 +00912 +00913 +00914 +00915 +00916 +00917 +00918 +00919 +00920 +00921 +00922 +00923 +00924 +00925 +00926 +00927 +00928 +00929 +00930 +00931 +00932 +00933 +00934 +00935 +00936 +00937 +00938 +00939 +00940 +00941 +00942 +00943 +00944 +00945 +00946 +00947 +00948 +00949 +00950 +00951 +00952 +00953 +00954 +00955 +00956 +00957 +00958 +00959 +00960 +00961 +00962 +00963 +00964 +00965 +00966 +00967 +00968 +00969 +00970 +00971 +00972 +00973 +00974 +00975 +00976 +00977 +00978 +00979 +00980 +00981 +00982 +00983 +00984 +00985 +00986 +00987 +00988 +00989 +00990 +00991 +00992 +00993 +00994 +00995 +00996 +00997 +00998 +00999 +01000 +01001 +01002 +01003 +01004 +01005 +01006 +01007 +01008 +01009 +01010 +01011 +01012 +01013 +01014 +01015 +01016 +01017 +01018 +01019 +01020 +01021 +01022 +01023 +01024 +01025 +01026 +01027 +01028 +01029 +01030 +01031 +01032 +01033 +01034 +01035 +01036 +01037 +01038 +01039 +01040 +01041 +01042 +01043 +01044 +01045 +01046 +01047 +01048 +01049 +01050 +01051 +01052 +01053 +01054 +01055 +01056 +01057 +01058 +01059 +01060 +01061 +01062 +01063 +01064 +01065 +01066 +01067 +01068 +01069 +01070 +01071 +01072 +01073 +01074 +01075 +01076 +01077 +01078 +01079 +01080 +01081 +01082 +01083 +01084 +01085 +01086 +01087 +01088 +01089 +01090 +01091 +01092 +01093 +01094 +01095 +01096 +01097 +01098 +01099 +01100 +01101 +01102 +01103 +01104 +01105 +01106 +01107 +01108 +01109 +01110 +01111 +01112 +01113 +01114 +01115 +01116 +01117 +01118 +01119 +01120 +01121 +01122 +01123 +01124 +01125 +01126 +01127 +01128 +01129 +01130 +01131 +01132 +01133 +01134 +01135 +01136 +01137 +01138 +01139 +01140 +01141 +01142 +01143 +01144 +01145 +01146 +01147 +01148 +01149 +01150 +01151 +01152 +01153 +01154 +01155 +01156 +01157 +01158 +01159 +01160 +01161 +01162 +01163 +01164 +01165 +01166 +01167 +01168 +01169 +01170 +01171 +01172 +01173 +01174 +01175 +01176 +01177 +01178 +01179 +01180 +01181 +01182 +01183 +01184 +01185 +01186 +01187 +01188 +01189 +01190 +01191 +01192 +01193 +01194 +01195 +01196 +01197 +01198 +01199 +01200 +01201 +01202 +01203 +01204 +01205 +01206 +01207 +01208 +01209 +01210 +01211 +01212 +01213 +01214 +01215 +01216 +01217 +01218 +01219 +01220 +01221 +01222 +01223 +01224 +01225 +01226 +01227 +01228 +01229 +01230 +01231 +01232 +01233 +01234 +01235 +01236 +01237 +01238 +01239 +01240 +01241 +01242 +01243 +01244 +01245 +01246 +01247 +01248 +01249 +01250 +01251 +01252 +01253 +01254 +01255 +01256 +01257 +01258 +01259 +01260 +01261 +01262 +01263 +01264 +01265 +01266 +01267 +01268 +01269 +01270 +01271 +01272 +01273 +01274 +01275 +01276 +01277 +01278 +01279 +01280 +01281 +01282 +01283 +01284 +01285 +01286 +01287 +01288 +01289 +01290 +01291 +01292 +01293 +01294 +01295 +01296 +01297 +01298 +01299 +01300 +01301 +01302 +01303 +01304 +01305 +01306 +01307 +01308 +01309 +01310 +01311 +01312 +01313 +01314 +01315 +01316 +01317 +01318 +01319 +01320 +01321 +01322 +01323 +01324 +01325 +01326 +01327 +01328 +01329 +01330 +01331 +01332 +01333 +01334 +01335 +01336 +01337 +01338 +01339 +01340 +01341 +01342 +01343 +01344 +01345 +01346 +01347 +01348 +01349 +01350 +01351 +01352 +01353 +01354 +01355 +01356 +01357 +01358 +01359 +01360 +01361 +01362 +01363 +01364 +01365 +01366 +01367 +01368 +01369 +01370 +01371 +01372 +01373 +01374 +01375 +01376 +01377 +01378 +01379 +01380 +01381 +01382 +01383 +01384 +01385 +01386 +01387 +01388 +01389 +01390 +01391 +01392 +01393 +01394 +01395 +01396 +01397 +01398 +01399 +01400 +01401 +01402 +01403 +01404 +01405 +01406 +01407 +01408 +01409 +01410 +01411 +01412 +01413 +01414 +01415 +01416 +01417 +01418 +01419 +01420 +01421 +01422 +01423 +01424 +01425 +01426 +01427 +01428 +01429 +01430 +01431 +01432 +01433 +01434 +01435 +01436 +01437 +01438 +01439 +01440 +01441 +01442 +01443 +01444 +01445 +01446 +01447 +01448 +01449 +01450 +01451 +01452 +01453 +01454 +01455 +01456 +01457 +01458 +01459 +01460 +01461 +01462 +01463 +01464 +01465 +01466 +01467 +01468 +01469 +01470 +01471 +01472 +01473 +01474 +01475 +01476 +01477 +01478 +01479 +01480 +01481 +01482 +01483 +01484 +01485 +01486 +01487 +01488 +01489 +01490 +01491 +01492 +01493 +01494 +01495 +01496 +01497 +01498 +01499 +01500 +01501 +01502 +01503 +01504 +01505 +01506 +01507 +01508 +01509 +01510 +01511 +01512 +01513 +01514 +01515 +01516 +01517 +01518 +01519 +01520 +01521 +01522 +01523 +01524 +01525 +01526 +01527 +01528 +01529 +01530 +01531 +01532 +01533 +01534 +01535 +01536 +01537 +01538 +01539 +01540 +01541 +01542 +01543 +01544 +01545 +01546 +01547 +01548 +01549 +01550 +01551 +01552 +01553 +01554 +01555 +01556 +01557 +01558 +01559 +01560 +01561 +01562 +01563 +01564 +01565 +01566 +01567 +01568 +01569 +01570 +01571 +01572 +01573 +01574 +01575 +01576 +01577 +01578 +01579 +01580 +01581 +01582 +01583 +01584 +01585 +01586 +01587 +01588 +01589 +01590 +01591 +01592 +01593 +01594 +01595 +01596 +01597 +01598 +01599 +01600 +01601 +01602 +01603 +01604 +01605 +01606 +01607 +01608 +01609 +01610 +01611 +01612 +01613 +01614 +01615 +01616 +01617 +01618 +01619 +01620 +01621 +01622 +01623 +01624 +01625 +01626 +01627 +01628 +01629 +01630 +01631 +01632 +01633 +01634 +01635 +01636 +01637 +01638 +01639 +01640 +01641 +01642 +01643 +01644 +01645 +01646 +01647 +01648 +01649 +01650 +01651 +01652 +01653 +01654 +01655 +01656 +01657 +01658 +01659 +01660 +01661 +01662 +01663 +01664 +01665 +01666 +01667 +01668 +01669 +01670 +01671 +01672 +01673 +01674 +01675 +01676 +01677 +01678 +01679 +01680 +01681 +01682 +01683 +01684 +01685 +01686 +01687 +01688 +01689 +01690 +01691 +01692 +01693 +01694 +01695 +01696 +01697 +01698 +01699 +01700 +01701 +01702 +01703 +01704 +01705 +01706 +01707 +01708 +01709 +01710 +01711 +01712 +01713 +01714 +01715 +01716 +01717 +01718 +01719 +01720 +01721 +01722 +01723 +01724 +01725 +01726 +01727 +01728 +01729 +01730 +01731 +01732 +01733 +01734 +01735 +01736 +01737 +01738 +01739 +01740 +01741 +01742 +01743 +01744 +01745 +01746 +01747 +01748 +01749 +01750 +01751 +01752 +01753 +01754 +01755 +01756 +01757 +01758 +01759 +01760 +01761 +01762 +01763 +01764 +01765 +01766 +01767 +01768 +01769 +01770 +01771 +01772 +01773 +01774 +01775 +01776 +01777 +01778 +01779 +01780 +01781 +01782 +01783 +01784 +01785 +01786 +01787 +01788 +01789 +01790 +01791 +01792 +01793 +01794 +01795 +01796 +01797 +01798 +01799 +01800 +01801 +01802 +01803 +01804 +01805 +01806 +01807 +01808 +01809 +01810 +01811 +01812 +01813 +01814 +01815 +01816 +01817 +01818 +01819 +01820 +01821 +01822 +01823 +01824 +01825 +01826 +01827 +01828 +01829 +01830 +01831 +01832 +01833 +01834 +01835 +01836 +01837 +01838 +01839 +01840 +01841 +01842 +01843 +01844 +01845 +01846 +01847 +01848 +01849 +01850 +01851 +01852 +01853 +01854 +01855 +01856 +01857 +01858 +01859 +01860 +01861 +01862 +01863 +01864 +01865 +01866 +01867 +01868 +01869 +01870 +01871 +01872 +01873 +01874 +01875 +01876 +01877 +01878 +01879 +01880 +01881 +01882 +01883 +01884 +01885 +01886 +01887 +01888 +01889 +01890 +01891 +01892 +01893 +01894 +01895 +01896 +01897 +01898 +01899 +01900 +01901 +01902 +01903 +01904 +01905 +01906 +01907 +01908 +01909 +01910 +01911 +01912 +01913 +01914 +01915 +01916 +01917 +01918 +01919 +01920 +01921 +01922 +01923 +01924 +01925 +01926 +01927 +01928 +01929 +01930 +01931 +01932 +01933 +01934 +01935 +01936 +01937 +01938 +01939 +01940 +01941 +01942 +01943 +01944 +01945 +01946 +01947 +01948 +01949 +01950 +01951 +01952 +01953 +01954 +01955 +01956 +01957 +01958 +01959 +01960 +01961 +01962 +01963 +01964 +01965 +01966 +01967 +01968 +01969 +01970 +01971 +01972 +01973 +01974 +01975 +01976 +01977 +01978 +01979 +01980 +01981 +01982 +01983 +01984 +01985 +01986 +01987 +01988 +01989 +01990 +01991 +01992 +01993 +01994 +01995 +01996 +01997 +01998 +01999 +02000 +02001 +02002 +02003 +02004 +02005 +02006 +02007 +02008 +02009 +02010 +02011 +02012 +02013 +02014 +02015 +02016 +02017 +02018 +02019 +02020 +02021 +02022 +02023 +02024 +02025 +02026 +02027 +02028 +02029 +02030 +02031 +02032 +02033 +02034 +02035 +02036 +02037 +02038 +02039 +02040 +02041 +02042 +02043 +02044 +02045 +02046 +02047 +02048 +02049 +02050 +02051 +02052 +02053 +02054 +02055 +02056 +02057 +02058 +02059 +02060 +02061 +02062 +02063 +02064 +02065 +02066 +02067 +02068 +02069 +02070 +02071 +02072 +02073 +02074 +02075 +02076 +02077 +02078 +02079 +02080 +02081 +02082 +02083 +02084 +02085 +02086 +02087 +02088 +02089 +02090 +02091 +02092 +02093 +02094 +02095 +02096 +02097 +02098 +02099 +02100 +02101 +02102 +02103 +02104 +02105 +02106 +02107 +02108 +02109 +02110 +02111 +02112 +02113 +02114 +02115 +02116 +02117 +02118 +02119 +02120 +02121 +02122 +02123 +02124 +02125 +02126 +02127 +02128 +02129 +02130 +02131 +02132 +02133 +02134 +02135 +02136 +02137 +02138 +02139 +02140 +02141 +02142 +02143 +02144 +02145 +02146 +02147 +02148 +02149 +02150 +02151 +02152 +02153 +02154 +02155 +02156 +02157 +02158 +02159 +02160 +02161 +02162 +02163 +02164 +02165 +02166 +02167 +02168 +02169 +02170 +02171 +02172 +02173 +02174 +02175 +02176 +02177 +02178 +02179 +02180 +02181 +02182 +02183 +02184 +02185 +02186 +02187 +02188 +02189 +02190 +02191 +02192 +02193 +02194 +02195 +02196 +02197 +02198 +02199 +02200 +02201 +02202 +02203 +02204 +02205 +02206 +02207 +02208 +02209 +02210 +02211 +02212 +02213 +02214 +02215 +02216 +02217 +02218 +02219 +02220 +02221 +02222 +02223 +02224 +02225 +02226 +02227 +02228 +02229 +02230 +02231 +02232 +02233 +02234 +02235 +02236 +02237 +02238 +02239 +02240 +02241 +02242 +02243 +02244 +02245 +02246 +02247 +02248 +02249 +02250 +02251 +02252 +02253 +02254 +02255 +02256 +02257 +02258 +02259 +02260 +02261 +02262 +02263 +02264 +02265 +02266 +02267 +02268 +02269 +02270 +02271 +02272 +02273 +02274 +02275 +02276 +02277 +02278 +02279 +02280 +02281 +02282 +02283 +02284 +02285 +02286 +02287 +02288 +02289 +02290 +02291 +02292 +02293 +02294 +02295 +02296 +02297 +02298 +02299 +02300 +02301 +02302 +02303 +02304 +02305 +02306 +02307 +02308 +02309 +02310 +02311 +02312 +02313 +02314 +02315 +02316 +02317 +02318 +02319 +02320 +02321 +02322 +02323 +02324 +02325 +02326 +02327 +02328 +02329 +02330 +02331 +02332 +02333 +02334 +02335 +02336 +02337 +02338 +02339 diff --git a/VOCdevkit1/VOC2007/ImageSets/Segmentation/val.txt b/VOCdevkit1/VOC2007/ImageSets/Segmentation/val.txt new file mode 100644 index 0000000..d206f87 --- /dev/null +++ b/VOCdevkit1/VOC2007/ImageSets/Segmentation/val.txt @@ -0,0 +1,234 @@ +00009 +00017 +00027 +00033 +00042 +00051 +00070 +00082 +00089 +00092 +00102 +00111 +00117 +00130 +00131 +00133 +00137 +00138 +00145 +00162 +00171 +00185 +00189 +00191 +00203 +00208 +00211 +00218 +00223 +00233 +00234 +00249 +00255 +00263 +00278 +00297 +00319 +00320 +00322 +00327 +00329 +00373 +00385 +00395 +00411 +00417 +00422 +00429 +00438 +00453 +00457 +00470 +00493 +00497 +00498 +00516 +00530 +00588 +00595 +00601 +00604 +00616 +00635 +00636 +00669 +00696 +00701 +00709 +00716 +00724 +00732 +00741 +00748 +00756 +00757 +00780 +00789 +00812 +00837 +00850 +00858 +00867 +00869 +00876 +00879 +00884 +00887 +00901 +00906 +00910 +00911 +00926 +00965 +00979 +00986 +00987 +00991 +01001 +01014 +01026 +01035 +01048 +01050 +01069 +01076 +01083 +01092 +01093 +01113 +01124 +01126 +01131 +01140 +01142 +01159 +01161 +01168 +01178 +01185 +01190 +01193 +01203 +01219 +01224 +01254 +01258 +01259 +01261 +01267 +01271 +01279 +01284 +01291 +01313 +01314 +01320 +01324 +01325 +01352 +01364 +01396 +01413 +01421 +01423 +01434 +01436 +01447 +01454 +01465 +01469 +01472 +01480 +01489 +01495 +01511 +01517 +01529 +01534 +01541 +01545 +01553 +01554 +01595 +01597 +01618 +01625 +01629 +01630 +01631 +01640 +01641 +01670 +01684 +01691 +01750 +01757 +01773 +01777 +01779 +01782 +01787 +01788 +01793 +01795 +01798 +01816 +01819 +01821 +01833 +01862 +01881 +01892 +01902 +01938 +01939 +01940 +01948 +01950 +01955 +01964 +01990 +02014 +02019 +02049 +02061 +02066 +02068 +02082 +02084 +02090 +02091 +02093 +02095 +02118 +02121 +02133 +02157 +02161 +02173 +02183 +02200 +02203 +02204 +02212 +02221 +02222 +02229 +02230 +02241 +02253 +02263 +02279 +02319 +02332 diff --git a/deeplab.py b/deeplab.py new file mode 100644 index 0000000..d0cf30d --- /dev/null +++ b/deeplab.py @@ -0,0 +1,397 @@ +import colorsys +import copy +import time + +import cv2 +import numpy as np +import torch +import torch.nn.functional as F +from PIL import Image +from torch import nn + +from nets.deeplabv3_plus import DeepLab +from utils.utils import cvtColor, preprocess_input, resize_image, show_config + + +#-----------------------------------------------------------------------------------# +# 使用自己训练好的模型预测需要修改3个参数 +# model_path、backbone和num_classes都需要修改! +# 如果出现shape不匹配,一定要注意训练时的model_path、backbone和num_classes的修改 +#-----------------------------------------------------------------------------------# +class DeeplabV3(object): + _defaults = { + #-------------------------------------------------------------------# + # model_path指向logs文件夹下的权值文件 + # 训练好后logs文件夹下存在多个权值文件,选择验证集损失较低的即可。 + # 验证集损失较低不代表miou较高,仅代表该权值在验证集上泛化性能较好。 + #-------------------------------------------------------------------# + "model_path" : 'model_data/last_epoch_weights1.pth', + #----------------------------------------# + # 所需要区分的类的个数+1 + #----------------------------------------# + "num_classes" : 46, + #----------------------------------------# + # 所使用的的主干网络: + # mobilenet + # xception + #----------------------------------------# + "backbone" : "mobilenet", + #----------------------------------------# + # 输入图片的大小 + #----------------------------------------# + "input_shape" : [1024, 1042], + #----------------------------------------# + # 下采样的倍数,一般可选的为8和16 + # 与训练时设置的一样即可 + #----------------------------------------# + "downsample_factor" : 16, + #-------------------------------------------------# + # mix_type参数用于控制检测结果的可视化方式 + # + # mix_type = 0的时候代表原图与生成的图进行混合 + # mix_type = 1的时候代表仅保留生成的图 + # mix_type = 2的时候代表仅扣去背景,仅保留原图中的目标 + #-------------------------------------------------# + "mix_type" : 0, + #-------------------------------# + # 是否使用Cuda + # 没有GPU可以设置成False + #-------------------------------# + "cuda" : True, + } + + #---------------------------------------------------# + # 初始化Deeplab + #---------------------------------------------------# + def __init__(self, **kwargs): + self.__dict__.update(self._defaults) + for name, value in kwargs.items(): + setattr(self, name, value) + #---------------------------------------------------# + # 画框设置不同的颜色 + #---------------------------------------------------# + if self.num_classes <= 46: + self.colors = [ (0, 0, 0), + (128, 0, 0), + (0, 128, 0), + (128, 128, 0), + (0, 0, 128), + (128, 0, 128), + (0, 128, 128), + (128, 128, 128), + (64, 0, 0), + (192, 0, 0), + (64, 128, 0), + (192, 128, 0), + (64, 0, 128), + (192, 0, 128), + (64, 128, 128), + (192, 128, 128), + (0, 64, 0), + (128, 64, 0), + (0, 192, 0), + (128, 192, 0), + (0, 64, 128), + (128, 64, 12), + (0, 0, 142), + (119, 11, 32), + (244,164,140), + (188,143,143), + (64,224,205), + (127,255,0), + (199,97,20), + (189,252,201), + (0,255,127), + (160,32,240), + (138,42,226), + (255,97,0), + (255,215,0), + (255,128,0), + (189,252,201), + (240,255,240), + (0, 130, 180), + (152, 251, 152), + (107, 142, 35), + (153, 153, 153), + (190, 153, 153), + (250, 170, 30), + (220, 220, 0), + (107, 142, 35), + ] + + else: + hsv_tuples = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)] + self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) + self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors)) + #---------------------------------------------------# + # 获得模型 + #---------------------------------------------------# + self.generate() + + show_config(**self._defaults) + + #---------------------------------------------------# + # 获得所有的分类 + #---------------------------------------------------# + def generate(self, onnx=False): + #-------------------------------# + # 载入模型与权值 + #-------------------------------# + self.net = DeepLab(num_classes=self.num_classes, backbone=self.backbone, downsample_factor=self.downsample_factor, pretrained=False) + + device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + self.net.load_state_dict(torch.load(self.model_path, map_location=device)) + self.net = self.net.eval() + print('{} model, and classes loaded.'.format(self.model_path)) + if not onnx: + if self.cuda: + self.net = nn.DataParallel(self.net) + self.net = self.net.cuda() + + #---------------------------------------------------# + # 检测图片 + #---------------------------------------------------# + def detect_image(self, image, count=False, name_classes=None): + #---------------------------------------------------------# + # 在这里将图像转换成RGB图像,防止灰度图在预测时报错。 + # 代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB + #---------------------------------------------------------# + image = cvtColor(image) + #---------------------------------------------------# + # 对输入图像进行一个备份,后面用于绘图 + #---------------------------------------------------# + old_img = copy.deepcopy(image) + orininal_h = np.array(image).shape[0] + orininal_w = np.array(image).shape[1] + #---------------------------------------------------------# + # 给图像增加灰条,实现不失真的resize + # 也可以直接resize进行识别 + #---------------------------------------------------------# + image_data, nw, nh = resize_image(image, (self.input_shape[1],self.input_shape[0])) + #---------------------------------------------------------# + # 添加上batch_size维度 + #---------------------------------------------------------# + image_data = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, np.float32)), (2, 0, 1)), 0) + + with torch.no_grad(): + images = torch.from_numpy(image_data) + if self.cuda: + images = images.cuda() + + #---------------------------------------------------# + # 图片传入网络进行预测 + #---------------------------------------------------# + pr = self.net(images)[0] + #---------------------------------------------------# + # 取出每一个像素点的种类 + #---------------------------------------------------# + pr = F.softmax(pr.permute(1,2,0),dim = -1).cpu().numpy() + #--------------------------------------# + # 将灰条部分截取掉 + #--------------------------------------# + pr = pr[int((self.input_shape[0] - nh) // 2) : int((self.input_shape[0] - nh) // 2 + nh), + int((self.input_shape[1] - nw) // 2) : int((self.input_shape[1] - nw) // 2 + nw)] + #---------------------------------------------------# + # 进行图片的resize + #---------------------------------------------------# + pr = cv2.resize(pr, (orininal_w, orininal_h), interpolation = cv2.INTER_LINEAR) + #---------------------------------------------------# + # 取出每一个像素点的种类 + #---------------------------------------------------# + pr = pr.argmax(axis=-1) + + #---------------------------------------------------------# + # 计数 + #---------------------------------------------------------# + if count: + classes_nums = np.zeros([self.num_classes]) + total_points_num = orininal_h * orininal_w + print('-' * 63) + print("|%25s | %15s | %15s|"%("Key", "Value", "Ratio")) + print('-' * 63) + for i in range(self.num_classes): + num = np.sum(pr == i) + ratio = num / total_points_num * 100 + if num > 0: + print("|%25s | %15s | %14.2f%%|"%(str(name_classes[i]), str(num), ratio)) + print('-' * 63) + classes_nums[i] = num + print("classes_nums:", classes_nums) + + if self.mix_type == 0: + # seg_img = np.zeros((np.shape(pr)[0], np.shape(pr)[1], 3)) + # for c in range(self.num_classes): + # seg_img[:, :, 0] += ((pr[:, :] == c ) * self.colors[c][0]).astype('uint8') + # seg_img[:, :, 1] += ((pr[:, :] == c ) * self.colors[c][1]).astype('uint8') + # seg_img[:, :, 2] += ((pr[:, :] == c ) * self.colors[c][2]).astype('uint8') + seg_img = np.reshape(np.array(self.colors, np.uint8)[np.reshape(pr, [-1])], [orininal_h, orininal_w, -1]) + #------------------------------------------------# + # 将新图片转换成Image的形式 + #------------------------------------------------# + image = Image.fromarray(np.uint8(seg_img)) + #------------------------------------------------# + # 将新图与原图及进行混合 + #------------------------------------------------# + image = Image.blend(old_img, image, 0.7) + + elif self.mix_type == 1: + # seg_img = np.zeros((np.shape(pr)[0], np.shape(pr)[1], 3)) + # for c in range(self.num_classes): + # seg_img[:, :, 0] += ((pr[:, :] == c ) * self.colors[c][0]).astype('uint8') + # seg_img[:, :, 1] += ((pr[:, :] == c ) * self.colors[c][1]).astype('uint8') + # seg_img[:, :, 2] += ((pr[:, :] == c ) * self.colors[c][2]).astype('uint8') + seg_img = np.reshape(np.array(self.colors, np.uint8)[np.reshape(pr, [-1])], [orininal_h, orininal_w, -1]) + #------------------------------------------------# + # 将新图片转换成Image的形式 + #------------------------------------------------# + image = Image.fromarray(np.uint8(seg_img)) + + elif self.mix_type == 2: + seg_img = (np.expand_dims(pr != 0, -1) * np.array(old_img, np.float32)).astype('uint8') + #------------------------------------------------# + # 将新图片转换成Image的形式 + #------------------------------------------------# + image = Image.fromarray(np.uint8(seg_img)) + + return image + + def get_FPS(self, image, test_interval): + #---------------------------------------------------------# + # 在这里将图像转换成RGB图像,防止灰度图在预测时报错。 + # 代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB + #---------------------------------------------------------# + image = cvtColor(image) + #---------------------------------------------------------# + # 给图像增加灰条,实现不失真的resize + # 也可以直接resize进行识别 + #---------------------------------------------------------# + image_data, nw, nh = resize_image(image, (self.input_shape[1],self.input_shape[0])) + #---------------------------------------------------------# + # 添加上batch_size维度 + #---------------------------------------------------------# + image_data = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, np.float32)), (2, 0, 1)), 0) + + with torch.no_grad(): + images = torch.from_numpy(image_data) + if self.cuda: + images = images.cuda() + + #---------------------------------------------------# + # 图片传入网络进行预测 + #---------------------------------------------------# + pr = self.net(images)[0] + #---------------------------------------------------# + # 取出每一个像素点的种类 + #---------------------------------------------------# + pr = F.softmax(pr.permute(1,2,0),dim = -1).cpu().numpy().argmax(axis=-1) + #--------------------------------------# + # 将灰条部分截取掉 + #--------------------------------------# + pr = pr[int((self.input_shape[0] - nh) // 2) : int((self.input_shape[0] - nh) // 2 + nh), + int((self.input_shape[1] - nw) // 2) : int((self.input_shape[1] - nw) // 2 + nw)] + + t1 = time.time() + for _ in range(test_interval): + with torch.no_grad(): + #---------------------------------------------------# + # 图片传入网络进行预测 + #---------------------------------------------------# + pr = self.net(images)[0] + #---------------------------------------------------# + # 取出每一个像素点的种类 + #---------------------------------------------------# + pr = F.softmax(pr.permute(1,2,0),dim = -1).cpu().numpy().argmax(axis=-1) + #--------------------------------------# + # 将灰条部分截取掉 + #--------------------------------------# + pr = pr[int((self.input_shape[0] - nh) // 2) : int((self.input_shape[0] - nh) // 2 + nh), + int((self.input_shape[1] - nw) // 2) : int((self.input_shape[1] - nw) // 2 + nw)] + t2 = time.time() + tact_time = (t2 - t1) / test_interval + return tact_time + + def convert_to_onnx(self, simplify, model_path): + import onnx + self.generate(onnx=True) + + im = torch.zeros(1, 3, *self.input_shape).to('cpu') # image size(1, 3, 512, 512) BCHW + input_layer_names = ["images"] + output_layer_names = ["output"] + + # Export the model + print(f'Starting export with onnx {onnx.__version__}.') + torch.onnx.export(self.net, + im, + f = model_path, + verbose = False, + opset_version = 12, + training = torch.onnx.TrainingMode.EVAL, + do_constant_folding = True, + input_names = input_layer_names, + output_names = output_layer_names, + dynamic_axes = None) + + # Checks + model_onnx = onnx.load(model_path) # load onnx model + onnx.checker.check_model(model_onnx) # check onnx model + + # Simplify onnx + if simplify: + import onnxsim + print(f'Simplifying with onnx-simplifier {onnxsim.__version__}.') + model_onnx, check = onnxsim.simplify( + model_onnx, + dynamic_input_shape=False, + input_shapes=None) + assert check, 'assert check failed' + onnx.save(model_onnx, model_path) + + print('Onnx model save as {}'.format(model_path)) + + def get_miou_png(self, image): + #---------------------------------------------------------# + # 在这里将图像转换成RGB图像,防止灰度图在预测时报错。 + # 代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB + #---------------------------------------------------------# + image = cvtColor(image) + orininal_h = np.array(image).shape[0] + orininal_w = np.array(image).shape[1] + #---------------------------------------------------------# + # 给图像增加灰条,实现不失真的resize + # 也可以直接resize进行识别 + #---------------------------------------------------------# + image_data, nw, nh = resize_image(image, (self.input_shape[1],self.input_shape[0])) + #---------------------------------------------------------# + # 添加上batch_size维度 + #---------------------------------------------------------# + image_data = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, np.float32)), (2, 0, 1)), 0) + + with torch.no_grad(): + images = torch.from_numpy(image_data) + if self.cuda: + images = images.cuda() + + #---------------------------------------------------# + # 图片传入网络进行预测 + #---------------------------------------------------# + pr = self.net(images)[0] + #---------------------------------------------------# + # 取出每一个像素点的种类 + #---------------------------------------------------# + pr = F.softmax(pr.permute(1,2,0),dim = -1).cpu().numpy() + #--------------------------------------# + # 将灰条部分截取掉 + #--------------------------------------# + pr = pr[int((self.input_shape[0] - nh) // 2) : int((self.input_shape[0] - nh) // 2 + nh), + int((self.input_shape[1] - nw) // 2) : int((self.input_shape[1] - nw) // 2 + nw)] + #---------------------------------------------------# + # 进行图片的resize + #---------------------------------------------------# + pr = cv2.resize(pr, (orininal_w, orininal_h), interpolation = cv2.INTER_LINEAR) + #---------------------------------------------------# + # 取出每一个像素点的种类 + #---------------------------------------------------# + pr = pr.argmax(axis=-1) + + image = Image.fromarray(np.uint8(pr)) + return image diff --git a/get_miou.py b/get_miou.py new file mode 100644 index 0000000..cf476ae --- /dev/null +++ b/get_miou.py @@ -0,0 +1,62 @@ +import os + +from PIL import Image +from tqdm import tqdm + +from deeplab import DeeplabV3 +from utils.utils_metrics import compute_mIoU, show_results + +''' +进行指标评估需要注意以下几点: +1、该文件生成的图为灰度图,因为值比较小,按照PNG形式的图看是没有显示效果的,所以看到近似全黑的图是正常的。 +2、该文件计算的是验证集的miou,当前该库将测试集当作验证集使用,不单独划分测试集 +''' +if __name__ == "__main__": + #---------------------------------------------------------------------------# + # miou_mode用于指定该文件运行时计算的内容 + # miou_mode为0代表整个miou计算流程,包括获得预测结果、计算miou。 + # miou_mode为1代表仅仅获得预测结果。 + # miou_mode为2代表仅仅计算miou。 + #---------------------------------------------------------------------------# + miou_mode = 0 + #------------------------------# + # 分类个数+1、如2+1 + #------------------------------# + num_classes = 47 + #--------------------------------------------# + # 区分的种类,和json_to_dataset里面的一样 + #--------------------------------------------# + name_classes = ["_background_", "pl5", "pl20", "pl30", "pl40", "pl50", "pl60", "pl70", "pl80", "pl100", "pl120", "pm20", "pm55","pr40","p11", "pn", "pne", "p26", "i2", "i4", "i5", "ip", "il60", "il80", "il100", "p5", "p10", "p23", "p3", "pg", "p19", "p12", "p6", "p27", "ph4", "ph4.5", "ph5", "pm30", "w55", "w59", "w13", "w57", "w32", "wo", "io", "po", "indicative"] + # name_classes = ["_background_","cat","dog"] + #-------------------------------------------------------# + # 指向VOC数据集所在的文件夹 + # 默认指向根目录下的VOC数据集 + #-------------------------------------------------------# + VOCdevkit_path = 'VOCdevkit' + + image_ids = open(os.path.join(VOCdevkit_path, "VOC2007/ImageSets/Segmentation/val.txt"),'r').read().splitlines() + gt_dir = os.path.join(VOCdevkit_path, "VOC2007/SegmentationClass/") + miou_out_path = "miou_out" + pred_dir = os.path.join(miou_out_path, 'detection-results') + + if miou_mode == 0 or miou_mode == 1: + if not os.path.exists(pred_dir): + os.makedirs(pred_dir) + + print("Load model.") + deeplab = DeeplabV3() + print("Load model done.") + + print("Get predict result.") + for image_id in tqdm(image_ids): + image_path = os.path.join(VOCdevkit_path, "VOC2007/JPEGImages/"+image_id+".jpg") + image = Image.open(image_path) + image = deeplab.get_miou_png(image) + image.save(os.path.join(pred_dir, image_id + ".png")) + print("Get predict result done.") + + if miou_mode == 0 or miou_mode == 2: + print("Get miou.") + hist, IoUs, PA_Recall, Precision = compute_mIoU(gt_dir, pred_dir, image_ids, num_classes, name_classes) # 执行计算mIoU的函数 + print("Get miou done.") + show_results(miou_out_path, hist, IoUs, PA_Recall, Precision, name_classes) \ No newline at end of file diff --git a/json_to_dataset.py b/json_to_dataset.py new file mode 100644 index 0000000..4b84bc0 --- /dev/null +++ b/json_to_dataset.py @@ -0,0 +1,73 @@ +import base64 +import json +import os +import os.path as osp + +import numpy as np +import PIL.Image +from labelme import utils + +''' +制作自己的语义分割数据集需要注意以下几点: +1、我使用的labelme版本是3.16.7,建议使用该版本的labelme,有些版本的labelme会发生错误, + 具体错误为:Too many dimensions: 3 > 2 + 安装方式为命令行pip install labelme==3.16.7 +2、此处生成的标签图是8位彩色图,与视频中看起来的数据集格式不太一样。 + 虽然看起来是彩图,但事实上只有8位,此时每个像素点的值就是这个像素点所属的种类。 + 所以其实和视频中VOC数据集的格式一样。因此这样制作出来的数据集是可以正常使用的。也是正常的。 +''' +if __name__ == '__main__': + jpgs_path = "datasets/JPEGImages" + pngs_path = "datasets/SegmentationClass" + # classes = ["_background_", "pl5", "pl20", "pl30", "pl40", "pl50", "pl60", "pl70", "pl80", "pl100", "pl120", "pm20", "pm55","pr40","p11", "pn", "pne", "p26", "i2", "i4", "i5", "ip", "il60", "il80", "il100", "p5", "p10", "p23", "p3", "pg", "p19", "p12", "p6", "p27", "ph4", "ph4.5", "ph5", "pm30", "w55", "w59", "w13", "w57", "w32", "wo", "io", "po", "indicative"] + # classes = ["_background_","cat","dog"] + classes = ["_background_", "Historical Village", "Hydraulic Facilities", "Historical Buildings", "Green", "Sky", + "Water", "Bare Land", + "Infrastructure", "Park Related", "Enclosure", "Garbage and Debris", "Electric Poles", + "Modern Architecture", "Hard Surface", + "Human Activities", "Identification", "Water Pollution"] + count = os.listdir("./datasets/before/") + for i in range(0, len(count)): + path = os.path.join("./datasets/before", count[i]) + + if os.path.isfile(path) and path.endswith('json'): + data = json.load(open(path)) + + if data['imageData']: + imageData = data['imageData'] + else: + imagePath = os.path.join(os.path.dirname(path), data['imagePath']) + with open(imagePath, 'rb') as f: + imageData = f.read() + imageData = base64.b64encode(imageData).decode('utf-8') + + img = utils.img_b64_to_arr(imageData) + label_name_to_value = {'_background_': 0} + for shape in data['shapes']: + label_name = shape['label'] + if label_name in label_name_to_value: + label_value = label_name_to_value[label_name] + else: + label_value = len(label_name_to_value) + label_name_to_value[label_name] = label_value + + # label_values must be dense + label_values, label_names = [], [] + for ln, lv in sorted(label_name_to_value.items(), key=lambda x: x[1]): + label_values.append(lv) + label_names.append(ln) + assert label_values == list(range(len(label_values))) + + lbl = utils.shapes_to_label(img.shape, data['shapes'], label_name_to_value) + + + PIL.Image.fromarray(img).save(osp.join(jpgs_path, count[i].split(".")[0]+'.jpg')) + + new = np.zeros([np.shape(img)[0],np.shape(img)[1]]) + for name in label_names: + index_json = label_names.index(name) + index_all = classes.index(name) + new = new + index_all*(np.array(lbl) == index_json) + + utils.lblsave(osp.join(pngs_path, count[i].split(".")[0]+'.png'), new) + print('Saved ' + count[i].split(".")[0] + '.jpg and ' + count[i].split(".")[0] + '.png') diff --git a/logs/loss_2023_04_16_14_40_55/epoch_miou.txt b/logs/loss_2023_04_16_14_40_55/epoch_miou.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/logs/loss_2023_04_16_14_40_55/epoch_miou.txt @@ -0,0 +1 @@ +0 diff --git a/logs/loss_2023_04_16_15_03_17/epoch_miou.txt b/logs/loss_2023_04_16_15_03_17/epoch_miou.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/logs/loss_2023_04_16_15_03_17/epoch_miou.txt @@ -0,0 +1 @@ +0 diff --git a/logs/loss_2023_05_05_11_03_08/epoch_miou.txt b/logs/loss_2023_05_05_11_03_08/epoch_miou.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/logs/loss_2023_05_05_11_03_08/epoch_miou.txt @@ -0,0 +1 @@ +0 diff --git a/logs/loss_2023_05_06_10_18_49/epoch_miou.txt b/logs/loss_2023_05_06_10_18_49/epoch_miou.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/logs/loss_2023_05_06_10_18_49/epoch_miou.txt @@ -0,0 +1 @@ +0 diff --git a/logs/loss_2023_05_06_10_21_47/epoch_miou.txt b/logs/loss_2023_05_06_10_21_47/epoch_miou.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/logs/loss_2023_05_06_10_21_47/epoch_miou.txt @@ -0,0 +1 @@ +0 diff --git a/logs/loss_2023_05_06_10_22_54/epoch_miou.txt b/logs/loss_2023_05_06_10_22_54/epoch_miou.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/logs/loss_2023_05_06_10_22_54/epoch_miou.txt @@ -0,0 +1 @@ +0 diff --git a/logs/loss_2023_05_06_10_23_46/epoch_loss.txt b/logs/loss_2023_05_06_10_23_46/epoch_loss.txt new file mode 100644 index 0000000..a18b29a --- /dev/null +++ b/logs/loss_2023_05_06_10_23_46/epoch_loss.txt @@ -0,0 +1,300 @@ +0.6197003625021688 +0.039477425878832094 +0.021815583949108316 +0.017008269736862 +0.014811055252271856 +0.013597223319878143 +0.012746048395383834 +0.012807543290346975 +0.012045896844938397 +0.010721664282603068 +0.010831192051265884 +0.011085463909984711 +0.009701294567747602 +0.010043841666726134 +0.00960726743146154 +0.009713078478967395 +0.009528612792350268 +0.009614691838530023 +0.008979973745330489 +0.009033944955301035 +0.008963118615372552 +0.008663091998451704 +0.008887246418425622 +0.008487651522090736 +0.008931122144361951 +0.007925359707966645 +0.008151701880360426 +0.008162347928748356 +0.00812305118924028 +0.008164252225352104 +0.008294204591699087 +0.007984961868182334 +0.00793478730888931 +0.008040947269492503 +0.00789135160055649 +0.007976733407487264 +0.00797758857762094 +0.008167726175413385 +0.007854951135265312 +0.008030839253769401 +0.007845229877357304 +0.008030030800263351 +0.007429942826407575 +0.007581196240230885 +0.0074647688961980915 +0.00780516351261548 +0.007857467600289634 +0.007772660604855152 +0.007275923568677483 +0.0074134994388519015 +0.007543342298661632 +0.00698753386273796 +0.0072625635161698435 +0.007725435333457481 +0.0073359590019329585 +0.007243735706008716 +0.0073306868472508375 +0.007583740644047157 +0.007169632133567429 +0.0070728671461690795 +0.007072581034932753 +0.007413626354570171 +0.007152383307795334 +0.007606737600274724 +0.007405726957496099 +0.006981491556075957 +0.006948879280768533 +0.006812293091328538 +0.0068052408720115235 +0.007279976445494552 +0.007174421689196883 +0.006878163205626148 +0.00700332477561308 +0.006634817236924664 +0.006955325338784109 +0.006905557131819682 +0.007238527825420914 +0.007335566426007636 +0.007129638260602243 +0.007070887029107249 +0.00687503706913498 +0.0070434755843996775 +0.006843726146265071 +0.007250396561801802 +0.00696170583549717 +0.006585396268093399 +0.0068075423090835545 +0.007294489346982862 +0.006918249383104873 +0.00639713606566984 +0.00699184124159694 +0.006749085086918375 +0.007148223049795262 +0.006741083779517462 +0.006767992923981705 +0.006374543556189271 +0.006559330907636127 +0.006634276142492607 +0.006561781389961026 +0.006353535225965479 +0.006518312053092731 +0.00663000394097979 +0.006518688552194902 +0.006821648500144425 +0.006526681042131136 +0.006539963696598157 +0.006351140145825031 +0.006738816389279306 +0.006629471270944711 +0.006451651816054498 +0.006166263602535902 +0.006461152977959922 +0.006396989321943458 +0.006274158426758766 +0.006374230302477206 +0.0063116708989503015 +0.006304902248987538 +0.006369911974823605 +0.006575406290203007 +0.006191673439382062 +0.006340822853863296 +0.006553388611031094 +0.006342110292677955 +0.006331088343642283 +0.006240377007866767 +0.006275904750426657 +0.006147952992230045 +0.006477536654857628 +0.006378386527770854 +0.006447824759067111 +0.006113713274323895 +0.006700844290584836 +0.006149753932610948 +0.006174205401345748 +0.0064192439733558255 +0.006562992551951836 +0.0065121324476166255 +0.006085073481536977 +0.0063053527178540255 +0.006172239070867999 +0.006482244845472645 +0.006249061088031689 +0.006072588164904668 +0.005963505221399773 +0.006541932343873603 +0.005978238326231789 +0.006068465054679676 +0.006090859371722594 +0.006554332840656543 +0.006240069931691704 +0.0061388542280734265 +0.006210481463364917 +0.006177722078928646 +0.006109445868856752 +0.006113338514369251 +0.00638589895411816 +0.006318557595819971 +0.006174334730800709 +0.00610174891138768 +0.0063077077161549045 +0.006507697034478471 +0.006079476892845458 +0.006291852342832423 +0.006102473914141664 +0.0063502817660810945 +0.00603547172391603 +0.006014683098840339 +0.006272724872993256 +0.006180851822081571 +0.00613947869889339 +0.006143935481104079 +0.006179913735909551 +0.005999207273550586 +0.0060072671179394075 +0.006093241513384499 +0.006308488499111153 +0.006319105360894234 +0.006015585906743408 +0.006270462099530029 +0.0059296336665062566 +0.006237312077948321 +0.0061654297226841626 +0.006195027984062585 +0.005816533096124039 +0.006039109105375301 +0.006196394616723797 +0.00588476053879954 +0.006272064002244928 +0.006258616158862673 +0.006027525825560546 +0.006361286404645318 +0.006194578015826246 +0.0062392738952297905 +0.00614226611548886 +0.0060234401426402 +0.006119346779509104 +0.005998238505080285 +0.006297375983381538 +0.006022227832873993 +0.006159367399226463 +0.005989471511975526 +0.006161844625205028 +0.006265990433421255 +0.006152094558267341 +0.0058916246588430424 +0.006321959443631168 +0.00588216587521327 +0.00605715124025948 +0.006178620813180738 +0.005865271184186207 +0.006187718112850314 +0.0062731528347499755 +0.006187547586978671 +0.00586931534844518 +0.005711228119744122 +0.005954130673993506 +0.005987088050349782 +0.0062855214961475125 +0.0058403364900577485 +0.005747506121428464 +0.005805430818081766 +0.005949270271894454 +0.0059699209237882 +0.006146733909349591 +0.005705802928047262 +0.006105377703122511 +0.006335743928696436 +0.0058695421086716335 +0.006284519915819565 +0.006223392199650548 +0.006336943062284373 +0.005775144907996455 +0.006208539449524829 +0.00656147331872268 +0.0061425472279486495 +0.005778139089269585 +0.005739478614825769 +0.006324212335513997 +0.006061715773703396 +0.006094571465481172 +0.005793110492724231 +0.0064889906950741155 +0.006320204451226916 +0.006033010395268552 +0.006268571268150959 +0.005793830825586028 +0.005928737658940029 +0.0059360746009197414 +0.005921123769778772 +0.00580074743920151 +0.00600041084331478 +0.006065549175325823 +0.006224441010958228 +0.0059621825715980206 +0.006038792652233784 +0.006213274846356291 +0.005790815653258177 +0.006297106610720857 +0.00596215214027878 +0.005580317692923229 +0.005961467344189919 +0.0058768165754614675 +0.005721380075832878 +0.006118272315664748 +0.005865733843905844 +0.006051895516736879 +0.005892398204573009 +0.006239523179165155 +0.0064091585884614366 +0.00580754133513261 +0.005925991370594094 +0.005758367213261218 +0.005780674416010583 +0.005978336317611667 +0.006500314771969707 +0.006062155688170896 +0.005737047866668495 +0.006051646931957411 +0.006021980424641769 +0.006063678167749748 +0.006224919216328445 +0.006066763688130269 +0.005888556390947631 +0.005921683000655625 +0.006041925482163312 +0.0060582415669087055 +0.005892879506679316 +0.006194286557452704 +0.00605125249692809 +0.006066604279700539 +0.006147222191506126 +0.00621423757893561 +0.006306637827919605 +0.005826810339444491 +0.005905158232150337 +0.005995549125678327 +0.006102899812071623 +0.006010078881714961 +0.006307000559028734 +0.005901990987328607 diff --git a/logs/loss_2023_05_06_10_23_46/epoch_miou.txt b/logs/loss_2023_05_06_10_23_46/epoch_miou.txt new file mode 100644 index 0000000..930fc30 --- /dev/null +++ b/logs/loss_2023_05_06_10_23_46/epoch_miou.txt @@ -0,0 +1,61 @@ +0 +2.167944609686367 +2.167944609686367 +2.167944609686367 +2.5163406635704697 +2.6689392280869644 +2.670944657433233 +2.703679533595729 +2.6839131514854766 +2.742344626635652 +2.702516516895955 +2.695090191212251 +2.735516962860369 +2.762820157003522 +2.803803187338103 +2.810150036825074 +2.785170587287734 +2.791059580646515 +2.7656206207612173 +2.750095742323767 +2.7199225496891963 +2.7440055010236017 +2.8082342409328964 +2.814418398352213 +2.8107268204411624 +2.7995198932005354 +2.8011092609779977 +2.813604539250568 +2.8117495424778216 +2.8551548916813605 +2.839891923746138 +2.830180601681894 +2.8565925775780086 +2.8434723293551856 +2.8561996859138605 +2.7949647514086173 +2.8332743978696695 +2.8262352776644186 +2.8354105541311005 +2.852989126642651 +2.8432223736426883 +2.8606846598024553 +2.861047605790151 +2.8600708076704904 +2.87727084635111 +2.851273289108007 +2.88282154652109 +2.8663933562737225 +2.8731297790316663 +2.87792437948789 +2.8820779746904246 +2.877737893574461 +2.901723775915173 +2.8798679126394533 +2.8967890362822537 +2.8869220510911777 +2.886316192013369 +2.8947920914409986 +2.8912124346752957 +2.8855903610238225 +2.8622060446293918 diff --git a/logs/loss_2023_05_06_10_23_46/epoch_val_loss.txt b/logs/loss_2023_05_06_10_23_46/epoch_val_loss.txt new file mode 100644 index 0000000..2d13500 --- /dev/null +++ b/logs/loss_2023_05_06_10_23_46/epoch_val_loss.txt @@ -0,0 +1,300 @@ +0.07189631975930312 +0.03571391471757971 +0.027422866623463302 +0.021274538132651098 +0.019434706291890348 +0.0181045015984825 +0.0172115751092547 +0.016695918306579877 +0.016110792691851485 +0.015716335768329686 +0.015145999877231902 +0.014976732689758828 +0.014389945386812604 +0.013972413999124848 +0.013699803758284142 +0.01339970230413922 +0.013178563901576502 +0.012990865278346786 +0.012821075763424923 +0.012758990862117759 +0.012705465907166744 +0.012287154946283534 +0.012462963366174492 +0.012506939679512689 +0.012573993242955927 +0.012206647625385687 +0.012096314859608638 +0.012331344660949605 +0.012048744427939427 +0.012072740058446753 +0.011987858901506868 +0.011968210740978348 +0.011763962387139427 +0.011847949594837325 +0.011792505705921814 +0.011772599175636625 +0.01168673218966558 +0.011574136976409575 +0.011669780023331786 +0.011759012158767417 +0.011602205529423624 +0.011422211994770271 +0.011385530939903753 +0.011649176711216569 +0.011735210436043041 +0.011452095663367674 +0.011474372008173117 +0.011400713970691994 +0.011594116976805803 +0.01162711290867421 +0.011419184762856057 +0.011850407154395663 +0.01142614646333045 +0.011498499998887038 +0.009839700976515124 +0.011730902996877658 +0.011111496353586173 +0.011294754430780122 +0.011366692901556862 +0.011263233063549831 +0.0110595160613543 +0.011138797135509807 +0.011222220363159632 +0.011109685597555905 +0.011056809348921323 +0.011218598698554882 +0.010987450376731055 +0.011019065277650952 +0.011006714190067402 +0.010988561950367072 +0.010986150756221393 +0.011355467606335878 +0.010989442997579944 +0.01098421217616776 +0.011198728754795316 +0.011363271527506155 +0.011044791474103414 +0.010770803253198492 +0.01112097201483517 +0.010638197153357083 +0.010831893212964823 +0.010716704378739512 +0.010368685591323623 +0.010761569197899822 +0.010918065688797626 +0.010843201339694446 +0.010986347955747926 +0.010991036056958396 +0.011016894348672238 +0.01096486386136505 +0.010981223537939889 +0.010795045017810732 +0.010998073893053264 +0.010681337612713206 +0.010768676008065713 +0.010744555644562533 +0.010923682551445633 +0.010729674450603539 +0.01098158168767033 +0.01112275263936869 +0.010836047963399825 +0.010836834079939229 +0.010870976510040205 +0.010789354853653189 +0.01086848299821903 +0.010880841994015822 +0.010519333617312127 +0.010682976371126956 +0.01058533501104805 +0.010533275203137049 +0.010898187933168534 +0.01089936629708471 +0.010773020057842649 +0.010753131800748664 +0.010720436316754284 +0.010669665924947837 +0.010629855184655252 +0.010751658111232621 +0.010831298250383857 +0.01070010883669401 +0.010617443055299849 +0.01048437919435573 +0.010346002114037502 +0.010567514346270212 +0.010638203857273891 +0.010729511059692194 +0.010716044665153685 +0.010648740768625304 +0.010806642850090203 +0.010610603590913373 +0.010871732390710506 +0.010454296712474576 +0.01122654142693199 +0.01061759469227801 +0.010552432757384819 +0.010584884724611866 +0.010228246000820193 +0.010536318002589818 +0.010565901255427763 +0.010508602525203907 +0.010447882806304199 +0.010485738555997097 +0.010434291293394977 +0.010488350811446535 +0.01026412391039575 +0.010033505045455592 +0.010524095084261277 +0.01064964158235696 +0.010424483214215985 +0.010454660225338462 +0.010546684586282435 +0.010508425666244122 +0.010540762514775169 +0.010453253229758862 +0.010467348146605594 +0.010784732603371656 +0.010536088070286245 +0.010499941867551413 +0.010430208495657506 +0.009066679659460125 +0.009254690982272913 +0.010539520547950062 +0.010518653013197512 +0.01060755919376067 +0.010437106007132036 +0.010493663484872929 +0.01069221261021649 +0.010056320529688021 +0.01049118855936003 +0.01037861027851187 +0.010572533131223815 +0.010480090421785054 +0.010378657173814958 +0.010510338382024703 +0.010683570731559703 +0.010662932864165512 +0.010498907786376518 +0.01064154385300032 +0.010491117979560432 +0.010530859400550353 +0.010502434761167086 +0.01056128019338538 +0.010444778033757004 +0.010589376124071664 +0.01070712298829237 +0.010507027690844804 +0.01046000702852576 +0.010480330727095234 +0.010393654197004849 +0.010532267263223385 +0.010621749025223583 +0.01059725113084604 +0.010466801741256797 +0.010441247365790707 +0.010576903739751413 +0.010367687928072852 +0.010391760312406153 +0.010373844066634774 +0.010441387449551759 +0.01051324969639295 +0.010589498922164584 +0.010469761396083853 +0.01004613331390609 +0.010493891723132852 +0.010337132904357437 +0.010412660716422674 +0.010555118097570437 +0.010401249598262125 +0.010469417176048818 +0.010312642388302705 +0.010469515470723653 +0.010319499600004277 +0.01052881558907443 +0.010495687826889855 +0.010476063182256344 +0.010430275382281378 +0.01042456973087171 +0.010463053858357257 +0.010540624470289412 +0.010455657615615377 +0.01044396341553536 +0.010395392269731081 +0.010523632361457265 +0.010493589950532749 +0.010407621699289 +0.010477926068264863 +0.010362225199310944 +0.010507392584635266 +0.010485237970113241 +0.010403798909151349 +0.010422620135520038 +0.01039113621388016 +0.010345617493871471 +0.010484577702551052 +0.010535608792420605 +0.009211804059430444 +0.010571935406789697 +0.010426660244963292 +0.010452914501315561 +0.010462347955988913 +0.010413646770255833 +0.010408702667738343 +0.010426012686742791 +0.010548672494317951 +0.010507516892113048 +0.010526582253454575 +0.01042245962689149 +0.01042446452353535 +0.010473434427945778 +0.010495701796728475 +0.010512662473423728 +0.010494389996767557 +0.010509672984159711 +0.010496956135692268 +0.010419984805750949 +0.008929582906822706 +0.010441352597212997 +0.010464571400320736 +0.010399415591282064 +0.010455412115773251 +0.01057258246723434 +0.0105080163539869 +0.010404359782114625 +0.010481919033517098 +0.01050947531094325 +0.010400464565589511 +0.010359236191765502 +0.010454744012255606 +0.010623272428096369 +0.010368483839556575 +0.010493359793427175 +0.009050809256411317 +0.01053778325400219 +0.010499262250959873 +0.01051076638094824 +0.010474678538418535 +0.010002020031920281 +0.010449292515565095 +0.01030946695566948 +0.010532466622454852 +0.010416264388838718 +0.010429385599905047 +0.01049036902760894 +0.010462773698863798 +0.01032512606089485 +0.010482839268535889 +0.010443417997709635 +0.010451143398752501 +0.010503844787559375 +0.00892035136448926 +0.010418647643307159 +0.010451512692239264 +0.010512368560865008 +0.01043968412479193 +0.010323734408051803 +0.010429111742896253 +0.010425899531049975 +0.010525008904394405 +0.010326696856846583 +0.010493756890104249 diff --git a/nets/__init__.py b/nets/__init__.py new file mode 100644 index 0000000..4287ca8 --- /dev/null +++ b/nets/__init__.py @@ -0,0 +1 @@ +# \ No newline at end of file diff --git a/nets/__pycache__/__init__.cpython-38.pyc b/nets/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e1e87e56438271d067964a65a57048c4455487b GIT binary patch literal 144 zcmWIL<>g`kf|WuGk{E&XV-N=!fCL?YxR?b1#LT>yywsB7nE3e2yv&mLc)fzkTO2mI`6;D2sdgaaJ_9iW E05#hoHUIzs literal 0 HcmV?d00001 diff --git a/nets/__pycache__/__init__.cpython-39.pyc b/nets/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1dbd4e17ac786279547bd494a0d920217b49a7f5 GIT binary patch literal 161 zcmYe~<>g`kf>lBbk{E&XV-N=!fCL?YxR?b-)<0tO` literal 0 HcmV?d00001 diff --git a/nets/__pycache__/deeplabv3_plus.cpython-38.pyc b/nets/__pycache__/deeplabv3_plus.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..223ca40661b211cf6a475b86dde18adcb3aba59d GIT binary patch literal 4707 zcmbVQ&2t<_6`!8(nf;I~3uC*25lQi}6#ev#CfFhMLHow=it6kZqoMN{6^_$oIx~IF} z`@PqDt_I`=-+HW)X!`M?VdZgKm8 z7MR@OE^6yj&C~*$rjxY~)X-~jaeZavVubaML8FZy8?}Q%viuQ9p%tvu?(2K{KK6k# zt|vy%92!h3tWv+vhGuDQ*<9nsZ}bydsfBjol-ibkA6lVH`$9XhY2wW*8Fz2#;xC1d z9v=6oKWgIy#=orn;?&S0&C)ud?bS*Hd>#7!uD;Khc6)2Ah2Cg!|EGp^pz+7_JZ7w6 z#?QW>?sT@laShv5UlR;_fD>{0ktn)RGJ1k%q(eBOa~RQHjhZ0BBJMF-(c9+TBr4+3 zJY=ngwDLhe7J?L{8;0GqTZEzXHU?>@$g(6CwCjSlT)G=^R18F%O9SU0ctuu3N!aDv zfy=YIDRI&gQMwsRE9&=?dvZQZv%C;p9*5PeEnAv-oNUM=q>)FxeiDZp5vCH-?~Aw) zQ8$gbnp9f7jK_&&-G@wiVc1EcJP*T%+UKvw=Wp;h?k7?E&g!c(Z2ukx>fAij!zedk zq4~5e>?ebKxqna4x;>QqTOb;1vbyfFdA-h>s86zara$rZW2&Z>irZP7G0o-4HXX7~ z+Ckc;ODM{3OSYwLv7u2Kdpc+Pu+CeCC?GYsfpfpk&60%%-eHG$FG`);C$)-h=xBot zKP9o#Q?va`ebR5t!Pa3J&YCnYW$7J32GG(4d6FV6eMBZBzNX|O`dabwL8BwS0-lT@ zje0SRz})DjT-wzE%G#}1q;V4F-JMvv>No)iWPF@=)6JGCzD(+@CmK|H>bPhmaf+r; z2xh?C5~iH()Q1xeeva9mozn)RCV28z2YJ>^u*e6(n2OAXl1iUH$MwWibEz z!|&<&^MwwrW%3YTL!m>+`>?LVm|PfpW?^huB{m)S3?C?A{oLGjAhmH2u>D;fyaTDd z%l3h+p?gTnftFhui-=K*zDV19M%z<*6@8Jm|BSY;v@7}|?b_kBVokMO8!M{Mt`_f& z>5B2zJeX7G^+iGewNub;?Ao)MQ!TYmbIRMBYwCDZFj$8T6Re4Ef=IL7jdCEq+sk@! zS`2!?QKy>%HaoHM`w`jgs7^#V028ak)0x6#)j%X@&5Joy;w%w@PpPM=^smOZ2N)KC zN7@88D_qvzj*8CBwM_KzVty1~xh9=9B0}fp*$J~UVOA&1cVxZlb1v*)80k;m+Uc}K z#K}Oq)E;)CqGeUCidfb|$dV4#*`Sc7Kxhl>b_xgU3FReI;Gs|d9!f4i2>DIGjz0Z; zZaQ`5|J$xj*BzvagfMVh+Fjx&t=pPdHz(lmI?;ut|Dq4gz{{7)by+tc>78|F|S z8bgzkk3Ci*hzwy)d%!PnNM-gAKES!)5KO>%g|%lRv4ErP8$(~c`7k8)hF(tTqZd+~ zJ@w)$ zx*1SPuI>YYd4T(#bS@*mx)Qae*PEpK>w_Bo6&&+GUd*|k(}bGA1m^L#-`D8GMk-=q6% zAMO_(!Hm#=>(SVYyL6{mQ26|%H9?*ZKs}C1oB-)k#2(?wqKTi=>qT%QYTY?^Ek!=w z&qNXPrFPW0)y~p*>CDn6zZ!mWdg)poFC84m>7~h~Y*`%)p#OcE;DVLpHW9 z&VhDcqX}OIhi~~hDuwWyVDS9?f-*#;Le$baS|NS_zA{Bx0{nXXMb&k|zaAxn_#GjT zyxXwq9{iGwQkqfT?~B(-`3)jJAo3=W3m~$Q-^2ntgQCKF=d#H91l}$jt`fPOBv%|V zCY4pz(m}7f0K~ZnX(ZZ5wW9dtMm$EwuA1Aez-=u5BXecLCJ) zEU)Vb5c(7Nu#1}6k6q=KnjHj?l0as!C@4yho)5Sp5 zFmhcZv(q`#p9_i<4jqdjqOBOtC>m@1(%$J$gw8>o%+1nE?zg{$tCi?qvx9PUjQ zRW%wey2OpgKst6vkFNdhT6JR}OA$W?p{!yJHQ*T+GTDfd?k2W8!*xL9Rq{bw+45Dd z>Ui3uOjsm-O5`IVF_E7Txk_XMWE{xH5dOYo9}7yTXYlR-ZvP5-PB^HsV>VqGd>hBb z+$TB?N`GWq$00k$cIGCBFu_?vIa!5X;%A3PpQ0(5`YuXN|ID}soRevfn&$<{TTS)Z z(p6DvIZc&q2S+Eb%j&j_+YnMAf6A++2MxN1E>C<_VL>pjWCyN%XQjGe+aIV~f%2{@ l+foiV1%JiSj!5^7>IU&9d|Jy1m|Y|p3n+DCVR_->{{kicD%Jo1 literal 0 HcmV?d00001 diff --git a/nets/__pycache__/deeplabv3_plus.cpython-39.pyc b/nets/__pycache__/deeplabv3_plus.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d12b386dc8474a5b347781ff65cde6b981d2dfb7 GIT binary patch literal 4728 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 literal 0 HcmV?d00001 diff --git a/nets/__pycache__/mobilenetv2.cpython-39.pyc b/nets/__pycache__/mobilenetv2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4103ed0e867dc8ac871a020c1c3f9d45ab766e9 GIT binary patch literal 4323 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 literal 0 HcmV?d00001 diff --git a/nets/__pycache__/xception.cpython-39.pyc b/nets/__pycache__/xception.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25a45449a30d1b857d70318af4be0b3704324361 GIT binary patch literal 6079 zcma)ANpsxB6$UWC3})eQ5k--bERQX3BU@axnRaZ&a_Bg*8ONd>XCemyr@4|Pr> zSRa#?==VI%4aB7FIDX(qjw6jAkmiTpdml-Qd!7Eg%*O*Ji_Ggqn|FNS$?{un)ZAPR zd3TY?{CeQvKQgx-EW~9NWU&|eL8S7+ycnAoWz}s){%tq%oDH9g2-9K;ud^Y`e&94W z-5~HfLRP|lbQmjdc^qa(_}iY$i-`NolZBqkm>;yH&HRoV`C*{?#Am3J76kfUbev|# z6~b{IYL8alUcU7K4_n;r)>izECvJUk4?pDHnjd*xaf^8##&G@i(#ySjQOKK{7rU+> z+zPx%+`8LDzwwLN-aTGH-DP}Y5kf0f^{SrNjp6rO+aCK->-O|xt8g|Q_9o3j`jAfZ zbpxNc1u@Xt8Xp^Idsu4w`VN-fo{?#}RBLPE5>m`GzGr0`7G_)yc|Jj&LK4S@d#JD0 z?Rexy@hd(~Vv@v35>q5jk(h?48~ii^so&-2$)?0}WGQTf{Eo}nMMMM@(l9b5^$v&I zryd`Hq+GoaFH!2ZTBjFDK_utiLReBRiM;=;N^c=+RQu|t5A~G5q;ESF%2X-3N!$V>vH0oKJ^z& zJs%sMCTlHA$`GM${b>3D?YH_kr^(>7e$`@gv=ePH)0XOGwXYf_0O5w;i99YE zWx~NEA`3C{37K~zV7!neK>eK ztTsTH+4zdYFj-|6@B}tywQXjDQ;j5S%xag3cPbuNQKf8f(gMmHmKsr|#9F7qY8O?_W` zTlPkzqi2746HNSzP@kh zTJTa*hmAT6{&kXbk(Ks!ooZS_xl42!`O5h0YjtWmyS-WiPluQ2H$h<4R-=vmQ{EEG zS&S?}zZ(-{Pb^bp-Q%@zqo%mBC$g-o2g016B>GLYx@HPqIELeKU3<=Dl)fk@)wKXyBP?`NEcTpFPm za_g3I)rWpPX2b-`(j*2TRfffw853;d7(_&GAqf|ga488dCE?{Hypn`hlki#+zA6iG zJ@cuukSdF*vXm;9Qsr{0TuGIysd6n<(wZ0N;}f6iz*5|gOL2h4DZ(OTA!gZv&(l~v zMdGaLm%ZAQ{0XCMm!PJnL5k!IJ8HbI3x*AqC$UQ6S0p|l@oN$vlDJ7? zjl@SJJ|^)S5}%OxEr|w+-;tp6I{%c!?@2f${s58EN(;2IA93Q8jqwg~cn_q#?Hu%4$1{ydk#=q-54QWOd9F)dWo-~G1BZ2kmbF*I6nzopSfEe!%Y>g z%6c6iu-C@5mbBCjku*D@%hEcw!%*5}aQeJcw`2|hk5DBSRId$C^ysESmYZ&K(_?h7 z^M(dhP4*75(nWxx|C&0F5q5g_6++u){savaXp>5>eZl}b(55f8vkYc&YqftgL2= literal 0 HcmV?d00001 diff --git a/nets/deeplabv3_plus.py b/nets/deeplabv3_plus.py new file mode 100644 index 0000000..adb8608 --- /dev/null +++ b/nets/deeplabv3_plus.py @@ -0,0 +1,257 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from nets.xception import xception +from nets.mobilenetv2 import mobilenetv2 + + +class MobileNetV2(nn.Module): + def __init__(self, downsample_factor=8, pretrained=True): + super(MobileNetV2, self).__init__() + from functools import partial + + model = mobilenetv2(pretrained) + self.features = model.features[:-1] + + self.total_idx = len(self.features) + self.down_idx = [2, 4, 7, 14] + + if downsample_factor == 8: + for i in range(self.down_idx[-2], self.down_idx[-1]): + self.features[i].apply(partial(self._nostride_dilate, + dilate=2)) + for i in range(self.down_idx[-1], self.total_idx): + self.features[i].apply(partial(self._nostride_dilate, + dilate=4)) + elif downsample_factor == 16: + for i in range(self.down_idx[-1], self.total_idx): + self.features[i].apply(partial(self._nostride_dilate, + dilate=2)) + + def _nostride_dilate(self, m, dilate): + classname = m.__class__.__name__ + if classname.find('Conv') != -1: + if m.stride == (2, 2): + m.stride = (1, 1) + if m.kernel_size == (3, 3): + m.dilation = (dilate // 2, dilate // 2) + m.padding = (dilate // 2, dilate // 2) + else: + if m.kernel_size == (3, 3): + m.dilation = (dilate, dilate) + m.padding = (dilate, dilate) + + def forward(self, x): + low_level_features = self.features[:4](x) + x = self.features[4:](low_level_features) + return low_level_features, x + + +#-----------------------------------------# +# ASPP特征提取模块 +# 利用不同膨胀率的膨胀卷积进行特征提取 +#-----------------------------------------# +""" +卷积操作使用了膨胀率 dilation=6 * rate,这意味着卷积核内的采样点之间有 6 个像素的间隔。 +为了保持输出特征图的尺寸与输入数据相同,填充参数 padding 被设置为 padding=6 * rate。 +这样,卷积操作将在输入数据上以膨胀率为 6 的间隔进行卷积,并且在输出特征图的边缘周围填充 6 个像素的零,以确保输出特征图的尺寸不会缩小。 + + +bn_mom 在这段代码中是批量归一化层(Batch Normalization)的动量参数(momentum)。 +在 PyTorch 中的批量归一化层的实现中,动量参数控制了均值和方差的移动平均值的更新速度 + +dilation 是卷积操作的膨胀率参数 +""" +class ASPP(nn.Module): + def __init__(self, dim_in, dim_out, rate=1, bn_mom=0.1): + super(ASPP, self).__init__() + self.branch1 = nn.Sequential( + nn.Conv2d(dim_in, + dim_out, + 1, + 1, + padding=0, + dilation=rate, + bias=True), + nn.BatchNorm2d(dim_out, momentum=bn_mom), + nn.ReLU(inplace=True), + ) + self.branch2 = nn.Sequential( + nn.Conv2d(dim_in, + dim_out, + 3, + 1, + padding=6 * rate, + dilation=6 * rate, + bias=True), + nn.BatchNorm2d(dim_out, momentum=bn_mom), + nn.ReLU(inplace=True), + ) + self.branch3 = nn.Sequential( + nn.Conv2d(dim_in, + dim_out, + 3, + 1, + padding=12 * rate, + dilation=12 * rate, + bias=True), + nn.BatchNorm2d(dim_out, momentum=bn_mom), + nn.ReLU(inplace=True), + ) + self.branch4 = nn.Sequential( + nn.Conv2d(dim_in, + dim_out, + 3, + 1, + padding=18 * rate, + dilation=18 * rate, + bias=True), + nn.BatchNorm2d(dim_out, momentum=bn_mom), + nn.ReLU(inplace=True), + ) + # branch5是一个池化过程 + self.branch5_conv = nn.Conv2d(dim_in, dim_out, 1, 1, 0, bias=True) + self.branch5_bn = nn.BatchNorm2d(dim_out, momentum=bn_mom) + self.branch5_relu = nn.ReLU(inplace=True) + + self.conv_cat = nn.Sequential( + nn.Conv2d(dim_out * 5, dim_out, 1, 1, padding=0, bias=True), + nn.BatchNorm2d(dim_out, momentum=bn_mom), + nn.ReLU(inplace=True), + ) + + def forward(self, x): + [b, c, row, col] = x.size() + #-----------------------------------------# + # 一共五个分支 + #-----------------------------------------# + conv1x1 = self.branch1(x) + conv3x3_1 = self.branch2(x) + conv3x3_2 = self.branch3(x) + conv3x3_3 = self.branch4(x) + #-----------------------------------------# + # 第五个分支,全局平均池化+卷积 + #-----------------------------------------# + global_feature = torch.mean(x, 2, True) + global_feature = torch.mean(global_feature, 3, True) #两次平均池化 + global_feature = self.branch5_conv(global_feature) + global_feature = self.branch5_bn(global_feature) + global_feature = self.branch5_relu(global_feature)# 通道的整合 + global_feature = F.interpolate(global_feature, (row, col), None, + 'bilinear', True) + """ + 这一行代码首先使用 torch.mean 函数计算了 x 沿第 2 维度(通常是垂直方向)的平均值。 + True 参数表示要保持维度的数量,即在结果中保留该维度。这将生成一个张量 + 其形状为 [batch_size, num_channels, 1, width], + 其中 batch_size 是批量大小,num_channels 是通道数,width 是特征图的宽度,高度为 1。 + global_feature = torch.mean(global_feature, 3, True): + 同样使用 torch.mean 函数,计算了上一步结果中的 global_feature 沿第 3 维度(通常是水平方向)的平均值, + 并再次保持维度的数量。这将生成一个形状为 [batch_size, num_channels, 1, 1] 的张量, + 其中每个通道的值都是整个特征图在该通道上的平均值。 + + 这个过程的结果是对整个特征图的每个通道执行全局平均池化, + 最终生成一个形状为 [batch_size, num_channels, 1, 1] 的全局平均特征向量。 + + 将全局平均特征 global_feature 调整为指定的目标尺寸 (row, col) + """ + #-----------------------------------------# + # 将五个分支的内容堆叠起来 + # 然后1x1卷积整合特征。 + #-----------------------------------------# + feature_cat = torch.cat( + [conv1x1, conv3x3_1, conv3x3_2, conv3x3_3, global_feature], dim=1) + result = self.conv_cat(feature_cat) #1*1卷积 这个就是哪个绿色的 + return result +# ASPP就是使用不同膨胀率的膨胀卷积对特征进行提取 + +class DeepLab(nn.Module): + def __init__(self, + num_classes, + backbone="mobilenet", + pretrained=True, + downsample_factor=16): + super(DeepLab, self).__init__() + if backbone == "xception": + #----------------------------------# + # 获得两个特征层 + # 浅层特征 [128,128,256] + # 主干部分 [30,30,2048] + #----------------------------------# + self.backbone = xception(downsample_factor=downsample_factor, + pretrained=pretrained) + in_channels = 2048 + low_level_channels = 256 + elif backbone == "mobilenet": + #----------------------------------# + # 获得两个特征层 + # 浅层特征 [128,128,24] + # 主干部分 [30,30,320] + #----------------------------------# + self.backbone = MobileNetV2(downsample_factor=downsample_factor, + pretrained=pretrained) + in_channels = 320 + low_level_channels = 24 + else: + raise ValueError( + 'Unsupported backbone - `{}`, Use mobilenet, xception.'.format( + backbone)) + + #-----------------------------------------# + # ASPP特征提取模块 + # 利用不同膨胀率的膨胀卷积进行特征提取 + #-----------------------------------------# + self.aspp = ASPP(dim_in=in_channels, + dim_out=256, + rate=16 // downsample_factor) + + #----------------------------------# + # 浅层特征边 + #----------------------------------# + self.shortcut_conv = nn.Sequential( + nn.Conv2d(low_level_channels, 48, 1), + nn.BatchNorm2d(48), + nn.ReLU(inplace=True)) + + self.cat_conv = nn.Sequential( + nn.Conv2d(48 + 256, 256, 3, stride=1, padding=1), + nn.BatchNorm2d(256), + nn.ReLU(inplace=True), + nn.Dropout(0.5), + + nn.Conv2d(256, 256, 3, stride=1, padding=1), + nn.BatchNorm2d(256), + nn.ReLU(inplace=True), + nn.Dropout(0.1), + ) + self.cls_conv = nn.Conv2d(256, num_classes, 1, stride=1) + """ + 在每个训练批次中,以一定的概率(通常在 0.2 到 0.5 之间)随机选择一些神经元,并将它们的输出置零。 + 这表示在每次前向传播中,只有部分神经元的输出会被传递到下一层,而其他神经元的输出被设置为零。 + + 在每次反向传播中,只有那些没有被置零的神经元才会更新其权重。这意味着每个神经元都有机会被训练, + 而不是过度依赖于特定的神经元。 + """ + def forward(self, x): + H, W = x.size(2), x.size(3) + #-----------------------------------------# + # 获得两个特征层 + # low_level_features: 浅层特征-进行卷积处理 + # x : 主干部分-利用ASPP结构进行加强特征提取 + #-----------------------------------------# + low_level_features, x = self.backbone(x) + x = self.aspp(x) + low_level_features = self.shortcut_conv(low_level_features) + + #-----------------------------------------# + # 将加强特征边上采样 绿色的模块 + # 与浅层特征堆叠后利用卷积进行特征提取 + #-----------------------------------------# + x = F.interpolate(x, + size=(low_level_features.size(2), + low_level_features.size(3)), + mode='bilinear',#使用双线性插值进行的上采样操作 + align_corners=True) #上采样 + x = self.cat_conv(torch.cat((x, low_level_features), dim=1)) + x = self.cls_conv(x) + x = F.interpolate(x, size=(H, W), mode='bilinear', align_corners=True) + return x diff --git a/nets/deeplabv3_training.py b/nets/deeplabv3_training.py new file mode 100644 index 0000000..26d5cc1 --- /dev/null +++ b/nets/deeplabv3_training.py @@ -0,0 +1,113 @@ +import math +from functools import partial + +import torch +import torch.nn as nn +import torch.nn.functional as F + + +def CE_Loss(inputs, target, cls_weights, num_classes=21): + n, c, h, w = inputs.size() + nt, ht, wt = target.size() + if h != ht and w != wt: + inputs = F.interpolate(inputs, size=(ht, wt), mode="bilinear", align_corners=True) + + temp_inputs = inputs.transpose(1, 2).transpose(2, 3).contiguous().view(-1, c) + temp_target = target.view(-1) + + CE_loss = nn.CrossEntropyLoss(weight=cls_weights, ignore_index=num_classes)(temp_inputs, temp_target) + return CE_loss + +def Focal_Loss(inputs, target, cls_weights, num_classes=21, alpha=0.5, gamma=2): + n, c, h, w = inputs.size() + nt, ht, wt = target.size() + if h != ht and w != wt: + inputs = F.interpolate(inputs, size=(ht, wt), mode="bilinear", align_corners=True) + + temp_inputs = inputs.transpose(1, 2).transpose(2, 3).contiguous().view(-1, c) + temp_target = target.view(-1) + + logpt = -nn.CrossEntropyLoss(weight=cls_weights, ignore_index=num_classes, reduction='none')(temp_inputs, temp_target) + pt = torch.exp(logpt) + if alpha is not None: + logpt *= alpha + loss = -((1 - pt) ** gamma) * logpt + loss = loss.mean() + return loss + +def Dice_loss(inputs, target, beta=1, smooth = 1e-5): + n, c, h, w = inputs.size() + nt, ht, wt, ct = target.size() + if h != ht and w != wt: + inputs = F.interpolate(inputs, size=(ht, wt), mode="bilinear", align_corners=True) + + temp_inputs = torch.softmax(inputs.transpose(1, 2).transpose(2, 3).contiguous().view(n, -1, c),-1) + temp_target = target.view(n, -1, ct) + + #--------------------------------------------# + # 计算dice loss + #--------------------------------------------# + tp = torch.sum(temp_target[...,:-1] * temp_inputs, axis=[0,1]) + fp = torch.sum(temp_inputs , axis=[0,1]) - tp + fn = torch.sum(temp_target[...,:-1] , axis=[0,1]) - tp + + score = ((1 + beta ** 2) * tp + smooth) / ((1 + beta ** 2) * tp + beta ** 2 * fn + fp + smooth) + dice_loss = 1 - torch.mean(score) + return dice_loss + +def weights_init(net, init_type='normal', init_gain=0.02): + def init_func(m): + classname = m.__class__.__name__ + if hasattr(m, 'weight') and classname.find('Conv') != -1: + if init_type == 'normal': + torch.nn.init.normal_(m.weight.data, 0.0, init_gain) + elif init_type == 'xavier': + torch.nn.init.xavier_normal_(m.weight.data, gain=init_gain) + elif init_type == 'kaiming': + torch.nn.init.kaiming_normal_(m.weight.data, a=0, mode='fan_in') + elif init_type == 'orthogonal': + torch.nn.init.orthogonal_(m.weight.data, gain=init_gain) + else: + raise NotImplementedError('initialization method [%s] is not implemented' % init_type) + elif classname.find('BatchNorm2d') != -1: + torch.nn.init.normal_(m.weight.data, 1.0, 0.02) + torch.nn.init.constant_(m.bias.data, 0.0) + print('initialize network with %s type' % init_type) + net.apply(init_func) + +def get_lr_scheduler(lr_decay_type, lr, min_lr, total_iters, warmup_iters_ratio = 0.1, warmup_lr_ratio = 0.1, no_aug_iter_ratio = 0.3, step_num = 10): + def yolox_warm_cos_lr(lr, min_lr, total_iters, warmup_total_iters, warmup_lr_start, no_aug_iter, iters): + if iters <= warmup_total_iters: + # lr = (lr - warmup_lr_start) * iters / float(warmup_total_iters) + warmup_lr_start + lr = (lr - warmup_lr_start) * pow(iters / float(warmup_total_iters), 2) + warmup_lr_start + elif iters >= total_iters - no_aug_iter: + lr = min_lr + else: + lr = min_lr + 0.5 * (lr - min_lr) * ( + 1.0 + math.cos(math.pi* (iters - warmup_total_iters) / (total_iters - warmup_total_iters - no_aug_iter)) + ) + return lr + + def step_lr(lr, decay_rate, step_size, iters): + if step_size < 1: + raise ValueError("step_size must above 1.") + n = iters // step_size + out_lr = lr * decay_rate ** n + return out_lr + + if lr_decay_type == "cos": + warmup_total_iters = min(max(warmup_iters_ratio * total_iters, 1), 3) + warmup_lr_start = max(warmup_lr_ratio * lr, 1e-6) + no_aug_iter = min(max(no_aug_iter_ratio * total_iters, 1), 15) + func = partial(yolox_warm_cos_lr ,lr, min_lr, total_iters, warmup_total_iters, warmup_lr_start, no_aug_iter) + else: + decay_rate = (min_lr / lr) ** (1 / (step_num - 1)) + step_size = total_iters / step_num + func = partial(step_lr, lr, decay_rate, step_size) + + return func + +def set_optimizer_lr(optimizer, lr_scheduler_func, epoch): + lr = lr_scheduler_func(epoch) + for param_group in optimizer.param_groups: + param_group['lr'] = lr diff --git a/nets/img.png b/nets/img.png new file mode 100644 index 0000000000000000000000000000000000000000..3464b3575c53dd9aa4abd90e6e7935939c402f55 GIT binary patch literal 60969 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 z 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 256, 256, 16 + [6, 24, 2, 2], + # 256, 256, 16 -> 128, 128, 24 2 + [6, 32, 3, 2], + # 128, 128, 24 -> 64, 64, 32 4 + [6, 64, 4, 2], + # 64, 64, 32 -> 32, 32, 64 7 + [6, 96, 3, 1], + # 32, 32, 64 -> 32, 32, 96 + [6, 160, 3, 2], + # 32, 32, 96 -> 16, 16, 160 14 + [6, 320, 1, 1], + # 16, 16, 160 -> 16, 16, 320 + ] + + assert input_size % 32 == 0 + input_channel = int(input_channel * width_mult) + self.last_channel = int(last_channel * width_mult) if width_mult > 1.0 else last_channel + # 512, 512, 3 -> 256, 256, 32 + self.features = [conv_bn(3, input_channel, 2)] + + for t, c, n, s in interverted_residual_setting: + output_channel = int(c * width_mult) + for i in range(n): + if i == 0: + self.features.append(block(input_channel, output_channel, s, expand_ratio=t)) + else: + self.features.append(block(input_channel, output_channel, 1, expand_ratio=t)) + input_channel = output_channel + + self.features.append(conv_1x1_bn(input_channel, self.last_channel)) + self.features = nn.Sequential(*self.features) + + self.classifier = nn.Sequential( + nn.Dropout(0.2), + nn.Linear(self.last_channel, n_class), + ) + + self._initialize_weights() + + def forward(self, x): + x = self.features(x) + x = x.mean(3).mean(2) + x = self.classifier(x) + return x + + def _initialize_weights(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels + m.weight.data.normal_(0, math.sqrt(2. / n)) + if m.bias is not None: + m.bias.data.zero_() + elif isinstance(m, BatchNorm2d): + m.weight.data.fill_(1) + m.bias.data.zero_() + elif isinstance(m, nn.Linear): + n = m.weight.size(1) + m.weight.data.normal_(0, 0.01) + m.bias.data.zero_() + + +def load_url(url, model_dir='./model_data', map_location=None): + if not os.path.exists(model_dir): + os.makedirs(model_dir) + filename = url.split('/')[-1] + cached_file = os.path.join(model_dir, filename) + if os.path.exists(cached_file): + return torch.load(cached_file, map_location=map_location) + else: + return model_zoo.load_url(url,model_dir=model_dir) + +def mobilenetv2(pretrained=False, **kwargs): + model = MobileNetV2(n_class=1000, **kwargs) + if pretrained: + model.load_state_dict(load_url('https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/mobilenet_v2.pth.tar'), strict=False) + return model + +if __name__ == "__main__": + model = mobilenetv2() + for i, layer in enumerate(model.features): + print(i, layer) diff --git a/nets/test_net.py b/nets/test_net.py new file mode 100644 index 0000000..f1c641b --- /dev/null +++ b/nets/test_net.py @@ -0,0 +1,30 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from nets.xception import xception +from nets.mobilenetv2 import mobilenetv2 + +class ASPP(nn.Module): + def __init__(self, dim_in, dim_out, rate=1, bn_mom=0.1): + super(ASPP, self).__init__() + self.branch1 = nn.Sequential( + nn.Conv2d(dim_in, dim_out, 1, 1, padding=0, dilation=rate,bias=True), + nn.BatchNorm2d(dim_out, momentum=bn_mom), + nn.ReLU(inplace=True), + ) + self.branch2 = nn.Sequential( + nn.Conv2d(dim_in, dim_out, 3, 1, padding=6*rate, dilation=6*rate, bias=True), + nn.BatchNorm2d(dim_out, momentum=bn_mom), + nn.ReLU(inplace=True), + ) + self.branch3 = nn.Sequential( + nn.Conv2d(dim_in, dim_out, 3, 1, padding=12 * rate, dilation=12 * rate, bias=True), + nn.BatchNorm2d(dim_out, momentum=bn_mom), + nn.ReLU(inplace=True), + ) + self.branch4 = nn.Sequential( + nn.Conv2d(dim_in, dim_out, 3, 1, padding=18 * rate, dilation=18 * rate, bias=True), + nn.BatchNorm2d(dim_out, momentum=bn_mom), + nn.ReLU(inplace=True), + ) + self.branch5_conv = nn.Conv2d(dim_in, dim_out,1, 1, 0, bias=True), diff --git a/nets/xception.py b/nets/xception.py new file mode 100644 index 0000000..3f536c7 --- /dev/null +++ b/nets/xception.py @@ -0,0 +1,298 @@ +import math +import os +import torch +import torch.nn as nn +import torch.utils.model_zoo as model_zoo + +bn_mom = 0.0003 + + +class SeparableConv2d(nn.Module): + def __init__(self, + in_channels, + out_channels, + kernel_size=1, + stride=1, + padding=0, + dilation=1, + bias=False, + activate_first=True, + inplace=True): + super(SeparableConv2d, self).__init__() + self.relu0 = nn.ReLU(inplace=inplace) + self.depthwise = nn.Conv2d(in_channels, + in_channels, + kernel_size, + stride, + padding, + dilation, + groups=in_channels, + bias=bias) + self.bn1 = nn.BatchNorm2d(in_channels, momentum=bn_mom) + self.relu1 = nn.ReLU(inplace=True) + self.pointwise = nn.Conv2d(in_channels, + out_channels, + 1, + 1, + 0, + 1, + 1, + bias=bias) + self.bn2 = nn.BatchNorm2d(out_channels, momentum=bn_mom) + self.relu2 = nn.ReLU(inplace=True) + self.activate_first = activate_first + + def forward(self, x): + if self.activate_first: + x = self.relu0(x) + x = self.depthwise(x) + x = self.bn1(x) + if not self.activate_first: + x = self.relu1(x) + x = self.pointwise(x) + x = self.bn2(x) + if not self.activate_first: + x = self.relu2(x) + return x + + +class Block(nn.Module): + def __init__(self, + in_filters, + out_filters, + strides=1, + atrous=None, + grow_first=True, + activate_first=True, + inplace=True): + super(Block, self).__init__() + if atrous is None: + atrous = [1] * 3 + elif isinstance(atrous, int): + atrous_list = [atrous] * 3 + atrous = atrous_list + idx = 0 + self.head_relu = True + if out_filters != in_filters or strides != 1: + self.skip = nn.Conv2d(in_filters, + out_filters, + 1, + stride=strides, + bias=False) + self.skipbn = nn.BatchNorm2d(out_filters, momentum=bn_mom) + self.head_relu = False + else: + self.skip = None + + self.hook_layer = None + if grow_first: + filters = out_filters + else: + filters = in_filters + self.sepconv1 = SeparableConv2d(in_filters, + filters, + 3, + stride=1, + padding=1 * atrous[0], + dilation=atrous[0], + bias=False, + activate_first=activate_first, + inplace=self.head_relu) + self.sepconv2 = SeparableConv2d(filters, + out_filters, + 3, + stride=1, + padding=1 * atrous[1], + dilation=atrous[1], + bias=False, + activate_first=activate_first) + self.sepconv3 = SeparableConv2d(out_filters, + out_filters, + 3, + stride=strides, + padding=1 * atrous[2], + dilation=atrous[2], + bias=False, + activate_first=activate_first, + inplace=inplace) + + def forward(self, inp): + + if self.skip is not None: + skip = self.skip(inp) + skip = self.skipbn(skip) + else: + skip = inp + + x = self.sepconv1(inp) + x = self.sepconv2(x) + self.hook_layer = x + x = self.sepconv3(x) + + x += skip + return x + + +class Xception(nn.Module): + """ + Xception optimized for the ImageNet dataset, as specified in + https://arxiv.org/pdf/1610.02357.pdf + """ + def __init__(self, downsample_factor): + """ Constructor + Args: + num_classes: number of classes + """ + super(Xception, self).__init__() + + stride_list = None + if downsample_factor == 8: + stride_list = [2, 1, 1] + elif downsample_factor == 16: + stride_list = [2, 2, 1] + else: + raise ValueError( + 'xception.py: output stride=%d is not supported.' % os) + self.conv1 = nn.Conv2d(3, 32, 3, 2, 1, bias=False) + self.bn1 = nn.BatchNorm2d(32, momentum=bn_mom) + self.relu = nn.ReLU(inplace=True) + + self.conv2 = nn.Conv2d(32, 64, 3, 1, 1, bias=False) + self.bn2 = nn.BatchNorm2d(64, momentum=bn_mom) + #do relu here + + self.block1 = Block(64, 128, 2) + self.block2 = Block(128, 256, stride_list[0], inplace=False) + self.block3 = Block(256, 728, stride_list[1]) + + rate = 16 // downsample_factor + self.block4 = Block(728, 728, 1, atrous=rate) + self.block5 = Block(728, 728, 1, atrous=rate) + self.block6 = Block(728, 728, 1, atrous=rate) + self.block7 = Block(728, 728, 1, atrous=rate) + + self.block8 = Block(728, 728, 1, atrous=rate) + self.block9 = Block(728, 728, 1, atrous=rate) + self.block10 = Block(728, 728, 1, atrous=rate) + self.block11 = Block(728, 728, 1, atrous=rate) + + self.block12 = Block(728, 728, 1, atrous=rate) + self.block13 = Block(728, 728, 1, atrous=rate) + self.block14 = Block(728, 728, 1, atrous=rate) + self.block15 = Block(728, 728, 1, atrous=rate) + + self.block16 = Block(728, + 728, + 1, + atrous=[1 * rate, 1 * rate, 1 * rate]) + self.block17 = Block(728, + 728, + 1, + atrous=[1 * rate, 1 * rate, 1 * rate]) + self.block18 = Block(728, + 728, + 1, + atrous=[1 * rate, 1 * rate, 1 * rate]) + self.block19 = Block(728, + 728, + 1, + atrous=[1 * rate, 1 * rate, 1 * rate]) + + self.block20 = Block(728, + 1024, + stride_list[2], + atrous=rate, + grow_first=False) + self.conv3 = SeparableConv2d(1024, + 1536, + 3, + 1, + 1 * rate, + dilation=rate, + activate_first=False) + + self.conv4 = SeparableConv2d(1536, + 1536, + 3, + 1, + 1 * rate, + dilation=rate, + activate_first=False) + + self.conv5 = SeparableConv2d(1536, + 2048, + 3, + 1, + 1 * rate, + dilation=rate, + activate_first=False) + self.layers = [] + + #------- init weights -------- + for m in self.modules(): + if isinstance(m, nn.Conv2d): + n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels + m.weight.data.normal_(0, math.sqrt(2. / n)) + elif isinstance(m, nn.BatchNorm2d): + m.weight.data.fill_(1) + m.bias.data.zero_() + #----------------------------- + + def forward(self, input): + self.layers = [] + x = self.conv1(input) + x = self.bn1(x) + x = self.relu(x) + x = self.conv2(x) + x = self.bn2(x) + x = self.relu(x) + + x = self.block1(x) + x = self.block2(x) + low_featrue_layer = self.block2.hook_layer + x = self.block3(x) + x = self.block4(x) + x = self.block5(x) + x = self.block6(x) + x = self.block7(x) + x = self.block8(x) + x = self.block9(x) + x = self.block10(x) + x = self.block11(x) + x = self.block12(x) + x = self.block13(x) + x = self.block14(x) + x = self.block15(x) + x = self.block16(x) + x = self.block17(x) + x = self.block18(x) + x = self.block19(x) + x = self.block20(x) + + x = self.conv3(x) + + x = self.conv4(x) + + x = self.conv5(x) + return low_featrue_layer, x + + +def load_url(url, model_dir='./model_data', map_location=None): + if not os.path.exists(model_dir): + os.makedirs(model_dir) + filename = url.split('/')[-1] + cached_file = os.path.join(model_dir, filename) + if os.path.exists(cached_file): + return torch.load(cached_file, map_location=map_location) + else: + return model_zoo.load_url(url, model_dir=model_dir) + + +def xception(pretrained=True, downsample_factor=16): + model = Xception(downsample_factor=downsample_factor) + if pretrained: + model.load_state_dict(load_url( + 'https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/xception_pytorch_imagenet.pth' + ), + strict=False) + return model diff --git a/new_predict.py b/new_predict.py new file mode 100644 index 0000000..001311a --- /dev/null +++ b/new_predict.py @@ -0,0 +1,82 @@ +import os +import time +import json + +import torch +from torchvision import transforms +import numpy as np +from PIL import Image + +from src import deeplabv3_resnet50 + + +def time_synchronized(): + torch.cuda.synchronize() if torch.cuda.is_available() else None + return time.time() + + +def main(): + aux = False # inference time not need aux_classifier + classes = 20 + weights_path = "./save_weights/model_29.pth" + img_path = "./test.jpg" + palette_path = "./palette.json" + assert os.path.exists(weights_path), f"weights {weights_path} not found." + assert os.path.exists(img_path), f"image {img_path} not found." + assert os.path.exists(palette_path), f"palette {palette_path} not found." + with open(palette_path, "rb") as f: + pallette_dict = json.load(f) + pallette = [] + for v in pallette_dict.values(): + pallette += v + + # get devices + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + print("using {} device.".format(device)) + + # create model + model = deeplabv3_resnet50(aux=aux, num_classes=classes+1) + + # delete weights about aux_classifier + weights_dict = torch.load(weights_path, map_location='cpu')['model'] + for k in list(weights_dict.keys()): + if "aux" in k: + del weights_dict[k] + + # load weights + model.load_state_dict(weights_dict) + model.to(device) + + # load image + original_img = Image.open(img_path) + + # from pil image to tensor and normalize + data_transform = transforms.Compose([transforms.Resize(520), + transforms.ToTensor(), + transforms.Normalize(mean=(0.485, 0.456, 0.406), + std=(0.229, 0.224, 0.225))]) + img = data_transform(original_img) + # expand batch dimension + img = torch.unsqueeze(img, dim=0) + + model.eval() # 进入验证模式 + with torch.no_grad(): + # init model + img_height, img_width = img.shape[-2:] + init_img = torch.zeros((1, 3, img_height, img_width), device=device) + model(init_img) + + t_start = time_synchronized() + output = model(img.to(device)) + t_end = time_synchronized() + print("inference time: {}".format(t_end - t_start)) + + prediction = output['out'].argmax(1).squeeze(0) + prediction = prediction.to("cpu").numpy().astype(np.uint8) + mask = Image.fromarray(prediction) + mask.putpalette(pallette) + mask.save("test_result.png") + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/predict.py b/predict.py new file mode 100644 index 0000000..70d92dc --- /dev/null +++ b/predict.py @@ -0,0 +1,165 @@ +#----------------------------------------------------# +# 将单张图片预测、摄像头检测和FPS测试功能 +# 整合到了一个py文件中,通过指定mode进行模式的修改。 +#----------------------------------------------------# +import time + +import cv2 +import numpy as np +from PIL import Image + +from deeplab import DeeplabV3 + +if __name__ == "__main__": + #-------------------------------------------------------------------------# + # 如果想要修改对应种类的颜色,到__init__函数里修改self.colors即可 + #-------------------------------------------------------------------------# + deeplab = DeeplabV3() + #----------------------------------------------------------------------------------------------------------# + # mode用于指定测试的模式: + # 'predict' 表示单张图片预测,如果想对预测过程进行修改,如保存图片,截取对象等,可以先看下方详细的注释 + # 'video' 表示视频检测,可调用摄像头或者视频进行检测,详情查看下方注释。 + # 'fps' 表示测试fps,使用的图片是img里面的street.jpg,详情查看下方注释。 + # 'dir_predict' 表示遍历文件夹进行检测并保存。默认遍历img文件夹,保存img_out文件夹,详情查看下方注释。 + # 'export_onnx' 表示将模型导出为onnx,需要pytorch1.7.1以上。 + #----------------------------------------------------------------------------------------------------------# + mode = "predict" + #-------------------------------------------------------------------------# + # count 指定了是否进行目标的像素点计数(即面积)与比例计算 + # name_classes 区分的种类,和json_to_dataset里面的一样,用于打印种类和数量 + # + # count、name_classes仅在mode='predict'时有效 + #-------------------------------------------------------------------------# + count = False + name_classes = ["background", "pl5", "pl20", "pl30", "pl40", "pl50", "pl60", "pl70", "pl80", "pl100", "pl120", "pm20", "pm55","pr40","p11", "pn", "pne", "p26", "i2", "i4", "i5", "ip", "il60", "il80", "il100", "p5", "p10", "p23", "p3", "pg", "p19", "p12", "p6", "p27", "ph4", "ph4.5", "ph5", "pm30", "w55", "w59", "w13", "w57", "w32", "wo", "io", "po", "indicative"] + # name_classes = ["background","cat","dog"] + #----------------------------------------------------------------------------------------------------------# + # video_path 用于指定视频的路径,当video_path=0时表示检测摄像头 + # 想要检测视频,则设置如video_path = "xxx.mp4"即可,代表读取出根目录下的xxx.mp4文件。 + # video_save_path 表示视频保存的路径,当video_save_path=""时表示不保存 + # 想要保存视频,则设置如video_save_path = "yyy.mp4"即可,代表保存为根目录下的yyy.mp4文件。 + # video_fps 用于保存的视频的fps + # + # video_path、video_save_path和video_fps仅在mode='video'时有效 + # 保存视频时需要ctrl+c退出或者运行到最后一帧才会完成完整的保存步骤。 + #----------------------------------------------------------------------------------------------------------# + video_path = 0 + video_save_path = "" + video_fps = 25.0 + #----------------------------------------------------------------------------------------------------------# + # test_interval 用于指定测量fps的时候,图片检测的次数。理论上test_interval越大,fps越准确。 + # fps_image_path 用于指定测试的fps图片 + # + # test_interval和fps_image_path仅在mode='fps'有效 + #----------------------------------------------------------------------------------------------------------# + test_interval = 100 + fps_image_path = "img/73473.jpg" + #-------------------------------------------------------------------------# + # dir_origin_path 指定了用于检测的图片的文件夹路径 + # dir_save_path 指定了检测完图片的保存路径 + # + # dir_origin_path和dir_save_path仅在mode='dir_predict'时有效 + #-------------------------------------------------------------------------# + dir_origin_path = "imgs/" + dir_save_path = "img_out/" + #-------------------------------------------------------------------------# + # simplify 使用Simplify onnx + # onnx_save_path 指定了onnx的保存路径 + #-------------------------------------------------------------------------# + simplify = True + onnx_save_path = "model_data/models.onnx" + + if mode == "predict": + ''' + predict.py有几个注意点 + 1、该代码无法直接进行批量预测,如果想要批量预测,可以利用os.listdir()遍历文件夹,利用Image.open打开图片文件进行预测。 + 具体流程可以参考get_miou_prediction.py,在get_miou_prediction.py即实现了遍历。 + 2、如果想要保存,利用r_image.save("img.jpg")即可保存。 + 3、如果想要原图和分割图不混合,可以把blend参数设置成False。 + 4、如果想根据mask获取对应的区域,可以参考detect_image函数中,利用预测结果绘图的部分,判断每一个像素点的种类,然后根据种类获取对应的部分。 + seg_img = np.zeros((np.shape(pr)[0],np.shape(pr)[1],3)) + for c in range(self.num_classes): + seg_img[:, :, 0] += ((pr == c)*( self.colors[c][0] )).astype('uint8') + seg_img[:, :, 1] += ((pr == c)*( self.colors[c][1] )).astype('uint8') + seg_img[:, :, 2] += ((pr == c)*( self.colors[c][2] )).astype('uint8') + ''' + while True: + img = input('Input image filename:') + try: + image = Image.open(img) + except: + print('Open Error! Try again!') + continue + else: + r_image = deeplab.detect_image(image, count=count, name_classes=name_classes) + r_image.show() + + elif mode == "video": + capture=cv2.VideoCapture(video_path) + if video_save_path!="": + fourcc = cv2.VideoWriter_fourcc(*'XVID') + size = (int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)), int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))) + out = cv2.VideoWriter(video_save_path, fourcc, video_fps, size) + + ref, frame = capture.read() + if not ref: + raise ValueError("未能正确读取摄像头(视频),请注意是否正确安装摄像头(是否正确填写视频路径)。") + + fps = 0.0 + while(True): + t1 = time.time() + # 读取某一帧 + ref, frame = capture.read() + if not ref: + break + # 格式转变,BGRtoRGB + frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB) + # 转变成Image + frame = Image.fromarray(np.uint8(frame)) + # 进行检测 + frame = np.array(deeplab.detect_image(frame)) + # RGBtoBGR满足opencv显示格式 + frame = cv2.cvtColor(frame,cv2.COLOR_RGB2BGR) + + fps = ( fps + (1./(time.time()-t1)) ) / 2 + print("fps= %.2f"%(fps)) + frame = cv2.putText(frame, "fps= %.2f"%(fps), (0, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + + cv2.imshow("video",frame) + c= cv2.waitKey(1) & 0xff + if video_save_path!="": + out.write(frame) + + if c==27: + capture.release() + break + print("Video Detection Done!") + capture.release() + if video_save_path!="": + print("Save processed video to the path :" + video_save_path) + out.release() + cv2.destroyAllWindows() + + elif mode == "fps": + img = Image.open(fps_image_path) + tact_time = deeplab.get_FPS(img, test_interval) + print(str(tact_time) + ' seconds, ' + str(1/tact_time) + 'FPS, @batch_size 1') + + elif mode == "dir_predict": + import os + from tqdm import tqdm + + img_names = os.listdir(dir_origin_path) + for img_name in tqdm(img_names): + if img_name.lower().endswith(('.bmp', '.dib', '.png', '.jpg', '.jpeg', '.pbm', '.pgm', '.ppm', '.tif', '.tiff')): + image_path = os.path.join(dir_origin_path, img_name) + image = Image.open(image_path) + r_image = deeplab.detect_image(image) + if not os.path.exists(dir_save_path): + os.makedirs(dir_save_path) + r_image.save(os.path.join(dir_save_path, img_name)) + elif mode == "export_onnx": + deeplab.convert_to_onnx(simplify, onnx_save_path) + + else: + raise AssertionError("Please specify the correct mode: 'predict', 'video', 'fps' or 'dir_predict'.") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a4e6b7d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +scipy==1.2.1 +numpy==1.17.0 +matplotlib==3.1.2 +opencv_python==4.1.2.30 +torch==1.2.0 +torchvision==0.4.0 +tqdm==4.60.0 +Pillow==8.2.0 +h5py==2.10.0 diff --git a/summary.py b/summary.py new file mode 100644 index 0000000..3c2cb7c --- /dev/null +++ b/summary.py @@ -0,0 +1,30 @@ +#--------------------------------------------# +# 该部分代码用于看网络结构 +#--------------------------------------------# +import torch +from thop import clever_format, profile +from torchsummary import summary + +from nets.deeplabv3_plus import DeepLab + +if __name__ == "__main__": + input_shape = [512, 512] + num_classes = 47 + backbone = 'mobilenet' + + device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + model = DeepLab(num_classes=num_classes, backbone=backbone, downsample_factor=16, pretrained=False).to(device) + summary(model, (3, input_shape[0], input_shape[1])) + + dummy_input = torch.randn(1, 3, input_shape[0], input_shape[1]).to(device) + flops, params = profile(model.to(device), (dummy_input, ), verbose=False) + #--------------------------------------------------------# + # flops * 2是因为profile没有将卷积作为两个operations + # 有些论文将卷积算乘法、加法两个operations。此时乘2 + # 有些论文只考虑乘法的运算次数,忽略加法。此时不乘2 + # 本代码选择乘2,参考YOLOX。 + #--------------------------------------------------------# + flops = flops * 2 + flops, params = clever_format([flops, params], "%.3f") + print('Total GFLOPS: %s' % (flops)) + print('Total params: %s' % (params)) diff --git a/train.py b/train.py new file mode 100644 index 0000000..9df77b8 --- /dev/null +++ b/train.py @@ -0,0 +1,522 @@ +import os +import datetime + +import numpy as np +import torch +import torch.backends.cudnn as cudnn +import torch.distributed as dist +import torch.optim as optim +from torch.utils.data import DataLoader + +from nets.deeplabv3_plus import DeepLab +from nets.deeplabv3_training import (get_lr_scheduler, set_optimizer_lr, + weights_init) +from utils.callbacks import LossHistory, EvalCallback +from utils.dataloader import DeeplabDataset, deeplab_dataset_collate +from utils.utils import download_weights, show_config +from utils.utils_fit import fit_one_epoch + +''' +训练自己的语义分割模型一定需要注意以下几点: +1、训练前仔细检查自己的格式是否满足要求,该库要求数据集格式为VOC格式,需要准备好的内容有输入图片和标签 + 输入图片为.jpg图片,无需固定大小,传入训练前会自动进行resize。 + 灰度图会自动转成RGB图片进行训练,无需自己修改。 + 输入图片如果后缀非jpg,需要自己批量转成jpg后再开始训练。 + + 标签为png图片,无需固定大小,传入训练前会自动进行resize。 + 由于许多同学的数据集是网络上下载的,标签格式并不符合,需要再度处理。一定要注意!标签的每个像素点的值就是这个像素点所属的种类。 + 网上常见的数据集总共对输入图片分两类,背景的像素点值为0,目标的像素点值为255。这样的数据集可以正常运行但是预测是没有效果的! + 需要改成,背景的像素点值为0,目标的像素点值为1。 + 如果格式有误,参考:https://github.com/bubbliiiing/segmentation-format-fix + +2、损失值的大小用于判断是否收敛,比较重要的是有收敛的趋势,即验证集损失不断下降,如果验证集损失基本上不改变的话,模型基本上就收敛了。 + 损失值的具体大小并没有什么意义,大和小只在于损失的计算方式,并不是接近于0才好。如果想要让损失好看点,可以直接到对应的损失函数里面除上10000。 + 训练过程中的损失值会保存在logs文件夹下的loss_%Y_%m_%d_%H_%M_%S文件夹中 + +3、训练好的权值文件保存在logs文件夹中,每个训练世代(Epoch)包含若干训练步长(Step),每个训练步长(Step)进行一次梯度下降。 + 如果只是训练了几个Step是不会保存的,Epoch和Step的概念要捋清楚一下。 +''' +if __name__ == "__main__": + #---------------------------------# + # Cuda 是否使用Cuda + # 没有GPU可以设置成False + #---------------------------------# + Cuda = True + #---------------------------------------------------------------------# + # distributed 用于指定是否使用单机多卡分布式运行 + # 终端指令仅支持Ubuntu。CUDA_VISIBLE_DEVICES用于在Ubuntu下指定显卡。 + # Windows系统下默认使用DP模式调用所有显卡,不支持DDP。 + # DP模式: + # 设置 distributed = False + # 在终端中输入 CUDA_VISIBLE_DEVICES=0,1 python train.py + # DDP模式: + # 设置 distributed = True + # 在终端中输入 CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 train.py + #---------------------------------------------------------------------# + distributed = False + #---------------------------------------------------------------------# + # sync_bn 是否使用sync_bn,DDP模式多卡可用 + #---------------------------------------------------------------------# + sync_bn = False + #---------------------------------------------------------------------# + # fp16 是否使用混合精度训练 + # 可减少约一半的显存、需要pytorch1.7.1以上 + #---------------------------------------------------------------------# + fp16 = False + #-----------------------------------------------------# + # num_classes 训练自己的数据集必须要修改的 + # 自己需要的分类个数+1,如2+1 + #-----------------------------------------------------# + num_classes = 46 + #---------------------------------# + # 所使用的的主干网络: + # mobilenet + # xception + #---------------------------------# + backbone = "mobilenet" + #----------------------------------------------------------------------------------------------------------------------------# + # pretrained 是否使用主干网络的预训练权重,此处使用的是主干的权重,因此是在模型构建的时候进行加载的。 + # 如果设置了model_path,则主干的权值无需加载,pretrained的值无意义。 + # 如果不设置model_path,pretrained = True,此时仅加载主干开始训练。 + # 如果不设置model_path,pretrained = False,Freeze_Train = Fasle,此时从0开始训练,且没有冻结主干的过程。 + #----------------------------------------------------------------------------------------------------------------------------# + pretrained = False + #----------------------------------------------------------------------------------------------------------------------------# + # 权值文件的下载请看README,可以通过网盘下载。模型的 预训练权重 对不同数据集是通用的,因为特征是通用的。 + # 模型的 预训练权重 比较重要的部分是 主干特征提取网络的权值部分,用于进行特征提取。 + # 预训练权重对于99%的情况都必须要用,不用的话主干部分的权值太过随机,特征提取效果不明显,网络训练的结果也不会好 + # 训练自己的数据集时提示维度不匹配正常,预测的东西都不一样了自然维度不匹配 + # + # 如果训练过程中存在中断训练的操作,可以将model_path设置成logs文件夹下的权值文件,将已经训练了一部分的权值再次载入。 + # 同时修改下方的 冻结阶段 或者 解冻阶段 的参数,来保证模型epoch的连续性。 + # + # 当model_path = ''的时候不加载整个模型的权值。 + # + # 此处使用的是整个模型的权重,因此是在train.py进行加载的,pretrain不影响此处的权值加载。 + # 如果想要让模型从主干的预训练权值开始训练,则设置model_path = '',pretrain = True,此时仅加载主干。 + # 如果想要让模型从0开始训练,则设置model_path = '',pretrain = Fasle,Freeze_Train = Fasle,此时从0开始训练,且没有冻结主干的过程。 + # + # 一般来讲,网络从0开始的训练效果会很差,因为权值太过随机,特征提取效果不明显,因此非常、非常、非常不建议大家从0开始训练! + # 如果一定要从0开始,可以了解imagenet数据集,首先训练分类模型,获得网络的主干部分权值,分类模型的 主干部分 和该模型通用,基于此进行训练。 + #----------------------------------------------------------------------------------------------------------------------------# + model_path = "model_data/deeplab_mobilenetv2.pth" + #---------------------------------------------------------# + # downsample_factor 下采样的倍数8、16 + # 8下采样的倍数较小、理论上效果更好。 + # 但也要求更大的显存 + #---------------------------------------------------------# + downsample_factor = 16 + #------------------------------# + # 输入图片的大小 + #------------------------------# + input_shape = [1024, 1024] + + #----------------------------------------------------------------------------------------------------------------------------# + # 训练分为两个阶段,分别是冻结阶段和解冻阶段。设置冻结阶段是为了满足机器性能不足的同学的训练需求。 + # 冻结训练需要的显存较小,显卡非常差的情况下,可设置Freeze_Epoch等于UnFreeze_Epoch,此时仅仅进行冻结训练。 + # + # 在此提供若干参数设置建议,各位训练者根据自己的需求进行灵活调整: + # (一)从整个模型的预训练权重开始训练: + # Adam: + # Init_Epoch = 0,Freeze_Epoch = 50,UnFreeze_Epoch = 100,Freeze_Train = True,optimizer_type = 'adam',Init_lr = 5e-4,weight_decay = 0。(冻结) + # Init_Epoch = 0,UnFreeze_Epoch = 100,Freeze_Train = False,optimizer_type = 'adam',Init_lr = 5e-4,weight_decay = 0。(不冻结) + # SGD: + # Init_Epoch = 0,Freeze_Epoch = 50,UnFreeze_Epoch = 100,Freeze_Train = True,optimizer_type = 'sgd',Init_lr = 7e-3,weight_decay = 1e-4。(冻结) + # Init_Epoch = 0,UnFreeze_Epoch = 100,Freeze_Train = False,optimizer_type = 'sgd',Init_lr = 7e-3,weight_decay = 1e-4。(不冻结) + # 其中:UnFreeze_Epoch可以在100-300之间调整。 + # (二)从主干网络的预训练权重开始训练: + # Adam: + # Init_Epoch = 0,Freeze_Epoch = 50,UnFreeze_Epoch = 100,Freeze_Train = True,optimizer_type = 'adam',Init_lr = 5e-4,weight_decay = 0。(冻结) + # Init_Epoch = 0,UnFreeze_Epoch = 100,Freeze_Train = False,optimizer_type = 'adam',Init_lr = 5e-4,weight_decay = 0。(不冻结) + # SGD: + # Init_Epoch = 0,Freeze_Epoch = 50,UnFreeze_Epoch = 120,Freeze_Train = True,optimizer_type = 'sgd',Init_lr = 7e-3,weight_decay = 1e-4。(冻结) + # Init_Epoch = 0,UnFreeze_Epoch = 120,Freeze_Train = False,optimizer_type = 'sgd',Init_lr = 7e-3,weight_decay = 1e-4。(不冻结) + # 其中:由于从主干网络的预训练权重开始训练,主干的权值不一定适合语义分割,需要更多的训练跳出局部最优解。 + # UnFreeze_Epoch可以在120-300之间调整。 + # Adam相较于SGD收敛的快一些。因此UnFreeze_Epoch理论上可以小一点,但依然推荐更多的Epoch。 + # (三)batch_size的设置: + # 在显卡能够接受的范围内,以大为好。显存不足与数据集大小无关,提示显存不足(OOM或者CUDA out of memory)请调小batch_size。 + # 受到BatchNorm层影响,batch_size最小为2,不能为1。 + # 正常情况下Freeze_batch_size建议为Unfreeze_batch_size的1-2倍。不建议设置的差距过大,因为关系到学习率的自动调整。 + #----------------------------------------------------------------------------------------------------------------------------# + #------------------------------------------------------------------# + # 冻结阶段训练参数 + # 此时模型的主干被冻结了,特征提取网络不发生改变 + # 占用的显存较小,仅对网络进行微调 + # Init_Epoch 模型当前开始的训练世代,其值可以大于Freeze_Epoch,如设置: + # Init_Epoch = 60、Freeze_Epoch = 50、UnFreeze_Epoch = 100 + # 会跳过冻结阶段,直接从60代开始,并调整对应的学习率。 + # (断点续练时使用) + # Freeze_Epoch 模型冻结训练的Freeze_Epoch + # (当Freeze_Train=False时失效) + # Freeze_batch_size 模型冻结训练的batch_size + # (当Freeze_Train=False时失效) + #------------------------------------------------------------------# + Init_Epoch = 0 + Freeze_Epoch = 400 + Freeze_batch_size = 8 + #------------------------------------------------------------------# + # 解冻阶段训练参数 + # 此时模型的主干不被冻结了,特征提取网络会发生改变 + # 占用的显存较大,网络所有的参数都会发生改变 + # UnFreeze_Epoch 模型总共训练的epoch + # Unfreeze_batch_size 模型在解冻后的batch_size + #------------------------------------------------------------------# + UnFreeze_Epoch = 200 + Unfreeze_batch_size = 4 + #------------------------------------------------------------------# + # Freeze_Train 是否进行冻结训练 + # 默认先冻结主干训练后解冻训练。 + #------------------------------------------------------------------# + Freeze_Train = True + + #------------------------------------------------------------------# + # 其它训练参数:学习率、优化器、学习率下降有关 + #------------------------------------------------------------------# + #------------------------------------------------------------------# + # Init_lr 模型的最大学习率 + # 当使用Adam优化器时建议设置 Init_lr=5e-4 + # 当使用SGD优化器时建议设置 Init_lr=7e-3 + # Min_lr 模型的最小学习率,默认为最大学习率的0.01 + #------------------------------------------------------------------# + Init_lr = 7e-3 + Min_lr = Init_lr * 0.01 + #------------------------------------------------------------------# + # optimizer_type 使用到的优化器种类,可选的有adam、sgd + # 当使用Adam优化器时建议设置 Init_lr=5e-4 + # 当使用SGD优化器时建议设置 Init_lr=7e-3 + # momentum 优化器内部使用到的momentum参数 + # weight_decay 权值衰减,可防止过拟合 + # adam会导致weight_decay错误,使用adam时建议设置为0。 + #------------------------------------------------------------------# + optimizer_type = "sgd" + momentum = 0.9 + weight_decay = 1e-4 + #------------------------------------------------------------------# + # lr_decay_type 使用到的学习率下降方式,可选的有'step'、'cos' + #------------------------------------------------------------------# + lr_decay_type = 'cos' + #------------------------------------------------------------------# + # save_period 多少个epoch保存一次权值 + #------------------------------------------------------------------# + save_period = 5 + #------------------------------------------------------------------# + # save_dir 权值与日志文件保存的文件夹 + #------------------------------------------------------------------# + save_dir = 'logs' + #------------------------------------------------------------------# + # eval_flag 是否在训练时进行评估,评估对象为验证集 + # eval_period 代表多少个epoch评估一次,不建议频繁的评估 + # 评估需要消耗较多的时间,频繁评估会导致训练非常慢 + # 此处获得的mAP会与get_map.py获得的会有所不同,原因有二: + # (一)此处获得的mAP为验证集的mAP。 + # (二)此处设置评估参数较为保守,目的是加快评估速度。 + #------------------------------------------------------------------# + eval_flag = True + eval_period = 5 + + #------------------------------------------------------------------# + # VOCdevkit_path 数据集路径 + #------------------------------------------------------------------# + VOCdevkit_path = 'VOCdevkit' + #------------------------------------------------------------------# + # 建议选项: + # 种类少(几类)时,设置为True + # 种类多(十几类)时,如果batch_size比较大(10以上),那么设置为True + # 种类多(十几类)时,如果batch_size比较小(10以下),那么设置为False + #------------------------------------------------------------------# + dice_loss = False + #------------------------------------------------------------------# + # 是否使用focal loss来防止正负样本不平衡 + #------------------------------------------------------------------# + focal_loss = False + #------------------------------------------------------------------# + # 是否给不同种类赋予不同的损失权值,默认是平衡的。 + # 设置的话,注意设置成numpy形式的,长度和num_classes一样。 + # 如: + # num_classes = 3 + # cls_weights = np.array([1, 2, 3], np.float32) + #------------------------------------------------------------------# + cls_weights = np.ones([num_classes], np.float32) + #------------------------------------------------------------------# + # num_workers 用于设置是否使用多线程读取数据,1代表关闭多线程 + # 开启后会加快数据读取速度,但是会占用更多内存 + # keras里开启多线程有些时候速度反而慢了许多 + # 在IO为瓶颈的时候再开启多线程,即GPU运算速度远大于读取图片的速度。 + #------------------------------------------------------------------# + num_workers = 4 + + #------------------------------------------------------# + # 设置用到的显卡 + #------------------------------------------------------# + ngpus_per_node = torch.cuda.device_count() + if distributed: + dist.init_process_group(backend="nccl") + local_rank = int(os.environ["LOCAL_RANK"]) + rank = int(os.environ["RANK"]) + device = torch.device("cuda", local_rank) + if local_rank == 0: + print(f"[{os.getpid()}] (rank = {rank}, local_rank = {local_rank}) training...") + print("Gpu Device Count : ", ngpus_per_node) + else: + device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + local_rank = 0 + + #----------------------------------------------------# + # 下载预训练权重 + #----------------------------------------------------# + if pretrained: + if distributed: + if local_rank == 0: + download_weights(backbone) + dist.barrier() + else: + download_weights(backbone) + + model = DeepLab(num_classes=num_classes, backbone=backbone, downsample_factor=downsample_factor, pretrained=pretrained) + if not pretrained: + weights_init(model) + if model_path != '': + #------------------------------------------------------# + # 权值文件请看README,百度网盘下载 + #------------------------------------------------------# + if local_rank == 0: + print('Load weights {}.'.format(model_path)) + + #------------------------------------------------------# + # 根据预训练权重的Key和模型的Key进行加载 + #------------------------------------------------------# + model_dict = model.state_dict() + pretrained_dict = torch.load(model_path, map_location = device) + load_key, no_load_key, temp_dict = [], [], {} + for k, v in pretrained_dict.items(): + if k in model_dict.keys() and np.shape(model_dict[k]) == np.shape(v): + temp_dict[k] = v + load_key.append(k) + else: + no_load_key.append(k) + model_dict.update(temp_dict) + model.load_state_dict(model_dict) + #------------------------------------------------------# + # 显示没有匹配上的Key + #------------------------------------------------------# + if local_rank == 0: + print("\nSuccessful Load Key:", str(load_key)[:500], "……\nSuccessful Load Key Num:", len(load_key)) + print("\nFail To Load Key:", str(no_load_key)[:500], "……\nFail To Load Key num:", len(no_load_key)) + print("\n\033[1;33;44m温馨提示,head部分没有载入是正常现象,Backbone部分没有载入是错误的。\033[0m") + + #----------------------# + # 记录Loss + #----------------------# + if local_rank == 0: + time_str = datetime.datetime.strftime(datetime.datetime.now(),'%Y_%m_%d_%H_%M_%S') + log_dir = os.path.join(save_dir, "loss_" + str(time_str)) + loss_history = LossHistory(log_dir, model, input_shape=input_shape) + else: + loss_history = None + + #------------------------------------------------------------------# + # torch 1.2不支持amp,建议使用torch 1.7.1及以上正确使用fp16 + # 因此torch1.2这里显示"could not be resolve" + #------------------------------------------------------------------# + if fp16: + from torch.cuda.amp import GradScaler as GradScaler + scaler = GradScaler() + else: + scaler = None + + model_train = model.train() + #----------------------------# + # 多卡同步Bn + #----------------------------# + if sync_bn and ngpus_per_node > 1 and distributed: + model_train = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model_train) + elif sync_bn: + print("Sync_bn is not support in one gpu or not distributed.") + + if Cuda: + if distributed: + #----------------------------# + # 多卡平行运行 + #----------------------------# + model_train = model_train.cuda(local_rank) + model_train = torch.nn.parallel.DistributedDataParallel(model_train, device_ids=[local_rank], find_unused_parameters=True) + else: + model_train = torch.nn.DataParallel(model) + cudnn.benchmark = True + model_train = model_train.cuda() + + #---------------------------# + # 读取数据集对应的txt + #---------------------------# + with open(os.path.join(VOCdevkit_path, "VOC2007/ImageSets/Segmentation/train.txt"),"r") as f: + train_lines = f.readlines() + with open(os.path.join(VOCdevkit_path, "VOC2007/ImageSets/Segmentation/val.txt"),"r") as f: + val_lines = f.readlines() + num_train = len(train_lines) + num_val = len(val_lines) + + if local_rank == 0: + show_config( + num_classes = num_classes, backbone = backbone, model_path = model_path, input_shape = input_shape, + Init_Epoch = Init_Epoch, Freeze_Epoch = Freeze_Epoch, UnFreeze_Epoch = UnFreeze_Epoch, Freeze_batch_size = Freeze_batch_size, Unfreeze_batch_size = Unfreeze_batch_size, Freeze_Train = Freeze_Train, + Init_lr = Init_lr, Min_lr = Min_lr, optimizer_type = optimizer_type, momentum = momentum, lr_decay_type = lr_decay_type, + save_period = save_period, save_dir = save_dir, num_workers = num_workers, num_train = num_train, num_val = num_val + ) + #---------------------------------------------------------# + # 总训练世代指的是遍历全部数据的总次数 + # 总训练步长指的是梯度下降的总次数 + # 每个训练世代包含若干训练步长,每个训练步长进行一次梯度下降。 + # 此处仅建议最低训练世代,上不封顶,计算时只考虑了解冻部分 + #----------------------------------------------------------# + wanted_step = 1.5e4 if optimizer_type == "sgd" else 0.5e4 + total_step = num_train // Unfreeze_batch_size * UnFreeze_Epoch + if total_step <= wanted_step: + if num_train // Unfreeze_batch_size == 0: + raise ValueError('数据集过小,无法进行训练,请扩充数据集。') + wanted_epoch = wanted_step // (num_train // Unfreeze_batch_size) + 1 + print("\n\033[1;33;44m[Warning] 使用%s优化器时,建议将训练总步长设置到%d以上。\033[0m"%(optimizer_type, wanted_step)) + print("\033[1;33;44m[Warning] 本次运行的总训练数据量为%d,Unfreeze_batch_size为%d,共训练%d个Epoch,计算出总训练步长为%d。\033[0m"%(num_train, Unfreeze_batch_size, UnFreeze_Epoch, total_step)) + print("\033[1;33;44m[Warning] 由于总训练步长为%d,小于建议总步长%d,建议设置总世代为%d。\033[0m"%(total_step, wanted_step, wanted_epoch)) + + #------------------------------------------------------# + # 主干特征提取网络特征通用,冻结训练可以加快训练速度 + # 也可以在训练初期防止权值被破坏。 + # Init_Epoch为起始世代 + # Interval_Epoch为冻结训练的世代 + # Epoch总训练世代 + # 提示OOM或者显存不足请调小Batch_size + #------------------------------------------------------# + if True: + UnFreeze_flag = False + #------------------------------------# + # 冻结一定部分训练 + #------------------------------------# + if Freeze_Train: + for param in model.backbone.parameters(): + param.requires_grad = False + + #-------------------------------------------------------------------# + # 如果不冻结训练的话,直接设置batch_size为Unfreeze_batch_size + #-------------------------------------------------------------------# + batch_size = Freeze_batch_size if Freeze_Train else Unfreeze_batch_size + + #-------------------------------------------------------------------# + # 判断当前batch_size,自适应调整学习率 + #-------------------------------------------------------------------# + nbs = 16 + lr_limit_max = 5e-4 if optimizer_type == 'adam' else 1e-1 + lr_limit_min = 3e-4 if optimizer_type == 'adam' else 5e-4 + if backbone == "xception": + lr_limit_max = 1e-4 if optimizer_type == 'adam' else 1e-1 + lr_limit_min = 1e-4 if optimizer_type == 'adam' else 5e-4 + Init_lr_fit = min(max(batch_size / nbs * Init_lr, lr_limit_min), lr_limit_max) + Min_lr_fit = min(max(batch_size / nbs * Min_lr, lr_limit_min * 1e-2), lr_limit_max * 1e-2) + + #---------------------------------------# + # 根据optimizer_type选择优化器 + #---------------------------------------# + optimizer = { + 'adam' : optim.Adam(model.parameters(), Init_lr_fit, betas = (momentum, 0.999), weight_decay = weight_decay), + 'sgd' : optim.SGD(model.parameters(), Init_lr_fit, momentum = momentum, nesterov=True, weight_decay = weight_decay) + }[optimizer_type] + + #---------------------------------------# + # 获得学习率下降的公式 + #---------------------------------------# + lr_scheduler_func = get_lr_scheduler(lr_decay_type, Init_lr_fit, Min_lr_fit, UnFreeze_Epoch) + + #---------------------------------------# + # 判断每一个世代的长度 + #---------------------------------------# + epoch_step = num_train // batch_size + epoch_step_val = num_val // batch_size + + if epoch_step == 0 or epoch_step_val == 0: + raise ValueError("数据集过小,无法继续进行训练,请扩充数据集。") + + train_dataset = DeeplabDataset(train_lines, input_shape, num_classes, True, VOCdevkit_path) + val_dataset = DeeplabDataset(val_lines, input_shape, num_classes, False, VOCdevkit_path) + + if distributed: + train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset, shuffle=True,) + val_sampler = torch.utils.data.distributed.DistributedSampler(val_dataset, shuffle=False,) + batch_size = batch_size // ngpus_per_node + shuffle = False + else: + train_sampler = None + val_sampler = None + shuffle = True + + gen = DataLoader(train_dataset, shuffle = shuffle, batch_size = batch_size, num_workers = num_workers, pin_memory=True, + drop_last = True, collate_fn = deeplab_dataset_collate, sampler=train_sampler) + gen_val = DataLoader(val_dataset , shuffle = shuffle, batch_size = batch_size, num_workers = num_workers, pin_memory=True, + drop_last = True, collate_fn = deeplab_dataset_collate, sampler=val_sampler) + + #----------------------# + # 记录eval的map曲线 + #----------------------# + if local_rank == 0: + eval_callback = EvalCallback(model, input_shape, num_classes, val_lines, VOCdevkit_path, log_dir, Cuda, eval_flag=eval_flag, period=eval_period) + else: + eval_callback = None + + #---------------------------------------# + # 开始模型训练 + #---------------------------------------# + for epoch in range(Init_Epoch, UnFreeze_Epoch): + #---------------------------------------# + # 如果模型有冻结学习部分 + # 则解冻,并设置参数 + #---------------------------------------# + if epoch >= Freeze_Epoch and not UnFreeze_flag and Freeze_Train: + batch_size = Unfreeze_batch_size + + #-------------------------------------------------------------------# + # 判断当前batch_size,自适应调整学习率 + #-------------------------------------------------------------------# + nbs = 16 + lr_limit_max = 5e-4 if optimizer_type == 'adam' else 1e-1 + lr_limit_min = 3e-4 if optimizer_type == 'adam' else 5e-4 + if backbone == "xception": + lr_limit_max = 1e-4 if optimizer_type == 'adam' else 1e-1 + lr_limit_min = 1e-4 if optimizer_type == 'adam' else 5e-4 + Init_lr_fit = min(max(batch_size / nbs * Init_lr, lr_limit_min), lr_limit_max) + Min_lr_fit = min(max(batch_size / nbs * Min_lr, lr_limit_min * 1e-2), lr_limit_max * 1e-2) + #---------------------------------------# + # 获得学习率下降的公式 + #---------------------------------------# + lr_scheduler_func = get_lr_scheduler(lr_decay_type, Init_lr_fit, Min_lr_fit, UnFreeze_Epoch) + + for param in model.backbone.parameters(): + param.requires_grad = True + + epoch_step = num_train // batch_size + epoch_step_val = num_val // batch_size + + if epoch_step == 0 or epoch_step_val == 0: + raise ValueError("数据集过小,无法继续进行训练,请扩充数据集。") + + if distributed: + batch_size = batch_size // ngpus_per_node + + gen = DataLoader(train_dataset, shuffle = shuffle, batch_size = batch_size, num_workers = num_workers, pin_memory=True, + drop_last = True, collate_fn = deeplab_dataset_collate, sampler=train_sampler) + gen_val = DataLoader(val_dataset , shuffle = shuffle, batch_size = batch_size, num_workers = num_workers, pin_memory=True, + drop_last = True, collate_fn = deeplab_dataset_collate, sampler=val_sampler) + + UnFreeze_flag = True + + if distributed: + train_sampler.set_epoch(epoch) + + set_optimizer_lr(optimizer, lr_scheduler_func, epoch) + + fit_one_epoch(model_train, model, loss_history, eval_callback, optimizer, epoch, + epoch_step, epoch_step_val, gen, gen_val, UnFreeze_Epoch, Cuda, dice_loss, focal_loss, cls_weights, num_classes, fp16, scaler, save_period, save_dir, local_rank) + + if distributed: + dist.barrier() + + if local_rank == 0: + loss_history.writer.close() diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..4287ca8 --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1 @@ +# \ No newline at end of file diff --git a/utils/__pycache__/__init__.cpython-38.pyc b/utils/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61e8e95830c6011c05b32f667a00d5b1b8519a28 GIT binary patch literal 145 zcmWIL<>g`kf|WuGk{E&XV-N=!fCL?YxR?b1#LT>y(vr-a;+XjO%)HE!_;|g7%3B;Zx%nxjIjMFa^F9MH F0{~p^A+7)b literal 0 HcmV?d00001 diff --git a/utils/__pycache__/__init__.cpython-39.pyc b/utils/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cbb20ccf9799945213f181802a7e797b7d052e30 GIT binary patch literal 162 zcmYe~<>g`kf>lBbk{E&XV-N=!fCL?YxR?bCWnK{KV f@$s2?nI-Y@dIgoYIBatBQ%ZAE?LZcM24V&P3C}2J literal 0 HcmV?d00001 diff --git a/utils/__pycache__/callbacks.cpython-38.pyc b/utils/__pycache__/callbacks.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a56fde5f6f674d458010ebc09c1c60a51ba565dc GIT binary patch literal 5481 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 YCoDFP{i(4cZOIQ&HMYoVTH|N`1I`CY4*&oF literal 0 HcmV?d00001 diff --git a/utils/__pycache__/dataloader.cpython-38.pyc b/utils/__pycache__/dataloader.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..37f2fab2337841c70ed6131aea75be9128f67737 GIT binary patch literal 4111 zcmaJ^OK%*<5uTpc&c2t+72lF5SwOsT%qWx-JBAb4q8_F}Qvylpuuw1NdO9PE&_Z_4#DnGcOytn`3nJZ>r;RL5qQlZhg_VmdX^7ULVCArs=I5ds;jH2 zYaZtF84c}Kd9?f2MNRuB4bCTAHdgq7HYNR$!biE^_rocX3fM?53P<}vl-DC zVXEWQ9EF=Py3k{tY-UVzv!2MrM}iot>!?lX<4`(1pUz4ooG|H&j=&j znDpplAh8xRp&jZYeXKD}0C$bp6zq^NOXz^MFaT4+1ayQ2nD(`8?G@XsW~3Dj`T^&} zS2`|s*os|Omb`Aa7khE5*LA~IH;AMW23=_bG~+#)Rfce*J+B|gY!BA#AhTlb zwYoCrCzf>kUc6Ver5OcbgOdyJJb?m%B7qXXD<;!!!oqc5Y5)Bw`1rA(*x>1n_s*dG z{kX^Ld+&E36w90rcc}(HKD<|LrFqv{)SjTl|Fo<*jjUCLqAySdl)iRNw5oZTo zNz@^mBnmdK4RxW9*ch<@+=%rN&b4ig>;!@;%Oi&ByxC99nU9l#BC#1!2b7c5$p9GDHmXou)Acg<7#7hu$rLgL*!(KGCo; zMRUFbDNIFR3 z_3I!0_=^X3?x`q^4$PHyzj-i!ys{lMJ3%*b)irWL5cIoEM6=DkuHWk%k+F}6-O+bw z)rxXM_glLd#$eWOj&FOv70b*$<-3~CC`<5w=dIh_iohh zMFFQQAT5t`Z(pVwaLM>*A1FVUMz9~q6c3VcF&}B`MEvPsMcFkPSmw}h;CH6!8VBrmS zJb6{wn-haNrI+J&oNAPF66aWJGfFi`ENo7zz)@#U!o%QKK*47Lq)9fb8p$@9C)w^f z#rX=zK0rT?mFaWJ5rfw^RQvnQ*MImuwZGha-B9gcH+Sk3>&NXXkdq+#6SR(@*&!RU z7~00h$P@?=I2p8VTX6UEFe3~+v%{QlqJN*x#R`NXa=-n8P)-KwOIn)Gz{8yIu)t$VOo4~~e|VI{R72m^pd~aG+9>ZMlpqC=f*xn|#@wPdEUVd+PobR` zW#4|HeZ%+`c9<7gwc8@_q9`Xj@6M@}+=(3uq73`6;alwR=B`K<5}ETv8%~R9$oVs@ zSAgA$I1>i?V_b(6($-4$ODSkrIhj+|Q*#$iZQYpI`qXlmNi3IGzalP7tesf$bX1Y68ZbWK6mSuy*K&TH}1s4Ql1WfRx4>vLib>r&*BvnANjhx6bxJ*xN_ zKMQ^f@l^YQm=_CKoQR)$X$%)JGb3iiBAqh4XR1Bx>wf;FHJqW9#9I?>&d|)Ecpw^R z3-0rN0o;mX14-Bxsisi@_gQVY1YIF0$~F?felahlu|=?3q0RE|$_-8)!8anm|Lw(j80_yLV=qoW`qNdJ zyXOs}sO5EUg#(`C))b{iP{?Vd@2yFh_N5o|*7McdGV{#i{m&W=RBbZ5zPWQ}%T2V% zCxeN6nHqFkjUMlCl;fJruif9czvaUAtDkQ_;=fS)@F1ueNE?d7B+{&ht-iE60mr1V z@o?w(ExfTxr*`%DR-<}aTI#}57iP_Fx8gY9(%2i|)yRvb@ze{G?dYv%Qg7`^hW|7E zbCRrgG1c9ZX&BCJ5x1H`6~~%QvmTdbgY?n;=ji(~qv+HnEzMmR3+F(#f&O(A2oUb5 zF-ls=zLbn7n`F;xsc;axP%J^9T!N5-6=;(Hs`2RtI(L0@H3gI7t~4uODJ2*Y=K>~FPRI>Hp^^= zEwLs2UGO=$bnccsSDsY$(+H_r{3pKTWKS|_t z(T^-#E?7BV0E|*&a6h3%-Fb%Az*UWUnkvww-gn^oeUyeiK5OW^LuM%aU3~B0Q$y7{ zN&Ktm$lj%zZzt$R_-Hi49%|5(w06B1@7c;Xqne3gF5*AJMy1i}H2Hgk*>5lGX8q&~ zNO?3qO5J)d4B^gy#5l;Zr zn^W6bQD0IkRNg88VVp9e%q9y8CP`2wSUO%M=FH)%K%v8z&L@dS{gO)Vh@8UF9fS(} N00B}mO25&j{|7tV1r7iJ literal 0 HcmV?d00001 diff --git a/utils/__pycache__/dataloader.cpython-39.pyc b/utils/__pycache__/dataloader.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a605f5117cb7b981a2603cd208e969647c109019 GIT binary patch literal 4138 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 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/utils.cpython-38.pyc b/utils/__pycache__/utils.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca964bd11f090d3d68963c6271b9b3ae425ce027 GIT binary patch literal 1995 zcmb_dOK%%D5awfdwUXsWjG~Agv@p;FsgPPqo%B#6XoI+D;kFQp8o3BAgw>Lk*4|f1 zN{WSDpn!t@iXNn+{}N9Hdh%b$q3Dp3T*v27;LLEA4*z;1>Qu) zn=2&hu>ajNet60_&*PvsdiLa&m|v<)goFJgh|<%ciekB-&HPgP;Yb~2aV8+1NE(9h zL+p?m78@Xk#*@`B!S`U=m;MPtfzHt>DM?8S4BRPQg$t)34^ZJ2^Z}ZZ(o>`$h4&Gf z;<5rVUCO?BeNid=GoqYxcTP&AoSDaPff)(SmHUvkU2~(qkj}`QU{qA? zX=8_VvpQ~;xJ_9S#M9cVRSMPabAa1;}9vQ3^0OL>40=Ny*?t==U zzq6N%>kUrs8rsSQ&qWq;DZ5dc57lGXv^42s@eo!a%$q?$GFbZwrtAPwU~-DF;bh67 zm_G(w{jUQ|;kgOb9-tAI1ol$E9Go*KoZ>UG*#G+%t6OZ+Ki zY}T&bdpDqKa*&O?&V{fGJE(LEa!zMXf#+DhD9Fq;64WDw3dK{XOdC_L zEevV^nfK!?U|p#K#k(vDRrgF}Np~pXNia| zSrSddYT8YQNq#9Ru+sFGJWv<6|jpZ@}UAIAI}0|E<8 OFzop?|DNx_ulgSiC)$Pp literal 0 HcmV?d00001 diff --git a/utils/__pycache__/utils.cpython-39.pyc b/utils/__pycache__/utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7eadcbc2845ed292f1fbac55f74402281c73e583 GIT binary patch literal 2012 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 Rmv64%wqei9Mnmn${0G94C<_1p literal 0 HcmV?d00001 diff --git a/utils/__pycache__/utils_fit.cpython-39.pyc b/utils/__pycache__/utils_fit.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7835bfc73e885a8e49daf9b75d26da198b10fac5 GIT binary patch literal 3076 zcmbVO&2JpH6(?snyR);q(n_+lvOY()40ID%wPBzIj35aR+X&LcFyJ6X2TBK{omuTj z^JS89YJrO!)ML{KxuywrfnIto`ZokU7HH7}ugN91qR1%-f;@6Y@(MCgqcb3fe2;wm zz2AEzCup}F1kb;Jdo%oa6``+HXZkUr^ClGLK^3A9C&(+m*u&5_5+gM|GbJ8LEze48 zUJWa~nbgy|SJ!Ql*lELS=(d$OY13;$+YW1C{rA{wh4?Y*HNFM==wXk@`djz?cXP&M z<6hj4{6saK2YEk8N^$F>DBd4(=Eqsgdsq_wBuwF3+mE=PP-xW$KI`W+>Mtsr)pG;B zO(<_cVQ*XD3?3~)S%T7qvMjEI_|Twla&YPdW5ltjg{H7V(nX)6&=St)=nKLP1^W{+ zx+>7voFEuSgnfeVp;P0-*LRK4%4k)rh}xk=hZtl}J`=0E=4frSKHA{cxHdtbK7cva zxGrWUc(f^+^si!VY>O+=@8qa7PWIU_B**`)YHj+*{H`)et+b8JLKfw1KAFTZ4 zOCtR0|3>(qX@r+NFj!|=FXqPcVrzmcKjy}>f=nPIK)#?oTNGR4D--lfG+Go(;(FCy zsxs)2xO%WSKrs@FVnt=oDuPi}i!TXsf4u$z?r{h=$Hd{Nb(hoU9*OVEaeNO7dj+Qda)=Vpg7-^qrT@%c)qJ z^x&mpY0}SL5X&tqPGd_raCz~mp=NXUa(;$H}yqO)P zem@Bqix_n6;E2N&z)zP8GjN4)VcfLj*o}%Cx9)^D)g`6I+&o)Db#@9q zJnlUXo<{Co#JHR0VU)N$*F8T{{P@*friU)6Q7@7M3onrOwxv~2pp4cO2_%gB9Jp2N z%SHtBMKs`%Vw?7dvYzGsJ`Fi`cNScgOSPrO@4&z|a*WMX9lcWgvP?Ccj+%%IYqYKKg3E9)T0ZF7d z_Dd2h-H|O79Df)y!2L|N)zUtIO_W=*krzBpV_-m9IwI0315>*CyzMK*=6;mPI#jx6 z-B%%`@YjT71Mr>ws|@tNidt1R`w2UXF`ZQd$)LFX3t7{2QD6vM^oxkbc_?k&31cdq zM4O;Nc1Y)7me(#*zWT4xE_+SoNF@PQ_5vy!Ksy8-@?Bbc0q+kz5~ur$vkdB566{3@ zll3a;WIaFP#SsU+0_!zWNF7CV(>t#g&vcU9R2FxSc$~1Z_6ITFfy8UW*Uz)440#KTv%f=yFu}x7 z#l|*vq0B?6VaIT=13FbZ_-6!U#3lsK8yy3**ft#Uie{J$3a0jq1TQ0t3Y=gZOgT4p8^sqh_ zdu{qE^yo5FvYSPm?VR&eCTg7RPwhFq1>yG;mD{?h+1t^z-b3EPl*CUXPUAk)J5{f$ gBQ;gMtCqE2gRO9sM6avUm?^q#@Ojy2DUZyb06@q+l>h($ literal 0 HcmV?d00001 diff --git a/utils/__pycache__/utils_metrics.cpython-38.pyc b/utils/__pycache__/utils_metrics.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2df3e3f9b4d45fe3fe3ec89e19538ef2ea5750e GIT binary patch literal 5993 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 literal 0 HcmV?d00001 diff --git a/utils/__pycache__/utils_metrics.cpython-39.pyc b/utils/__pycache__/utils_metrics.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00f27d6218039fcaa248cdb562d6f3473b881d9e GIT binary patch literal 5996 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 DaLUGg literal 0 HcmV?d00001 diff --git a/utils/callbacks.py b/utils/callbacks.py new file mode 100644 index 0000000..16ca6e5 --- /dev/null +++ b/utils/callbacks.py @@ -0,0 +1,200 @@ +import os + +import matplotlib +import torch +import torch.nn.functional as F + +matplotlib.use('Agg') +from matplotlib import pyplot as plt +import scipy.signal + +import cv2 +import shutil +import numpy as np + +from PIL import Image +from tqdm import tqdm +from torch.utils.tensorboard import SummaryWriter +from .utils import cvtColor, preprocess_input, resize_image +from .utils_metrics import compute_mIoU + + +class LossHistory(): + def __init__(self, log_dir, model, input_shape): + self.log_dir = log_dir + self.losses = [] + self.val_loss = [] + + os.makedirs(self.log_dir) + self.writer = SummaryWriter(self.log_dir) + try: + dummy_input = torch.randn(2, 3, input_shape[0], input_shape[1]) + self.writer.add_graph(model, dummy_input) + except: + pass + + def append_loss(self, epoch, loss, val_loss): + if not os.path.exists(self.log_dir): + os.makedirs(self.log_dir) + + self.losses.append(loss) + self.val_loss.append(val_loss) + + with open(os.path.join(self.log_dir, "epoch_loss.txt"), 'a') as f: + f.write(str(loss)) + f.write("\n") + with open(os.path.join(self.log_dir, "epoch_val_loss.txt"), 'a') as f: + f.write(str(val_loss)) + f.write("\n") + + self.writer.add_scalar('loss', loss, epoch) + self.writer.add_scalar('val_loss', val_loss, epoch) + self.loss_plot() + + def loss_plot(self): + iters = range(len(self.losses)) + + plt.figure() + plt.plot(iters, self.losses, 'red', linewidth = 2, label='train loss') + plt.plot(iters, self.val_loss, 'coral', linewidth = 2, label='val loss') + try: + if len(self.losses) < 25: + num = 5 + else: + num = 15 + + plt.plot(iters, scipy.signal.savgol_filter(self.losses, num, 3), 'green', linestyle = '--', linewidth = 2, label='smooth train loss') + plt.plot(iters, scipy.signal.savgol_filter(self.val_loss, num, 3), '#8B4513', linestyle = '--', linewidth = 2, label='smooth val loss') + except: + pass + + plt.grid(True) + plt.xlabel('Epoch') + plt.ylabel('Loss') + plt.legend(loc="upper right") + + plt.savefig(os.path.join(self.log_dir, "epoch_loss.png")) + + plt.cla() + plt.close("all") + +class EvalCallback(): + def __init__(self, net, input_shape, num_classes, image_ids, dataset_path, log_dir, cuda, + miou_out_path=".temp_miou_out", eval_flag=True, period=1): + super(EvalCallback, self).__init__() + + self.net = net + self.input_shape = input_shape + self.num_classes = num_classes + self.image_ids = image_ids + self.dataset_path = dataset_path + self.log_dir = log_dir + self.cuda = cuda + self.miou_out_path = miou_out_path + self.eval_flag = eval_flag + self.period = period + + self.image_ids = [image_id.split()[0] for image_id in image_ids] + self.mious = [0] + self.epoches = [0] + if self.eval_flag: + with open(os.path.join(self.log_dir, "epoch_miou.txt"), 'a') as f: + f.write(str(0)) + f.write("\n") + + def get_miou_png(self, image): + #---------------------------------------------------------# + # 在这里将图像转换成RGB图像,防止灰度图在预测时报错。 + # 代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB + #---------------------------------------------------------# + image = cvtColor(image) + orininal_h = np.array(image).shape[0] + orininal_w = np.array(image).shape[1] + #---------------------------------------------------------# + # 给图像增加灰条,实现不失真的resize + # 也可以直接resize进行识别 + #---------------------------------------------------------# + image_data, nw, nh = resize_image(image, (self.input_shape[1],self.input_shape[0])) + #---------------------------------------------------------# + # 添加上batch_size维度 + #---------------------------------------------------------# + image_data = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, np.float32)), (2, 0, 1)), 0) + + with torch.no_grad(): + images = torch.from_numpy(image_data) + if self.cuda: + images = images.cuda() + + #---------------------------------------------------# + # 图片传入网络进行预测 + #---------------------------------------------------# + pr = self.net(images)[0] + #---------------------------------------------------# + # 取出每一个像素点的种类 + #---------------------------------------------------# + pr = F.softmax(pr.permute(1,2,0),dim = -1).cpu().numpy() + #--------------------------------------# + # 将灰条部分截取掉 + #--------------------------------------# + pr = pr[int((self.input_shape[0] - nh) // 2) : int((self.input_shape[0] - nh) // 2 + nh), + int((self.input_shape[1] - nw) // 2) : int((self.input_shape[1] - nw) // 2 + nw)] + #---------------------------------------------------# + # 进行图片的resize + #---------------------------------------------------# + pr = cv2.resize(pr, (orininal_w, orininal_h), interpolation = cv2.INTER_LINEAR) + #---------------------------------------------------# + # 取出每一个像素点的种类 + #---------------------------------------------------# + pr = pr.argmax(axis=-1) + + image = Image.fromarray(np.uint8(pr)) + return image + + def on_epoch_end(self, epoch, model_eval): + if epoch % self.period == 0 and self.eval_flag: + self.net = model_eval + gt_dir = os.path.join(self.dataset_path, "VOC2007/SegmentationClass/") + pred_dir = os.path.join(self.miou_out_path, 'detection-results') + if not os.path.exists(self.miou_out_path): + os.makedirs(self.miou_out_path) + if not os.path.exists(pred_dir): + os.makedirs(pred_dir) + print("Get miou.") + for image_id in tqdm(self.image_ids): + #-------------------------------# + # 从文件中读取图像 + #-------------------------------# + image_path = os.path.join(self.dataset_path, "VOC2007/JPEGImages/"+image_id+".jpg") + image = Image.open(image_path) + #------------------------------# + # 获得预测txt + #------------------------------# + image = self.get_miou_png(image) + image.save(os.path.join(pred_dir, image_id + ".png")) + + print("Calculate miou.") + _, IoUs, _, _ = compute_mIoU(gt_dir, pred_dir, self.image_ids, self.num_classes, None) # 执行计算mIoU的函数 + temp_miou = np.nanmean(IoUs) * 100 + + self.mious.append(temp_miou) + self.epoches.append(epoch) + + with open(os.path.join(self.log_dir, "epoch_miou.txt"), 'a') as f: + f.write(str(temp_miou)) + f.write("\n") + + plt.figure() + plt.plot(self.epoches, self.mious, 'red', linewidth = 2, label='train miou') + + plt.grid(True) + plt.xlabel('Epoch') + plt.ylabel('Miou') + plt.title('A Miou Curve') + plt.legend(loc="upper right") + + plt.savefig(os.path.join(self.log_dir, "epoch_miou.png")) + plt.cla() + plt.close("all") + + print("Get miou done.") + shutil.rmtree(self.miou_out_path) diff --git a/utils/dataloader.py b/utils/dataloader.py new file mode 100644 index 0000000..9ab78b3 --- /dev/null +++ b/utils/dataloader.py @@ -0,0 +1,169 @@ +import os + +import cv2 +import numpy as np +import torch +from PIL import Image +from torch.utils.data.dataset import Dataset + +from utils.utils import cvtColor, preprocess_input + + +class DeeplabDataset(Dataset): + def __init__(self, annotation_lines, input_shape, num_classes, train, dataset_path): + super(DeeplabDataset, self).__init__() + self.annotation_lines = annotation_lines + self.length = len(annotation_lines) + self.input_shape = input_shape + self.num_classes = num_classes + self.train = train + self.dataset_path = dataset_path + + def __len__(self): + return self.length + + def __getitem__(self, index): + annotation_line = self.annotation_lines[index] + name = annotation_line.split()[0] + + #-------------------------------# + # 从文件中读取图像 + #-------------------------------# + jpg = Image.open(os.path.join(os.path.join(self.dataset_path, "VOC2007/JPEGImages"), name + ".jpg")) + png = Image.open(os.path.join(os.path.join(self.dataset_path, "VOC2007/SegmentationClass"), name + ".png")) + #-------------------------------# + # 数据增强 + #-------------------------------# + jpg, png = self.get_random_data(jpg, png, self.input_shape, random = self.train) + + jpg = np.transpose(preprocess_input(np.array(jpg, np.float64)), [2,0,1]) + png = np.array(png) + png[png >= self.num_classes] = self.num_classes + #-------------------------------------------------------# + # 转化成one_hot的形式 + # 在这里需要+1是因为voc数据集有些标签具有白边部分 + # 我们需要将白边部分进行忽略,+1的目的是方便忽略。 + #-------------------------------------------------------# + seg_labels = np.eye(self.num_classes + 1)[png.reshape([-1])] + seg_labels = seg_labels.reshape((int(self.input_shape[0]), int(self.input_shape[1]), self.num_classes + 1)) + + return jpg, png, seg_labels + + def rand(self, a=0, b=1): + return np.random.rand() * (b - a) + a + + def get_random_data(self, image, label, input_shape, jitter=.3, hue=.1, sat=0.7, val=0.3, random=True): + image = cvtColor(image) + label = Image.fromarray(np.array(label)) + #------------------------------# + # 获得图像的高宽与目标高宽 + #------------------------------# + iw, ih = image.size + h, w = input_shape + + if not random: + iw, ih = image.size + scale = min(w/iw, h/ih) + nw = int(iw*scale) + nh = int(ih*scale) + + image = image.resize((nw,nh), Image.BICUBIC) + new_image = Image.new('RGB', [w, h], (128,128,128)) + new_image.paste(image, ((w-nw)//2, (h-nh)//2)) + + label = label.resize((nw,nh), Image.NEAREST) + new_label = Image.new('L', [w, h], (0)) + new_label.paste(label, ((w-nw)//2, (h-nh)//2)) + return new_image, new_label + + #------------------------------------------# + # 对图像进行缩放并且进行长和宽的扭曲 + #------------------------------------------# + new_ar = iw/ih * self.rand(1-jitter,1+jitter) / self.rand(1-jitter,1+jitter) + scale = self.rand(0.25, 2) + if new_ar < 1: + nh = int(scale*h) + nw = int(nh*new_ar) + else: + nw = int(scale*w) + nh = int(nw/new_ar) + image = image.resize((nw,nh), Image.BICUBIC) + label = label.resize((nw,nh), Image.NEAREST) + + #------------------------------------------# + # 翻转图像 + #------------------------------------------# + flip = self.rand()<.5 + if flip: + image = image.transpose(Image.FLIP_LEFT_RIGHT) + label = label.transpose(Image.FLIP_LEFT_RIGHT) + + #------------------------------------------# + # 将图像多余的部分加上灰条 + #------------------------------------------# + dx = int(self.rand(0, w-nw)) + dy = int(self.rand(0, h-nh)) + new_image = Image.new('RGB', (w,h), (128,128,128)) + new_label = Image.new('L', (w,h), (0)) + new_image.paste(image, (dx, dy)) + new_label.paste(label, (dx, dy)) + image = new_image + label = new_label + + image_data = np.array(image, np.uint8) + + #------------------------------------------# + # 高斯模糊 + #------------------------------------------# + blur = self.rand() < 0.25 + if blur: + image_data = cv2.GaussianBlur(image_data, (5, 5), 0) + + #------------------------------------------# + # 旋转 + #------------------------------------------# + rotate = self.rand() < 0.25 + if rotate: + center = (w // 2, h // 2) + rotation = np.random.randint(-10, 11) + M = cv2.getRotationMatrix2D(center, -rotation, scale=1) + image_data = cv2.warpAffine(image_data, M, (w, h), flags=cv2.INTER_CUBIC, borderValue=(128,128,128)) + label = cv2.warpAffine(np.array(label, np.uint8), M, (w, h), flags=cv2.INTER_NEAREST, borderValue=(0)) + + #---------------------------------# + # 对图像进行色域变换 + # 计算色域变换的参数 + #---------------------------------# + r = np.random.uniform(-1, 1, 3) * [hue, sat, val] + 1 + #---------------------------------# + # 将图像转到HSV上 + #---------------------------------# + hue, sat, val = cv2.split(cv2.cvtColor(image_data, cv2.COLOR_RGB2HSV)) + dtype = image_data.dtype + #---------------------------------# + # 应用变换 + #---------------------------------# + 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) + + image_data = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))) + image_data = cv2.cvtColor(image_data, cv2.COLOR_HSV2RGB) + + return image_data, label + + +# DataLoader中collate_fn使用 +def deeplab_dataset_collate(batch): + images = [] + pngs = [] + seg_labels = [] + for img, png, labels in batch: + images.append(img) + pngs.append(png) + seg_labels.append(labels) + images = torch.from_numpy(np.array(images)).type(torch.FloatTensor) + pngs = torch.from_numpy(np.array(pngs)).long() + seg_labels = torch.from_numpy(np.array(seg_labels)).type(torch.FloatTensor) + return images, pngs, seg_labels diff --git a/utils/utils.py b/utils/utils.py new file mode 100644 index 0000000..63f2be1 --- /dev/null +++ b/utils/utils.py @@ -0,0 +1,64 @@ +import numpy as np +from PIL import Image + +#---------------------------------------------------------# +# 将图像转换成RGB图像,防止灰度图在预测时报错。 +# 代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB +#---------------------------------------------------------# +def cvtColor(image): + if len(np.shape(image)) == 3 and np.shape(image)[2] == 3: + return image + else: + image = image.convert('RGB') + return image + +#---------------------------------------------------# +# 对输入图像进行resize +#---------------------------------------------------# +def resize_image(image, size): + iw, ih = image.size + w, h = size + + scale = min(w/iw, h/ih) + nw = int(iw*scale) + nh = int(ih*scale) + + image = image.resize((nw,nh), Image.BICUBIC) + new_image = Image.new('RGB', size, (128,128,128)) + new_image.paste(image, ((w-nw)//2, (h-nh)//2)) + + return new_image, nw, nh + +#---------------------------------------------------# +# 获得学习率 +#---------------------------------------------------# +def get_lr(optimizer): + for param_group in optimizer.param_groups: + return param_group['lr'] + +def preprocess_input(image): + image /= 255.0 + return image + +def show_config(**kwargs): + print('Configurations:') + print('-' * 70) + print('|%25s | %40s|' % ('keys', 'values')) + print('-' * 70) + for key, value in kwargs.items(): + print('|%25s | %40s|' % (str(key), str(value))) + print('-' * 70) + +def download_weights(backbone, model_dir="./model_data"): + import os + from torch.hub import load_state_dict_from_url + + download_urls = { + 'mobilenet' : 'https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/mobilenet_v2.pth.tar', + 'xception' : 'https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/xception_pytorch_imagenet.pth', + } + url = download_urls[backbone] + + if not os.path.exists(model_dir): + os.makedirs(model_dir) + load_state_dict_from_url(url, model_dir) \ No newline at end of file diff --git a/utils/utils_fit.py b/utils/utils_fit.py new file mode 100644 index 0000000..f113705 --- /dev/null +++ b/utils/utils_fit.py @@ -0,0 +1,174 @@ +import os + +import torch +from nets.deeplabv3_training import (CE_Loss, Dice_loss, Focal_Loss, + weights_init) +from tqdm import tqdm + +from utils.utils import get_lr +from utils.utils_metrics import f_score + + +def fit_one_epoch(model_train, model, loss_history, eval_callback, optimizer, epoch, epoch_step, epoch_step_val, gen, gen_val, Epoch, cuda, dice_loss, focal_loss, cls_weights, num_classes, fp16, scaler, save_period, save_dir, local_rank=0): + total_loss = 0 + total_f_score = 0 + + val_loss = 0 + val_f_score = 0 + + if local_rank == 0: + print('Start Train') + pbar = tqdm(total=epoch_step,desc=f'Epoch {epoch + 1}/{Epoch}',postfix=dict,mininterval=0.3) + model_train.train() + for iteration, batch in enumerate(gen): + if iteration >= epoch_step: + break + imgs, pngs, labels = batch + + with torch.no_grad(): + weights = torch.from_numpy(cls_weights) + if cuda: + imgs = imgs.cuda(local_rank) + pngs = pngs.cuda(local_rank) + labels = labels.cuda(local_rank) + weights = weights.cuda(local_rank) + #----------------------# + # 清零梯度 + #----------------------# + optimizer.zero_grad() + if not fp16: + #----------------------# + # 前向传播 + #----------------------# + outputs = model_train(imgs) + #----------------------# + # 计算损失 + #----------------------# + if focal_loss: + loss = Focal_Loss(outputs, pngs, weights, num_classes = num_classes) + else: + loss = CE_Loss(outputs, pngs, weights, num_classes = num_classes) + + if dice_loss: + main_dice = Dice_loss(outputs, labels) + loss = loss + main_dice + + with torch.no_grad(): + #-------------------------------# + # 计算f_score + #-------------------------------# + _f_score = f_score(outputs, labels) + + #----------------------# + # 反向传播 + #----------------------# + loss.backward() + optimizer.step() + else: + from torch.cuda.amp import autocast + with autocast(): + #----------------------# + # 前向传播 + #----------------------# + outputs = model_train(imgs) + #----------------------# + # 计算损失 + #----------------------# + if focal_loss: + loss = Focal_Loss(outputs, pngs, weights, num_classes = num_classes) + else: + loss = CE_Loss(outputs, pngs, weights, num_classes = num_classes) + + if dice_loss: + main_dice = Dice_loss(outputs, labels) + loss = loss + main_dice + + with torch.no_grad(): + #-------------------------------# + # 计算f_score + #-------------------------------# + _f_score = f_score(outputs, labels) + + #----------------------# + # 反向传播 + #----------------------# + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + + total_loss += loss.item() + total_f_score += _f_score.item() + + if local_rank == 0: + pbar.set_postfix(**{'total_loss': total_loss / (iteration + 1), + 'f_score' : total_f_score / (iteration + 1), + 'lr' : get_lr(optimizer)}) + pbar.update(1) + + if local_rank == 0: + pbar.close() + print('Finish Train') + print('Start Validation') + pbar = tqdm(total=epoch_step_val, desc=f'Epoch {epoch + 1}/{Epoch}',postfix=dict,mininterval=0.3) + + model_train.eval() + for iteration, batch in enumerate(gen_val): + if iteration >= epoch_step_val: + break + imgs, pngs, labels = batch + with torch.no_grad(): + weights = torch.from_numpy(cls_weights) + if cuda: + imgs = imgs.cuda(local_rank) + pngs = pngs.cuda(local_rank) + labels = labels.cuda(local_rank) + weights = weights.cuda(local_rank) + + #----------------------# + # 前向传播 + #----------------------# + outputs = model_train(imgs) + #----------------------# + # 计算损失 + #----------------------# + if focal_loss: + loss = Focal_Loss(outputs, pngs, weights, num_classes = num_classes) + else: + loss = CE_Loss(outputs, pngs, weights, num_classes = num_classes) + + if dice_loss: + main_dice = Dice_loss(outputs, labels) + loss = loss + main_dice + #-------------------------------# + # 计算f_score + #-------------------------------# + _f_score = f_score(outputs, labels) + + val_loss += loss.item() + val_f_score += _f_score.item() + + if local_rank == 0: + pbar.set_postfix(**{'val_loss' : val_loss / (iteration + 1), + 'f_score' : val_f_score / (iteration + 1), + 'lr' : get_lr(optimizer)}) + pbar.update(1) + + if local_rank == 0: + pbar.close() + print('Finish Validation') + loss_history.append_loss(epoch + 1, total_loss / epoch_step, val_loss / epoch_step_val) + eval_callback.on_epoch_end(epoch + 1, model_train) + print('Epoch:'+ str(epoch + 1) + '/' + str(Epoch)) + print('Total Loss: %.3f || Val Loss: %.3f ' % (total_loss / epoch_step, val_loss / epoch_step_val)) + + #-----------------------------------------------# + # 保存权值 + #-----------------------------------------------# + if (epoch + 1) % save_period == 0 or epoch + 1 == Epoch: + torch.save(model.state_dict(), os.path.join(save_dir, 'ep%03d-loss%.3f-val_loss%.3f.pth' % (epoch + 1, total_loss / epoch_step, val_loss / epoch_step_val))) + + if len(loss_history.val_loss) <= 1 or (val_loss / epoch_step_val) <= min(loss_history.val_loss): + print('Save best model to best_epoch_weights.pth') + torch.save(model.state_dict(), os.path.join(save_dir, "best_epoch_weights.pth")) + + torch.save(model.state_dict(), os.path.join(save_dir, "last_epoch_weights.pth")) \ No newline at end of file diff --git a/utils/utils_metrics.py b/utils/utils_metrics.py new file mode 100644 index 0000000..84a22a2 --- /dev/null +++ b/utils/utils_metrics.py @@ -0,0 +1,182 @@ +import csv +import os +from os.path import join + +import matplotlib.pyplot as plt +import numpy as np +import torch +import torch.nn.functional as F +from PIL import Image + + +def f_score(inputs, target, beta=1, smooth = 1e-5, threhold = 0.5): + n, c, h, w = inputs.size() + nt, ht, wt, ct = target.size() + if h != ht and w != wt: + inputs = F.interpolate(inputs, size=(ht, wt), mode="bilinear", align_corners=True) + + temp_inputs = torch.softmax(inputs.transpose(1, 2).transpose(2, 3).contiguous().view(n, -1, c),-1) + temp_target = target.view(n, -1, ct) + + #--------------------------------------------# + # 计算dice系数 + #--------------------------------------------# + temp_inputs = torch.gt(temp_inputs, threhold).float() + tp = torch.sum(temp_target[...,:-1] * temp_inputs, axis=[0,1]) + fp = torch.sum(temp_inputs , axis=[0,1]) - tp + fn = torch.sum(temp_target[...,:-1] , axis=[0,1]) - tp + + score = ((1 + beta ** 2) * tp + smooth) / ((1 + beta ** 2) * tp + beta ** 2 * fn + fp + smooth) + score = torch.mean(score) + return score + +# 设标签宽W,长H +def fast_hist(a, b, n): + #--------------------------------------------------------------------------------# + # a是转化成一维数组的标签,形状(H×W,);b是转化成一维数组的预测结果,形状(H×W,) + #--------------------------------------------------------------------------------# + k = (a >= 0) & (a < n) + #--------------------------------------------------------------------------------# + # np.bincount计算了从0到n**2-1这n**2个数中每个数出现的次数,返回值形状(n, n) + # 返回中,写对角线上的为分类正确的像素点 + #--------------------------------------------------------------------------------# + return np.bincount(n * a[k].astype(int) + b[k], minlength=n ** 2).reshape(n, n) + +def per_class_iu(hist): + return np.diag(hist) / np.maximum((hist.sum(1) + hist.sum(0) - np.diag(hist)), 1) + +def per_class_PA_Recall(hist): + return np.diag(hist) / np.maximum(hist.sum(1), 1) + +def per_class_Precision(hist): + return np.diag(hist) / np.maximum(hist.sum(0), 1) + +def per_Accuracy(hist): + return np.sum(np.diag(hist)) / np.maximum(np.sum(hist), 1) + +def compute_mIoU(gt_dir, pred_dir, png_name_list, num_classes, name_classes=None): + print('Num classes', num_classes) + #-----------------------------------------# + # 创建一个全是0的矩阵,是一个混淆矩阵 + #-----------------------------------------# + hist = np.zeros((num_classes, num_classes)) + + #------------------------------------------------# + # 获得验证集标签路径列表,方便直接读取 + # 获得验证集图像分割结果路径列表,方便直接读取 + #------------------------------------------------# + gt_imgs = [join(gt_dir, x + ".png") for x in png_name_list] + pred_imgs = [join(pred_dir, x + ".png") for x in png_name_list] + + #------------------------------------------------# + # 读取每一个(图片-标签)对 + #------------------------------------------------# + for ind in range(len(gt_imgs)): + #------------------------------------------------# + # 读取一张图像分割结果,转化成numpy数组 + #------------------------------------------------# + pred = np.array(Image.open(pred_imgs[ind])) + #------------------------------------------------# + # 读取一张对应的标签,转化成numpy数组 + #------------------------------------------------# + label = np.array(Image.open(gt_imgs[ind])) + + # 如果图像分割结果与标签的大小不一样,这张图片就不计算 + if len(label.flatten()) != len(pred.flatten()): + print( + 'Skipping: len(gt) = {:d}, len(pred) = {:d}, {:s}, {:s}'.format( + len(label.flatten()), len(pred.flatten()), gt_imgs[ind], + pred_imgs[ind])) + continue + + #------------------------------------------------# + # 对一张图片计算21×21的hist矩阵,并累加 + #------------------------------------------------# + hist += fast_hist(label.flatten(), pred.flatten(), num_classes) + # 每计算10张就输出一下目前已计算的图片中所有类别平均的mIoU值 + if name_classes is not None and ind > 0 and ind % 10 == 0: + print('{:d} / {:d}: mIou-{:0.2f}%; mPA-{:0.2f}%; Accuracy-{:0.2f}%'.format( + ind, + len(gt_imgs), + 100 * np.nanmean(per_class_iu(hist)), + 100 * np.nanmean(per_class_PA_Recall(hist)), + 100 * per_Accuracy(hist) + ) + ) + #------------------------------------------------# + # 计算所有验证集图片的逐类别mIoU值 + #------------------------------------------------# + IoUs = per_class_iu(hist) + PA_Recall = per_class_PA_Recall(hist) + Precision = per_class_Precision(hist) + #------------------------------------------------# + # 逐类别输出一下mIoU值 + #------------------------------------------------# + if name_classes is not None: + for ind_class in range(num_classes): + print('===>' + name_classes[ind_class] + ':\tIou-' + str(round(IoUs[ind_class] * 100, 2)) + + '; Recall (equal to the PA)-' + str(round(PA_Recall[ind_class] * 100, 2))+ '; Precision-' + str(round(Precision[ind_class] * 100, 2))) + + #-----------------------------------------------------------------# + # 在所有验证集图像上求所有类别平均的mIoU值,计算时忽略NaN值 + #-----------------------------------------------------------------# + print('===> mIoU: ' + str(round(np.nanmean(IoUs) * 100, 2)) + '; mPA: ' + str(round(np.nanmean(PA_Recall) * 100, 2)) + '; Accuracy: ' + str(round(per_Accuracy(hist) * 100, 2))) + return np.array(hist, np.int), IoUs, PA_Recall, Precision + +def adjust_axes(r, t, fig, axes): + bb = t.get_window_extent(renderer=r) + text_width_inches = bb.width / fig.dpi + current_fig_width = fig.get_figwidth() + new_fig_width = current_fig_width + text_width_inches + propotion = new_fig_width / current_fig_width + x_lim = axes.get_xlim() + axes.set_xlim([x_lim[0], x_lim[1] * propotion]) + +def draw_plot_func(values, name_classes, plot_title, x_label, output_path, tick_font_size = 12, plt_show = True): + fig = plt.gcf() + axes = plt.gca() + plt.barh(range(len(values)), values, color='royalblue') + plt.title(plot_title, fontsize=tick_font_size + 2) + plt.xlabel(x_label, fontsize=tick_font_size) + plt.yticks(range(len(values)), name_classes, fontsize=tick_font_size) + r = fig.canvas.get_renderer() + for i, val in enumerate(values): + str_val = " " + str(val) + if val < 1.0: + str_val = " {0:.2f}".format(val) + t = plt.text(val, i, str_val, color='royalblue', va='center', fontweight='bold') + if i == (len(values)-1): + adjust_axes(r, t, fig, axes) + + fig.tight_layout() + fig.savefig(output_path) + if plt_show: + plt.show() + plt.close() + +def show_results(miou_out_path, hist, IoUs, PA_Recall, Precision, name_classes, tick_font_size = 12): + draw_plot_func(IoUs, name_classes, "mIoU = {0:.2f}%".format(np.nanmean(IoUs)*100), "Intersection over Union", + os.path.join(miou_out_path, "mIoU.png"), tick_font_size = tick_font_size, plt_show = True) + print("Save mIoU out to " + os.path.join(miou_out_path, "mIoU.png")) + + draw_plot_func(PA_Recall, name_classes, "mPA = {0:.2f}%".format(np.nanmean(PA_Recall)*100), "Pixel Accuracy", + os.path.join(miou_out_path, "mPA.png"), tick_font_size = tick_font_size, plt_show = False) + print("Save mPA out to " + os.path.join(miou_out_path, "mPA.png")) + + draw_plot_func(PA_Recall, name_classes, "mRecall = {0:.2f}%".format(np.nanmean(PA_Recall)*100), "Recall", + os.path.join(miou_out_path, "Recall.png"), tick_font_size = tick_font_size, plt_show = False) + print("Save Recall out to " + os.path.join(miou_out_path, "Recall.png")) + + draw_plot_func(Precision, name_classes, "mPrecision = {0:.2f}%".format(np.nanmean(Precision)*100), "Precision", + os.path.join(miou_out_path, "Precision.png"), tick_font_size = tick_font_size, plt_show = False) + print("Save Precision out to " + os.path.join(miou_out_path, "Precision.png")) + + with open(os.path.join(miou_out_path, "confusion_matrix.csv"), 'w', newline='') as f: + writer = csv.writer(f) + writer_list = [] + writer_list.append([' '] + [str(c) for c in name_classes]) + for i in range(len(hist)): + writer_list.append([name_classes[i]] + [str(x) for x in hist[i]]) + writer.writerows(writer_list) + print("Save confusion_matrix out to " + os.path.join(miou_out_path, "confusion_matrix.csv")) + \ No newline at end of file diff --git a/voc_annotation.py b/voc_annotation.py new file mode 100644 index 0000000..c04a46d --- /dev/null +++ b/voc_annotation.py @@ -0,0 +1,100 @@ +import os +import random + +import numpy as np +from PIL import Image +from tqdm import tqdm + +#-------------------------------------------------------# +# 想要增加测试集修改trainval_percent +# 修改train_percent用于改变验证集的比例 9:1 +# +# 当前该库将测试集当作验证集使用,不单独划分测试集 +#-------------------------------------------------------# +trainval_percent = 1 +train_percent = 0.9 +#-------------------------------------------------------# +# 指向VOC数据集所在的文件夹 +# 默认指向根目录下的VOC数据集 +#-------------------------------------------------------# +VOCdevkit_path = 'VOCdevkit' + +if __name__ == "__main__": + random.seed(0) + print("Generate txt in ImageSets.") + segfilepath = os.path.join(VOCdevkit_path, 'VOC2007/SegmentationClass') + saveBasePath = os.path.join(VOCdevkit_path, 'VOC2007/ImageSets/Segmentation') + + temp_seg = os.listdir(segfilepath) + total_seg = [] + for seg in temp_seg: + if seg.endswith(".png"): + total_seg.append(seg) + + num = len(total_seg) + list = range(num) + tv = int(num*trainval_percent) + tr = int(tv*train_percent) + trainval= random.sample(list,tv) + train = random.sample(trainval,tr) + + print("train and val size",tv) + print("traub suze",tr) + ftrainval = open(os.path.join(saveBasePath,'trainval.txt'), 'w') + ftest = open(os.path.join(saveBasePath,'test.txt'), 'w') + ftrain = open(os.path.join(saveBasePath,'train.txt'), 'w') + fval = open(os.path.join(saveBasePath,'val.txt'), 'w') + + for i in list: + name = total_seg[i][:-4]+'\n' + if i in trainval: + ftrainval.write(name) + if i in train: + ftrain.write(name) + else: + fval.write(name) + else: + ftest.write(name) + + ftrainval.close() + ftrain.close() + fval.close() + ftest.close() + print("Generate txt in ImageSets done.") + + print("Check datasets format, this may take a while.") + print("检查数据集格式是否符合要求,这可能需要一段时间。") + # classes_nums = np.zeros([256], np.int) + classes_nums = np.zeros([256], dtype=int) # 使用内置int + + for i in tqdm(list): + name = total_seg[i] + png_file_name = os.path.join(segfilepath, name) + if not os.path.exists(png_file_name): + raise ValueError("未检测到标签图片%s,请查看具体路径下文件是否存在以及后缀是否为png。"%(png_file_name)) + + png = np.array(Image.open(png_file_name), np.uint8) + if len(np.shape(png)) > 2: + print("标签图片%s的shape为%s,不属于灰度图或者八位彩图,请仔细检查数据集格式。"%(name, str(np.shape(png)))) + #print("标签图片需要为灰度图或者八位彩图,标签的每个像素点的值就是这个像素点所属的种类。"%(name, str(np.shape(png)))) + + classes_nums += np.bincount(np.reshape(png, [-1]), minlength=256) + + print("打印像素点的值与数量。") + print('-' * 37) + print("| %15s | %15s |"%("Key", "Value")) + print('-' * 37) + for i in range(256): + if classes_nums[i] > 0: + print("| %15s | %15s |"%(str(i), str(classes_nums[i]))) + print('-' * 37) + + if classes_nums[255] > 0 and classes_nums[0] > 0 and np.sum(classes_nums[1:255]) == 0: + print("检测到标签中像素点的值仅包含0与255,数据格式有误。") + print("二分类问题需要将标签修改为背景的像素点值为0,目标的像素点值为1。") + elif classes_nums[0] > 0 and np.sum(classes_nums[1:]) == 0: + print("检测到标签中仅仅包含背景像素点,数据格式有误,请仔细检查数据集格式。") + + print("JPEGImages中的图片应当为.jpg文件、SegmentationClass中的图片应当为.png文件。") + print("如果格式有误,参考:") + print("https://github.com/bubbliiiing/segmentation-format-fix") \ No newline at end of file diff --git a/常见问题汇总.md b/常见问题汇总.md new file mode 100644 index 0000000..7586fe3 --- /dev/null +++ b/常见问题汇总.md @@ -0,0 +1,554 @@ +问题汇总的博客地址为[https://blog.csdn.net/weixin_44791964/article/details/107517428](https://blog.csdn.net/weixin_44791964/article/details/107517428)。 + +# 问题汇总 +## 1、下载问题 +### a、代码下载 +**问:up主,可以给我发一份代码吗,代码在哪里下载啊? +答:Github上的地址就在视频简介里。复制一下就能进去下载了。** + +**问:up主,为什么我下载的代码提示压缩包损坏? +答:重新去Github下载。** + +**问:up主,为什么我下载的代码和你在视频以及博客上的代码不一样? +答:我常常会对代码进行更新,最终以实际的代码为准。** + +### b、 权值下载 +**问:up主,为什么我下载的代码里面,model_data下面没有.pth或者.h5文件? +答:我一般会把权值上传到Github和百度网盘,在GITHUB的README里面就能找到。** + +### c、 数据集下载 +**问:up主,XXXX数据集在哪里下载啊? +答:一般数据集的下载地址我会放在README里面,基本上都有,没有的话请及时联系我添加,直接发github的issue即可**。 + +## 2、环境配置问题 +### a、现在库中所用的环境 +**pytorch代码对应的pytorch版本为1.2,博客地址对应**[https://blog.csdn.net/weixin_44791964/article/details/106037141](https://blog.csdn.net/weixin_44791964/article/details/106037141)。 + +**keras代码对应的tensorflow版本为1.13.2,keras版本是2.1.5,博客地址对应**[https://blog.csdn.net/weixin_44791964/article/details/104702142](https://blog.csdn.net/weixin_44791964/article/details/104702142)。 + +**tf2代码对应的tensorflow版本为2.2.0,无需安装keras,博客地址对应**[https://blog.csdn.net/weixin_44791964/article/details/109161493](https://blog.csdn.net/weixin_44791964/article/details/109161493)。 + +**问:你的代码某某某版本的tensorflow和pytorch能用嘛? +答:最好按照我推荐的配置,配置教程也有!其它版本的我没有试过!可能出现问题但是一般问题不大。仅需要改少量代码即可。** + +### b、30系列显卡环境配置 +30系显卡由于框架更新不可使用上述环境配置教程。 +当前我已经测试的可以用的30显卡配置如下: +**pytorch代码对应的pytorch版本为1.7.0,cuda为11.0,cudnn为8.0.5**。 + +**keras代码无法在win10下配置cuda11,在ubuntu下可以百度查询一下,配置tensorflow版本为1.15.4,keras版本是2.1.5或者2.3.1(少量函数接口不同,代码可能还需要少量调整。)** + +**tf2代码对应的tensorflow版本为2.4.0,cuda为11.0,cudnn为8.0.5**。 + +### c、GPU利用问题与环境使用问题 +**问:为什么我安装了tensorflow-gpu但是却没用利用GPU进行训练呢? +答:确认tensorflow-gpu已经装好,利用pip list查看tensorflow版本,然后查看任务管理器或者利用nvidia命令看看是否使用了gpu进行训练,任务管理器的话要看显存使用情况。** + +**问:up主,我好像没有在用gpu进行训练啊,怎么看是不是用了GPU进行训练? +答:查看是否使用GPU进行训练一般使用NVIDIA在命令行的查看命令,如果要看任务管理器的话,请看性能部分GPU的显存是否利用,或者查看任务管理器的Cuda,而非Copy。** +![在这里插入图片描述](https://img-blog.csdnimg.cn/20201013234241524.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDc5MTk2NA==,size_16,color_FFFFFF,t_70#pic_center) + +**问:up主,为什么我按照你的环境配置后还是不能使用? +答:请把你的GPU、CUDA、CUDNN、TF版本以及PYTORCH版本B站私聊告诉我。** + +**问:出现如下错误** +```python +Traceback (most recent call last): + File "C:\Users\focus\Anaconda3\ana\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\pywrap_tensorflow.py", line 58, in + from tensorflow.python.pywrap_tensorflow_internal import * +File "C:\Users\focus\Anaconda3\ana\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\pywrap_tensorflow_internal.py", line 28, in +pywrap_tensorflow_internal = swig_import_helper() + File "C:\Users\focus\Anaconda3\ana\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\pywrap_tensorflow_internal.py", line 24, in swig_import_helper + _mod = imp.load_module('_pywrap_tensorflow_internal', fp, pathname, description) +File "C:\Users\focus\Anaconda3\ana\envs\tensorflow-gpu\lib\imp.py", line 243, in load_modulereturn load_dynamic(name, filename, file) +File "C:\Users\focus\Anaconda3\ana\envs\tensorflow-gpu\lib\imp.py", line 343, in load_dynamic + return _load(spec) +ImportError: DLL load failed: 找不到指定的模块。 +``` +**答:如果没重启过就重启一下,否则重新按照步骤安装,还无法解决则把你的GPU、CUDA、CUDNN、TF版本以及PYTORCH版本私聊告诉我。** + +### d、no module问题 +**问:为什么提示说no module name utils.utils(no module name nets.yolo、no module name nets.ssd等一系列问题)啊? +答:utils并不需要用pip装,它就在我上传的仓库的根目录,出现这个问题的原因是根目录不对,查查相对目录和根目录的概念。查了基本上就明白了。** + +**问:为什么提示说no module name matplotlib(no module name PIL,no module name cv2等等)? +答:这个库没安装打开命令行安装就好。pip install matplotlib** + +**问:为什么我已经用pip装了opencv(pillow、matplotlib等),还是提示no module name cv2? +答:没有激活环境装,要激活对应的conda环境进行安装才可以正常使用** + +**问:为什么提示说No module named 'torch' ? +答:其实我也真的很想知道为什么会有这个问题……这个pytorch没装是什么情况?一般就俩情况,一个是真的没装,还有一个是装到其它环境了,当前激活的环境不是自己装的环境。** + +**问:为什么提示说No module named 'tensorflow' ? +答:同上。** + +### e、cuda安装失败问题 +一般cuda安装前需要安装Visual Studio,装个2017版本即可。 + +### f、Ubuntu系统问题 +**所有代码在Ubuntu下可以使用,我两个系统都试过。** + +### g、VSCODE提示错误的问题 +**问:为什么在VSCODE里面提示一大堆的错误啊? +答:我也提示一大堆的错误,但是不影响,是VSCODE的问题,如果不想看错误的话就装Pycharm。** + +### h、使用cpu进行训练与预测的问题 +**对于keras和tf2的代码而言,如果想用cpu进行训练和预测,直接装cpu版本的tensorflow就可以了。** + +**对于pytorch的代码而言,如果想用cpu进行训练和预测,需要将cuda=True修改成cuda=False。** + +### i、tqdm没有pos参数问题 +**问:运行代码提示'tqdm' object has no attribute 'pos'。 +答:重装tqdm,换个版本就可以了。** + +### j、提示decode(“utf-8”)的问题 +**由于h5py库的更新,安装过程中会自动安装h5py=3.0.0以上的版本,会导致decode("utf-8")的错误! +各位一定要在安装完tensorflow后利用命令装h5py=2.10.0!** +``` +pip install h5py==2.10.0 +``` + +### k、提示TypeError: __array__() takes 1 positional argument but 2 were given错误 +可以修改pillow版本解决。 +``` +pip install pillow==8.2.0 +``` + +### l、其它问题 +**问:为什么提示TypeError: cat() got an unexpected keyword argument 'axis',Traceback (most recent call last),AttributeError: 'Tensor' object has no attribute 'bool'? +答:这是版本问题,建议使用torch1.2以上版本** +**其它有很多稀奇古怪的问题,很多是版本问题,建议按照我的视频教程安装Keras和tensorflow。比如装的是tensorflow2,就不用问我说为什么我没法运行Keras-yolo啥的。那是必然不行的。** + +## 3、目标检测库问题汇总(人脸检测和分类库也可参考) +### a、shape不匹配问题 +#### 1)、训练时shape不匹配问题 +**问:up主,为什么运行train.py会提示shape不匹配啊? +答:在keras环境中,因为你训练的种类和原始的种类不同,网络结构会变化,所以最尾部的shape会有少量不匹配。** + +#### 2)、预测时shape不匹配问题 +**问:为什么我运行predict.py会提示我说shape不匹配呀。 +在Pytorch里面是这样的:** +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200722171631901.png) +在Keras里面是这样的: +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200722171523380.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDc5MTk2NA==,size_16,color_FFFFFF,t_70) +**答:原因主要有仨: +1、在ssd、FasterRCNN里面,可能是train.py里面的num_classes没改。 +2、model_path没改。 +3、classes_path没改。 +请检查清楚了!确定自己所用的model_path和classes_path是对应的!训练的时候用到的num_classes或者classes_path也需要检查!** + +### b、显存不足问题 +**问:为什么我运行train.py下面的命令行闪的贼快,还提示OOM啥的? +答:这是在keras中出现的,爆显存了,可以改小batch_size,SSD的显存占用率是最小的,建议用SSD; +2G显存:SSD、YOLOV4-TINY +4G显存:YOLOV3 +6G显存:YOLOV4、Retinanet、M2det、Efficientdet、Faster RCNN等 +8G+显存:随便选吧。** +**需要注意的是,受到BatchNorm2d影响,batch_size不可为1,至少为2。** + +**问:为什么提示 RuntimeError: CUDA out of memory. Tried to allocate 52.00 MiB (GPU 0; 15.90 GiB total capacity; 14.85 GiB already allocated; 51.88 MiB free; 15.07 GiB reserved in total by PyTorch)? +答:这是pytorch中出现的,爆显存了,同上。** + +**问:为什么我显存都没利用,就直接爆显存了? +答:都爆显存了,自然就不利用了,模型没有开始训练。** +### c、训练问题(冻结训练,LOSS问题、训练效果问题等) +**问:为什么要冻结训练和解冻训练呀? +答:这是迁移学习的思想,因为神经网络主干特征提取部分所提取到的特征是通用的,我们冻结起来训练可以加快训练效率,也可以防止权值被破坏。** +在冻结阶段,模型的主干被冻结了,特征提取网络不发生改变。占用的显存较小,仅对网络进行微调。 +在解冻阶段,模型的主干不被冻结了,特征提取网络会发生改变。占用的显存较大,网络所有的参数都会发生改变。 + +**问:为什么我的网络不收敛啊,LOSS是XXXX。 +答:不同网络的LOSS不同,LOSS只是一个参考指标,用于查看网络是否收敛,而非评价网络好坏,我的yolo代码都没有归一化,所以LOSS值看起来比较高,LOSS的值不重要,重要的是是否在变小,预测是否有效果。** + +**问:为什么我的训练效果不好?预测了没有框(框不准)。 +答:** + +考虑几个问题: +1、目标信息问题,查看2007_train.txt文件是否有目标信息,没有的话请修改voc_annotation.py。 +2、数据集问题,小于500的自行考虑增加数据集,同时测试不同的模型,确认数据集是好的。 +3、是否解冻训练,如果数据集分布与常规画面差距过大需要进一步解冻训练,调整主干,加强特征提取能力。 +4、网络问题,比如SSD不适合小目标,因为先验框固定了。 +5、训练时长问题,有些同学只训练了几代表示没有效果,按默认参数训练完。 +6、确认自己是否按照步骤去做了,如果比如voc_annotation.py里面的classes是否修改了等。 +7、不同网络的LOSS不同,LOSS只是一个参考指标,用于查看网络是否收敛,而非评价网络好坏,LOSS的值不重要,重要的是是否收敛。 + +**问:我怎么出现了gbk什么的编码错误啊:** +```python +UnicodeDecodeError: 'gbk' codec can't decode byte 0xa6 in position 446: illegal multibyte sequence +``` +**答:标签和路径不要使用中文,如果一定要使用中文,请注意处理的时候编码的问题,改成打开文件的encoding方式改为utf-8。** + +**问:我的图片是xxx*xxx的分辨率的,可以用吗!** +**答:可以用,代码里面会自动进行resize或者数据增强。** + +**问:怎么进行多GPU训练? +答:pytorch的大多数代码可以直接使用gpu训练,keras的话直接百度就好了,实现并不复杂,我没有多卡没法详细测试,还需要各位同学自己努力了。** +### d、灰度图问题 +**问:能不能训练灰度图(预测灰度图)啊? +答:我的大多数库会将灰度图转化成RGB进行训练和预测,如果遇到代码不能训练或者预测灰度图的情况,可以尝试一下在get_random_data里面将Image.open后的结果转换成RGB,预测的时候也这样试试。(仅供参考)** + +### e、断点续练问题 +**问:我已经训练过几个世代了,能不能从这个基础上继续开始训练 +答:可以,你在训练前,和载入预训练权重一样载入训练过的权重就行了。一般训练好的权重会保存在logs文件夹里面,将model_path修改成你要开始的权值的路径即可。** + +### f、预训练权重的问题 +**问:如果我要训练其它的数据集,预训练权重要怎么办啊?** +**答:数据的预训练权重对不同数据集是通用的,因为特征是通用的,预训练权重对于99%的情况都必须要用,不用的话权值太过随机,特征提取效果不明显,网络训练的结果也不会好。** + +**问:up,我修改了网络,预训练权重还能用吗? +答:修改了主干的话,如果不是用的现有的网络,基本上预训练权重是不能用的,要么就自己判断权值里卷积核的shape然后自己匹配,要么只能自己预训练去了;修改了后半部分的话,前半部分的主干部分的预训练权重还是可以用的,如果是pytorch代码的话,需要自己修改一下载入权值的方式,判断shape后载入,如果是keras代码,直接by_name=True,skip_mismatch=True即可。** +权值匹配的方式可以参考如下: +```python +# 加快模型训练的效率 +print('Loading weights into state dict...') +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') +model_dict = model.state_dict() +pretrained_dict = torch.load(model_path, map_location=device) +a = {} +for k, v in pretrained_dict.items(): + try: + if np.shape(model_dict[k]) == np.shape(v): + a[k]=v + except: + pass +model_dict.update(a) +model.load_state_dict(model_dict) +print('Finished!') +``` + +**问:我要怎么不使用预训练权重啊? +答:把载入预训练权重的代码注释了就行。** + +**问:为什么我不使用预训练权重效果这么差啊? +答:因为随机初始化的权值不好,提取的特征不好,也就导致了模型训练的效果不好,voc07+12、coco+voc07+12效果都不一样,预训练权重还是非常重要的。** + +### g、视频检测问题与摄像头检测问题 +**问:怎么用摄像头检测呀? +答:predict.py修改参数可以进行摄像头检测,也有视频详细解释了摄像头检测的思路。** + +**问:怎么用视频检测呀? +答:同上** +### h、从0开始训练问题 +**问:怎么在模型上从0开始训练? +答:在算力不足与调参能力不足的情况下从0开始训练毫无意义。模型特征提取能力在随机初始化参数的情况下非常差。没有好的参数调节能力和算力,无法使得网络正常收敛。** +如果一定要从0开始,那么训练的时候请注意几点: + - 不载入预训练权重。 + - 不要进行冻结训练,注释冻结模型的代码。 + +**问:为什么我不使用预训练权重效果这么差啊? +答:因为随机初始化的权值不好,提取的特征不好,也就导致了模型训练的效果不好,voc07+12、coco+voc07+12效果都不一样,预训练权重还是非常重要的。** + +### i、保存问题 +**问:检测完的图片怎么保存? +答:一般目标检测用的是Image,所以查询一下PIL库的Image如何进行保存。详细看看predict.py文件的注释。** + +**问:怎么用视频保存呀? +答:详细看看predict.py文件的注释。** + +### j、遍历问题 +**问:如何对一个文件夹的图片进行遍历? +答:一般使用os.listdir先找出文件夹里面的所有图片,然后根据predict.py文件里面的执行思路检测图片就行了,详细看看predict.py文件的注释。** + +**问:如何对一个文件夹的图片进行遍历?并且保存。 +答:遍历的话一般使用os.listdir先找出文件夹里面的所有图片,然后根据predict.py文件里面的执行思路检测图片就行了。保存的话一般目标检测用的是Image,所以查询一下PIL库的Image如何进行保存。如果有些库用的是cv2,那就是查一下cv2怎么保存图片。详细看看predict.py文件的注释。** + +### k、路径问题(No such file or directory) +**问:我怎么出现了这样的错误呀:** +```python +FileNotFoundError: 【Errno 2】 No such file or directory +…………………………………… +…………………………………… +``` +**答:去检查一下文件夹路径,查看是否有对应文件;并且检查一下2007_train.txt,其中文件路径是否有错。** +关于路径有几个重要的点: +**文件夹名称中一定不要有空格。 +注意相对路径和绝对路径。 +多百度路径相关的知识。** + +**所有的路径问题基本上都是根目录问题,好好查一下相对目录的概念!** +### l、和原版比较问题 +**问:你这个代码和原版比怎么样,可以达到原版的效果么? +答:基本上可以达到,我都用voc数据测过,我没有好显卡,没有能力在coco上测试与训练。** + +**问:你有没有实现yolov4所有的tricks,和原版差距多少? +答:并没有实现全部的改进部分,由于YOLOV4使用的改进实在太多了,很难完全实现与列出来,这里只列出来了一些我比较感兴趣,而且非常有效的改进。论文中提到的SAM(注意力机制模块),作者自己的源码也没有使用。还有其它很多的tricks,不是所有的tricks都有提升,我也没法实现全部的tricks。至于和原版的比较,我没有能力训练coco数据集,根据使用过的同学反应差距不大。** + +### m、FPS问题(检测速度问题) +**问:你这个FPS可以到达多少,可以到 XX FPS么? +答:FPS和机子的配置有关,配置高就快,配置低就慢。** + +**问:为什么我用服务器去测试yolov4(or others)的FPS只有十几? +答:检查是否正确安装了tensorflow-gpu或者pytorch的gpu版本,如果已经正确安装,可以去利用time.time()的方法查看detect_image里面,哪一段代码耗时更长(不仅只有网络耗时长,其它处理部分也会耗时,如绘图等)。** + +**问:为什么论文中说速度可以达到XX,但是这里却没有? +答:检查是否正确安装了tensorflow-gpu或者pytorch的gpu版本,如果已经正确安装,可以去利用time.time()的方法查看detect_image里面,哪一段代码耗时更长(不仅只有网络耗时长,其它处理部分也会耗时,如绘图等)。有些论文还会使用多batch进行预测,我并没有去实现这个部分。** + +### n、预测图片不显示问题 +**问:为什么你的代码在预测完成后不显示图片?只是在命令行告诉我有什么目标。 +答:给系统安装一个图片查看器就行了。** + +### o、算法评价问题(目标检测的map、PR曲线、Recall、Precision等) +**问:怎么计算map? +答:看map视频,都一个流程。** + +**问:计算map的时候,get_map.py里面有一个MINOVERLAP是什么用的,是iou吗? +答:是iou,它的作用是判断预测框和真实框的重合成度,如果重合程度大于MINOVERLAP,则预测正确。** + +**问:为什么get_map.py里面的self.confidence(self.score)要设置的那么小? +答:看一下map的视频的原理部分,要知道所有的结果然后再进行pr曲线的绘制。** + +**问:能不能说说怎么绘制PR曲线啥的呀。 +答:可以看mAP视频,结果里面有PR曲线。** + +**问:怎么计算Recall、Precision指标。 +答:这俩指标应该是相对于特定的置信度的,计算map的时候也会获得。** + +### p、coco数据集训练问题 +**问:目标检测怎么训练COCO数据集啊?。 +答:coco数据训练所需要的txt文件可以参考qqwweee的yolo3的库,格式都是一样的。** + +### q、模型优化(模型修改)问题 +**问:up,YOLO系列使用Focal LOSS的代码你有吗,有提升吗? +答:很多人试过,提升效果也不大(甚至变的更Low),它自己有自己的正负样本的平衡方式。** + +**问:up,我修改了网络,预训练权重还能用吗? +答:修改了主干的话,如果不是用的现有的网络,基本上预训练权重是不能用的,要么就自己判断权值里卷积核的shape然后自己匹配,要么只能自己预训练去了;修改了后半部分的话,前半部分的主干部分的预训练权重还是可以用的,如果是pytorch代码的话,需要自己修改一下载入权值的方式,判断shape后载入,如果是keras代码,直接by_name=True,skip_mismatch=True即可。** +权值匹配的方式可以参考如下: +```python +# 加快模型训练的效率 +print('Loading weights into state dict...') +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') +model_dict = model.state_dict() +pretrained_dict = torch.load(model_path, map_location=device) +a = {} +for k, v in pretrained_dict.items(): + try: + if np.shape(model_dict[k]) == np.shape(v): + a[k]=v + except: + pass +model_dict.update(a) +model.load_state_dict(model_dict) +print('Finished!') +``` + +**问:up,怎么修改模型啊,我想发个小论文! +答:建议看看yolov3和yolov4的区别,然后看看yolov4的论文,作为一个大型调参现场非常有参考意义,使用了很多tricks。我能给的建议就是多看一些经典模型,然后拆解里面的亮点结构并使用。** + +### r、部署问题 +我没有具体部署到手机等设备上过,所以很多部署问题我并不了解…… + +## 4、语义分割库问题汇总 +### a、shape不匹配问题 +#### 1)、训练时shape不匹配问题 +**问:up主,为什么运行train.py会提示shape不匹配啊? +答:在keras环境中,因为你训练的种类和原始的种类不同,网络结构会变化,所以最尾部的shape会有少量不匹配。** + +#### 2)、预测时shape不匹配问题 +**问:为什么我运行predict.py会提示我说shape不匹配呀。 +在Pytorch里面是这样的:** +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200722171631901.png) +在Keras里面是这样的: +![在这里插入图片描述](https://img-blog.csdnimg.cn/20200722171523380.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDc5MTk2NA==,size_16,color_FFFFFF,t_70) +**答:原因主要有二: +1、train.py里面的num_classes没改。 +2、预测时num_classes没改。 +请检查清楚!训练和预测的时候用到的num_classes都需要检查!** + +### b、显存不足问题 +**问:为什么我运行train.py下面的命令行闪的贼快,还提示OOM啥的? +答:这是在keras中出现的,爆显存了,可以改小batch_size。** + +**需要注意的是,受到BatchNorm2d影响,batch_size不可为1,至少为2。** + +**问:为什么提示 RuntimeError: CUDA out of memory. Tried to allocate 52.00 MiB (GPU 0; 15.90 GiB total capacity; 14.85 GiB already allocated; 51.88 MiB free; 15.07 GiB reserved in total by PyTorch)? +答:这是pytorch中出现的,爆显存了,同上。** + +**问:为什么我显存都没利用,就直接爆显存了? +答:都爆显存了,自然就不利用了,模型没有开始训练。** + +### c、训练问题(冻结训练,LOSS问题、训练效果问题等) +**问:为什么要冻结训练和解冻训练呀? +答:这是迁移学习的思想,因为神经网络主干特征提取部分所提取到的特征是通用的,我们冻结起来训练可以加快训练效率,也可以防止权值被破坏。** +**在冻结阶段,模型的主干被冻结了,特征提取网络不发生改变。占用的显存较小,仅对网络进行微调。** +**在解冻阶段,模型的主干不被冻结了,特征提取网络会发生改变。占用的显存较大,网络所有的参数都会发生改变。** + +**问:为什么我的网络不收敛啊,LOSS是XXXX。 +答:不同网络的LOSS不同,LOSS只是一个参考指标,用于查看网络是否收敛,而非评价网络好坏,我的yolo代码都没有归一化,所以LOSS值看起来比较高,LOSS的值不重要,重要的是是否在变小,预测是否有效果。** + +**问:为什么我的训练效果不好?预测了没有目标,结果是一片黑。 +答:** +**考虑几个问题: +1、数据集问题,这是最重要的问题。小于500的自行考虑增加数据集;一定要检查数据集的标签,视频中详细解析了VOC数据集的格式,但并不是有输入图片有输出标签即可,还需要确认标签的每一个像素值是否为它对应的种类。很多同学的标签格式不对,最常见的错误格式就是标签的背景为黑,目标为白,此时目标的像素点值为255,无法正常训练,目标需要为1才行。 +2、是否解冻训练,如果数据集分布与常规画面差距过大需要进一步解冻训练,调整主干,加强特征提取能力。 +3、网络问题,可以尝试不同的网络。 +4、训练时长问题,有些同学只训练了几代表示没有效果,按默认参数训练完。 +5、确认自己是否按照步骤去做了。 +6、不同网络的LOSS不同,LOSS只是一个参考指标,用于查看网络是否收敛,而非评价网络好坏,LOSS的值不重要,重要的是是否收敛。** + + + +**问:为什么我的训练效果不好?对小目标预测不准确。 +答:对于deeplab和pspnet而言,可以修改一下downsample_factor,当downsample_factor为16的时候下采样倍数过多,效果不太好,可以修改为8。** + +**问:我怎么出现了gbk什么的编码错误啊:** +```python +UnicodeDecodeError: 'gbk' codec can't decode byte 0xa6 in position 446: illegal multibyte sequence +``` +**答:标签和路径不要使用中文,如果一定要使用中文,请注意处理的时候编码的问题,改成打开文件的encoding方式改为utf-8。** + +**问:我的图片是xxx*xxx的分辨率的,可以用吗!** +**答:可以用,代码里面会自动进行resize或者数据增强。** + +**问:怎么进行多GPU训练? +答:pytorch的大多数代码可以直接使用gpu训练,keras的话直接百度就好了,实现并不复杂,我没有多卡没法详细测试,还需要各位同学自己努力了。** + +### d、灰度图问题 +**问:能不能训练灰度图(预测灰度图)啊? +答:我的大多数库会将灰度图转化成RGB进行训练和预测,如果遇到代码不能训练或者预测灰度图的情况,可以尝试一下在get_random_data里面将Image.open后的结果转换成RGB,预测的时候也这样试试。(仅供参考)** + +### e、断点续练问题 +**问:我已经训练过几个世代了,能不能从这个基础上继续开始训练 +答:可以,你在训练前,和载入预训练权重一样载入训练过的权重就行了。一般训练好的权重会保存在logs文件夹里面,将model_path修改成你要开始的权值的路径即可。** + +### f、预训练权重的问题 + +**问:如果我要训练其它的数据集,预训练权重要怎么办啊?** +**答:数据的预训练权重对不同数据集是通用的,因为特征是通用的,预训练权重对于99%的情况都必须要用,不用的话权值太过随机,特征提取效果不明显,网络训练的结果也不会好。** + +**问:up,我修改了网络,预训练权重还能用吗? +答:修改了主干的话,如果不是用的现有的网络,基本上预训练权重是不能用的,要么就自己判断权值里卷积核的shape然后自己匹配,要么只能自己预训练去了;修改了后半部分的话,前半部分的主干部分的预训练权重还是可以用的,如果是pytorch代码的话,需要自己修改一下载入权值的方式,判断shape后载入,如果是keras代码,直接by_name=True,skip_mismatch=True即可。** +权值匹配的方式可以参考如下: + +```python +# 加快模型训练的效率 +print('Loading weights into state dict...') +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') +model_dict = model.state_dict() +pretrained_dict = torch.load(model_path, map_location=device) +a = {} +for k, v in pretrained_dict.items(): + try: + if np.shape(model_dict[k]) == np.shape(v): + a[k]=v + except: + pass +model_dict.update(a) +model.load_state_dict(model_dict) +print('Finished!') +``` + +**问:我要怎么不使用预训练权重啊? +答:把载入预训练权重的代码注释了就行。** + +**问:为什么我不使用预训练权重效果这么差啊? +答:因为随机初始化的权值不好,提取的特征不好,也就导致了模型训练的效果不好,预训练权重还是非常重要的。** + +### g、视频检测问题与摄像头检测问题 +**问:怎么用摄像头检测呀? +答:predict.py修改参数可以进行摄像头检测,也有视频详细解释了摄像头检测的思路。** + +**问:怎么用视频检测呀? +答:同上** + +### h、从0开始训练问题 +**问:怎么在模型上从0开始训练? +答:在算力不足与调参能力不足的情况下从0开始训练毫无意义。模型特征提取能力在随机初始化参数的情况下非常差。没有好的参数调节能力和算力,无法使得网络正常收敛。** +如果一定要从0开始,那么训练的时候请注意几点: + - 不载入预训练权重。 + - 不要进行冻结训练,注释冻结模型的代码。 + +**问:为什么我不使用预训练权重效果这么差啊? +答:因为随机初始化的权值不好,提取的特征不好,也就导致了模型训练的效果不好,预训练权重还是非常重要的。** + +### i、保存问题 +**问:检测完的图片怎么保存? +答:一般目标检测用的是Image,所以查询一下PIL库的Image如何进行保存。详细看看predict.py文件的注释。** + +**问:怎么用视频保存呀? +答:详细看看predict.py文件的注释。** + +### j、遍历问题 +**问:如何对一个文件夹的图片进行遍历? +答:一般使用os.listdir先找出文件夹里面的所有图片,然后根据predict.py文件里面的执行思路检测图片就行了,详细看看predict.py文件的注释。** + +**问:如何对一个文件夹的图片进行遍历?并且保存。 +答:遍历的话一般使用os.listdir先找出文件夹里面的所有图片,然后根据predict.py文件里面的执行思路检测图片就行了。保存的话一般目标检测用的是Image,所以查询一下PIL库的Image如何进行保存。如果有些库用的是cv2,那就是查一下cv2怎么保存图片。详细看看predict.py文件的注释。** + +### k、路径问题(No such file or directory) +**问:我怎么出现了这样的错误呀:** +```python +FileNotFoundError: 【Errno 2】 No such file or directory +…………………………………… +…………………………………… +``` + +**答:去检查一下文件夹路径,查看是否有对应文件;并且检查一下2007_train.txt,其中文件路径是否有错。** +关于路径有几个重要的点: +**文件夹名称中一定不要有空格。 +注意相对路径和绝对路径。 +多百度路径相关的知识。** + +**所有的路径问题基本上都是根目录问题,好好查一下相对目录的概念!** + +### l、FPS问题(检测速度问题) +**问:你这个FPS可以到达多少,可以到 XX FPS么? +答:FPS和机子的配置有关,配置高就快,配置低就慢。** + +**问:为什么论文中说速度可以达到XX,但是这里却没有? +答:检查是否正确安装了tensorflow-gpu或者pytorch的gpu版本,如果已经正确安装,可以去利用time.time()的方法查看detect_image里面,哪一段代码耗时更长(不仅只有网络耗时长,其它处理部分也会耗时,如绘图等)。有些论文还会使用多batch进行预测,我并没有去实现这个部分。** + +### m、预测图片不显示问题 +**问:为什么你的代码在预测完成后不显示图片?只是在命令行告诉我有什么目标。 +答:给系统安装一个图片查看器就行了。** + +### n、算法评价问题(miou) +**问:怎么计算miou? +答:参考视频里的miou测量部分。** + +**问:怎么计算Recall、Precision指标。 +答:现有的代码还无法获得,需要各位同学理解一下混淆矩阵的概念,然后自行计算一下。** + +### o、模型优化(模型修改)问题 +**问:up,我修改了网络,预训练权重还能用吗? +答:修改了主干的话,如果不是用的现有的网络,基本上预训练权重是不能用的,要么就自己判断权值里卷积核的shape然后自己匹配,要么只能自己预训练去了;修改了后半部分的话,前半部分的主干部分的预训练权重还是可以用的,如果是pytorch代码的话,需要自己修改一下载入权值的方式,判断shape后载入,如果是keras代码,直接by_name=True,skip_mismatch=True即可。** +权值匹配的方式可以参考如下: + +```python +# 加快模型训练的效率 +print('Loading weights into state dict...') +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') +model_dict = model.state_dict() +pretrained_dict = torch.load(model_path, map_location=device) +a = {} +for k, v in pretrained_dict.items(): + try: + if np.shape(model_dict[k]) == np.shape(v): + a[k]=v + except: + pass +model_dict.update(a) +model.load_state_dict(model_dict) +print('Finished!') +``` + + + +**问:up,怎么修改模型啊,我想发个小论文! +答:建议看看目标检测中yolov4的论文,作为一个大型调参现场非常有参考意义,使用了很多tricks。我能给的建议就是多看一些经典模型,然后拆解里面的亮点结构并使用。常用的tricks如注意力机制什么的,可以试试。** + +### p、部署问题 +我没有具体部署到手机等设备上过,所以很多部署问题我并不了解…… + +## 5、交流群问题 +**问:up,有没有QQ群啥的呢? +答:没有没有,我没有时间管理QQ群……** + +## 6、怎么学习的问题 +**问:up,你的学习路线怎么样的?我是个小白我要怎么学? +答:这里有几点需要注意哈 +1、我不是高手,很多东西我也不会,我的学习路线也不一定适用所有人。 +2、我实验室不做深度学习,所以我很多东西都是自学,自己摸索,正确与否我也不知道。 +3、我个人觉得学习更靠自学** +学习路线的话,我是先学习了莫烦的python教程,从tensorflow、keras、pytorch入门,入门完之后学的SSD,YOLO,然后了解了很多经典的卷积网,后面就开始学很多不同的代码了,我的学习方法就是一行一行的看,了解整个代码的执行流程,特征层的shape变化等,花了很多时间也没有什么捷径,就是要花时间吧。 \ No newline at end of file

7c)NWG4`!@ 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%0Am3G0fU2sut7i| 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