[Test] Add CE (#2)

own
Lin Manhui 2 years ago committed by GitHub
parent a373e11835
commit feebf47002
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      docs/apis/data.md
  2. 10
      docs/apis/infer.md
  3. 12
      docs/apis/train.md
  4. 6
      docs/dev/dev_guide.md
  5. 13
      docs/intro/model_zoo.md
  6. 30
      docs/intro/transforms.md
  7. 2
      examples/rs_research/README.md
  8. 2
      examples/rs_research/config_utils.py
  9. 2
      paddlers/datasets/__init__.py
  10. 10
      paddlers/datasets/cd_dataset.py
  11. 83
      paddlers/datasets/res_dataset.py
  12. 2
      paddlers/datasets/seg_dataset.py
  13. 99
      paddlers/datasets/sr_dataset.py
  14. 21
      paddlers/deploy/predictor.py
  15. 1
      paddlers/models/__init__.py
  16. 0
      paddlers/models/ppdet/metrics/json_results.py
  17. 0
      paddlers/models/ppdet/modeling/architectures/centernet.py
  18. 0
      paddlers/models/ppdet/modeling/architectures/fairmot.py
  19. 0
      paddlers/models/ppdet/modeling/backbones/darknet.py
  20. 0
      paddlers/models/ppdet/modeling/backbones/dla.py
  21. 0
      paddlers/models/ppdet/modeling/backbones/resnet.py
  22. 0
      paddlers/models/ppdet/modeling/backbones/vgg.py
  23. 0
      paddlers/models/ppdet/modeling/heads/centernet_head.py
  24. 0
      paddlers/models/ppdet/modeling/losses/fairmot_loss.py
  25. 0
      paddlers/models/ppdet/modeling/necks/centernet_fpn.py
  26. 0
      paddlers/models/ppdet/modeling/reid/fairmot_embedding_head.py
  27. 0
      paddlers/models/ppseg/models/losses/focal_loss.py
  28. 0
      paddlers/models/ppseg/models/losses/kl_loss.py
  29. 2
      paddlers/rs_models/cd/__init__.py
  30. 478
      paddlers/rs_models/cd/fccdn.py
  31. 15
      paddlers/rs_models/cd/losses/__init__.py
  32. 170
      paddlers/rs_models/cd/losses/fccdn_loss.py
  33. 2
      paddlers/rs_models/res/__init__.py
  34. 27
      paddlers/rs_models/res/generators/param_init.py
  35. 41
      paddlers/rs_models/res/generators/rcan.py
  36. 106
      paddlers/rs_models/res/rcan_model.py
  37. 2
      paddlers/tasks/__init__.py
  38. 56
      paddlers/tasks/base.py
  39. 61
      paddlers/tasks/change_detector.py
  40. 65
      paddlers/tasks/classifier.py
  41. 786
      paddlers/tasks/image_restorer.py
  42. 79
      paddlers/tasks/object_detector.py
  43. 936
      paddlers/tasks/restorer.py
  44. 29
      paddlers/tasks/segmenter.py
  45. 39
      paddlers/tasks/utils/infer_nets.py
  46. 132
      paddlers/tasks/utils/res_adapters.py
  47. 4
      paddlers/transforms/functions.py
  48. 119
      paddlers/transforms/operators.py
  49. 2
      paddlers/utils/__init__.py
  50. 30
      paddlers/utils/utils.py
  51. 20
      test_tipc/README.md
  52. 10
      test_tipc/common_func.sh
  53. 2
      test_tipc/config_utils.py
  54. 2
      test_tipc/configs/cd/_base_/airchange.yaml
  55. 2
      test_tipc/configs/cd/bit/bit_airchange.yaml
  56. 2
      test_tipc/configs/cd/bit/bit_levircd.yaml
  57. 2
      test_tipc/configs/cd/bit/train_infer_python.txt
  58. 8
      test_tipc/configs/cd/cdnet/cdnet_airchange.yaml
  59. 8
      test_tipc/configs/cd/cdnet/cdnet_levircd.yaml
  60. 53
      test_tipc/configs/cd/cdnet/train_infer_python.txt
  61. 8
      test_tipc/configs/cd/changeformer/changeformer_airchange.yaml
  62. 8
      test_tipc/configs/cd/changeformer/changeformer_levircd.yaml
  63. 10
      test_tipc/configs/cd/changeformer/train_infer_python.txt
  64. 8
      test_tipc/configs/cd/dsamnet/dsamnet_airchange.yaml
  65. 8
      test_tipc/configs/cd/dsamnet/dsamnet_levircd.yaml
  66. 53
      test_tipc/configs/cd/dsamnet/train_infer_python.txt
  67. 8
      test_tipc/configs/cd/dsifn/dsifn_airchange.yaml
  68. 8
      test_tipc/configs/cd/dsifn/dsifn_levircd.yaml
  69. 53
      test_tipc/configs/cd/dsifn/train_infer_python.txt
  70. 8
      test_tipc/configs/cd/fc_ef/fc_ef_airchange.yaml
  71. 8
      test_tipc/configs/cd/fc_ef/fc_ef_levircd.yaml
  72. 53
      test_tipc/configs/cd/fc_ef/train_infer_python.txt
  73. 8
      test_tipc/configs/cd/fc_siam_conc/fc_siam_conc_airchange.yaml
  74. 8
      test_tipc/configs/cd/fc_siam_conc/fc_siam_conc_levircd.yaml
  75. 53
      test_tipc/configs/cd/fc_siam_conc/train_infer_python.txt
  76. 8
      test_tipc/configs/cd/fc_siam_diff/fc_siam_diff_airchange.yaml
  77. 8
      test_tipc/configs/cd/fc_siam_diff/fc_siam_diff_levircd.yaml
  78. 53
      test_tipc/configs/cd/fc_siam_diff/train_infer_python.txt
  79. 13
      test_tipc/configs/cd/fccdn/fccdn_airchange.yaml
  80. 8
      test_tipc/configs/cd/fccdn/fccdn_levircd.yaml
  81. 53
      test_tipc/configs/cd/fccdn/train_infer_python.txt
  82. 8
      test_tipc/configs/cd/snunet/snunet_airchange.yaml
  83. 8
      test_tipc/configs/cd/snunet/snunet_levircd.yaml
  84. 53
      test_tipc/configs/cd/snunet/train_infer_python.txt
  85. 8
      test_tipc/configs/cd/stanet/stanet_airchange.yaml
  86. 8
      test_tipc/configs/cd/stanet/stanet_levircd.yaml
  87. 53
      test_tipc/configs/cd/stanet/train_infer_python.txt
  88. 2
      test_tipc/configs/clas/_base_/ucmerced.yaml
  89. 10
      test_tipc/configs/clas/hrnet/hrnet_ucmerced.yaml
  90. 6
      test_tipc/configs/clas/hrnet/train_infer_python.txt
  91. 10
      test_tipc/configs/clas/mobilenetv3/mobilenetv3_ucmerced.yaml
  92. 53
      test_tipc/configs/clas/mobilenetv3/train_infer_python.txt
  93. 10
      test_tipc/configs/clas/resnet50_vd/resnet50_vd_ucmerced.yaml
  94. 53
      test_tipc/configs/clas/resnet50_vd/train_infer_python.txt
  95. 72
      test_tipc/configs/det/_base_/rsod.yaml
  96. 4
      test_tipc/configs/det/_base_/sarship.yaml
  97. 10
      test_tipc/configs/det/faster_rcnn/faster_rcnn_rsod.yaml
  98. 10
      test_tipc/configs/det/faster_rcnn/faster_rcnn_sarship.yaml
  99. 53
      test_tipc/configs/det/faster_rcnn/train_infer_python.txt
  100. 10
      test_tipc/configs/det/ppyolo/ppyolo_rsod.yaml
  101. Some files were not shown because too many files have changed in this diff Show More

@ -84,6 +84,9 @@
- file list中的每一行应该包含2个以空格分隔的项,依次表示输入影像相对`data_dir`的路径以及[Pascal VOC格式](http://host.robots.ox.ac.uk/pascal/VOC/)标注文件相对`data_dir`的路径。
### 图像复原数据集`ResDataset`
### 图像分割数据集`SegDataset`
`SegDataset`定义在:https://github.com/PaddlePaddle/PaddleRS/blob/develop/paddlers/datasets/seg_dataset.py
@ -143,6 +146,7 @@
|`'aux_masks'`|图像分割/变化检测任务中的辅助标签路径或数据。|
|`'gt_bbox'`|目标检测任务中的检测框标注数据。|
|`'gt_poly'`|目标检测任务中的多边形标注数据。|
|`'target'`|图像复原中的目标影像路径或数据。|
### 组合数据变换算子

@ -26,7 +26,7 @@ def predict(self, img_file, transforms=None):
若`img_file`是一个元组,则返回对象为包含下列键值对的字典:
```
{"label map": 输出类别标签(以[h, w]格式排布),"score_map": 模型输出的各类别概率(以[h, w, c]格式排布)}
{"label_map": 输出类别标签(以[h, w]格式排布),"score_map": 模型输出的各类别概率(以[h, w, c]格式排布)}
```
若`img_file`是一个列表,则返回对象为与`img_file`等长的列表,其中的每一项为一个字典(键值对如上所示),顺序对应`img_file`中的每个元素。
@ -51,7 +51,7 @@ def predict(self, img_file, transforms=None):
若`img_file`是一个字符串或NumPy数组,则返回对象为包含下列键值对的字典:
```
{"label map": 输出类别标签,
{"label_map": 输出类别标签,
"scores_map": 输出类别概率,
"label_names_map": 输出类别名称}
```
@ -87,6 +87,10 @@ def predict(self, img_file, transforms=None):
若`img_file`是一个列表,则返回对象为与`img_file`等长的列表,其中的每一项为一个由字典(键值对如上所示)构成的列表,顺序对应`img_file`中的每个元素。
#### `BaseRestorer.predict()`
#### `BaseSegmenter.predict()`
接口形式:
@ -107,7 +111,7 @@ def predict(self, img_file, transforms=None):
若`img_file`是一个字符串或NumPy数组,则返回对象为包含下列键值对的字典:
```
{"label map": 输出类别标签(以[h, w]格式排布),"score_map": 模型输出的各类别概率(以[h, w, c]格式排布)}
{"label_map": 输出类别标签(以[h, w]格式排布),"score_map": 模型输出的各类别概率(以[h, w, c]格式排布)}
```
若`img_file`是一个列表,则返回对象为与`img_file`等长的列表,其中的每一项为一个字典(键值对如上所示),顺序对应`img_file`中的每个元素。

@ -18,11 +18,15 @@
- `use_mixed_loss`参将在未来被弃用,因此不建议使用。
- 不同的子类支持与模型相关的输入参数,详情请参考[模型定义](https://github.com/PaddlePaddle/PaddleRS/blob/develop/paddlers/rs_models/clas)和[训练器定义](https://github.com/PaddlePaddle/PaddleRS/blob/develop/paddlers/tasks/classifier.py)。
### 初始化`Baseetector`子类对象
### 初始化`BaseDetector`子类对象
- 一般支持设置`num_classes`和`backbone`参数,分别表示模型输出类别数以及所用的骨干网络类型。相比其它任务,目标检测任务的训练器支持设置的初始化参数较多,囊括网络结构、损失函数、后处理策略等方面。
- 不同的子类支持与模型相关的输入参数,详情请参考[模型定义](https://github.com/PaddlePaddle/PaddleRS/blob/develop/paddlers/rs_models/det)和[训练器定义](https://github.com/PaddlePaddle/PaddleRS/blob/develop/paddlers/tasks/object_detector.py)。
### 初始化`BaseRestorer`子类对象
### 初始化`BaseSegmenter`子类对象
- 一般支持设置`in_channels`、`num_classes`以及`use_mixed_loss`参数,分别表示输入通道数、输出类别数以及是否使用预置的混合损失。部分模型如`FarSeg`暂不支持对`in_channels`参数的设置。
@ -170,6 +174,9 @@ def train(self,
|`use_vdl`|`bool`|是否启用VisualDL日志。|`True`|
|`resume_checkpoint`|`str` \| `None`|检查点路径。PaddleRS支持从检查点(包含先前训练过程中存储的模型权重和优化器权重)继续训练,但需注意`resume_checkpoint`与`pretrain_weights`不得同时设置为`None`以外的值。|`None`|
### `BaseRestorer.train()`
### `BaseSegmenter.train()`
接口形式:
@ -311,6 +318,9 @@ def evaluate(self,
"mask": 预测得到的掩模图信息}
```
### `BaseRestorer.evaluate()`
### `BaseSegmenter.evaluate()`
接口形式:

@ -22,7 +22,7 @@
在子目录中新建文件,以`{模型名称小写}.py`命名。在文件中编写完整的模型定义。
新模型必须是`paddle.nn.Layer`的子类。对于图像分割、目标检测和场景分类任务,分别需要遵循[PaddleSeg](https://github.com/PaddlePaddle/PaddleSeg)、[PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection)[PaddleClas](https://github.com/PaddlePaddle/PaddleClas)套件中制定的相关规范。**对于变化检测、场景分类和图像分割任务,模型构造时必须传入`num_classes`参数以指定输出的类别数目**对于变化检测任务,模型定义需遵循的规范与分割模型类似,但有以下几点不同:
新模型必须是`paddle.nn.Layer`的子类。对于图像分割、目标检测、场景分类和图像复原任务,分别需要遵循[PaddleSeg](https://github.com/PaddlePaddle/PaddleSeg)、[PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection)[PaddleClas](https://github.com/PaddlePaddle/PaddleClas)和[PaddleGAN](https://github.com/PaddlePaddle/PaddleGAN)套件中制定的相关规范。**对于变化检测、场景分类和图像分割任务,模型构造时必须传入`num_classes`参数以指定输出的类别数目。对于图像复原任务,模型构造时必须传入`rs_factor`参数以指定超分辨率缩放倍数(对于非超分辨率模型,将此参数设置为`None`)。**对于变化检测任务,模型定义需遵循的规范与分割模型类似,但有以下几点不同:
- `forward()`方法接受3个输入参数,分别是`self`、`t1`和`t2`,其中`t1`和`t2`分别表示前、后两个时相的输入影像。
- 对于多任务变化检测模型(例如模型同时输出变化检测结果与两个时相的建筑物提取结果),需要指定类的`USE_MULTITASK_DECODER`属性为`True`,同时在`OUT_TYPES`属性中设置模型前向输出的列表中每一个元素对应的标签类型。可参考`ChangeStar`模型的定义。
@ -64,7 +64,7 @@ Args:
2. 在`paddlers/tasks`目录中找到任务对应的训练器定义文件(例如变化检测任务对应`paddlers/tasks/change_detector.py`)。
3. 在文件尾部追加新的训练器定义。训练器需要继承自相关的基类(例如`BaseChangeDetector`),重写`__init__()`方法,并根据需要重写其他方法。对训练器`__init__()`方法编写的要求如下:
- 对于变化检测、场景分类、目标检测、图像分割任务,`__init__()`方法的第1个输入参数是`num_classes`,表示模型输出类别数对于变化检测、场景分类、图像分割任务,第2个输入参数是`use_mixed_loss`,表示用户是否使用默认定义的混合损失。
- 对于变化检测、场景分类、目标检测、图像分割任务,`__init__()`方法的第1个输入参数是`num_classes`,表示模型输出类别数对于变化检测、场景分类、图像分割任务,第2个输入参数是`use_mixed_loss`,表示用户是否使用默认定义的混合损失;第3个输入参数是`losses`,表示训练时使用的损失函数。对于图像复原任务,第1个参数是`losses`,含义同上;第2个参数是`rs_factor`,表示超分辨率缩放倍数
- `__init__()`的所有输入参数都必须有默认值,且在**取默认值的情况下,模型接收3通道RGB输入**。
- 在`__init__()`中需要更新`params`字典,该字典中的键值对将被用作模型构造时的输入参数。
@ -78,7 +78,7 @@ Args:
### 2.2 新增数据预处理/数据增强算子
在`paddlers/transforms/operators.py`中定义新算子,所有算子均继承自`paddlers.transforms.Transform`类。算子的`apply()`方法接收一个字典`sample`作为输入,取出其中存储的相关对象,处理后对字典进行in-place修改,最后返回修改后的字典。在定义算子时,只有极少数的情况需要重写`apply()`方法。大多数情况下,只需要重写`apply_im()`、`apply_mask()`、`apply_bbox()`和`apply_segm()`方法就分别可以实现对输入图像、分割标签、目标框以及目标多边形的处理。
在`paddlers/transforms/operators.py`中定义新算子,所有算子均继承自`paddlers.transforms.Transform`类。算子的`apply()`方法接收一个字典`sample`作为输入,取出其中存储的相关对象,处理后对字典进行in-place修改,最后返回修改后的字典。在定义算子时,只有极少数的情况需要重写`apply()`方法。大多数情况下,只需要重写`apply_im()`、`apply_mask()`、`apply_bbox()`和`apply_segm()`方法就分别可以实现对图像、分割标签、目标框以及目标多边形的处理。
如果处理逻辑较为复杂,建议先封装为函数,添加到`paddlers/transforms/functions.py`中,然后在算子的`apply*()`方法中调用函数。

@ -10,19 +10,20 @@ PaddleRS目前已支持的全部模型如下(标注\*的为遥感专用模型
|--------|---------|------|
| 变化检测 | \*BIT | 是 |
| 变化检测 | \*CDNet | 是 |
| 变化检测 | \*ChangeFormer | 是 |
| 变化检测 | \*ChangeStar | 否 |
| 变化检测 | \*DSAMNet | 是 |
| 变化检测 | \*DSIFN | 否 |
| 变化检测 | \*SNUNet | 是 |
| 变化检测 | \*STANet | 是 |
| 变化检测 | \*FC-EF | 是 |
| 变化检测 | \*FC-Siam-conc | 是 |
| 变化检测 | \*FC-Siam-diff | 是 |
| 变化检测 | \*ChangeStar | 否 |
| 变化检测 | \*ChangeFormer | 是 |
| 变化检测 | \*FCCDN | 是 |
| 变化检测 | \*SNUNet | 是 |
| 变化检测 | \*STANet | 是 |
| 场景分类 | CondenseNetV2 | 是 |
| 场景分类 | HRNet | 是 |
| 场景分类 | MobileNetV3 | 是 |
| 场景分类 | ResNet50-vd | 是 |
| 场景分类 | CondenseNetV2 | 是 |
| 图像复原 | DRN | 否 |
| 图像复原 | ESRGAN | 否 |
| 图像复原 | LESRCNN | 否 |
@ -32,5 +33,5 @@ PaddleRS目前已支持的全部模型如下(标注\*的为遥感专用模型
| 目标检测 | PP-YOLOv2 | 是 |
| 目标检测 | YOLOv3 | 是 |
| 图像分割 | DeepLab V3+ | 是 |
| 图像分割 | UNet | 是 |
| 图像分割 | \*FarSeg | 否 |
| 图像分割 | UNet | 是 |

@ -6,26 +6,26 @@ PaddleRS对不同遥感任务需要的数据预处理/数据增强(合称为
| 数据变换算子名 | 用途 | 任务 | ... |
| -------------------- | ------------------------------------------------- | -------- | ---- |
| Resize | 调整输入影像大小。 | 所有任务 | ... |
| RandomResize | 随机调整输入影像大小。 | 所有任务 | ... |
| ResizeByShort | 调整输入影像大小,保持纵横比不变(根据短边计算缩放系数)。 | 所有任务 | ... |
| RandomResizeByShort | 随机调整输入影像大小,保持纵横比不变(根据短边计算缩放系数)。 | 所有任务 | ... |
| ResizeByLong | 调整输入影像大小,保持纵横比不变(根据长边计算缩放系数)。 | 所有任务 | ... |
| RandomHorizontalFlip | 随机水平翻转输入影像。 | 所有任务 | ... |
| RandomVerticalFlip | 随机垂直翻转输入影像。 | 所有任务 | ... |
| Normalize | 对输入影像应用标准化。 | 所有任务 | ... |
| CenterCrop | 对输入影像进行中心裁剪。 | 所有任务 | ... |
| RandomCrop | 对输入影像进行随机中心裁剪。 | 所有任务 | ... |
| RandomScaleAspect | 裁剪输入影像并重新缩放到原始尺寸。 | 所有任务 | ... |
| RandomExpand | 根据随机偏移扩展输入影像。 | 所有任务 | ... |
| Pad | 将输入影像填充到指定的大小。 | 所有任务 | ... |
| Dehaze | 对输入图像进行去雾。 | 所有任务 | ... |
| MixupImage | 将两幅影像(及对应的目标检测标注)混合在一起作为新的样本。 | 目标检测 | ... |
| RandomDistort | 对输入施加随机色彩变换。 | 所有任务 | ... |
| Normalize | 对输入影像应用标准化。 | 所有任务 | ... |
| Pad | 将输入影像填充到指定的大小。 | 所有任务 | ... |
| RandomBlur | 对输入施加随机模糊。 | 所有任务 | ... |
| Dehaze | 对输入图像进行去雾。 | 所有任务 | ... |
| RandomCrop | 对输入影像进行随机中心裁剪。 | 所有任务 | ... |
| RandomDistort | 对输入施加随机色彩变换。 | 所有任务 | ... |
| RandomExpand | 根据随机偏移扩展输入影像。 | 所有任务 | ... |
| RandomHorizontalFlip | 随机水平翻转输入影像。 | 所有任务 | ... |
| RandomResize | 随机调整输入影像大小。 | 所有任务 | ... |
| RandomResizeByShort | 随机调整输入影像大小,保持纵横比不变(根据短边计算缩放系数)。 | 所有任务 | ... |
| RandomScaleAspect | 裁剪输入影像并重新缩放到原始尺寸。 | 所有任务 | ... |
| RandomSwap | 随机交换两个时相的输入影像。 | 变化检测 | ... |
| RandomVerticalFlip | 随机竖直翻转输入影像。 | 所有任务 | ... |
| ReduceDim | 对输入图像进行波段降维。 | 所有任务 | ... |
| Resize | 调整输入影像大小。 | 所有任务 | ... |
| ResizeByLong | 调整输入影像大小,保持纵横比不变(根据长边计算缩放系数)。 | 所有任务 | ... |
| ResizeByShort | 调整输入影像大小,保持纵横比不变(根据短边计算缩放系数)。 | 所有任务 | ... |
| SelectBand | 对输入影像进行波段选择。 | 所有任务 | ... |
| RandomSwap | 随机交换两个时相的输入影像。 | 变化检测 | ... |
| ... | ... | ... | ... |
## 组合算子

@ -187,7 +187,7 @@ python train_cd.py
#### 4.2.1 配置文件编写
本案例提供一个基于[YAML][https://yaml.org/]的轻量级配置系统,使用者可以通过修改yaml文件达到调整超参数、更换模型、更换数据集等目的,或通过编写yaml文件增加新的配置。
本案例提供一个基于[YAML](https://yaml.org/)的轻量级配置系统,使用者可以通过修改yaml文件达到调整超参数、更换模型、更换数据集等目的,或通过编写yaml文件增加新的配置。
关于本案例中配置文件的编写规则,请参考[此项目](https://aistudio.baidu.com/aistudio/projectdetail/4203534)。

@ -132,7 +132,7 @@ def parse_args(*args, **kwargs):
conflict_handler='resolve', parents=[cfg_parser])
# Global settings
parser.add_argument('cmd', choices=['train', 'eval'])
parser.add_argument('task', choices=['cd', 'clas', 'det', 'seg'])
parser.add_argument('task', choices=['cd', 'clas', 'det', 'res', 'seg'])
# Data
parser.add_argument('--datasets', type=dict, default={})

@ -17,4 +17,4 @@ from .coco import COCODetDataset
from .seg_dataset import SegDataset
from .cd_dataset import CDDataset
from .clas_dataset import ClasDataset
from .sr_dataset import SRdataset, ComposeTrans
from .res_dataset import ResDataset

@ -95,23 +95,23 @@ class CDDataset(BaseDataset):
full_path_label))):
continue
if not osp.exists(full_path_im_t1):
raise IOError('Image file {} does not exist!'.format(
raise IOError("Image file {} does not exist!".format(
full_path_im_t1))
if not osp.exists(full_path_im_t2):
raise IOError('Image file {} does not exist!'.format(
raise IOError("Image file {} does not exist!".format(
full_path_im_t2))
if not osp.exists(full_path_label):
raise IOError('Label file {} does not exist!'.format(
raise IOError("Label file {} does not exist!".format(
full_path_label))
if with_seg_labels:
full_path_seg_label_t1 = osp.join(data_dir, items[3])
full_path_seg_label_t2 = osp.join(data_dir, items[4])
if not osp.exists(full_path_seg_label_t1):
raise IOError('Label file {} does not exist!'.format(
raise IOError("Label file {} does not exist!".format(
full_path_seg_label_t1))
if not osp.exists(full_path_seg_label_t2):
raise IOError('Label file {} does not exist!'.format(
raise IOError("Label file {} does not exist!".format(
full_path_seg_label_t2))
item_dict = dict(

@ -0,0 +1,83 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os.path as osp
import copy
from .base import BaseDataset
from paddlers.utils import logging, get_encoding, norm_path, is_pic
class ResDataset(BaseDataset):
"""
Dataset for image restoration tasks.
Args:
data_dir (str): Root directory of the dataset.
file_list (str): Path of the file that contains relative paths of source and target image files.
transforms (paddlers.transforms.Compose): Data preprocessing and data augmentation operators to apply.
num_workers (int|str, optional): Number of processes used for data loading. If `num_workers` is 'auto',
the number of workers will be automatically determined according to the number of CPU cores: If
there are more than 16 cores8 workers will be used. Otherwise, the number of workers will be half
the number of CPU cores. Defaults: 'auto'.
shuffle (bool, optional): Whether to shuffle the samples. Defaults to False.
sr_factor (int|None, optional): Scaling factor of image super-resolution task. None for other image
restoration tasks. Defaults to None.
"""
def __init__(self,
data_dir,
file_list,
transforms,
num_workers='auto',
shuffle=False,
sr_factor=None):
super(ResDataset, self).__init__(data_dir, None, transforms,
num_workers, shuffle)
self.batch_transforms = None
self.file_list = list()
with open(file_list, encoding=get_encoding(file_list)) as f:
for line in f:
items = line.strip().split()
if len(items) > 2:
raise ValueError(
"A space is defined as the delimiter to separate the source and target image path, " \
"so the space cannot be in the source image or target image path, but the line[{}] of " \
" file_list[{}] has a space in the two paths.".format(line, file_list))
items[0] = norm_path(items[0])
items[1] = norm_path(items[1])
full_path_im = osp.join(data_dir, items[0])
full_path_tar = osp.join(data_dir, items[1])
if not is_pic(full_path_im) or not is_pic(full_path_tar):
continue
if not osp.exists(full_path_im):
raise IOError("Source image file {} does not exist!".format(
full_path_im))
if not osp.exists(full_path_tar):
raise IOError("Target image file {} does not exist!".format(
full_path_tar))
sample = {
'image': full_path_im,
'target': full_path_tar,
}
if sr_factor is not None:
sample['sr_factor'] = sr_factor
self.file_list.append(sample)
self.num_samples = len(self.file_list)
logging.info("{} samples in file {}".format(
len(self.file_list), file_list))
def __len__(self):
return len(self.file_list)

@ -44,7 +44,7 @@ class SegDataset(BaseDataset):
shuffle=False):
super(SegDataset, self).__init__(data_dir, label_list, transforms,
num_workers, shuffle)
# TODO batch padding
# TODO: batch padding
self.batch_transforms = None
self.file_list = list()
self.labels = list()

@ -1,99 +0,0 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 超分辨率数据集定义
class SRdataset(object):
def __init__(self,
mode,
gt_floder,
lq_floder,
transforms,
scale,
num_workers=4,
batch_size=8):
if mode == 'train':
preprocess = []
preprocess.append({
'name': 'LoadImageFromFile',
'key': 'lq'
}) # 加载方式
preprocess.append({'name': 'LoadImageFromFile', 'key': 'gt'})
preprocess.append(transforms) # 变换方式
self.dataset = {
'name': 'SRDataset',
'gt_folder': gt_floder,
'lq_folder': lq_floder,
'num_workers': num_workers,
'batch_size': batch_size,
'scale': scale,
'preprocess': preprocess
}
if mode == "test":
preprocess = []
preprocess.append({'name': 'LoadImageFromFile', 'key': 'lq'})
preprocess.append({'name': 'LoadImageFromFile', 'key': 'gt'})
preprocess.append(transforms)
self.dataset = {
'name': 'SRDataset',
'gt_folder': gt_floder,
'lq_folder': lq_floder,
'scale': scale,
'preprocess': preprocess
}
def __call__(self):
return self.dataset
# 对定义的transforms处理方式组合,返回字典
class ComposeTrans(object):
def __init__(self, input_keys, output_keys, pipelines):
if not isinstance(pipelines, list):
raise TypeError(
'Type of transforms is invalid. Must be List, but received is {}'
.format(type(pipelines)))
if len(pipelines) < 1:
raise ValueError(
'Length of transforms must not be less than 1, but received is {}'
.format(len(pipelines)))
self.transforms = pipelines
self.output_length = len(output_keys) # 当output_keys的长度为3时,是DRN训练
self.input_keys = input_keys
self.output_keys = output_keys
def __call__(self):
pipeline = []
for op in self.transforms:
if op['name'] == 'SRPairedRandomCrop':
op['keys'] = ['image'] * 2
else:
op['keys'] = ['image'] * self.output_length
pipeline.append(op)
if self.output_length == 2:
transform_dict = {
'name': 'Transforms',
'input_keys': self.input_keys,
'pipeline': pipeline
}
else:
transform_dict = {
'name': 'Transforms',
'input_keys': self.input_keys,
'output_keys': self.output_keys,
'pipeline': pipeline
}
return transform_dict

@ -105,7 +105,7 @@ class Predictor(object):
logging.warning(
"Semantic segmentation models do not support TensorRT acceleration, "
"TensorRT is forcibly disabled.")
elif 'RCNN' in self._model.__class__.__name__:
elif self._model.model_type == 'detector' and 'RCNN' in self._model.__class__.__name__:
logging.warning(
"RCNN models do not support TensorRT acceleration, "
"TensorRT is forcibly disabled.")
@ -163,13 +163,23 @@ class Predictor(object):
'image2': preprocessed_samples[1],
'ori_shape': preprocessed_samples[2]
}
elif self._model.model_type == 'restorer':
preprocessed_samples = {
'image': preprocessed_samples[0],
'tar_shape': preprocessed_samples[1]
}
else:
logging.error(
"Invalid model type {}".format(self._model.model_type),
exit=True)
return preprocessed_samples
def postprocess(self, net_outputs, topk=1, ori_shape=None, transforms=None):
def postprocess(self,
net_outputs,
topk=1,
ori_shape=None,
tar_shape=None,
transforms=None):
if self._model.model_type == 'classifier':
true_topk = min(self._model.num_classes, topk)
if self._model.postprocess is None:
@ -201,6 +211,12 @@ class Predictor(object):
for k, v in zip(['bbox', 'bbox_num', 'mask'], net_outputs)
}
preds = self._model.postprocess(net_outputs)
elif self._model.model_type == 'restorer':
res_maps = self._model.postprocess(
net_outputs[0],
batch_tar_shape=tar_shape,
transforms=transforms.transforms)
preds = [{'res_map': res_map} for res_map in res_maps]
else:
logging.error(
"Invalid model type {}.".format(self._model.model_type),
@ -244,6 +260,7 @@ class Predictor(object):
net_outputs,
topk,
ori_shape=preprocessed_input.get('ori_shape', None),
tar_shape=preprocessed_input.get('tar_shape', None),
transforms=transforms)
self.timer.postprocess_time_s.end(iter_num=len(images))

@ -16,3 +16,4 @@ from . import ppcls, ppdet, ppseg, ppgan
import paddlers.models.ppseg.models.losses as seg_losses
import paddlers.models.ppdet.modeling.losses as det_losses
import paddlers.models.ppcls.loss as clas_losses
import paddlers.models.ppgan.models.criterions as res_losses

@ -23,3 +23,5 @@ from .fc_ef import FCEarlyFusion
from .fc_siam_conc import FCSiamConc
from .fc_siam_diff import FCSiamDiff
from .changeformer import ChangeFormer
from .fccdn import FCCDN
from .losses import fccdn_ssl_loss

@ -0,0 +1,478 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import paddle
import paddle.nn as nn
import paddle.nn.functional as F
from .layers import BasicConv, MaxPool2x2, Conv1x1, Conv3x3
bn_mom = 1 - 0.0003
class NLBlock(nn.Layer):
def __init__(self, in_channels):
super(NLBlock, self).__init__()
self.conv_v = BasicConv(
in_ch=in_channels,
out_ch=in_channels,
kernel_size=3,
norm=nn.BatchNorm2D(
in_channels, momentum=0.9))
self.W = BasicConv(
in_ch=in_channels,
out_ch=in_channels,
kernel_size=3,
norm=nn.BatchNorm2D(
in_channels, momentum=0.9),
act=nn.ReLU())
def forward(self, x):
batch_size, c, h, w = x.shape[0], x.shape[1], x.shape[2], x.shape[3]
value = self.conv_v(x)
value = value.reshape([batch_size, c, value.shape[2] * value.shape[3]])
value = value.transpose([0, 2, 1]) # B * (H*W) * value_channels
key = x.reshape([batch_size, c, h * w]) # B * key_channels * (H*W)
query = x.reshape([batch_size, c, h * w])
query = query.transpose([0, 2, 1])
sim_map = paddle.matmul(query, key) # B * (H*W) * (H*W)
sim_map = (c**-.5) * sim_map # B * (H*W) * (H*W)
sim_map = nn.functional.softmax(sim_map, axis=-1) # B * (H*W) * (H*W)
context = paddle.matmul(sim_map, value)
context = context.transpose([0, 2, 1])
context = context.reshape([batch_size, c, *x.shape[2:]])
context = self.W(context)
return context
class NLFPN(nn.Layer):
""" Non-local feature parymid network"""
def __init__(self, in_dim, reduction=True):
super(NLFPN, self).__init__()
if reduction:
self.reduction = BasicConv(
in_ch=in_dim,
out_ch=in_dim // 4,
kernel_size=1,
norm=nn.BatchNorm2D(
in_dim // 4, momentum=bn_mom),
act=nn.ReLU())
self.re_reduction = BasicConv(
in_ch=in_dim // 4,
out_ch=in_dim,
kernel_size=1,
norm=nn.BatchNorm2D(
in_dim, momentum=bn_mom),
act=nn.ReLU())
in_dim = in_dim // 4
else:
self.reduction = None
self.re_reduction = None
self.conv_e1 = BasicConv(
in_dim,
in_dim,
kernel_size=3,
norm=nn.BatchNorm2D(
in_dim, momentum=bn_mom),
act=nn.ReLU())
self.conv_e2 = BasicConv(
in_dim,
in_dim * 2,
kernel_size=3,
norm=nn.BatchNorm2D(
in_dim * 2, momentum=bn_mom),
act=nn.ReLU())
self.conv_e3 = BasicConv(
in_dim * 2,
in_dim * 4,
kernel_size=3,
norm=nn.BatchNorm2D(
in_dim * 4, momentum=bn_mom),
act=nn.ReLU())
self.conv_d1 = BasicConv(
in_dim,
in_dim,
kernel_size=3,
norm=nn.BatchNorm2D(
in_dim, momentum=bn_mom),
act=nn.ReLU())
self.conv_d2 = BasicConv(
in_dim * 2,
in_dim,
kernel_size=3,
norm=nn.BatchNorm2D(
in_dim, momentum=bn_mom),
act=nn.ReLU())
self.conv_d3 = BasicConv(
in_dim * 4,
in_dim * 2,
kernel_size=3,
norm=nn.BatchNorm2D(
in_dim * 2, momentum=bn_mom),
act=nn.ReLU())
self.nl3 = NLBlock(in_dim * 2)
self.nl2 = NLBlock(in_dim)
self.nl1 = NLBlock(in_dim)
self.downsample_x2 = nn.MaxPool2D(stride=2, kernel_size=2)
self.upsample_x2 = nn.UpsamplingBilinear2D(scale_factor=2)
def forward(self, x):
if self.reduction is not None:
x = self.reduction(x)
e1 = self.conv_e1(x) # C,H,W
e2 = self.conv_e2(self.downsample_x2(e1)) # 2C,H/2,W/2
e3 = self.conv_e3(self.downsample_x2(e2)) # 4C,H/4,W/4
d3 = self.conv_d3(e3) # 2C,H/4,W/4
nl = self.nl3(d3)
d3 = self.upsample_x2(paddle.multiply(d3, nl)) ##2C,H/2,W/2
d2 = self.conv_d2(e2 + d3) # C,H/2,W/2
nl = self.nl2(d2)
d2 = self.upsample_x2(paddle.multiply(d2, nl)) # C,H,W
d1 = self.conv_d1(e1 + d2)
nl = self.nl1(d1)
d1 = paddle.multiply(d1, nl) # C,H,W
if self.re_reduction is not None:
d1 = self.re_reduction(d1)
return d1
class Cat(nn.Layer):
def __init__(self, in_chn_high, in_chn_low, out_chn, upsample=False):
super(Cat, self).__init__()
self.do_upsample = upsample
self.upsample = nn.Upsample(scale_factor=2, mode="nearest")
self.conv2d = BasicConv(
in_chn_high + in_chn_low,
out_chn,
kernel_size=1,
norm=nn.BatchNorm2D(
out_chn, momentum=bn_mom),
act=nn.ReLU())
def forward(self, x, y):
if self.do_upsample:
x = self.upsample(x)
x = paddle.concat((x, y), 1)
return self.conv2d(x)
class DoubleConv(nn.Layer):
def __init__(self, in_chn, out_chn, stride=1, dilation=1):
super(DoubleConv, self).__init__()
self.conv = nn.Sequential(
nn.Conv2D(
in_chn,
out_chn,
kernel_size=3,
stride=stride,
dilation=dilation,
padding=dilation),
nn.BatchNorm2D(
out_chn, momentum=bn_mom),
nn.ReLU(),
nn.Conv2D(
out_chn, out_chn, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2D(
out_chn, momentum=bn_mom),
nn.ReLU())
def forward(self, x):
x = self.conv(x)
return x
class SEModule(nn.Layer):
def __init__(self, channels, reduction_channels):
super(SEModule, self).__init__()
self.fc1 = nn.Conv2D(
channels,
reduction_channels,
kernel_size=1,
padding=0,
bias_attr=True)
self.ReLU = nn.ReLU()
self.fc2 = nn.Conv2D(
reduction_channels,
channels,
kernel_size=1,
padding=0,
bias_attr=True)
def forward(self, x):
x_se = x.reshape(
[x.shape[0], x.shape[1], x.shape[2] * x.shape[3]]).mean(-1).reshape(
[x.shape[0], x.shape[1], 1, 1])
x_se = self.fc1(x_se)
x_se = self.ReLU(x_se)
x_se = self.fc2(x_se)
return x * F.sigmoid(x_se)
class BasicBlock(nn.Layer):
expansion = 1
def __init__(self,
inplanes,
planes,
downsample=None,
use_se=False,
stride=1,
dilation=1):
super(BasicBlock, self).__init__()
first_planes = planes
outplanes = planes * self.expansion
self.conv1 = DoubleConv(inplanes, first_planes)
self.conv2 = DoubleConv(
first_planes, outplanes, stride=stride, dilation=dilation)
self.se = SEModule(outplanes, planes // 4) if use_se else None
self.downsample = MaxPool2x2() if downsample else None
self.ReLU = nn.ReLU()
def forward(self, x):
out = self.conv1(x)
residual = out
out = self.conv2(out)
if self.se is not None:
out = self.se(out)
if self.downsample is not None:
residual = self.downsample(residual)
out = out + residual
out = self.ReLU(out)
return out
class DenseCatAdd(nn.Layer):
def __init__(self, in_chn, out_chn):
super(DenseCatAdd, self).__init__()
self.conv1 = BasicConv(in_chn, in_chn, kernel_size=3, act=nn.ReLU())
self.conv2 = BasicConv(in_chn, in_chn, kernel_size=3, act=nn.ReLU())
self.conv3 = BasicConv(in_chn, in_chn, kernel_size=3, act=nn.ReLU())
self.conv_out = BasicConv(
in_chn,
out_chn,
kernel_size=1,
norm=nn.BatchNorm2D(
out_chn, momentum=bn_mom),
act=nn.ReLU())
def forward(self, x, y):
x1 = self.conv1(x)
x2 = self.conv2(x1)
x3 = self.conv3(x2 + x1)
y1 = self.conv1(y)
y2 = self.conv2(y1)
y3 = self.conv3(y2 + y1)
return self.conv_out(x1 + x2 + x3 + y1 + y2 + y3)
class DenseCatDiff(nn.Layer):
def __init__(self, in_chn, out_chn):
super(DenseCatDiff, self).__init__()
self.conv1 = BasicConv(in_chn, in_chn, kernel_size=3, act=nn.ReLU())
self.conv2 = BasicConv(in_chn, in_chn, kernel_size=3, act=nn.ReLU())
self.conv3 = BasicConv(in_chn, in_chn, kernel_size=3, act=nn.ReLU())
self.conv_out = BasicConv(
in_ch=in_chn,
out_ch=out_chn,
kernel_size=1,
norm=nn.BatchNorm2D(
out_chn, momentum=bn_mom),
act=nn.ReLU())
def forward(self, x, y):
x1 = self.conv1(x)
x2 = self.conv2(x1)
x3 = self.conv3(x2 + x1)
y1 = self.conv1(y)
y2 = self.conv2(y1)
y3 = self.conv3(y2 + y1)
out = self.conv_out(paddle.abs(x1 + x2 + x3 - y1 - y2 - y3))
return out
class DFModule(nn.Layer):
"""Dense connection-based feature fusion module"""
def __init__(self, dim_in, dim_out, reduction=True):
super(DFModule, self).__init__()
if reduction:
self.reduction = Conv1x1(
dim_in,
dim_in // 2,
norm=nn.BatchNorm2D(
dim_in // 2, momentum=bn_mom),
act=nn.ReLU())
dim_in = dim_in // 2
else:
self.reduction = None
self.cat1 = DenseCatAdd(dim_in, dim_out)
self.cat2 = DenseCatDiff(dim_in, dim_out)
self.conv1 = Conv3x3(
dim_out,
dim_out,
norm=nn.BatchNorm2D(
dim_out, momentum=bn_mom),
act=nn.ReLU())
def forward(self, x1, x2):
if self.reduction is not None:
x1 = self.reduction(x1)
x2 = self.reduction(x2)
x_add = self.cat1(x1, x2)
x_diff = self.cat2(x1, x2)
y = self.conv1(x_diff) + x_add
return y
class FCCDN(nn.Layer):
"""
The FCCDN implementation based on PaddlePaddle.
The original article refers to
Pan Chen, et al., "FCCDN: Feature Constraint Network for VHR Image Change Detection"
(https://arxiv.org/pdf/2105.10860.pdf).
Args:
in_channels (int): Number of input channels. Default: 3.
num_classes (int): Number of target classes. Default: 2.
os (int): Number of output stride. Default: 16.
use_se (bool): Whether to use SEModule. Default: True.
"""
def __init__(self, in_channels=3, num_classes=2, os=16, use_se=True):
super(FCCDN, self).__init__()
if os >= 16:
dilation_list = [1, 1, 1, 1]
stride_list = [2, 2, 2, 2]
pool_list = [True, True, True, True]
elif os == 8:
dilation_list = [2, 1, 1, 1]
stride_list = [1, 2, 2, 2]
pool_list = [False, True, True, True]
else:
dilation_list = [2, 2, 1, 1]
stride_list = [1, 1, 2, 2]
pool_list = [False, False, True, True]
se_list = [use_se, use_se, use_se, use_se]
channel_list = [256, 128, 64, 32]
# Encoder
self.block1 = BasicBlock(in_channels, channel_list[3], pool_list[3],
se_list[3], stride_list[3], dilation_list[3])
self.block2 = BasicBlock(channel_list[3], channel_list[2], pool_list[2],
se_list[2], stride_list[2], dilation_list[2])
self.block3 = BasicBlock(channel_list[2], channel_list[1], pool_list[1],
se_list[1], stride_list[1], dilation_list[1])
self.block4 = BasicBlock(channel_list[1], channel_list[0], pool_list[0],
se_list[0], stride_list[0], dilation_list[0])
# Center
self.center = NLFPN(channel_list[0], True)
# Decoder
self.decoder3 = Cat(channel_list[0],
channel_list[1],
channel_list[1],
upsample=pool_list[0])
self.decoder2 = Cat(channel_list[1],
channel_list[2],
channel_list[2],
upsample=pool_list[1])
self.decoder1 = Cat(channel_list[2],
channel_list[3],
channel_list[3],
upsample=pool_list[2])
self.df1 = DFModule(channel_list[3], channel_list[3], True)
self.df2 = DFModule(channel_list[2], channel_list[2], True)
self.df3 = DFModule(channel_list[1], channel_list[1], True)
self.df4 = DFModule(channel_list[0], channel_list[0], True)
self.catc3 = Cat(channel_list[0],
channel_list[1],
channel_list[1],
upsample=pool_list[0])
self.catc2 = Cat(channel_list[1],
channel_list[2],
channel_list[2],
upsample=pool_list[1])
self.catc1 = Cat(channel_list[2],
channel_list[3],
channel_list[3],
upsample=pool_list[2])
self.upsample_x2 = nn.Sequential(
nn.Conv2D(
channel_list[3], 8, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2D(
8, momentum=bn_mom),
nn.ReLU(),
nn.UpsamplingBilinear2D(scale_factor=2))
self.conv_out = nn.Conv2D(
8, num_classes, kernel_size=3, stride=1, padding=1)
self.conv_out_class = nn.Conv2D(
channel_list[3], 1, kernel_size=1, stride=1, padding=0)
def forward(self, t1, t2):
e1_1 = self.block1(t1)
e2_1 = self.block2(e1_1)
e3_1 = self.block3(e2_1)
y1 = self.block4(e3_1)
e1_2 = self.block1(t2)
e2_2 = self.block2(e1_2)
e3_2 = self.block3(e2_2)
y2 = self.block4(e3_2)
y1 = self.center(y1)
y2 = self.center(y2)
c = self.df4(y1, y2)
y1 = self.decoder3(y1, e3_1)
y2 = self.decoder3(y2, e3_2)
c = self.catc3(c, self.df3(y1, y2))
y1 = self.decoder2(y1, e2_1)
y2 = self.decoder2(y2, e2_2)
c = self.catc2(c, self.df2(y1, y2))
y1 = self.decoder1(y1, e1_1)
y2 = self.decoder1(y2, e1_2)
c = self.catc1(c, self.df1(y1, y2))
y = self.conv_out(self.upsample_x2(c))
if self.training:
y1 = self.conv_out_class(y1)
y2 = self.conv_out_class(y2)
return [y, [y1, y2]]
else:
return [y]

@ -1,4 +1,4 @@
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -12,15 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
from ....models.ppgan.utils.registry import Registry
GENERATORS = Registry("GENERATOR")
def build_generator(cfg):
cfg_copy = copy.deepcopy(cfg)
name = cfg_copy.pop('name')
generator = GENERATORS.get(name)(**cfg_copy)
return generator
from .fccdn_loss import fccdn_ssl_loss

@ -0,0 +1,170 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import paddle
import paddle.nn as nn
import paddle.nn.functional as F
class DiceLoss(nn.Layer):
def __init__(self, batch=True):
super(DiceLoss, self).__init__()
self.batch = batch
def soft_dice_coeff(self, y_pred, y_true):
smooth = 0.00001
if self.batch:
i = paddle.sum(y_true)
j = paddle.sum(y_pred)
intersection = paddle.sum(y_true * y_pred)
else:
i = y_true.sum(1).sum(1).sum(1)
j = y_pred.sum(1).sum(1).sum(1)
intersection = (y_true * y_pred).sum(1).sum(1).sum(1)
score = (2. * intersection + smooth) / (i + j + smooth)
return score.mean()
def soft_dice_loss(self, y_pred, y_true):
loss = 1 - self.soft_dice_coeff(y_pred, y_true)
return loss
def forward(self, y_pred, y_true):
return self.soft_dice_loss(y_pred.astype(paddle.float32), y_true)
class MultiClassDiceLoss(nn.Layer):
def __init__(
self,
weight,
batch=True,
ignore_index=-1,
do_softmax=False,
**kwargs, ):
super(MultiClassDiceLoss, self).__init__()
self.ignore_index = ignore_index
self.weight = weight
self.do_softmax = do_softmax
self.binary_diceloss = DiceLoss(batch)
def forward(self, y_pred, y_true):
if self.do_softmax:
y_pred = paddle.nn.functional.softmax(y_pred, axis=1)
y_true = F.one_hot(y_true.long(), y_pred.shape[1]).permute(0, 3, 1, 2)
total_loss = 0.0
tmp_i = 0.0
for i in range(y_pred.shape[1]):
if i != self.ignore_index:
diceloss = self.binary_diceloss(y_pred[:, i, :, :],
y_true[:, i, :, :])
total_loss += paddle.multiply(diceloss, self.weight[i])
tmp_i += 1.0
return total_loss / tmp_i
class DiceBCELoss(nn.Layer):
"""Binary change detection task loss"""
def __init__(self):
super(DiceBCELoss, self).__init__()
self.bce_loss = nn.BCELoss()
self.binnary_dice = DiceLoss()
def forward(self, scores, labels, do_sigmoid=True):
if len(scores.shape) > 3:
scores = scores.squeeze(1)
if len(labels.shape) > 3:
labels = labels.squeeze(1)
if do_sigmoid:
scores = paddle.nn.functional.sigmoid(scores.clone())
diceloss = self.binnary_dice(scores, labels)
bceloss = self.bce_loss(scores, labels)
return diceloss + bceloss
class McDiceBCELoss(nn.Layer):
"""Multi-class change detection task loss"""
def __init__(self, weight, do_sigmoid=True):
super(McDiceBCELoss, self).__init__()
self.ce_loss = nn.CrossEntropyLoss(weight)
self.dice = MultiClassDiceLoss(weight, do_sigmoid)
def forward(self, scores, labels):
if len(scores.shape) < 4:
scores = scores.unsqueeze(1)
if len(labels.shape) < 4:
labels = labels.unsqueeze(1)
diceloss = self.dice(scores, labels)
bceloss = self.ce_loss(scores, labels)
return diceloss + bceloss
def fccdn_ssl_loss(logits_list, labels):
"""
Self-supervised learning loss for change detection.
The original article refers to
Pan Chen, et al., "FCCDN: Feature Constraint Network for VHR Image Change Detection"
(https://arxiv.org/pdf/2105.10860.pdf).
Args:
logits_list (list[paddle.Tensor]): Single-channel segmentation logit maps for each of the two temporal phases.
labels (paddle.Tensor): Binary change labels.
"""
# Create loss
criterion_ssl = DiceBCELoss()
# Get downsampled change map
h, w = logits_list[0].shape[-2], logits_list[0].shape[-1]
labels_downsample = F.interpolate(x=labels.unsqueeze(1), size=[h, w])
labels_type = str(labels_downsample.dtype)
assert "int" in labels_type or "bool" in labels_type,\
f"Expected dtype of labels to be int or bool, but got {labels_type}"
# Seg map
out1 = paddle.nn.functional.sigmoid(logits_list[0]).clone()
out2 = paddle.nn.functional.sigmoid(logits_list[1]).clone()
out3 = out1.clone()
out4 = out2.clone()
out1 = paddle.where(labels_downsample == 1, paddle.zeros_like(out1), out1)
out2 = paddle.where(labels_downsample == 1, paddle.zeros_like(out2), out2)
out3 = paddle.where(labels_downsample != 1, paddle.zeros_like(out3), out3)
out4 = paddle.where(labels_downsample != 1, paddle.zeros_like(out4), out4)
pred_seg_pre_tmp1 = paddle.where(out1 <= 0.5,
paddle.zeros_like(out1),
paddle.ones_like(out1))
pred_seg_post_tmp1 = paddle.where(out2 <= 0.5,
paddle.zeros_like(out2),
paddle.ones_like(out2))
pred_seg_pre_tmp2 = paddle.where(out3 <= 0.5,
paddle.zeros_like(out3),
paddle.ones_like(out3))
pred_seg_post_tmp2 = paddle.where(out4 <= 0.5,
paddle.zeros_like(out4),
paddle.ones_like(out4))
# Seg loss
labels_downsample = labels_downsample.astype(paddle.float32)
loss_aux = 0.2 * criterion_ssl(out1, pred_seg_post_tmp1, False)
loss_aux += 0.2 * criterion_ssl(out2, pred_seg_pre_tmp1, False)
loss_aux += 0.2 * criterion_ssl(
out3, labels_downsample - pred_seg_post_tmp2, False)
loss_aux += 0.2 * criterion_ssl(out4, labels_downsample - pred_seg_pre_tmp2,
False)
return loss_aux

@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from .rcan_model import RCANModel
from .generators import *

@ -0,0 +1,27 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import paddle
import paddle.nn as nn
from paddlers.models.ppgan.modules.init import reset_parameters
def init_sr_weight(net):
def reset_func(m):
if hasattr(m, 'weight') and (
not isinstance(m, (nn.BatchNorm, nn.BatchNorm2D))):
reset_parameters(m)
net.apply(reset_func)

@ -1,10 +1,25 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Based on https://github.com/kongdebug/RCAN-Paddle
import math
import paddle
import paddle.nn as nn
from .builder import GENERATORS
from .param_init import init_sr_weight
def default_conv(in_channels, out_channels, kernel_size, bias=True):
@ -63,8 +78,10 @@ class RCAB(nn.Layer):
bias=True,
bn=False,
act=nn.ReLU(),
res_scale=1):
res_scale=1,
use_init_weight=False):
super(RCAB, self).__init__()
modules_body = []
for i in range(2):
modules_body.append(conv(n_feat, n_feat, kernel_size, bias=bias))
@ -74,6 +91,9 @@ class RCAB(nn.Layer):
self.body = nn.Sequential(*modules_body)
self.res_scale = res_scale
if use_init_weight:
init_sr_weight(self)
def forward(self, x):
res = self.body(x)
res += x
@ -128,21 +148,19 @@ class Upsampler(nn.Sequential):
super(Upsampler, self).__init__(*m)
@GENERATORS.register()
class RCAN(nn.Layer):
def __init__(
self,
scale,
n_resgroups,
n_resblocks,
def __init__(self,
sr_factor=4,
n_resgroups=10,
n_resblocks=20,
n_feats=64,
n_colors=3,
rgb_range=255,
kernel_size=3,
reduction=16,
conv=default_conv, ):
conv=default_conv):
super(RCAN, self).__init__()
self.scale = scale
self.scale = sr_factor
act = nn.ReLU()
n_resgroups = n_resgroups
@ -150,7 +168,6 @@ class RCAN(nn.Layer):
n_feats = n_feats
kernel_size = kernel_size
reduction = reduction
scale = scale
act = nn.ReLU()
rgb_mean = (0.4488, 0.4371, 0.4040)
@ -171,7 +188,7 @@ class RCAN(nn.Layer):
# Define tail module
modules_tail = [
Upsampler(
conv, scale, n_feats, act=False),
conv, self.scale, n_feats, act=False),
conv(n_feats, n_colors, kernel_size)
]

@ -1,106 +0,0 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import paddle
import paddle.nn as nn
from .generators.builder import build_generator
from ...models.ppgan.models.criterions.builder import build_criterion
from ...models.ppgan.models.base_model import BaseModel
from ...models.ppgan.models.builder import MODELS
from ...models.ppgan.utils.visual import tensor2img
from ...models.ppgan.modules.init import reset_parameters
@MODELS.register()
class RCANModel(BaseModel):
"""
Base SR model for single image super-resolution.
"""
def __init__(self, generator, pixel_criterion=None, use_init_weight=False):
"""
Args:
generator (dict): config of generator.
pixel_criterion (dict): config of pixel criterion.
"""
super(RCANModel, self).__init__()
self.nets['generator'] = build_generator(generator)
self.error_last = 1e8
self.batch = 0
if pixel_criterion:
self.pixel_criterion = build_criterion(pixel_criterion)
if use_init_weight:
init_sr_weight(self.nets['generator'])
def setup_input(self, input):
self.lq = paddle.to_tensor(input['lq'])
self.visual_items['lq'] = self.lq
if 'gt' in input:
self.gt = paddle.to_tensor(input['gt'])
self.visual_items['gt'] = self.gt
self.image_paths = input['lq_path']
def forward(self):
pass
def train_iter(self, optims=None):
optims['optim'].clear_grad()
self.output = self.nets['generator'](self.lq)
self.visual_items['output'] = self.output
# pixel loss
loss_pixel = self.pixel_criterion(self.output, self.gt)
self.losses['loss_pixel'] = loss_pixel
skip_threshold = 1e6
if loss_pixel.item() < skip_threshold * self.error_last:
loss_pixel.backward()
optims['optim'].step()
else:
print('Skip this batch {}! (Loss: {})'.format(self.batch + 1,
loss_pixel.item()))
self.batch += 1
if self.batch % 1000 == 0:
self.error_last = loss_pixel.item() / 1000
print("update error_last:{}".format(self.error_last))
def test_iter(self, metrics=None):
self.nets['generator'].eval()
with paddle.no_grad():
self.output = self.nets['generator'](self.lq)
self.visual_items['output'] = self.output
self.nets['generator'].train()
out_img = []
gt_img = []
for out_tensor, gt_tensor in zip(self.output, self.gt):
out_img.append(tensor2img(out_tensor, (0., 255.)))
gt_img.append(tensor2img(gt_tensor, (0., 255.)))
if metrics is not None:
for metric in metrics.values():
metric.update(out_img, gt_img)
def init_sr_weight(net):
def reset_func(m):
if hasattr(m, 'weight') and (
not isinstance(m, (nn.BatchNorm, nn.BatchNorm2D))):
reset_parameters(m)
net.apply(reset_func)

@ -16,7 +16,7 @@ import paddlers.tasks.object_detector as detector
import paddlers.tasks.segmenter as segmenter
import paddlers.tasks.change_detector as change_detector
import paddlers.tasks.classifier as classifier
import paddlers.tasks.image_restorer as restorer
import paddlers.tasks.restorer as restorer
from .load_model import load_model
# Shorter aliases

@ -30,12 +30,11 @@ from paddleslim import L1NormFilterPruner, FPGMFilterPruner
import paddlers
import paddlers.utils.logging as logging
from paddlers.utils import (seconds_to_hms, get_single_card_bs, dict2str,
get_pretrain_weights, load_pretrain_weights,
load_checkpoint, SmoothedValue, TrainingStats,
_get_shared_memory_size_in_M, EarlyStop)
from paddlers.utils import (
seconds_to_hms, get_single_card_bs, dict2str, get_pretrain_weights,
load_pretrain_weights, load_checkpoint, SmoothedValue, TrainingStats,
_get_shared_memory_size_in_M, EarlyStop, to_data_parallel, scheduler_step)
from .slim.prune import _pruner_eval_fn, _pruner_template_input, sensitive_prune
from .utils.infer_nets import InferNet, InferCDNet
class ModelMeta(type):
@ -268,7 +267,7 @@ class BaseModel(metaclass=ModelMeta):
'The volume of dataset({}) must be larger than batch size({}).'
.format(dataset.num_samples, batch_size))
batch_size_each_card = get_single_card_bs(batch_size=batch_size)
# TODO detection eval阶段需做判断
batch_sampler = DistributedBatchSampler(
dataset,
batch_size=batch_size_each_card,
@ -308,7 +307,7 @@ class BaseModel(metaclass=ModelMeta):
use_vdl=True):
self._check_transforms(train_dataset.transforms, 'train')
if "RCNN" in self.__class__.__name__ and train_dataset.pos_num < len(
if self.model_type == 'detector' and 'RCNN' in self.__class__.__name__ and train_dataset.pos_num < len(
train_dataset.file_list):
nranks = 1
else:
@ -321,10 +320,10 @@ class BaseModel(metaclass=ModelMeta):
if not paddle.distributed.parallel.parallel_helper._is_parallel_ctx_initialized(
):
paddle.distributed.init_parallel_env()
ddp_net = paddle.DataParallel(
ddp_net = to_data_parallel(
self.net, find_unused_parameters=find_unused_parameters)
else:
ddp_net = paddle.DataParallel(
ddp_net = to_data_parallel(
self.net, find_unused_parameters=find_unused_parameters)
if use_vdl:
@ -365,24 +364,14 @@ class BaseModel(metaclass=ModelMeta):
for step, data in enumerate(self.train_data_loader()):
if nranks > 1:
outputs = self.run(ddp_net, data, mode='train')
outputs = self.train_step(step, data, ddp_net)
else:
outputs = self.run(self.net, data, mode='train')
loss = outputs['loss']
loss.backward()
self.optimizer.step()
self.optimizer.clear_grad()
lr = self.optimizer.get_lr()
if isinstance(self.optimizer._learning_rate,
paddle.optimizer.lr.LRScheduler):
# If ReduceOnPlateau is used as the scheduler, use the loss value as the metric.
if isinstance(self.optimizer._learning_rate,
paddle.optimizer.lr.ReduceOnPlateau):
self.optimizer._learning_rate.step(loss.item())
else:
self.optimizer._learning_rate.step()
outputs = self.train_step(step, data, self.net)
scheduler_step(self.optimizer, outputs['loss'])
train_avg_metrics.update(outputs)
lr = self.optimizer.get_lr()
outputs['lr'] = lr
if ema is not None:
ema.update(self.net)
@ -622,14 +611,7 @@ class BaseModel(metaclass=ModelMeta):
return pipeline_info
def _build_inference_net(self):
if self.model_type in ('classifier', 'detector'):
infer_net = self.net
elif self.model_type == 'change_detector':
infer_net = InferCDNet(self.net)
else:
infer_net = InferNet(self.net, self.model_type)
infer_net.eval()
return infer_net
raise NotImplementedError
def _export_inference_model(self, save_dir, image_shape=None):
self.test_inputs = self._get_test_inputs(image_shape)
@ -674,6 +656,16 @@ class BaseModel(metaclass=ModelMeta):
logging.info("The inference model for deployment is saved in {}.".
format(save_dir))
def train_step(self, step, data, net):
outputs = self.run(net, data, mode='train')
loss = outputs['loss']
loss.backward()
self.optimizer.step()
self.optimizer.clear_grad()
return outputs
def _check_transforms(self, transforms, mode):
# NOTE: Check transforms and transforms.arrange and give user-friendly error messages.
if not isinstance(transforms, paddlers.transforms.Compose):

@ -30,14 +30,15 @@ import paddlers.rs_models.cd as cmcd
import paddlers.utils.logging as logging
from paddlers.models import seg_losses
from paddlers.transforms import Resize, decode_image
from paddlers.utils import get_single_card_bs, DisablePrint
from paddlers.utils import get_single_card_bs
from paddlers.utils.checkpoint import seg_pretrain_weights_dict
from .base import BaseModel
from .utils import seg_metrics as metrics
from .utils.infer_nets import InferCDNet
__all__ = [
"CDNet", "FCEarlyFusion", "FCSiamConc", "FCSiamDiff", "STANet", "BIT",
"SNUNet", "DSIFN", "DSAMNet", "ChangeStar", "ChangeFormer"
"SNUNet", "DSIFN", "DSAMNet", "ChangeStar", "ChangeFormer", "FCCDN"
]
@ -69,6 +70,11 @@ class BaseChangeDetector(BaseModel):
**params)
return net
def _build_inference_net(self):
infer_net = InferCDNet(self.net)
infer_net.eval()
return infer_net
def _fix_transforms_shape(self, image_shape):
if hasattr(self, 'test_transforms'):
if self.test_transforms is not None:
@ -399,7 +405,8 @@ class BaseChangeDetector(BaseModel):
Defaults to False.
Returns:
collections.OrderedDict with key-value pairs:
If `return_details` is False, return collections.OrderedDict with
key-value pairs:
For binary change detection (number of classes == 2), the key-value
pairs are like:
{"iou": `intersection over union for the change class`,
@ -527,12 +534,12 @@ class BaseChangeDetector(BaseModel):
Returns:
If `img_file` is a tuple of string or np.array, the result is a dict with
key-value pairs:
{"label map": `label map`, "score_map": `score map`}.
the following key-value pairs:
label_map (np.ndarray): Predicted label map (HW).
score_map (np.ndarray): Prediction score map (HWC).
If `img_file` is a list, the result is a list composed of dicts with the
corresponding fields:
label_map (np.ndarray): the predicted label map (HW)
score_map (np.ndarray): the prediction score map (HWC)
above keys.
"""
if transforms is None and not hasattr(self, 'test_transforms'):
@ -787,11 +794,11 @@ class BaseChangeDetector(BaseModel):
elif item[0] == 'padding':
x, y = item[2]
if isinstance(label_map, np.ndarray):
label_map = label_map[..., y:y + h, x:x + w]
score_map = score_map[..., y:y + h, x:x + w]
label_map = label_map[y:y + h, x:x + w]
score_map = score_map[y:y + h, x:x + w]
else:
label_map = label_map[:, :, y:y + h, x:x + w]
score_map = score_map[:, :, y:y + h, x:x + w]
label_map = label_map[:, y:y + h, x:x + w, :]
score_map = score_map[:, y:y + h, x:x + w, :]
else:
pass
label_map = label_map.squeeze()
@ -1053,7 +1060,7 @@ class ChangeStar(BaseChangeDetector):
if self.use_mixed_loss is False:
return {
# XXX: make sure the shallow copy works correctly here.
'types': [seglosses.CrossEntropyLoss()] * 4,
'types': [seg_losses.CrossEntropyLoss()] * 4,
'coef': [1.0] * 4
}
else:
@ -1082,3 +1089,31 @@ class ChangeFormer(BaseChangeDetector):
use_mixed_loss=use_mixed_loss,
losses=losses,
**params)
class FCCDN(BaseChangeDetector):
def __init__(self,
in_channels=3,
num_classes=2,
use_mixed_loss=False,
losses=None,
**params):
params.update({'in_channels': in_channels})
super(FCCDN, self).__init__(
model_name='FCCDN',
num_classes=num_classes,
use_mixed_loss=use_mixed_loss,
losses=losses,
**params)
def default_loss(self):
if self.use_mixed_loss is False:
return {
'types':
[seg_losses.CrossEntropyLoss(), cmcd.losses.fccdn_ssl_loss],
'coef': [1.0, 1.0]
}
else:
raise ValueError(
f"Currently `use_mixed_loss` must be set to False for {self.__class__}"
)

@ -83,6 +83,11 @@ class BaseClassifier(BaseModel):
self.in_channels = 3
return net
def _build_inference_net(self):
infer_net = self.net
infer_net.eval()
return infer_net
def _fix_transforms_shape(self, image_shape):
if hasattr(self, 'test_transforms'):
if self.test_transforms is not None:
@ -373,7 +378,8 @@ class BaseClassifier(BaseModel):
Defaults to False.
Returns:
collections.OrderedDict with key-value pairs:
If `return_details` is False, return collections.OrderedDict with
key-value pairs:
{"top1": `acc of top1`,
"top5": `acc of top5`}.
"""
@ -389,21 +395,18 @@ class BaseClassifier(BaseModel):
):
paddle.distributed.init_parallel_env()
batch_size_each_card = get_single_card_bs(batch_size)
if batch_size_each_card > 1:
batch_size_each_card = 1
batch_size = batch_size_each_card * paddlers.env_info['num']
if batch_size > 1:
logging.warning(
"Classifier only supports batch_size=1 for each gpu/cpu card " \
"during evaluation, so batch_size " \
"is forcibly set to {}.".format(batch_size))
"Classifier only supports single card evaluation with batch_size=1 "
"during evaluation, so batch_size is forcibly set to 1.")
batch_size = 1
if nranks < 2 or local_rank == 0:
self.eval_data_loader = self.build_data_loader(
eval_dataset, batch_size=batch_size, mode='eval')
logging.info(
"Start to evaluate(total_samples={}, total_steps={})...".format(
eval_dataset.num_samples,
math.ceil(eval_dataset.num_samples * 1.0 / batch_size)))
eval_dataset.num_samples, eval_dataset.num_samples))
top1s = []
top5s = []
@ -417,9 +420,11 @@ class BaseClassifier(BaseModel):
top1 = np.mean(top1s)
top5 = np.mean(top5s)
eval_metrics = OrderedDict(zip(['top1', 'top5'], [top1, top5]))
if return_details:
# TODO: add details
# TODO: Add details
return eval_metrics, None
return eval_metrics
def predict(self, img_file, transforms=None):
@ -435,16 +440,14 @@ class BaseClassifier(BaseModel):
Defaults to None.
Returns:
If `img_file` is a string or np.array, the result is a dict with key-value
pairs:
{"label map": `class_ids_map`,
"scores_map": `scores_map`,
"label_names_map": `label_names_map`}.
If `img_file` is a string or np.array, the result is a dict with the
following key-value pairs:
class_ids_map (np.ndarray): IDs of predicted classes.
scores_map (np.ndarray): Scores of predicted classes.
label_names_map (np.ndarray): Names of predicted classes.
If `img_file` is a list, the result is a list composed of dicts with the
corresponding fields:
class_ids_map (np.ndarray): class_ids
scores_map (np.ndarray): scores
label_names_map (np.ndarray): label_names
above keys.
"""
if transforms is None and not hasattr(self, 'test_transforms'):
@ -555,6 +558,26 @@ class BaseClassifier(BaseModel):
raise TypeError(
"`transforms.arrange` must be an ArrangeClassifier object.")
def build_data_loader(self, dataset, batch_size, mode='train'):
if dataset.num_samples < batch_size:
raise ValueError(
'The volume of dataset({}) must be larger than batch size({}).'
.format(dataset.num_samples, batch_size))
if mode != 'train':
return paddle.io.DataLoader(
dataset,
batch_size=batch_size,
shuffle=dataset.shuffle,
drop_last=False,
collate_fn=dataset.batch_transforms,
num_workers=dataset.num_workers,
return_list=True,
use_shared_memory=False)
else:
return super(BaseClassifier, self).build_data_loader(
dataset, batch_size, mode)
class ResNet50_vd(BaseClassifier):
def __init__(self,

@ -1,786 +0,0 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import time
import datetime
import paddle
from paddle.distributed import ParallelEnv
from ..models.ppgan.datasets.builder import build_dataloader
from ..models.ppgan.models.builder import build_model
from ..models.ppgan.utils.visual import tensor2img, save_image
from ..models.ppgan.utils.filesystem import makedirs, save, load
from ..models.ppgan.utils.timer import TimeAverager
from ..models.ppgan.utils.profiler import add_profiler_step
from ..models.ppgan.utils.logger import setup_logger
# 定义AttrDict类实现动态属性
class AttrDict(dict):
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)
def __setattr__(self, key, value):
if key in self.__dict__:
self.__dict__[key] = value
else:
self[key] = value
# 创建AttrDict类
def create_attr_dict(config_dict):
from ast import literal_eval
for key, value in config_dict.items():
if type(value) is dict:
config_dict[key] = value = AttrDict(value)
if isinstance(value, str):
try:
value = literal_eval(value)
except BaseException:
pass
if isinstance(value, AttrDict):
create_attr_dict(config_dict[key])
else:
config_dict[key] = value
# 数据加载类
class IterLoader:
def __init__(self, dataloader):
self._dataloader = dataloader
self.iter_loader = iter(self._dataloader)
self._epoch = 1
@property
def epoch(self):
return self._epoch
def __next__(self):
try:
data = next(self.iter_loader)
except StopIteration:
self._epoch += 1
self.iter_loader = iter(self._dataloader)
data = next(self.iter_loader)
return data
def __len__(self):
return len(self._dataloader)
# 基础训练类
class Restorer:
"""
# trainer calling logic:
#
# build_model || model(BaseModel)
# | ||
# build_dataloader || dataloader
# | ||
# model.setup_lr_schedulers || lr_scheduler
# | ||
# model.setup_optimizers || optimizers
# | ||
# train loop (model.setup_input + model.train_iter) || train loop
# | ||
# print log (model.get_current_losses) ||
# | ||
# save checkpoint (model.nets) \/
"""
def __init__(self, cfg, logger):
# base config
# self.logger = logging.getLogger(__name__)
self.logger = logger
self.cfg = cfg
self.output_dir = cfg.output_dir
self.max_eval_steps = cfg.model.get('max_eval_steps', None)
self.local_rank = ParallelEnv().local_rank
self.world_size = ParallelEnv().nranks
self.log_interval = cfg.log_config.interval
self.visual_interval = cfg.log_config.visiual_interval
self.weight_interval = cfg.snapshot_config.interval
self.start_epoch = 1
self.current_epoch = 1
self.current_iter = 1
self.inner_iter = 1
self.batch_id = 0
self.global_steps = 0
# build model
self.model = build_model(cfg.model)
# multiple gpus prepare
if ParallelEnv().nranks > 1:
self.distributed_data_parallel()
# build metrics
self.metrics = None
self.is_save_img = True
validate_cfg = cfg.get('validate', None)
if validate_cfg and 'metrics' in validate_cfg:
self.metrics = self.model.setup_metrics(validate_cfg['metrics'])
if validate_cfg and 'save_img' in validate_cfg:
self.is_save_img = validate_cfg['save_img']
self.enable_visualdl = cfg.get('enable_visualdl', False)
if self.enable_visualdl:
import visualdl
self.vdl_logger = visualdl.LogWriter(logdir=cfg.output_dir)
# evaluate only
if not cfg.is_train:
return
# build train dataloader
self.train_dataloader = build_dataloader(cfg.dataset.train)
self.iters_per_epoch = len(self.train_dataloader)
# build lr scheduler
# TODO: has a better way?
if 'lr_scheduler' in cfg and 'iters_per_epoch' in cfg.lr_scheduler:
cfg.lr_scheduler.iters_per_epoch = self.iters_per_epoch
self.lr_schedulers = self.model.setup_lr_schedulers(cfg.lr_scheduler)
# build optimizers
self.optimizers = self.model.setup_optimizers(self.lr_schedulers,
cfg.optimizer)
self.epochs = cfg.get('epochs', None)
if self.epochs:
self.total_iters = self.epochs * self.iters_per_epoch
self.by_epoch = True
else:
self.by_epoch = False
self.total_iters = cfg.total_iters
if self.by_epoch:
self.weight_interval *= self.iters_per_epoch
self.validate_interval = -1
if cfg.get('validate', None) is not None:
self.validate_interval = cfg.validate.get('interval', -1)
self.time_count = {}
self.best_metric = {}
self.model.set_total_iter(self.total_iters)
self.profiler_options = cfg.profiler_options
def distributed_data_parallel(self):
paddle.distributed.init_parallel_env()
find_unused_parameters = self.cfg.get('find_unused_parameters', False)
for net_name, net in self.model.nets.items():
self.model.nets[net_name] = paddle.DataParallel(
net, find_unused_parameters=find_unused_parameters)
def learning_rate_scheduler_step(self):
if isinstance(self.model.lr_scheduler, dict):
for lr_scheduler in self.model.lr_scheduler.values():
lr_scheduler.step()
elif isinstance(self.model.lr_scheduler,
paddle.optimizer.lr.LRScheduler):
self.model.lr_scheduler.step()
else:
raise ValueError(
'lr schedulter must be a dict or an instance of LRScheduler')
def train(self):
reader_cost_averager = TimeAverager()
batch_cost_averager = TimeAverager()
iter_loader = IterLoader(self.train_dataloader)
# set model.is_train = True
self.model.setup_train_mode(is_train=True)
while self.current_iter < (self.total_iters + 1):
self.current_epoch = iter_loader.epoch
self.inner_iter = self.current_iter % self.iters_per_epoch
add_profiler_step(self.profiler_options)
start_time = step_start_time = time.time()
data = next(iter_loader)
reader_cost_averager.record(time.time() - step_start_time)
# unpack data from dataset and apply preprocessing
# data input should be dict
self.model.setup_input(data)
self.model.train_iter(self.optimizers)
batch_cost_averager.record(
time.time() - step_start_time,
num_samples=self.cfg['dataset']['train'].get('batch_size', 1))
step_start_time = time.time()
if self.current_iter % self.log_interval == 0:
self.data_time = reader_cost_averager.get_average()
self.step_time = batch_cost_averager.get_average()
self.ips = batch_cost_averager.get_ips_average()
self.print_log()
reader_cost_averager.reset()
batch_cost_averager.reset()
if self.current_iter % self.visual_interval == 0 and self.local_rank == 0:
self.visual('visual_train')
self.learning_rate_scheduler_step()
if self.validate_interval > -1 and self.current_iter % self.validate_interval == 0:
self.test()
if self.current_iter % self.weight_interval == 0:
self.save(self.current_iter, 'weight', keep=-1)
self.save(self.current_iter)
self.current_iter += 1
def test(self):
if not hasattr(self, 'test_dataloader'):
self.test_dataloader = build_dataloader(
self.cfg.dataset.test, is_train=False)
iter_loader = IterLoader(self.test_dataloader)
if self.max_eval_steps is None:
self.max_eval_steps = len(self.test_dataloader)
if self.metrics:
for metric in self.metrics.values():
metric.reset()
# set model.is_train = False
self.model.setup_train_mode(is_train=False)
for i in range(self.max_eval_steps):
if self.max_eval_steps < self.log_interval or i % self.log_interval == 0:
self.logger.info('Test iter: [%d/%d]' % (
i * self.world_size, self.max_eval_steps * self.world_size))
data = next(iter_loader)
self.model.setup_input(data)
self.model.test_iter(metrics=self.metrics)
if self.is_save_img:
visual_results = {}
current_paths = self.model.get_image_paths()
current_visuals = self.model.get_current_visuals()
if len(current_visuals) > 0 and list(current_visuals.values())[
0].shape == 4:
num_samples = list(current_visuals.values())[0].shape[0]
else:
num_samples = 1
for j in range(num_samples):
if j < len(current_paths):
short_path = os.path.basename(current_paths[j])
basename = os.path.splitext(short_path)[0]
else:
basename = '{:04d}_{:04d}'.format(i, j)
for k, img_tensor in current_visuals.items():
name = '%s_%s' % (basename, k)
if len(img_tensor.shape) == 4:
visual_results.update({name: img_tensor[j]})
else:
visual_results.update({name: img_tensor})
self.visual(
'visual_test',
visual_results=visual_results,
step=self.batch_id,
is_save_image=True)
if self.metrics:
for metric_name, metric in self.metrics.items():
self.logger.info("Metric {}: {:.4f}".format(
metric_name, metric.accumulate()))
def print_log(self):
losses = self.model.get_current_losses()
message = ''
if self.by_epoch:
message += 'Epoch: %d/%d, iter: %d/%d ' % (
self.current_epoch, self.epochs, self.inner_iter,
self.iters_per_epoch)
else:
message += 'Iter: %d/%d ' % (self.current_iter, self.total_iters)
message += f'lr: {self.current_learning_rate:.3e} '
for k, v in losses.items():
message += '%s: %.3f ' % (k, v)
if self.enable_visualdl:
self.vdl_logger.add_scalar(k, v, step=self.global_steps)
if hasattr(self, 'step_time'):
message += 'batch_cost: %.5f sec ' % self.step_time
if hasattr(self, 'data_time'):
message += 'reader_cost: %.5f sec ' % self.data_time
if hasattr(self, 'ips'):
message += 'ips: %.5f images/s ' % self.ips
if hasattr(self, 'step_time'):
eta = self.step_time * (self.total_iters - self.current_iter)
eta = eta if eta > 0 else 0
eta_str = str(datetime.timedelta(seconds=int(eta)))
message += f'eta: {eta_str}'
# print the message
self.logger.info(message)
@property
def current_learning_rate(self):
for optimizer in self.model.optimizers.values():
return optimizer.get_lr()
def visual(self,
results_dir,
visual_results=None,
step=None,
is_save_image=False):
"""
visual the images, use visualdl or directly write to the directory
Parameters:
results_dir (str) -- directory name which contains saved images
visual_results (dict) -- the results images dict
step (int) -- global steps, used in visualdl
is_save_image (bool) -- weather write to the directory or visualdl
"""
self.model.compute_visuals()
if visual_results is None:
visual_results = self.model.get_current_visuals()
min_max = self.cfg.get('min_max', None)
if min_max is None:
min_max = (-1., 1.)
image_num = self.cfg.get('image_num', None)
if (image_num is None) or (not self.enable_visualdl):
image_num = 1
for label, image in visual_results.items():
image_numpy = tensor2img(image, min_max, image_num)
if (not is_save_image) and self.enable_visualdl:
self.vdl_logger.add_image(
results_dir + '/' + label,
image_numpy,
step=step if step else self.global_steps,
dataformats="HWC" if image_num == 1 else "NCHW")
else:
if self.cfg.is_train:
if self.by_epoch:
msg = 'epoch%.3d_' % self.current_epoch
else:
msg = 'iter%.3d_' % self.current_iter
else:
msg = ''
makedirs(os.path.join(self.output_dir, results_dir))
img_path = os.path.join(self.output_dir, results_dir,
msg + '%s.png' % (label))
save_image(image_numpy, img_path)
def save(self, epoch, name='checkpoint', keep=1):
if self.local_rank != 0:
return
assert name in ['checkpoint', 'weight']
state_dicts = {}
if self.by_epoch:
save_filename = 'epoch_%s_%s.pdparams' % (
epoch // self.iters_per_epoch, name)
else:
save_filename = 'iter_%s_%s.pdparams' % (epoch, name)
os.makedirs(self.output_dir, exist_ok=True)
save_path = os.path.join(self.output_dir, save_filename)
for net_name, net in self.model.nets.items():
state_dicts[net_name] = net.state_dict()
if name == 'weight':
save(state_dicts, save_path)
return
state_dicts['epoch'] = epoch
for opt_name, opt in self.model.optimizers.items():
state_dicts[opt_name] = opt.state_dict()
save(state_dicts, save_path)
if keep > 0:
try:
if self.by_epoch:
checkpoint_name_to_be_removed = os.path.join(
self.output_dir, 'epoch_%s_%s.pdparams' % (
(epoch - keep * self.weight_interval) //
self.iters_per_epoch, name))
else:
checkpoint_name_to_be_removed = os.path.join(
self.output_dir, 'iter_%s_%s.pdparams' %
(epoch - keep * self.weight_interval, name))
if os.path.exists(checkpoint_name_to_be_removed):
os.remove(checkpoint_name_to_be_removed)
except Exception as e:
self.logger.info('remove old checkpoints error: {}'.format(e))
def resume(self, checkpoint_path):
state_dicts = load(checkpoint_path)
if state_dicts.get('epoch', None) is not None:
self.start_epoch = state_dicts['epoch'] + 1
self.global_steps = self.iters_per_epoch * state_dicts['epoch']
self.current_iter = state_dicts['epoch'] + 1
for net_name, net in self.model.nets.items():
net.set_state_dict(state_dicts[net_name])
for opt_name, opt in self.model.optimizers.items():
opt.set_state_dict(state_dicts[opt_name])
def load(self, weight_path):
state_dicts = load(weight_path)
for net_name, net in self.model.nets.items():
if net_name in state_dicts:
net.set_state_dict(state_dicts[net_name])
self.logger.info('Loaded pretrained weight for net {}'.format(
net_name))
else:
self.logger.warning(
'Can not find state dict of net {}. Skip load pretrained weight for net {}'
.format(net_name, net_name))
def close(self):
"""
when finish the training need close file handler or other.
"""
if self.enable_visualdl:
self.vdl_logger.close()
# 基础超分模型训练类
class BasicSRNet:
def __init__(self):
self.model = {}
self.optimizer = {}
self.lr_scheduler = {}
self.min_max = ''
def train(
self,
total_iters,
train_dataset,
test_dataset,
output_dir,
validate,
snapshot,
log,
lr_rate,
evaluate_weights='',
resume='',
pretrain_weights='',
periods=[100000],
restart_weights=[1], ):
self.lr_scheduler['learning_rate'] = lr_rate
if self.lr_scheduler['name'] == 'CosineAnnealingRestartLR':
self.lr_scheduler['periods'] = periods
self.lr_scheduler['restart_weights'] = restart_weights
validate = {
'interval': validate,
'save_img': False,
'metrics': {
'psnr': {
'name': 'PSNR',
'crop_border': 4,
'test_y_channel': True
},
'ssim': {
'name': 'SSIM',
'crop_border': 4,
'test_y_channel': True
}
}
}
log_config = {'interval': log, 'visiual_interval': 500}
snapshot_config = {'interval': snapshot}
cfg = {
'total_iters': total_iters,
'output_dir': output_dir,
'min_max': self.min_max,
'model': self.model,
'dataset': {
'train': train_dataset,
'test': test_dataset
},
'lr_scheduler': self.lr_scheduler,
'optimizer': self.optimizer,
'validate': validate,
'log_config': log_config,
'snapshot_config': snapshot_config
}
cfg = AttrDict(cfg)
create_attr_dict(cfg)
cfg.is_train = True
cfg.profiler_options = None
cfg.timestamp = time.strftime('-%Y-%m-%d-%H-%M', time.localtime())
if cfg.model.name == 'BaseSRModel':
floderModelName = cfg.model.generator.name
else:
floderModelName = cfg.model.name
cfg.output_dir = os.path.join(cfg.output_dir,
floderModelName + cfg.timestamp)
logger_cfg = setup_logger(cfg.output_dir)
logger_cfg.info('Configs: {}'.format(cfg))
if paddle.is_compiled_with_cuda():
paddle.set_device('gpu')
else:
paddle.set_device('cpu')
# build trainer
trainer = Restorer(cfg, logger_cfg)
# continue train or evaluate, checkpoint need contain epoch and optimizer info
if len(resume) > 0:
trainer.resume(resume)
# evaluate or finute, only load generator weights
elif len(pretrain_weights) > 0:
trainer.load(pretrain_weights)
if len(evaluate_weights) > 0:
trainer.load(evaluate_weights)
trainer.test()
return
# training, when keyboard interrupt save weights
try:
trainer.train()
except KeyboardInterrupt as e:
trainer.save(trainer.current_epoch)
trainer.close()
# DRN模型训练
class DRNet(BasicSRNet):
def __init__(self,
n_blocks=30,
n_feats=16,
n_colors=3,
rgb_range=255,
negval=0.2):
super(DRNet, self).__init__()
self.min_max = '(0., 255.)'
self.generator = {
'name': 'DRNGenerator',
'scale': (2, 4),
'n_blocks': n_blocks,
'n_feats': n_feats,
'n_colors': n_colors,
'rgb_range': rgb_range,
'negval': negval
}
self.pixel_criterion = {'name': 'L1Loss'}
self.model = {
'name': 'DRN',
'generator': self.generator,
'pixel_criterion': self.pixel_criterion
}
self.optimizer = {
'optimG': {
'name': 'Adam',
'net_names': ['generator'],
'weight_decay': 0.0,
'beta1': 0.9,
'beta2': 0.999
},
'optimD': {
'name': 'Adam',
'net_names': ['dual_model_0', 'dual_model_1'],
'weight_decay': 0.0,
'beta1': 0.9,
'beta2': 0.999
}
}
self.lr_scheduler = {
'name': 'CosineAnnealingRestartLR',
'eta_min': 1e-07
}
# 轻量化超分模型LESRCNN训练
class LESRCNNet(BasicSRNet):
def __init__(self, scale=4, multi_scale=False, group=1):
super(LESRCNNet, self).__init__()
self.min_max = '(0., 1.)'
self.generator = {
'name': 'LESRCNNGenerator',
'scale': scale,
'multi_scale': False,
'group': 1
}
self.pixel_criterion = {'name': 'L1Loss'}
self.model = {
'name': 'BaseSRModel',
'generator': self.generator,
'pixel_criterion': self.pixel_criterion
}
self.optimizer = {
'name': 'Adam',
'net_names': ['generator'],
'beta1': 0.9,
'beta2': 0.99
}
self.lr_scheduler = {
'name': 'CosineAnnealingRestartLR',
'eta_min': 1e-07
}
# ESRGAN模型训练
# 若loss_type='gan' 使用感知损失、对抗损失和像素损失
# 若loss_type = 'pixel' 只使用像素损失
class ESRGANet(BasicSRNet):
def __init__(self, loss_type='gan', in_nc=3, out_nc=3, nf=64, nb=23):
super(ESRGANet, self).__init__()
self.min_max = '(0., 1.)'
self.generator = {
'name': 'RRDBNet',
'in_nc': in_nc,
'out_nc': out_nc,
'nf': nf,
'nb': nb
}
if loss_type == 'gan':
# 定义损失函数
self.pixel_criterion = {'name': 'L1Loss', 'loss_weight': 0.01}
self.discriminator = {
'name': 'VGGDiscriminator128',
'in_channels': 3,
'num_feat': 64
}
self.perceptual_criterion = {
'name': 'PerceptualLoss',
'layer_weights': {
'34': 1.0
},
'perceptual_weight': 1.0,
'style_weight': 0.0,
'norm_img': False
}
self.gan_criterion = {
'name': 'GANLoss',
'gan_mode': 'vanilla',
'loss_weight': 0.005
}
# 定义模型
self.model = {
'name': 'ESRGAN',
'generator': self.generator,
'discriminator': self.discriminator,
'pixel_criterion': self.pixel_criterion,
'perceptual_criterion': self.perceptual_criterion,
'gan_criterion': self.gan_criterion
}
self.optimizer = {
'optimG': {
'name': 'Adam',
'net_names': ['generator'],
'weight_decay': 0.0,
'beta1': 0.9,
'beta2': 0.99
},
'optimD': {
'name': 'Adam',
'net_names': ['discriminator'],
'weight_decay': 0.0,
'beta1': 0.9,
'beta2': 0.99
}
}
self.lr_scheduler = {
'name': 'MultiStepDecay',
'milestones': [50000, 100000, 200000, 300000],
'gamma': 0.5
}
else:
self.pixel_criterion = {'name': 'L1Loss'}
self.model = {
'name': 'BaseSRModel',
'generator': self.generator,
'pixel_criterion': self.pixel_criterion
}
self.optimizer = {
'name': 'Adam',
'net_names': ['generator'],
'beta1': 0.9,
'beta2': 0.99
}
self.lr_scheduler = {
'name': 'CosineAnnealingRestartLR',
'eta_min': 1e-07
}
# RCAN模型训练
class RCANet(BasicSRNet):
def __init__(
self,
scale=2,
n_resgroups=10,
n_resblocks=20, ):
super(RCANet, self).__init__()
self.min_max = '(0., 255.)'
self.generator = {
'name': 'RCAN',
'scale': scale,
'n_resgroups': n_resgroups,
'n_resblocks': n_resblocks
}
self.pixel_criterion = {'name': 'L1Loss'}
self.model = {
'name': 'RCANModel',
'generator': self.generator,
'pixel_criterion': self.pixel_criterion
}
self.optimizer = {
'name': 'Adam',
'net_names': ['generator'],
'beta1': 0.9,
'beta2': 0.99
}
self.lr_scheduler = {
'name': 'MultiStepDecay',
'milestones': [250000, 500000, 750000, 1000000],
'gamma': 0.5
}

@ -61,6 +61,11 @@ class BaseDetector(BaseModel):
net = ppdet.modeling.__dict__[self.model_name](**params)
return net
def _build_inference_net(self):
infer_net = self.net
infer_net.eval()
return infer_net
def _fix_transforms_shape(self, image_shape):
raise NotImplementedError("_fix_transforms_shape: not implemented!")
@ -250,32 +255,18 @@ class BaseDetector(BaseModel):
"""
args = self._pre_train(locals())
args.pop('self')
return self._real_train(**args)
def _pre_train(self, in_args):
return in_args
def _real_train(self,
num_epochs,
train_dataset,
train_batch_size=64,
eval_dataset=None,
optimizer=None,
save_interval_epochs=1,
log_interval_steps=10,
save_dir='output',
pretrain_weights='IMAGENET',
learning_rate=.001,
warmup_steps=0,
warmup_start_lr=0.0,
lr_decay_epochs=(216, 243),
lr_decay_gamma=0.1,
metric=None,
use_ema=False,
early_stop=False,
early_stop_patience=5,
use_vdl=True,
resume_checkpoint=None):
def _real_train(
self, num_epochs, train_dataset, train_batch_size, eval_dataset,
optimizer, save_interval_epochs, log_interval_steps, save_dir,
pretrain_weights, learning_rate, warmup_steps, warmup_start_lr,
lr_decay_epochs, lr_decay_gamma, metric, use_ema, early_stop,
early_stop_patience, use_vdl, resume_checkpoint):
if self.status == 'Infer':
logging.error(
@ -485,7 +476,7 @@ class BaseDetector(BaseModel):
Defaults to False.
Returns:
collections.OrderedDict with key-value pairs:
If `return_details` is False, return collections.OrderedDict with key-value pairs:
{"bbox_mmap":`mean average precision (0.50, 11point)`}.
"""
@ -584,21 +575,17 @@ class BaseDetector(BaseModel):
Returns:
If `img_file` is a string or np.array, the result is a list of dict with
key-value pairs:
{"category_id": `category_id`,
"category": `category`,
"bbox": `[x, y, w, h]`,
"score": `score`,
"mask": `mask`}.
If `img_file` is a list, the result is a list composed of list of dicts
with the corresponding fields:
category_id(int): the predicted category ID. 0 represents the first
the following key-value pairs:
category_id (int): Predicted category ID. 0 represents the first
category in the dataset, and so on.
category(str): category name
bbox(list): bounding box in [x, y, w, h] format
score(str): confidence
mask(dict): Only for instance segmentation task. Mask of the object in
RLE format
category (str): Category name.
bbox (list): Bounding box in [x, y, w, h] format.
score (str): Confidence.
mask (dict): Only for instance segmentation task. Mask of the object in
RLE format.
If `img_file` is a list, the result is a list composed of list of dicts
with the above keys.
"""
if transforms is None and not hasattr(self, 'test_transforms'):
@ -926,6 +913,26 @@ class PicoDet(BaseDetector):
in_args['optimizer'] = optimizer
return in_args
def build_data_loader(self, dataset, batch_size, mode='train'):
if dataset.num_samples < batch_size:
raise ValueError(
'The volume of dataset({}) must be larger than batch size({}).'
.format(dataset.num_samples, batch_size))
if mode != 'train':
return paddle.io.DataLoader(
dataset,
batch_size=batch_size,
shuffle=dataset.shuffle,
drop_last=False,
collate_fn=dataset.batch_transforms,
num_workers=dataset.num_workers,
return_list=True,
use_shared_memory=False)
else:
return super(BaseDetector, self).build_data_loader(dataset,
batch_size, mode)
class YOLOv3(BaseDetector):
def __init__(self,

@ -0,0 +1,936 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import os.path as osp
from collections import OrderedDict
import numpy as np
import cv2
import paddle
import paddle.nn.functional as F
from paddle.static import InputSpec
import paddlers
import paddlers.models.ppgan as ppgan
import paddlers.rs_models.res as cmres
import paddlers.models.ppgan.metrics as metrics
import paddlers.utils.logging as logging
from paddlers.models import res_losses
from paddlers.transforms import Resize, decode_image
from paddlers.transforms.functions import calc_hr_shape
from paddlers.utils import get_single_card_bs
from .base import BaseModel
from .utils.res_adapters import GANAdapter, OptimizerAdapter
from .utils.infer_nets import InferResNet
__all__ = ["DRN", "LESRCNN", "ESRGAN"]
class BaseRestorer(BaseModel):
MIN_MAX = (0., 1.)
TEST_OUT_KEY = None
def __init__(self, model_name, losses=None, sr_factor=None, **params):
self.init_params = locals()
if 'with_net' in self.init_params:
del self.init_params['with_net']
super(BaseRestorer, self).__init__('restorer')
self.model_name = model_name
self.losses = losses
self.sr_factor = sr_factor
if params.get('with_net', True):
params.pop('with_net', None)
self.net = self.build_net(**params)
self.find_unused_parameters = True
def build_net(self, **params):
# Currently, only use models from cmres.
if not hasattr(cmres, self.model_name):
raise ValueError("ERROR: There is no model named {}.".format(
model_name))
net = dict(**cmres.__dict__)[self.model_name](**params)
return net
def _build_inference_net(self):
# For GAN models, only the generator will be used for inference.
if isinstance(self.net, GANAdapter):
infer_net = InferResNet(
self.net.generator, out_key=self.TEST_OUT_KEY)
else:
infer_net = InferResNet(self.net, out_key=self.TEST_OUT_KEY)
infer_net.eval()
return infer_net
def _fix_transforms_shape(self, image_shape):
if hasattr(self, 'test_transforms'):
if self.test_transforms is not None:
has_resize_op = False
resize_op_idx = -1
normalize_op_idx = len(self.test_transforms.transforms)
for idx, op in enumerate(self.test_transforms.transforms):
name = op.__class__.__name__
if name == 'Normalize':
normalize_op_idx = idx
if 'Resize' in name:
has_resize_op = True
resize_op_idx = idx
if not has_resize_op:
self.test_transforms.transforms.insert(
normalize_op_idx, Resize(target_size=image_shape))
else:
self.test_transforms.transforms[resize_op_idx] = Resize(
target_size=image_shape)
def _get_test_inputs(self, image_shape):
if image_shape is not None:
if len(image_shape) == 2:
image_shape = [1, 3] + image_shape
self._fix_transforms_shape(image_shape[-2:])
else:
image_shape = [None, 3, -1, -1]
self.fixed_input_shape = image_shape
input_spec = [
InputSpec(
shape=image_shape, name='image', dtype='float32')
]
return input_spec
def run(self, net, inputs, mode):
outputs = OrderedDict()
if mode == 'test':
tar_shape = inputs[1]
if self.status == 'Infer':
net_out = net(inputs[0])
res_map_list = self.postprocess(
net_out, tar_shape, transforms=inputs[2])
else:
if isinstance(net, GANAdapter):
net_out = net.generator(inputs[0])
else:
net_out = net(inputs[0])
if self.TEST_OUT_KEY is not None:
net_out = net_out[self.TEST_OUT_KEY]
pred = self.postprocess(
net_out, tar_shape, transforms=inputs[2])
res_map_list = []
for res_map in pred:
res_map = self._tensor_to_images(res_map)
res_map_list.append(res_map)
outputs['res_map'] = res_map_list
if mode == 'eval':
if isinstance(net, GANAdapter):
net_out = net.generator(inputs[0])
else:
net_out = net(inputs[0])
if self.TEST_OUT_KEY is not None:
net_out = net_out[self.TEST_OUT_KEY]
tar = inputs[1]
tar_shape = [tar.shape[-2:]]
pred = self.postprocess(
net_out, tar_shape, transforms=inputs[2])[0] # NCHW
pred = self._tensor_to_images(pred)
outputs['pred'] = pred
tar = self._tensor_to_images(tar)
outputs['tar'] = tar
if mode == 'train':
# This is used by non-GAN models.
# For GAN models, self.run_gan() should be used.
net_out = net(inputs[0])
loss = self.losses(net_out, inputs[1])
outputs['loss'] = loss
return outputs
def run_gan(self, net, inputs, mode, gan_mode):
raise NotImplementedError
def default_loss(self):
return res_losses.L1Loss()
def default_optimizer(self,
parameters,
learning_rate,
num_epochs,
num_steps_each_epoch,
lr_decay_power=0.9):
decay_step = num_epochs * num_steps_each_epoch
lr_scheduler = paddle.optimizer.lr.PolynomialDecay(
learning_rate, decay_step, end_lr=0, power=lr_decay_power)
optimizer = paddle.optimizer.Momentum(
learning_rate=lr_scheduler,
parameters=parameters,
momentum=0.9,
weight_decay=4e-5)
return optimizer
def train(self,
num_epochs,
train_dataset,
train_batch_size=2,
eval_dataset=None,
optimizer=None,
save_interval_epochs=1,
log_interval_steps=2,
save_dir='output',
pretrain_weights=None,
learning_rate=0.01,
lr_decay_power=0.9,
early_stop=False,
early_stop_patience=5,
use_vdl=True,
resume_checkpoint=None):
"""
Train the model.
Args:
num_epochs (int): Number of epochs.
train_dataset (paddlers.datasets.ResDataset): Training dataset.
train_batch_size (int, optional): Total batch size among all cards used in
training. Defaults to 2.
eval_dataset (paddlers.datasets.ResDataset|None, optional): Evaluation dataset.
If None, the model will not be evaluated during training process.
Defaults to None.
optimizer (paddle.optimizer.Optimizer|None, optional): Optimizer used in
training. If None, a default optimizer will be used. Defaults to None.
save_interval_epochs (int, optional): Epoch interval for saving the model.
Defaults to 1.
log_interval_steps (int, optional): Step interval for printing training
information. Defaults to 2.
save_dir (str, optional): Directory to save the model. Defaults to 'output'.
pretrain_weights (str|None, optional): None or name/path of pretrained
weights. If None, no pretrained weights will be loaded.
Defaults to None.
learning_rate (float, optional): Learning rate for training. Defaults to .01.
lr_decay_power (float, optional): Learning decay power. Defaults to .9.
early_stop (bool, optional): Whether to adopt early stop strategy. Defaults
to False.
early_stop_patience (int, optional): Early stop patience. Defaults to 5.
use_vdl (bool, optional): Whether to use VisualDL to monitor the training
process. Defaults to True.
resume_checkpoint (str|None, optional): Path of the checkpoint to resume
training from. If None, no training checkpoint will be resumed. At most
Aone of `resume_checkpoint` and `pretrain_weights` can be set simultaneously.
Defaults to None.
"""
if self.status == 'Infer':
logging.error(
"Exported inference model does not support training.",
exit=True)
if pretrain_weights is not None and resume_checkpoint is not None:
logging.error(
"pretrain_weights and resume_checkpoint cannot be set simultaneously.",
exit=True)
if self.losses is None:
self.losses = self.default_loss()
if optimizer is None:
num_steps_each_epoch = train_dataset.num_samples // train_batch_size
if isinstance(self.net, GANAdapter):
parameters = {'params_g': [], 'params_d': []}
for net_g in self.net.generators:
parameters['params_g'].append(net_g.parameters())
for net_d in self.net.discriminators:
parameters['params_d'].append(net_d.parameters())
else:
parameters = self.net.parameters()
self.optimizer = self.default_optimizer(
parameters, learning_rate, num_epochs, num_steps_each_epoch,
lr_decay_power)
else:
self.optimizer = optimizer
if pretrain_weights is not None and not osp.exists(pretrain_weights):
logging.warning("Path of pretrain_weights('{}') does not exist!".
format(pretrain_weights))
elif pretrain_weights is not None and osp.exists(pretrain_weights):
if osp.splitext(pretrain_weights)[-1] != '.pdparams':
logging.error(
"Invalid pretrain weights. Please specify a '.pdparams' file.",
exit=True)
pretrained_dir = osp.join(save_dir, 'pretrain')
is_backbone_weights = pretrain_weights == 'IMAGENET'
self.net_initialize(
pretrain_weights=pretrain_weights,
save_dir=pretrained_dir,
resume_checkpoint=resume_checkpoint,
is_backbone_weights=is_backbone_weights)
self.train_loop(
num_epochs=num_epochs,
train_dataset=train_dataset,
train_batch_size=train_batch_size,
eval_dataset=eval_dataset,
save_interval_epochs=save_interval_epochs,
log_interval_steps=log_interval_steps,
save_dir=save_dir,
early_stop=early_stop,
early_stop_patience=early_stop_patience,
use_vdl=use_vdl)
def quant_aware_train(self,
num_epochs,
train_dataset,
train_batch_size=2,
eval_dataset=None,
optimizer=None,
save_interval_epochs=1,
log_interval_steps=2,
save_dir='output',
learning_rate=0.0001,
lr_decay_power=0.9,
early_stop=False,
early_stop_patience=5,
use_vdl=True,
resume_checkpoint=None,
quant_config=None):
"""
Quantization-aware training.
Args:
num_epochs (int): Number of epochs.
train_dataset (paddlers.datasets.ResDataset): Training dataset.
train_batch_size (int, optional): Total batch size among all cards used in
training. Defaults to 2.
eval_dataset (paddlers.datasets.ResDataset|None, optional): Evaluation dataset.
If None, the model will not be evaluated during training process.
Defaults to None.
optimizer (paddle.optimizer.Optimizer|None, optional): Optimizer used in
training. If None, a default optimizer will be used. Defaults to None.
save_interval_epochs (int, optional): Epoch interval for saving the model.
Defaults to 1.
log_interval_steps (int, optional): Step interval for printing training
information. Defaults to 2.
save_dir (str, optional): Directory to save the model. Defaults to 'output'.
learning_rate (float, optional): Learning rate for training.
Defaults to .0001.
lr_decay_power (float, optional): Learning decay power. Defaults to .9.
early_stop (bool, optional): Whether to adopt early stop strategy.
Defaults to False.
early_stop_patience (int, optional): Early stop patience. Defaults to 5.
use_vdl (bool, optional): Whether to use VisualDL to monitor the training
process. Defaults to True.
quant_config (dict|None, optional): Quantization configuration. If None,
a default rule of thumb configuration will be used. Defaults to None.
resume_checkpoint (str|None, optional): Path of the checkpoint to resume
quantization-aware training from. If None, no training checkpoint will
be resumed. Defaults to None.
"""
self._prepare_qat(quant_config)
self.train(
num_epochs=num_epochs,
train_dataset=train_dataset,
train_batch_size=train_batch_size,
eval_dataset=eval_dataset,
optimizer=optimizer,
save_interval_epochs=save_interval_epochs,
log_interval_steps=log_interval_steps,
save_dir=save_dir,
pretrain_weights=None,
learning_rate=learning_rate,
lr_decay_power=lr_decay_power,
early_stop=early_stop,
early_stop_patience=early_stop_patience,
use_vdl=use_vdl,
resume_checkpoint=resume_checkpoint)
def evaluate(self, eval_dataset, batch_size=1, return_details=False):
"""
Evaluate the model.
Args:
eval_dataset (paddlers.datasets.ResDataset): Evaluation dataset.
batch_size (int, optional): Total batch size among all cards used for
evaluation. Defaults to 1.
return_details (bool, optional): Whether to return evaluation details.
Defaults to False.
Returns:
If `return_details` is False, return collections.OrderedDict with
key-value pairs:
{"psnr": `peak signal-to-noise ratio`,
"ssim": `structural similarity`}.
"""
self._check_transforms(eval_dataset.transforms, 'eval')
self.net.eval()
nranks = paddle.distributed.get_world_size()
local_rank = paddle.distributed.get_rank()
if nranks > 1:
# Initialize parallel environment if not done.
if not paddle.distributed.parallel.parallel_helper._is_parallel_ctx_initialized(
):
paddle.distributed.init_parallel_env()
# TODO: Distributed evaluation
if batch_size > 1:
logging.warning(
"Restorer only supports single card evaluation with batch_size=1 "
"during evaluation, so batch_size is forcibly set to 1.")
batch_size = 1
if nranks < 2 or local_rank == 0:
self.eval_data_loader = self.build_data_loader(
eval_dataset, batch_size=batch_size, mode='eval')
# XXX: Hard-code crop_border and test_y_channel
psnr = metrics.PSNR(crop_border=4, test_y_channel=True)
ssim = metrics.SSIM(crop_border=4, test_y_channel=True)
logging.info(
"Start to evaluate(total_samples={}, total_steps={})...".format(
eval_dataset.num_samples, eval_dataset.num_samples))
with paddle.no_grad():
for step, data in enumerate(self.eval_data_loader):
data.append(eval_dataset.transforms.transforms)
outputs = self.run(self.net, data, 'eval')
psnr.update(outputs['pred'], outputs['tar'])
ssim.update(outputs['pred'], outputs['tar'])
# DO NOT use psnr.accumulate() here, otherwise the program hangs in multi-card training.
assert len(psnr.results) > 0
assert len(ssim.results) > 0
eval_metrics = OrderedDict(
zip(['psnr', 'ssim'],
[np.mean(psnr.results), np.mean(ssim.results)]))
if return_details:
# TODO: Add details
return eval_metrics, None
return eval_metrics
def predict(self, img_file, transforms=None):
"""
Do inference.
Args:
img_file (list[np.ndarray|str] | str | np.ndarray): Image path or decoded
image data, which also could constitute a list, meaning all images to be
predicted as a mini-batch.
transforms (paddlers.transforms.Compose|None, optional): Transforms for
inputs. If None, the transforms for evaluation process will be used.
Defaults to None.
Returns:
If `img_file` is a tuple of string or np.array, the result is a dict with
the following key-value pairs:
res_map (np.ndarray): Restored image (HWC).
If `img_file` is a list, the result is a list composed of dicts with the
above keys.
"""
if transforms is None and not hasattr(self, 'test_transforms'):
raise ValueError("transforms need to be defined, now is None.")
if transforms is None:
transforms = self.test_transforms
if isinstance(img_file, (str, np.ndarray)):
images = [img_file]
else:
images = img_file
batch_im, batch_tar_shape = self.preprocess(images, transforms,
self.model_type)
self.net.eval()
data = (batch_im, batch_tar_shape, transforms.transforms)
outputs = self.run(self.net, data, 'test')
res_map_list = outputs['res_map']
if isinstance(img_file, list):
prediction = [{'res_map': m} for m in res_map_list]
else:
prediction = {'res_map': res_map_list[0]}
return prediction
def preprocess(self, images, transforms, to_tensor=True):
self._check_transforms(transforms, 'test')
batch_im = list()
batch_tar_shape = list()
for im in images:
if isinstance(im, str):
im = decode_image(im, to_rgb=False)
ori_shape = im.shape[:2]
sample = {'image': im}
im = transforms(sample)[0]
batch_im.append(im)
batch_tar_shape.append(self._get_target_shape(ori_shape))
if to_tensor:
batch_im = paddle.to_tensor(batch_im)
else:
batch_im = np.asarray(batch_im)
return batch_im, batch_tar_shape
def _get_target_shape(self, ori_shape):
if self.sr_factor is None:
return ori_shape
else:
return calc_hr_shape(ori_shape, self.sr_factor)
@staticmethod
def get_transforms_shape_info(batch_tar_shape, transforms):
batch_restore_list = list()
for tar_shape in batch_tar_shape:
restore_list = list()
h, w = tar_shape[0], tar_shape[1]
for op in transforms:
if op.__class__.__name__ == 'Resize':
restore_list.append(('resize', (h, w)))
h, w = op.target_size
elif op.__class__.__name__ == 'ResizeByShort':
restore_list.append(('resize', (h, w)))
im_short_size = min(h, w)
im_long_size = max(h, w)
scale = float(op.short_size) / float(im_short_size)
if 0 < op.max_size < np.round(scale * im_long_size):
scale = float(op.max_size) / float(im_long_size)
h = int(round(h * scale))
w = int(round(w * scale))
elif op.__class__.__name__ == 'ResizeByLong':
restore_list.append(('resize', (h, w)))
im_long_size = max(h, w)
scale = float(op.long_size) / float(im_long_size)
h = int(round(h * scale))
w = int(round(w * scale))
elif op.__class__.__name__ == 'Pad':
if op.target_size:
target_h, target_w = op.target_size
else:
target_h = int(
(np.ceil(h / op.size_divisor) * op.size_divisor))
target_w = int(
(np.ceil(w / op.size_divisor) * op.size_divisor))
if op.pad_mode == -1:
offsets = op.offsets
elif op.pad_mode == 0:
offsets = [0, 0]
elif op.pad_mode == 1:
offsets = [(target_h - h) // 2, (target_w - w) // 2]
else:
offsets = [target_h - h, target_w - w]
restore_list.append(('padding', (h, w), offsets))
h, w = target_h, target_w
batch_restore_list.append(restore_list)
return batch_restore_list
def postprocess(self, batch_pred, batch_tar_shape, transforms):
batch_restore_list = BaseRestorer.get_transforms_shape_info(
batch_tar_shape, transforms)
if self.status == 'Infer':
return self._infer_postprocess(
batch_res_map=batch_pred, batch_restore_list=batch_restore_list)
results = []
if batch_pred.dtype == paddle.float32:
mode = 'bilinear'
else:
mode = 'nearest'
for pred, restore_list in zip(batch_pred, batch_restore_list):
pred = paddle.unsqueeze(pred, axis=0)
for item in restore_list[::-1]:
h, w = item[1][0], item[1][1]
if item[0] == 'resize':
pred = F.interpolate(
pred, (h, w), mode=mode, data_format='NCHW')
elif item[0] == 'padding':
x, y = item[2]
pred = pred[:, :, y:y + h, x:x + w]
else:
pass
results.append(pred)
return results
def _infer_postprocess(self, batch_res_map, batch_restore_list):
res_maps = []
for res_map, restore_list in zip(batch_res_map, batch_restore_list):
if not isinstance(res_map, np.ndarray):
res_map = paddle.unsqueeze(res_map, axis=0)
for item in restore_list[::-1]:
h, w = item[1][0], item[1][1]
if item[0] == 'resize':
if isinstance(res_map, np.ndarray):
res_map = cv2.resize(
res_map, (w, h), interpolation=cv2.INTER_LINEAR)
else:
res_map = F.interpolate(
res_map, (h, w),
mode='bilinear',
data_format='NHWC')
elif item[0] == 'padding':
x, y = item[2]
if isinstance(res_map, np.ndarray):
res_map = res_map[y:y + h, x:x + w]
else:
res_map = res_map[:, y:y + h, x:x + w, :]
else:
pass
res_map = res_map.squeeze()
if not isinstance(res_map, np.ndarray):
res_map = res_map.numpy()
res_map = self._normalize(res_map)
res_maps.append(res_map.squeeze())
return res_maps
def _check_transforms(self, transforms, mode):
super()._check_transforms(transforms, mode)
if not isinstance(transforms.arrange,
paddlers.transforms.ArrangeRestorer):
raise TypeError(
"`transforms.arrange` must be an ArrangeRestorer object.")
def build_data_loader(self, dataset, batch_size, mode='train'):
if dataset.num_samples < batch_size:
raise ValueError(
'The volume of dataset({}) must be larger than batch size({}).'
.format(dataset.num_samples, batch_size))
if mode != 'train':
return paddle.io.DataLoader(
dataset,
batch_size=batch_size,
shuffle=dataset.shuffle,
drop_last=False,
collate_fn=dataset.batch_transforms,
num_workers=dataset.num_workers,
return_list=True,
use_shared_memory=False)
else:
return super(BaseRestorer, self).build_data_loader(dataset,
batch_size, mode)
def set_losses(self, losses):
self.losses = losses
def _tensor_to_images(self,
tensor,
transpose=True,
squeeze=True,
quantize=True):
if transpose:
tensor = paddle.transpose(tensor, perm=[0, 2, 3, 1]) # NHWC
if squeeze:
tensor = tensor.squeeze()
images = tensor.numpy().astype('float32')
images = self._normalize(
images, copy=True, clip=True, quantize=quantize)
return images
def _normalize(self, im, copy=False, clip=True, quantize=True):
if copy:
im = im.copy()
if clip:
im = np.clip(im, self.MIN_MAX[0], self.MIN_MAX[1])
im -= im.min()
im /= im.max() + 1e-32
if quantize:
im *= 255
im = im.astype('uint8')
return im
class DRN(BaseRestorer):
TEST_OUT_KEY = -1
def __init__(self,
losses=None,
sr_factor=4,
scales=(2, 4),
n_blocks=30,
n_feats=16,
n_colors=3,
rgb_range=1.0,
negval=0.2,
lq_loss_weight=0.1,
dual_loss_weight=0.1,
**params):
if sr_factor != max(scales):
raise ValueError(f"`sr_factor` must be equal to `max(scales)`.")
params.update({
'scale': scales,
'n_blocks': n_blocks,
'n_feats': n_feats,
'n_colors': n_colors,
'rgb_range': rgb_range,
'negval': negval
})
self.lq_loss_weight = lq_loss_weight
self.dual_loss_weight = dual_loss_weight
self.scales = scales
super(DRN, self).__init__(
model_name='DRN', losses=losses, sr_factor=sr_factor, **params)
def build_net(self, **params):
from ppgan.modules.init import init_weights
generators = [ppgan.models.generators.DRNGenerator(**params)]
init_weights(generators[-1])
for scale in params['scale']:
dual_model = ppgan.models.generators.drn.DownBlock(
params['negval'], params['n_feats'], params['n_colors'], 2)
generators.append(dual_model)
init_weights(generators[-1])
return GANAdapter(generators, [])
def default_optimizer(self, parameters, *args, **kwargs):
optims_g = [
super(DRN, self).default_optimizer(params_g, *args, **kwargs)
for params_g in parameters['params_g']
]
return OptimizerAdapter(*optims_g)
def run_gan(self, net, inputs, mode, gan_mode='forward_primary'):
if mode != 'train':
raise ValueError("`mode` is not 'train'.")
outputs = OrderedDict()
if gan_mode == 'forward_primary':
sr = net.generator(inputs[0])
lr = [inputs[0]]
lr.extend([
F.interpolate(
inputs[0], scale_factor=s, mode='bicubic')
for s in self.scales[:-1]
])
loss = self.losses(sr[-1], inputs[1])
for i in range(1, len(sr)):
if self.lq_loss_weight > 0:
loss += self.losses(sr[i - 1 - len(sr)],
lr[i - len(sr)]) * self.lq_loss_weight
outputs['loss_prim'] = loss
outputs['sr'] = sr
outputs['lr'] = lr
elif gan_mode == 'forward_dual':
sr, lr = inputs[0], inputs[1]
sr2lr = []
n_scales = len(self.scales)
for i in range(n_scales):
sr2lr_i = net.generators[1 + i](sr[i - n_scales])
sr2lr.append(sr2lr_i)
loss = self.losses(sr2lr[0], lr[0])
for i in range(1, n_scales):
if self.dual_loss_weight > 0.0:
loss += self.losses(sr2lr[i], lr[i]) * self.dual_loss_weight
outputs['loss_dual'] = loss
else:
raise ValueError("Invalid `gan_mode`!")
return outputs
def train_step(self, step, data, net):
outputs = self.run_gan(
net, data, mode='train', gan_mode='forward_primary')
outputs.update(
self.run_gan(
net, (outputs['sr'], outputs['lr']),
mode='train',
gan_mode='forward_dual'))
self.optimizer.clear_grad()
(outputs['loss_prim'] + outputs['loss_dual']).backward()
self.optimizer.step()
return {
'loss': outputs['loss_prim'] + outputs['loss_dual'],
'loss_prim': outputs['loss_prim'],
'loss_dual': outputs['loss_dual']
}
class LESRCNN(BaseRestorer):
def __init__(self,
losses=None,
sr_factor=4,
multi_scale=False,
group=1,
**params):
params.update({
'scale': sr_factor,
'multi_scale': multi_scale,
'group': group
})
super(LESRCNN, self).__init__(
model_name='LESRCNN', losses=losses, sr_factor=sr_factor, **params)
def build_net(self, **params):
net = ppgan.models.generators.LESRCNNGenerator(**params)
return net
class ESRGAN(BaseRestorer):
def __init__(self,
losses=None,
sr_factor=4,
use_gan=True,
in_channels=3,
out_channels=3,
nf=64,
nb=23,
**params):
if sr_factor != 4:
raise ValueError("`sr_factor` must be 4.")
params.update({
'in_nc': in_channels,
'out_nc': out_channels,
'nf': nf,
'nb': nb
})
self.use_gan = use_gan
super(ESRGAN, self).__init__(
model_name='ESRGAN', losses=losses, sr_factor=sr_factor, **params)
def build_net(self, **params):
from ppgan.modules.init import init_weights
generator = ppgan.models.generators.RRDBNet(**params)
init_weights(generator)
if self.use_gan:
discriminator = ppgan.models.discriminators.VGGDiscriminator128(
in_channels=params['out_nc'], num_feat=64)
net = GANAdapter(
generators=[generator], discriminators=[discriminator])
else:
net = generator
return net
def default_loss(self):
if self.use_gan:
return {
'pixel': res_losses.L1Loss(loss_weight=0.01),
'perceptual': res_losses.PerceptualLoss(
layer_weights={'34': 1.0},
perceptual_weight=1.0,
style_weight=0.0,
norm_img=False),
'gan': res_losses.GANLoss(
gan_mode='vanilla', loss_weight=0.005)
}
else:
return res_losses.L1Loss()
def default_optimizer(self, parameters, *args, **kwargs):
if self.use_gan:
optim_g = super(ESRGAN, self).default_optimizer(
parameters['params_g'][0], *args, **kwargs)
optim_d = super(ESRGAN, self).default_optimizer(
parameters['params_d'][0], *args, **kwargs)
return OptimizerAdapter(optim_g, optim_d)
else:
return super(ESRGAN, self).default_optimizer(parameters, *args,
**kwargs)
def run_gan(self, net, inputs, mode, gan_mode='forward_g'):
if mode != 'train':
raise ValueError("`mode` is not 'train'.")
outputs = OrderedDict()
if gan_mode == 'forward_g':
loss_g = 0
g_pred = net.generator(inputs[0])
loss_pix = self.losses['pixel'](g_pred, inputs[1])
loss_perc, loss_sty = self.losses['perceptual'](g_pred, inputs[1])
loss_g += loss_pix
if loss_perc is not None:
loss_g += loss_perc
if loss_sty is not None:
loss_g += loss_sty
self._set_requires_grad(net.discriminator, False)
real_d_pred = net.discriminator(inputs[1]).detach()
fake_g_pred = net.discriminator(g_pred)
loss_g_real = self.losses['gan'](
real_d_pred - paddle.mean(fake_g_pred), False,
is_disc=False) * 0.5
loss_g_fake = self.losses['gan'](
fake_g_pred - paddle.mean(real_d_pred), True,
is_disc=False) * 0.5
loss_g_gan = loss_g_real + loss_g_fake
outputs['g_pred'] = g_pred.detach()
outputs['loss_g_pps'] = loss_g
outputs['loss_g_gan'] = loss_g_gan
elif gan_mode == 'forward_d':
self._set_requires_grad(net.discriminator, True)
# Real
fake_d_pred = net.discriminator(inputs[0]).detach()
real_d_pred = net.discriminator(inputs[1])
loss_d_real = self.losses['gan'](
real_d_pred - paddle.mean(fake_d_pred), True,
is_disc=True) * 0.5
# Fake
fake_d_pred = net.discriminator(inputs[0].detach())
loss_d_fake = self.losses['gan'](
fake_d_pred - paddle.mean(real_d_pred.detach()),
False,
is_disc=True) * 0.5
outputs['loss_d'] = loss_d_real + loss_d_fake
else:
raise ValueError("Invalid `gan_mode`!")
return outputs
def train_step(self, step, data, net):
if self.use_gan:
optim_g, optim_d = self.optimizer
outputs = self.run_gan(
net, data, mode='train', gan_mode='forward_g')
optim_g.clear_grad()
(outputs['loss_g_pps'] + outputs['loss_g_gan']).backward()
optim_g.step()
outputs.update(
self.run_gan(
net, (outputs['g_pred'], data[1]),
mode='train',
gan_mode='forward_d'))
optim_d.clear_grad()
outputs['loss_d'].backward()
optim_d.step()
outputs['loss'] = outputs['loss_g_pps'] + outputs[
'loss_g_gan'] + outputs['loss_d']
return {
'loss': outputs['loss'],
'loss_g_pps': outputs['loss_g_pps'],
'loss_g_gan': outputs['loss_g_gan'],
'loss_d': outputs['loss_d']
}
else:
return super(ESRGAN, self).train_step(step, data, net)
def _set_requires_grad(self, net, requires_grad):
for p in net.parameters():
p.trainable = requires_grad
class RCAN(BaseRestorer):
def __init__(self,
losses=None,
sr_factor=4,
n_resgroups=10,
n_resblocks=20,
n_feats=64,
n_colors=3,
rgb_range=1.0,
kernel_size=3,
reduction=16,
**params):
params.update({
'n_resgroups': n_resgroups,
'n_resblocks': n_resblocks,
'n_feats': n_feats,
'n_colors': n_colors,
'rgb_range': rgb_range,
'kernel_size': kernel_size,
'reduction': reduction
})
super(RCAN, self).__init__(
model_name='RCAN', losses=losses, sr_factor=sr_factor, **params)

@ -33,6 +33,7 @@ from paddlers.utils import get_single_card_bs, DisablePrint
from paddlers.utils.checkpoint import seg_pretrain_weights_dict
from .base import BaseModel
from .utils import seg_metrics as metrics
from .utils.infer_nets import InferSegNet
__all__ = ["UNet", "DeepLabV3P", "FastSCNN", "HRNet", "BiSeNetV2", "FarSeg"]
@ -64,11 +65,16 @@ class BaseSegmenter(BaseModel):
def build_net(self, **params):
# TODO: when using paddle.utils.unique_name.guard,
# DeepLabv3p and HRNet will raise a error
# DeepLabv3p and HRNet will raise an error.
net = dict(ppseg.models.__dict__, **cmseg.__dict__)[self.model_name](
num_classes=self.num_classes, **params)
return net
def _build_inference_net(self):
infer_net = InferSegNet(self.net)
infer_net.eval()
return infer_net
def _fix_transforms_shape(self, image_shape):
if hasattr(self, 'test_transforms'):
if self.test_transforms is not None:
@ -472,7 +478,6 @@ class BaseSegmenter(BaseModel):
conf_mat_all.append(conf_mat)
class_iou, miou = ppseg.utils.metrics.mean_iou(
intersect_area_all, pred_area_all, label_area_all)
# TODO 确认是按oacc还是macc
class_acc, oacc = ppseg.utils.metrics.accuracy(intersect_area_all,
pred_area_all)
kappa = ppseg.utils.metrics.kappa(intersect_area_all, pred_area_all,
@ -504,13 +509,13 @@ class BaseSegmenter(BaseModel):
Defaults to None.
Returns:
If `img_file` is a string or np.array, the result is a dict with key-value
pairs:
{"label map": `label map`, "score_map": `score map`}.
If `img_file` is a tuple of string or np.array, the result is a dict with
the following key-value pairs:
label_map (np.ndarray): Predicted label map (HW).
score_map (np.ndarray): Prediction score map (HWC).
If `img_file` is a list, the result is a list composed of dicts with the
corresponding fields:
label_map (np.ndarray): the predicted label map (HW)
score_map (np.ndarray): the prediction score map (HWC)
above keys.
"""
if transforms is None and not hasattr(self, 'test_transforms'):
@ -750,11 +755,11 @@ class BaseSegmenter(BaseModel):
elif item[0] == 'padding':
x, y = item[2]
if isinstance(label_map, np.ndarray):
label_map = label_map[..., y:y + h, x:x + w]
score_map = score_map[..., y:y + h, x:x + w]
label_map = label_map[y:y + h, x:x + w]
score_map = score_map[y:y + h, x:x + w]
else:
label_map = label_map[:, :, y:y + h, x:x + w]
score_map = score_map[:, :, y:y + h, x:x + w]
label_map = label_map[:, y:y + h, x:x + w, :]
score_map = score_map[:, y:y + h, x:x + w, :]
else:
pass
label_map = label_map.squeeze()

@ -15,30 +15,36 @@
import paddle
class PostProcessor(paddle.nn.Layer):
def __init__(self, model_type):
super(PostProcessor, self).__init__()
self.model_type = model_type
class SegPostProcessor(paddle.nn.Layer):
def forward(self, net_outputs):
# label_map [NHW], score_map [NHWC]
logit = net_outputs[0]
outputs = paddle.argmax(logit, axis=1, keepdim=False, dtype='int32'), \
paddle.transpose(paddle.nn.functional.softmax(logit, axis=1), perm=[0, 2, 3, 1])
return outputs
class ResPostProcessor(paddle.nn.Layer):
def __init__(self, out_key=None):
super(ResPostProcessor, self).__init__()
self.out_key = out_key
def forward(self, net_outputs):
if self.out_key is not None:
net_outputs = net_outputs[self.out_key]
outputs = paddle.transpose(net_outputs, perm=[0, 2, 3, 1])
return outputs
class InferNet(paddle.nn.Layer):
def __init__(self, net, model_type):
super(InferNet, self).__init__()
class InferSegNet(paddle.nn.Layer):
def __init__(self, net):
super(InferSegNet, self).__init__()
self.net = net
self.postprocessor = PostProcessor(model_type)
self.postprocessor = SegPostProcessor()
def forward(self, x):
net_outputs = self.net(x)
outputs = self.postprocessor(net_outputs)
return outputs
@ -46,10 +52,21 @@ class InferCDNet(paddle.nn.Layer):
def __init__(self, net):
super(InferCDNet, self).__init__()
self.net = net
self.postprocessor = PostProcessor('change_detector')
self.postprocessor = SegPostProcessor()
def forward(self, x1, x2):
net_outputs = self.net(x1, x2)
outputs = self.postprocessor(net_outputs)
return outputs
class InferResNet(paddle.nn.Layer):
def __init__(self, net, out_key=None):
super(InferResNet, self).__init__()
self.net = net
self.postprocessor = ResPostProcessor(out_key=out_key)
def forward(self, x):
net_outputs = self.net(x)
outputs = self.postprocessor(net_outputs)
return outputs

@ -0,0 +1,132 @@
from functools import wraps
from inspect import isfunction, isgeneratorfunction, getmembers
from collections.abc import Sequence
from abc import ABC
import paddle
import paddle.nn as nn
__all__ = ['GANAdapter', 'OptimizerAdapter']
class _AttrDesc:
def __init__(self, key):
self.key = key
def __get__(self, instance, owner):
return tuple(getattr(ele, self.key) for ele in instance)
def __set__(self, instance, value):
for ele in instance:
setattr(ele, self.key, value)
def _func_deco(cls, func_name):
@wraps(getattr(cls.__ducktype__, func_name))
def _wrapper(self, *args, **kwargs):
return tuple(getattr(ele, func_name)(*args, **kwargs) for ele in self)
return _wrapper
def _generator_deco(cls, func_name):
@wraps(getattr(cls.__ducktype__, func_name))
def _wrapper(self, *args, **kwargs):
for ele in self:
yield from getattr(ele, func_name)(*args, **kwargs)
return _wrapper
class Adapter(Sequence, ABC):
__ducktype__ = object
__ava__ = ()
def __init__(self, *args):
if not all(map(self._check, args)):
raise TypeError("Please check the input type.")
self._seq = tuple(args)
def __getitem__(self, key):
return self._seq[key]
def __len__(self):
return len(self._seq)
def __repr__(self):
return repr(self._seq)
@classmethod
def _check(cls, obj):
for attr in cls.__ava__:
try:
getattr(obj, attr)
# TODO: Check function signature
except AttributeError:
return False
return True
def make_adapter(cls):
members = dict(getmembers(cls.__ducktype__))
for k in cls.__ava__:
if hasattr(cls, k):
continue
if k in members:
v = members[k]
if isgeneratorfunction(v):
setattr(cls, k, _generator_deco(cls, k))
elif isfunction(v):
setattr(cls, k, _func_deco(cls, k))
else:
setattr(cls, k, _AttrDesc(k))
return cls
class GANAdapter(nn.Layer):
__ducktype__ = nn.Layer
__ava__ = ('state_dict', 'set_state_dict', 'train', 'eval')
def __init__(self, generators, discriminators):
super(GANAdapter, self).__init__()
self.generators = nn.LayerList(generators)
self.discriminators = nn.LayerList(discriminators)
self._m = [*generators, *discriminators]
def __len__(self):
return len(self._m)
def __getitem__(self, key):
return self._m[key]
def __contains__(self, m):
return m in self._m
def __repr__(self):
return repr(self._m)
@property
def generator(self):
return self.generators[0]
@property
def discriminator(self):
return self.discriminators[0]
Adapter.register(GANAdapter)
@make_adapter
class OptimizerAdapter(Adapter):
__ducktype__ = paddle.optimizer.Optimizer
__ava__ = ('state_dict', 'set_state_dict', 'clear_grad', 'step', 'get_lr')
def set_state_dict(self, state_dicts):
# Special dispatching rule
for optim, state_dict in zip(self, state_dicts):
optim.set_state_dict(state_dict)
def get_lr(self):
# Return the lr of the first optimizer
return self[0].get_lr()

@ -638,3 +638,7 @@ def decode_seg_mask(mask_path):
mask = np.asarray(Image.open(mask_path))
mask = mask.astype('int64')
return mask
def calc_hr_shape(lr_shape, sr_factor):
return tuple(int(s * sr_factor) for s in lr_shape)

@ -35,7 +35,7 @@ from .functions import (
horizontal_flip_poly, horizontal_flip_rle, vertical_flip_poly,
vertical_flip_rle, crop_poly, crop_rle, expand_poly, expand_rle,
resize_poly, resize_rle, dehaze, select_bands, to_intensity, to_uint8,
img_flip, img_simple_rotate, decode_seg_mask)
img_flip, img_simple_rotate, decode_seg_mask, calc_hr_shape)
__all__ = [
"Compose", "DecodeImg", "Resize", "RandomResize", "ResizeByShort",
@ -44,7 +44,7 @@ __all__ = [
"RandomScaleAspect", "RandomExpand", "Pad", "MixupImage", "RandomDistort",
"RandomBlur", "RandomSwap", "Dehaze", "ReduceDim", "SelectBand",
"ArrangeSegmenter", "ArrangeChangeDetector", "ArrangeClassifier",
"ArrangeDetector", "RandomFlipOrRotate", "ReloadMask"
"ArrangeDetector", "ArrangeRestorer", "RandomFlipOrRotate", "ReloadMask"
]
interp_dict = {
@ -154,6 +154,8 @@ class Transform(object):
if 'aux_masks' in sample:
sample['aux_masks'] = list(
map(self.apply_mask, sample['aux_masks']))
if 'target' in sample:
sample['target'] = self.apply_im(sample['target'])
return sample
@ -336,6 +338,14 @@ class DecodeImg(Transform):
map(self.apply_mask, sample['aux_masks']))
# TODO: check the shape of auxiliary masks
if 'target' in sample:
if self.read_geo_info:
target, geo_info_dict = self.apply_im(sample['target'])
sample['target'] = target
sample['geo_info_dict_tar'] = geo_info_dict
else:
sample['target'] = self.apply_im(sample['target'])
sample['im_shape'] = np.array(
sample['image'].shape[:2], dtype=np.float32)
sample['scale_factor'] = np.array([1., 1.], dtype=np.float32)
@ -457,6 +467,17 @@ class Resize(Transform):
if 'gt_poly' in sample and len(sample['gt_poly']) > 0:
sample['gt_poly'] = self.apply_segm(
sample['gt_poly'], [im_h, im_w], [im_scale_x, im_scale_y])
if 'target' in sample:
if 'sr_factor' in sample:
# For SR tasks
sample['target'] = self.apply_im(
sample['target'], interp,
calc_hr_shape(target_size, sample['sr_factor']))
else:
# For non-SR tasks
sample['target'] = self.apply_im(sample['target'], interp,
target_size)
sample['im_shape'] = np.asarray(
sample['image'].shape[:2], dtype=np.float32)
if 'scale_factor' in sample:
@ -730,6 +751,9 @@ class RandomFlipOrRotate(Transform):
if 'gt_poly' in sample and len(sample['gt_poly']) > 0:
sample['gt_poly'] = self.apply_segm(sample['gt_poly'], mode_id,
True)
if 'target' in sample:
sample['target'] = self.apply_im(sample['target'], mode_id,
True)
elif p_m < self.probs[1]:
mode_p = random.random()
mode_id = self.judge_probs_range(mode_p, self.probsr)
@ -750,6 +774,9 @@ class RandomFlipOrRotate(Transform):
if 'gt_poly' in sample and len(sample['gt_poly']) > 0:
sample['gt_poly'] = self.apply_segm(sample['gt_poly'], mode_id,
False)
if 'target' in sample:
sample['target'] = self.apply_im(sample['target'], mode_id,
False)
return sample
@ -809,6 +836,8 @@ class RandomHorizontalFlip(Transform):
if 'gt_poly' in sample and len(sample['gt_poly']) > 0:
sample['gt_poly'] = self.apply_segm(sample['gt_poly'], im_h,
im_w)
if 'target' in sample:
sample['target'] = self.apply_im(sample['target'])
return sample
@ -867,6 +896,8 @@ class RandomVerticalFlip(Transform):
if 'gt_poly' in sample and len(sample['gt_poly']) > 0:
sample['gt_poly'] = self.apply_segm(sample['gt_poly'], im_h,
im_w)
if 'target' in sample:
sample['target'] = self.apply_im(sample['target'])
return sample
@ -883,16 +914,19 @@ class Normalize(Transform):
std (list[float] | tuple[float], optional): Standard deviation of input
image(s). Defaults to [0.229, 0.224, 0.225].
min_val (list[float] | tuple[float], optional): Minimum value of input
image(s). Defaults to [0, 0, 0, ].
max_val (list[float] | tuple[float], optional): Max value of input image(s).
Defaults to [255., 255., 255.].
image(s). If None, use 0 for all channels. Defaults to None.
max_val (list[float] | tuple[float], optional): Maximum value of input
image(s). If None, use 255. for all channels. Defaults to None.
apply_to_tar (bool, optional): Whether to apply transformation to the target
image. Defaults to True.
"""
def __init__(self,
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225],
min_val=None,
max_val=None):
max_val=None,
apply_to_tar=True):
super(Normalize, self).__init__()
channel = len(mean)
if min_val is None:
@ -914,6 +948,7 @@ class Normalize(Transform):
self.std = std
self.min_val = min_val
self.max_val = max_val
self.apply_to_tar = apply_to_tar
def apply_im(self, image):
image = image.astype(np.float32)
@ -927,6 +962,8 @@ class Normalize(Transform):
sample['image'] = self.apply_im(sample['image'])
if 'image2' in sample:
sample['image2'] = self.apply_im(sample['image2'])
if 'target' in sample and self.apply_to_tar:
sample['target'] = self.apply_im(sample['target'])
return sample
@ -964,6 +1001,8 @@ class CenterCrop(Transform):
if 'aux_masks' in sample:
sample['aux_masks'] = list(
map(self.apply_mask, sample['aux_masks']))
if 'target' in sample:
sample['target'] = self.apply_im(sample['target'])
return sample
@ -1165,6 +1204,14 @@ class RandomCrop(Transform):
self.apply_mask, crop=crop_box),
sample['aux_masks']))
if 'target' in sample:
if 'sr_factor' in sample:
sample['target'] = self.apply_im(
sample['target'],
calc_hr_shape(crop_box, sample['sr_factor']))
else:
sample['target'] = self.apply_im(sample['image'], crop_box)
if self.crop_size is not None:
sample = Resize(self.crop_size)(sample)
@ -1266,6 +1313,7 @@ class Pad(Transform):
pad_mode (int, optional): Pad mode. Currently only four modes are supported:
[-1, 0, 1, 2]. if -1, use specified offsets. If 0, only pad to right and bottom
If 1, pad according to center. If 2, only pad left and top. Defaults to 0.
offsets (list[int]|None, optional): Padding offsets. Defaults to None.
im_padding_value (list[float] | tuple[float]): RGB value of padded area.
Defaults to (127.5, 127.5, 127.5).
label_padding_value (int, optional): Filling value for the mask.
@ -1332,6 +1380,17 @@ class Pad(Transform):
expand_rle(segm, x, y, height, width, h, w))
return expanded_segms
def _get_offsets(self, im_h, im_w, h, w):
if self.pad_mode == -1:
offsets = self.offsets
elif self.pad_mode == 0:
offsets = [0, 0]
elif self.pad_mode == 1:
offsets = [(w - im_w) // 2, (h - im_h) // 2]
else:
offsets = [w - im_w, h - im_h]
return offsets
def apply(self, sample):
im_h, im_w = sample['image'].shape[:2]
if self.target_size:
@ -1349,14 +1408,7 @@ class Pad(Transform):
if h == im_h and w == im_w:
return sample
if self.pad_mode == -1:
offsets = self.offsets
elif self.pad_mode == 0:
offsets = [0, 0]
elif self.pad_mode == 1:
offsets = [(w - im_w) // 2, (h - im_h) // 2]
else:
offsets = [w - im_w, h - im_h]
offsets = self._get_offsets(im_h, im_w, h, w)
sample['image'] = self.apply_im(sample['image'], offsets, (h, w))
if 'image2' in sample:
@ -1373,6 +1425,16 @@ class Pad(Transform):
if 'gt_poly' in sample and len(sample['gt_poly']) > 0:
sample['gt_poly'] = self.apply_segm(
sample['gt_poly'], offsets, im_size=[im_h, im_w], size=[h, w])
if 'target' in sample:
if 'sr_factor' in sample:
hr_shape = calc_hr_shape((h, w), sample['sr_factor'])
hr_offsets = self._get_offsets(*sample['target'].shape[:2],
*hr_shape)
sample['target'] = self.apply_im(sample['target'], hr_offsets,
hr_shape)
else:
sample['target'] = self.apply_im(sample['target'], offsets,
(h, w))
return sample
@ -1688,15 +1750,18 @@ class ReduceDim(Transform):
Args:
joblib_path (str): Path of *.joblib file of PCA.
apply_to_tar (bool, optional): Whether to apply transformation to the target
image. Defaults to True.
"""
def __init__(self, joblib_path):
def __init__(self, joblib_path, apply_to_tar=True):
super(ReduceDim, self).__init__()
ext = joblib_path.split(".")[-1]
if ext != "joblib":
raise ValueError("`joblib_path` must be *.joblib, not *.{}.".format(
ext))
self.pca = load(joblib_path)
self.apply_to_tar = apply_to_tar
def apply_im(self, image):
H, W, C = image.shape
@ -1709,6 +1774,8 @@ class ReduceDim(Transform):
sample['image'] = self.apply_im(sample['image'])
if 'image2' in sample:
sample['image2'] = self.apply_im(sample['image2'])
if 'target' in sample and self.apply_to_tar:
sample['target'] = self.apply_im(sample['target'])
return sample
@ -1719,11 +1786,14 @@ class SelectBand(Transform):
Args:
band_list (list, optional): Bands to select (band index starts from 1).
Defaults to [1, 2, 3].
apply_to_tar (bool, optional): Whether to apply transformation to the target
image. Defaults to True.
"""
def __init__(self, band_list=[1, 2, 3]):
def __init__(self, band_list=[1, 2, 3], apply_to_tar=True):
super(SelectBand, self).__init__()
self.band_list = band_list
self.apply_to_tar = apply_to_tar
def apply_im(self, image):
image = select_bands(image, self.band_list)
@ -1733,6 +1803,8 @@ class SelectBand(Transform):
sample['image'] = self.apply_im(sample['image'])
if 'image2' in sample:
sample['image2'] = self.apply_im(sample['image2'])
if 'target' in sample and self.apply_to_tar:
sample['target'] = self.apply_im(sample['target'])
return sample
@ -1820,6 +1892,8 @@ class _Permute(Transform):
sample['image'] = permute(sample['image'], False)
if 'image2' in sample:
sample['image2'] = permute(sample['image2'], False)
if 'target' in sample:
sample['target'] = permute(sample['target'], False)
return sample
@ -1915,3 +1989,16 @@ class ArrangeDetector(Arrange):
if self.mode == 'eval' and 'gt_poly' in sample:
del sample['gt_poly']
return sample
class ArrangeRestorer(Arrange):
def apply(self, sample):
if 'target' in sample:
target = permute(sample['target'], False)
image = permute(sample['image'], False)
if self.mode == 'train':
return image, target
if self.mode == 'eval':
return image, target
if self.mode == 'test':
return image,

@ -16,7 +16,7 @@ from . import logging
from . import utils
from .utils import (seconds_to_hms, get_encoding, get_single_card_bs, dict2str,
EarlyStop, norm_path, is_pic, MyEncoder, DisablePrint,
Timer)
Timer, to_data_parallel, scheduler_step)
from .checkpoint import get_pretrain_weights, load_pretrain_weights, load_checkpoint
from .env import get_environ_info, get_num_workers, init_parallel_env
from .download import download_and_decompress, decompress

@ -20,11 +20,12 @@ import math
import imghdr
import chardet
import json
import platform
import numpy as np
import paddle
from . import logging
import platform
import paddlers
@ -237,3 +238,30 @@ class Timer(Times):
self.postprocess_time_s.reset()
self.img_num = 0
self.repeats = 0
def to_data_parallel(layers, *args, **kwargs):
from paddlers.tasks.utils.res_adapters import GANAdapter
if isinstance(layers, GANAdapter):
layers = GANAdapter(
[to_data_parallel(g, *args, **kwargs) for g in layers.generators], [
to_data_parallel(d, *args, **kwargs)
for d in layers.discriminators
])
else:
layers = paddle.DataParallel(layers, *args, **kwargs)
return layers
def scheduler_step(optimizer, loss=None):
from paddlers.tasks.utils.res_adapters import OptimizerAdapter
if not isinstance(optimizer, OptimizerAdapter):
optimizer = [optimizer]
for optim in optimizer:
if isinstance(optim._learning_rate, paddle.optimizer.lr.LRScheduler):
# If ReduceOnPlateau is used as the scheduler, use the loss value as the metric.
if isinstance(optim._learning_rate,
paddle.optimizer.lr.ReduceOnPlateau):
optim._learning_rate.step(loss.item())
else:
optim._learning_rate.step()

@ -23,11 +23,29 @@
| 任务类别 | 模型名称 | 基础<br>训练预测 | 更多<br>训练方式 | 更多<br>部署方式 | Slim<br>训练部署 | 更多<br>训练环境 |
| :--- | :--- | :----: | :--------: | :----: | :----: | :----: |
| 变化检测 | BIT | 支持 | - | - | - |
| 变化检测 | CDNet | 支持 | - | - | - |
| 变化检测 | DSAMNet | 支持 | - | - | - |
| 变化检测 | DSIFN | 支持 | - | - | - |
| 变化检测 | SNUNet | 支持 | - | - | - |
| 变化检测 | STANet | 支持 | - | - | - |
| 变化检测 | FC-EF | 支持 | - | - | - |
| 变化检测 | FC-Siam-conc | 支持 | - | - | - |
| 变化检测 | FC-Siam-diff | 支持 | - | - | - |
| 变化检测 | ChangeFormer | 支持 | - | - | - |
| 场景分类 | HRNet | 支持 | - | - | - |
| 场景分类 | MobileNetV3 | 支持 | - | - | - |
| 场景分类 | ResNet50-vd | 支持 | - | - | - |
| 图像复原 | DRN | 支持 | - | - | - |
| 图像复原 | EARGAN | 支持 | - | - | - |
| 图像复原 | LESRCNN | 支持 | - | - | - |
| 目标检测 | Faster R-CNN | 支持 | - | - | - |
| 目标检测 | PP-YOLO | 支持 | - | - | - |
| 目标检测 | PP-YOLO Tiny | 支持 | - | - | - |
| 目标检测 | PP-YOLOv2 | 支持 | - | - | - |
| 目标检测 | YOLOv3 | 支持 | - | - | - |
| 图像分割 | DeepLab V3+ | 支持 | - | - | - |
| 图像分割 | UNet | 支持 | - | - | - |
## 3 测试工具简介
### 3.1 目录介绍

@ -86,12 +86,14 @@ function download_and_unzip_dataset() {
rm -rf "${ds_path}"
fi
wget -nc -P "${ds_dir}" "${url}" --no-check-certificate
wget -O "${ds_dir}/${zip_name}" "${url}" --no-check-certificate
# The extracted file/directory must have the same name as the zip file.
cd "${ds_dir}" && unzip "${zip_name}" \
&& mv "${zip_name%.*}" ${ds_name} && cd - \
&& echo "Successfully downloaded ${zip_name} from ${url}. File saved in ${ds_path}. "
cd "${ds_dir}" && unzip "${zip_name}"
if [ "${zip_name%.*}" != "${ds_name}" ]; then
mv "${zip_name%.*}" "${ds_name}"
fi
cd -
}
function parse_extra_args() {

@ -118,7 +118,7 @@ def parse_args(*args, **kwargs):
conflict_handler='resolve', parents=[cfg_parser])
# Global settings
parser.add_argument('cmd', choices=['train', 'eval'])
parser.add_argument('task', choices=['cd', 'clas', 'det', 'seg'])
parser.add_argument('task', choices=['cd', 'clas', 'det', 'res', 'seg'])
# Data
parser.add_argument('--datasets', type=dict, default={})

@ -60,7 +60,7 @@ download_path: ./test_tipc/data/
num_epochs: 5
train_batch_size: 4
save_interval_epochs: 3
save_interval_epochs: 5
log_interval_steps: 50
save_dir: ./test_tipc/output/cd/
learning_rate: 0.01

@ -1,4 +1,4 @@
# Basic configurations of BIT with AirChange dataset
# Configurations of BIT with AirChange dataset
_base_: ../_base_/airchange.yaml

@ -1,4 +1,4 @@
# Basic configurations of BIT with LEVIR-CD dataset
# Configurations of BIT with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml

@ -6,7 +6,7 @@ use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=4
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/bit/bit_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/bit/bit_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/bit/bit_levircd.yaml
train_model_name:best_model

@ -0,0 +1,8 @@
# Configurations of CDNet with AirChange dataset
_base_: ../_base_/airchange.yaml
save_dir: ./test_tipc/output/cd/cdnet/
model: !Node
type: CDNet

@ -0,0 +1,8 @@
# Configurations of cdnet with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml
save_dir: ./test_tipc/output/cd/cdnet/
model: !Node
type: CDNet

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:cd:cdnet
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/cdnet/cdnet_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/cdnet/cdnet_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/cdnet/cdnet_levircd.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train cd
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:cdnet
null:null

@ -0,0 +1,8 @@
# Configurations of ChangeFormer with AirChange dataset
_base_: ../_base_/airchange.yaml
save_dir: ./test_tipc/output/cd/changeformer/
model: !Node
type: ChangeFormer

@ -0,0 +1,8 @@
# Configurations of ChangeFormer with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml
save_dir: ./test_tipc/output/cd/changeformer/
model: !Node
type: ChangeFormer

@ -6,14 +6,14 @@ use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=4
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/changeformer/changeformer_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/changeformer/changeformer_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/changeformer/changeformer_levircd.yaml
train_model_name:best_model
train_infer_file_list:./test_tipc/data/airchange/:./test_tipc/data/airchange/eval.txt
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train cd --config ./test_tipc/configs/cd/changeformer/changeformer.yaml
norm_train:test_tipc/run_task.py train cd
pact_train:null
fpgm_train:null
distill_train:null
@ -27,7 +27,7 @@ null:null
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[1,3,256,256]
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
@ -46,7 +46,7 @@ inference:test_tipc/infer.py
--use_trt:False
--precision:fp32
--model_dir:null
--file_list:null:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:changeformer

@ -0,0 +1,8 @@
# Configurations of DSAMNet with AirChange dataset
_base_: ../_base_/airchange.yaml
save_dir: ./test_tipc/output/cd/dsamnet/
model: !Node
type: DSAMNet

@ -0,0 +1,8 @@
# Configurations of DSAMNet with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml
save_dir: ./test_tipc/output/cd/dsamnet/
model: !Node
type: DSAMNet

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:cd:dsamnet
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/dsamnet/dsamnet_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/dsamnet/dsamnet_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/dsamnet/dsamnet_levircd.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train cd
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:dsamnet
null:null

@ -0,0 +1,8 @@
# Configurations of DSIFN with AirChange dataset
_base_: ../_base_/airchange.yaml
save_dir: ./test_tipc/output/cd/dsifn/
model: !Node
type: DSIFN

@ -0,0 +1,8 @@
# Configurations of DSIFN with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml
save_dir: ./test_tipc/output/cd/dsifn/
model: !Node
type: DSIFN

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:cd:dsifn
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/dsifn/dsifn_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/dsifn/dsifn_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/dsifn/dsifn_levircd.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train cd
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:dsifn
null:null

@ -0,0 +1,8 @@
# Configurations of FC-EF with AirChange dataset
_base_: ../_base_/airchange.yaml
save_dir: ./test_tipc/output/cd/fc_ef/
model: !Node
type: FCEarlyFusion

@ -0,0 +1,8 @@
# Configurations of FC-EF with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml
save_dir: ./test_tipc/output/cd/fc_ef/
model: !Node
type: FCEarlyFusion

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:cd:fc_ef
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=20
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/fc_ef/fc_ef_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/fc_ef/fc_ef_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/fc_ef/fc_ef_levircd.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train cd
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:fc_ef
null:null

@ -0,0 +1,8 @@
# Configurations of FC-Siam-conc with AirChange dataset
_base_: ../_base_/airchange.yaml
save_dir: ./test_tipc/output/cd/fc_siam_conc/
model: !Node
type: FCSiamConc

@ -0,0 +1,8 @@
# Configurations of FC-Siam-conc with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml
save_dir: ./test_tipc/output/cd/fc_siam_conc/
model: !Node
type: FCSiamConc

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:cd:fc_siam_conc
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=20
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/fc_siam_conc/fc_siam_conc_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/fc_siam_conc/fc_siam_conc_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/fc_siam_conc/fc_siam_conc_levircd.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train cd
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:fc_siam_conc
null:null

@ -0,0 +1,8 @@
# Configurations of FC-Siam-diff with AirChange dataset
_base_: ../_base_/airchange.yaml
save_dir: ./test_tipc/output/cd/fc_siam_diff/
model: !Node
type: FCSiamDiff

@ -0,0 +1,8 @@
# Configurations of FC-Siam-diff with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml
save_dir: ./test_tipc/output/cd/fc_siam_diff/
model: !Node
type: FCSiamDiff

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:cd:fc_siam_diff
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=20
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/fc_siam_diff/fc_siam_diff_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/fc_siam_diff/fc_siam_diff_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/fc_siam_diff/fc_siam_diff_levircd.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train cd
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:fc_siam_diff
null:null

@ -0,0 +1,13 @@
# Configurations of FCCDN with AirChange dataset
_base_: ../_base_/airchange.yaml
save_dir: ./test_tipc/output/cd/fccdn/
model: !Node
type: FCCDN
learning_rate: 0.07
lr_decay_power: 0.6
log_interval_steps: 100
save_interval_epochs: 3

@ -0,0 +1,8 @@
# Configurations of FCCDN with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml
save_dir: ./test_tipc/output/cd/fccdn/
model: !Node
type: FCCDN

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:cd:fccdn
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/fccdn/fccdn_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/fccdn/fccdn_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/fccdn/fccdn_levircd.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train cd
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:fccdn
null:null

@ -0,0 +1,8 @@
# Configurations of SNUNet with AirChange dataset
_base_: ../_base_/airchange.yaml
save_dir: ./test_tipc/output/cd/snunet/
model: !Node
type: SNUNet

@ -0,0 +1,8 @@
# Configurations of SNUNet with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml
save_dir: ./test_tipc/output/cd/snunet/
model: !Node
type: SNUNet

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:cd:snunet
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/snunet/snunet_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/snunet/snunet_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/snunet/snunet_levircd.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train cd
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:snunet
null:null

@ -0,0 +1,8 @@
# Configurations of STANet with AirChange dataset
_base_: ../_base_/airchange.yaml
save_dir: ./test_tipc/output/cd/stanet/
model: !Node
type: STANet

@ -0,0 +1,8 @@
# Configurations of STANet with LEVIR-CD dataset
_base_: ../_base_/levircd.yaml
save_dir: ./test_tipc/output/cd/stanet/
model: !Node
type: STANet

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:cd:stanet
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=5|lite_train_whole_infer=5|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=8
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/cd/stanet/stanet_airchange.yaml|lite_train_whole_infer=./test_tipc/configs/cd/stanet/stanet_airchange.yaml|whole_train_whole_infer=./test_tipc/configs/cd/stanet/stanet_levircd.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train cd
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:stanet
null:null

@ -62,7 +62,7 @@ download_path: ./test_tipc/data/
num_epochs: 2
train_batch_size: 16
save_interval_epochs: 5
save_interval_epochs: 10
log_interval_steps: 50
save_dir: ./test_tipc/output/clas/
learning_rate: 0.01

@ -0,0 +1,10 @@
# Configurations of HRNet with UCMerced dataset
_base_: ../_base_/ucmerced.yaml
save_dir: ./test_tipc/output/clas/hrnet/
model: !Node
type: HRNet_W18_C
args:
num_classes: 21

@ -8,12 +8,12 @@ use_gpu:null|null
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=16|lite_train_whole_infer=16|whole_train_whole_infer=16
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/clas/hrnet/hrnet_ucmerced.yaml|lite_train_whole_infer=./test_tipc/configs/clas/hrnet/hrnet_ucmerced.yaml|whole_train_whole_infer=./test_tipc/configs/clas/hrnet/hrnet_ucmerced.yaml
train_model_name:best_model
train_infer_file_list:./test_tipc/data/ucmerced/:./test_tipc/data/ucmerced/val.txt
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train clas --config ./test_tipc/configs/clas/hrnet/hrnet.yaml
norm_train:test_tipc/run_task.py train clas
pact_train:null
fpgm_train:null
distill_train:null
@ -46,7 +46,7 @@ inference:test_tipc/infer.py
--use_trt:False
--precision:fp32
--model_dir:null
--file_list:null:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:hrnet

@ -0,0 +1,10 @@
# Configurations of MobileNetV3 with UCMerced dataset
_base_: ../_base_/ucmerced.yaml
save_dir: ./test_tipc/output/clas/mobilenetv3/
model: !Node
type: MobileNetV3_small_x1_0
args:
num_classes: 21

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:clas:mobilenetv3
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=3|lite_train_whole_infer=3|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=16|lite_train_whole_infer=16|whole_train_whole_infer=16
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/clas/mobilenetv3/mobilenetv3_ucmerced.yaml|lite_train_whole_infer=./test_tipc/configs/clas/mobilenetv3/mobilenetv3_ucmerced.yaml|whole_train_whole_infer=./test_tipc/configs/clas/mobilenetv3/mobilenetv3_ucmerced.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train clas
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:mobilenetv3
null:null

@ -0,0 +1,10 @@
# Configurations of ResNet50-vd with UCMerced dataset
_base_: ../_base_/ucmerced.yaml
save_dir: ./test_tipc/output/clas/resnet50_vd/
model: !Node
type: ResNet50_vd
args:
num_classes: 21

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:clas:resnet50_vd
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=3|lite_train_whole_infer=3|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=16|lite_train_whole_infer=16|whole_train_whole_infer=16
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/clas/resnet50_vd/resnet50_vd_ucmerced.yaml|lite_train_whole_infer=./test_tipc/configs/clas/resnet50_vd/resnet50_vd_ucmerced.yaml|whole_train_whole_infer=./test_tipc/configs/clas/resnet50_vd/resnet50_vd_ucmerced.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train clas
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,256,256]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:resnet50_vd
null:null

@ -0,0 +1,72 @@
# Basic configurations of RSOD dataset
datasets:
train: !Node
type: VOCDetDataset
args:
data_dir: ./test_tipc/data/rsod/
file_list: ./test_tipc/data/rsod/train.txt
label_list: ./test_tipc/data/rsod/labels.txt
shuffle: True
eval: !Node
type: VOCDetDataset
args:
data_dir: ./test_tipc/data/rsod/
file_list: ./test_tipc/data/rsod/val.txt
label_list: ./test_tipc/data/rsod/labels.txt
shuffle: False
transforms:
train:
- !Node
type: DecodeImg
- !Node
type: RandomDistort
- !Node
type: RandomExpand
- !Node
type: RandomCrop
- !Node
type: RandomHorizontalFlip
- !Node
type: BatchRandomResize
args:
target_sizes: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608]
interp: RANDOM
- !Node
type: Normalize
args:
mean: [0.485, 0.456, 0.406]
std: [0.229, 0.224, 0.225]
- !Node
type: ArrangeDetector
args: ['train']
eval:
- !Node
type: DecodeImg
- !Node
type: Resize
args:
target_size: 608
interp: CUBIC
- !Node
type: Normalize
args:
mean: [0.485, 0.456, 0.406]
std: [0.229, 0.224, 0.225]
- !Node
type: ArrangeDetector
args: ['eval']
download_on: False
num_epochs: 10
train_batch_size: 4
save_interval_epochs: 10
log_interval_steps: 4
save_dir: ./test_tipc/output/det/
learning_rate: 0.0001
use_vdl: False
resume_checkpoint: ''
train:
pretrain_weights: COCO
warmup_steps: 0
warmup_start_lr: 0.0

@ -62,10 +62,10 @@ download_path: ./test_tipc/data/
num_epochs: 10
train_batch_size: 4
save_interval_epochs: 5
save_interval_epochs: 10
log_interval_steps: 4
save_dir: ./test_tipc/output/det/
learning_rate: 0.0005
learning_rate: 0.0001
use_vdl: False
resume_checkpoint: ''
train:

@ -0,0 +1,10 @@
# Configurations of Faster R-CNN with RSOD dataset
_base_: ../_base_/rsod.yaml
save_dir: ./test_tipc/output/det/faster_rcnn/
model: !Node
type: FasterRCNN
args:
num_classes: 4

@ -0,0 +1,10 @@
# Configurations of Faster R-CNN with SARShip dataset
_base_: ../_base_/sarship.yaml
save_dir: ./test_tipc/output/det/faster_rcnn/
model: !Node
type: FasterRCNN
args:
num_classes: 1

@ -0,0 +1,53 @@
===========================train_params===========================
model_name:det:faster_rcnn
python:python
gpu_list:0|0,1
use_gpu:null|null
--precision:null
--num_epochs:lite_train_lite_infer=3|lite_train_whole_infer=3|whole_train_whole_infer=10
--save_dir:adaptive
--train_batch_size:lite_train_lite_infer=4|lite_train_whole_infer=4|whole_train_whole_infer=4
--model_path:null
--config:lite_train_lite_infer=./test_tipc/configs/det/faster_rcnn/faster_rcnn_sarship.yaml|lite_train_whole_infer=./test_tipc/configs/det/faster_rcnn/faster_rcnn_sarship.yaml|whole_train_whole_infer=./test_tipc/configs/det/faster_rcnn/faster_rcnn_rsod.yaml
train_model_name:best_model
null:null
##
trainer:norm
norm_train:test_tipc/run_task.py train det
pact_train:null
fpgm_train:null
distill_train:null
null:null
null:null
##
===========================eval_params===========================
eval:null
null:null
##
===========================export_params===========================
--save_dir:adaptive
--model_dir:adaptive
--fixed_input_shape:[-1,3,608,608]
norm_export:deploy/export/export_model.py
quant_export:null
fpgm_export:null
distill_export:null
export1:null
export2:null
===========================infer_params===========================
infer_model:null
infer_export:null
infer_quant:False
inference:test_tipc/infer.py
--device:cpu|gpu
--enable_mkldnn:True
--cpu_threads:6
--batch_size:1
--use_trt:False
--precision:fp32
--model_dir:null
--config:null
--save_log_path:null
--benchmark:True
--model_name:faster_rcnn
null:null

@ -0,0 +1,10 @@
# Configurations of PP-YOLO with RSOD dataset
_base_: ../_base_/rsod.yaml
save_dir: ./test_tipc/output/det/ppyolo/
model: !Node
type: PPYOLO
args:
num_classes: 4

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save