Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
PaddleX
提交
4c3da33a
P
PaddleX
项目概览
PaddlePaddle
/
PaddleX
通知
138
Star
4
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
43
列表
看板
标记
里程碑
合并请求
5
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
PaddleX
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
43
Issue
43
列表
看板
标记
里程碑
合并请求
5
合并请求
5
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
4c3da33a
编写于
5月 11, 2020
作者:
C
Channingss
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'paddle/develop' into cpp_trt
上级
5433fc46
2a9b2fcf
变更
36
显示空白变更内容
内联
并排
Showing
36 changed file
with
1038 addition
and
197 deletion
+1038
-197
deploy/README.md
deploy/README.md
+3
-0
docs/apis/datasets.md
docs/apis/datasets.md
+63
-4
docs/apis/images/insect_bbox_pr_curve(iou-0.5).png
docs/apis/images/insect_bbox_pr_curve(iou-0.5).png
+0
-0
docs/apis/images/xiaoduxiong_bbox_pr_curve(iou-0.5).png
docs/apis/images/xiaoduxiong_bbox_pr_curve(iou-0.5).png
+0
-0
docs/apis/images/xiaoduxiong_segm_pr_curve(iou-0.5).png
docs/apis/images/xiaoduxiong_segm_pr_curve(iou-0.5).png
+0
-0
docs/apis/load_model.md
docs/apis/load_model.md
+1
-1
docs/apis/models.md
docs/apis/models.md
+19
-7
docs/apis/transforms/cls_transforms.md
docs/apis/transforms/cls_transforms.md
+3
-1
docs/apis/transforms/det_transforms.md
docs/apis/transforms/det_transforms.md
+9
-3
docs/apis/transforms/seg_transforms.md
docs/apis/transforms/seg_transforms.md
+3
-1
docs/apis/visualize.md
docs/apis/visualize.md
+18
-32
docs/datasets.md
docs/datasets.md
+173
-10
docs/images/visualized_maskrcnn.jpeg
docs/images/visualized_maskrcnn.jpeg
+0
-0
docs/model_zoo.md
docs/model_zoo.md
+25
-22
paddlex/__init__.py
paddlex/__init__.py
+11
-0
paddlex/cv/datasets/__init__.py
paddlex/cv/datasets/__init__.py
+3
-0
paddlex/cv/datasets/coco.py
paddlex/cv/datasets/coco.py
+4
-3
paddlex/cv/datasets/easydata_cls.py
paddlex/cv/datasets/easydata_cls.py
+86
-0
paddlex/cv/datasets/easydata_det.py
paddlex/cv/datasets/easydata_det.py
+190
-0
paddlex/cv/datasets/easydata_seg.py
paddlex/cv/datasets/easydata_seg.py
+116
-0
paddlex/cv/datasets/imagenet.py
paddlex/cv/datasets/imagenet.py
+1
-1
paddlex/cv/datasets/seg_dataset.py
paddlex/cv/datasets/seg_dataset.py
+1
-1
paddlex/cv/datasets/voc.py
paddlex/cv/datasets/voc.py
+5
-4
paddlex/cv/models/base.py
paddlex/cv/models/base.py
+10
-1
paddlex/cv/models/classifier.py
paddlex/cv/models/classifier.py
+9
-2
paddlex/cv/models/deeplabv3p.py
paddlex/cv/models/deeplabv3p.py
+9
-2
paddlex/cv/models/faster_rcnn.py
paddlex/cv/models/faster_rcnn.py
+17
-9
paddlex/cv/models/mask_rcnn.py
paddlex/cv/models/mask_rcnn.py
+14
-6
paddlex/cv/models/slim/visualize.py
paddlex/cv/models/slim/visualize.py
+4
-3
paddlex/cv/models/unet.py
paddlex/cv/models/unet.py
+13
-6
paddlex/cv/models/utils/visualize.py
paddlex/cv/models/utils/visualize.py
+145
-30
paddlex/cv/models/yolo_v3.py
paddlex/cv/models/yolo_v3.py
+13
-5
paddlex/cv/transforms/det_transforms.py
paddlex/cv/transforms/det_transforms.py
+30
-40
paddlex/cv/transforms/seg_transforms.py
paddlex/cv/transforms/seg_transforms.py
+2
-2
paddlex/utils/utils.py
paddlex/utils/utils.py
+36
-0
setup.py
setup.py
+2
-1
未找到文件。
deploy/README.md
0 → 100644
浏览文件 @
4c3da33a
# 模型部署
本目录为PaddleX模型部署代码。
docs/apis/datasets.md
浏览文件 @
4c3da33a
...
...
@@ -16,7 +16,7 @@ paddlex.datasets.ImageNet(data_dir, file_list, label_list, transforms=None, num_
> * **transforms** (paddlex.cls.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.cls.transforms](./transforms/cls_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'
thread
'(Windows和Mac下会强制使用thread,该参数无效)。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'
process
'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## VOCDetection类
...
...
@@ -37,7 +37,7 @@ paddlex.datasets.VOCDetection(data_dir, file_list, label_list, transforms=None,
> * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'
thread
'(Windows和Mac下会强制使用thread,该参数无效)。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'
process
'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## CocoDetection类
...
...
@@ -57,7 +57,7 @@ paddlex.datasets.CocoDetection(data_dir, ann_file, transforms=None, num_workers=
> * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'
thread
'(Windows和Mac下会强制使用thread,该参数无效)。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'
process
'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## SegDataset类
...
...
@@ -78,5 +78,64 @@ paddlex.datasets.SegDataset(data_dir, file_list, label_list, transforms=None, nu
> * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.seg.transforms](./transforms/seg_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'thread'(Windows和Mac下会强制使用thread,该参数无效)。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## EasyDataCls类
```
paddlex.datasets.SegDataset(data_dir, file_list, label_list, transforms=None, num_workers='auto', buffer_size=100, parallel_method='thread', shuffle=False)
```
读取EasyData图像分类数据集,并对样本进行相应的处理。EasyData图像分类任务数据集格式的介绍可查看文档:
[
数据集格式说明
](
../datasets.md
)
### 参数
> * **data_dir** (str): 数据集所在的目录路径。
> * **file_list** (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对`data_dir`的相对路径)。
> * **label_list** (str): 描述数据集包含的类别信息文件路径。
> * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.cls.transforms](./transforms/cls_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## EasyDataDet类
```
paddlex.datasets.EasyDataDet(data_dir, file_list, label_list, transforms=None, num_workers=‘auto’, buffer_size=100, parallel_method='thread', shuffle=False)
```
读取EasyData目标检测格式数据集,并对样本进行相应的处理,该格式的数据集同样可以应用到实例分割模型的训练中。EasyData目标检测或实例分割任务数据集格式的介绍可查看文档:
[
数据集格式说明
](
../datasets.md
)
### 参数
> * **data_dir** (str): 数据集所在的目录路径。
> * **file_list** (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对`data_dir`的相对路径)。
> * **label_list** (str): 描述数据集包含的类别信息文件路径。
> * **transforms** (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.det.transforms](./transforms/det_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
## EasyDataSeg类
```
paddlex.datasets.EasyDataSeg(data_dir, file_list, label_list, transforms=None, num_workers='auto', buffer_size=100, parallel_method='thread', shuffle=False)
```
读取EasyData语分分割任务数据集,并对样本进行相应的处理。EasyData语义分割任务数据集格式的介绍可查看文档:
[
数据集格式说明
](
../datasets.md
)
### 参数
> * **data_dir** (str): 数据集所在的目录路径。
> * **file_list** (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对`data_dir`的相对路径)。
> * **label_list** (str): 描述数据集包含的类别信息文件路径。
> * **transforms** (paddlex.seg.transforms): 数据集中每个样本的预处理/增强算子,详见[paddlex.seg.transforms](./transforms/seg_transforms.md)。
> * **num_workers** (int|str):数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
> * **buffer_size** (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
> * **parallel_method** (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
> * **shuffle** (bool): 是否需要对数据集中样本打乱顺序。默认为False。
\ No newline at end of file
docs/apis/images/insect_bbox_pr_curve(iou-0.5).png
查看替换文件 @
5433fc46
浏览文件 @
4c3da33a
711.2 KB
|
W:
|
H:
708.9 KB
|
W:
|
H:
2-up
Swipe
Onion skin
docs/apis/images/xiaoduxiong_bbox_pr_curve(iou-0.5).png
已删除
100644 → 0
浏览文件 @
5433fc46
315.9 KB
docs/apis/images/xiaoduxiong_segm_pr_curve(iou-0.5).png
已删除
100644 → 0
浏览文件 @
5433fc46
316.4 KB
docs/apis/load_model.md
浏览文件 @
4c3da33a
...
...
@@ -34,7 +34,7 @@ pred_result = model.predict('./xiaoduxiong_ins_det/JPEGImages/WechatIMG114.jpeg'
#
在验证集上进行评估
eval_reader
=
pdx
.
cv
.
datasets
.
CocoDetection
(
data_dir
=
data_dir
,
ann_file
=
ann_file
ann_file
=
ann_file
,
transforms
=
model
.
eval_transforms
)
eval_result
=
model
.
evaluate
(
eval_reader
,
batch_size
=
1
)
```
docs/apis/models.md
浏览文件 @
4c3da33a
...
...
@@ -17,7 +17,7 @@ paddlex.cls.ResNet50(num_classes=1000)
#### 分类器训练函数接口
> ```python
> train(self, num_epochs, train_dataset, train_batch_size=64, eval_dataset=None, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.025, lr_decay_epochs=[30, 60, 90], lr_decay_gamma=0.1, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05)
> train(self, num_epochs, train_dataset, train_batch_size=64, eval_dataset=None, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.025, lr_decay_epochs=[30, 60, 90], lr_decay_gamma=0.1, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05
, early_stop=False, early_stop_patience=5
)
> ```
>
> **参数:**
...
...
@@ -37,6 +37,8 @@ paddlex.cls.ResNet50(num_classes=1000)
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。
> > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
> > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### 分类器评估函数接口
...
...
@@ -75,7 +77,7 @@ paddlex.cls.ResNet50(num_classes=1000)
### 其它分类器类
除
`ResNet50`
外,
`paddlex.cls`
下还提供了
`ResNet18`
、
`ResNet34`
、
`ResNet101`
、
`ResNet50_vd`
、
`ResNet101_vd`
、
`
DarkNet53`
、
`MobileNetV1`
、
`MobileNetV2`
、
`MobileNetV3_small`
、
`MobileNetV3_large
`
、
`Xception41`
、
`Xception65`
、
`Xception71`
、
`ShuffleNetV2`
, 使用方式(包括函数接口和参数)均与
`ResNet50`
一致,各模型效果可参考
[
模型库
](
../model_zoo.md
)
中列表。
除
`ResNet50`
外,
`paddlex.cls`
下还提供了
`ResNet18`
、
`ResNet34`
、
`ResNet101`
、
`ResNet50_vd`
、
`ResNet101_vd`
、
`
ResNet50_vd_ssld`
、
`ResNet101_vd_ssld`
、
`DarkNet53`
、
`MobileNetV1`
、
`MobileNetV2`
、
`MobileNetV3_small`
、
`MobileNetV3_large`
、
`MobileNetV3_small_ssld`
、
`MobileNetV3_large_ssld
`
、
`Xception41`
、
`Xception65`
、
`Xception71`
、
`ShuffleNetV2`
, 使用方式(包括函数接口和参数)均与
`ResNet50`
一致,各模型效果可参考
[
模型库
](
../model_zoo.md
)
中列表。
...
...
@@ -109,7 +111,7 @@ paddlex.det.YOLOv3(num_classes=80, backbone='MobileNetV1', anchors=None, anchor_
#### YOLOv3训练函数接口
> ```python
> train(self, num_epochs, train_dataset, train_batch_size=8, eval_dataset=None, save_interval_epochs=20, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=1.0/8000, warmup_steps=1000, warmup_start_lr=0.0, lr_decay_epochs=[213, 240], lr_decay_gamma=0.1, metric=None, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05)
> train(self, num_epochs, train_dataset, train_batch_size=8, eval_dataset=None, save_interval_epochs=20, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=1.0/8000, warmup_steps=1000, warmup_start_lr=0.0, lr_decay_epochs=[213, 240], lr_decay_gamma=0.1, metric=None, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05
, early_stop=False, early_stop_patience=5
)
> ```
>
> **参数:**
...
...
@@ -132,6 +134,8 @@ paddlex.det.YOLOv3(num_classes=80, backbone='MobileNetV1', anchors=None, anchor_
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。
> > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在PascalVOC数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
> > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### YOLOv3评估函数接口
...
...
@@ -186,7 +190,7 @@ paddlex.det.FasterRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspec
#### FasterRCNN训练函数接口
> ```python
> train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, save_interval_epochs=1, log_interval_steps=2,save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.0025, warmup_steps=500, warmup_start_lr=1.0/1200, lr_decay_epochs=[8, 11], lr_decay_gamma=0.1, metric=None, use_vdl=False)
> train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, save_interval_epochs=1, log_interval_steps=2,save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.0025, warmup_steps=500, warmup_start_lr=1.0/1200, lr_decay_epochs=[8, 11], lr_decay_gamma=0.1, metric=None, use_vdl=False
, early_stop=False, early_stop_patience=5
)
>
> ```
>
...
...
@@ -208,6 +212,8 @@ paddlex.det.FasterRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspec
> > - **lr_decay_gamma** (float): 默认优化器的学习率衰减率。默认为0.1。
> > - **metric** (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。默认值为None。
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### FasterRCNN评估函数接口
...
...
@@ -264,7 +270,7 @@ paddlex.det.MaskRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspect_
#### MaskRCNN训练函数接口
> ```python
> train(self, num_epochs, train_dataset, train_batch_size=1, eval_dataset=None, save_interval_epochs=1, log_interval_steps=20, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=1.0/800, warmup_steps=500, warmup_start_lr=1.0 / 2400, lr_decay_epochs=[8, 11], lr_decay_gamma=0.1, metric=None, use_vdl=False)
> train(self, num_epochs, train_dataset, train_batch_size=1, eval_dataset=None, save_interval_epochs=1, log_interval_steps=20, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=1.0/800, warmup_steps=500, warmup_start_lr=1.0 / 2400, lr_decay_epochs=[8, 11], lr_decay_gamma=0.1, metric=None, use_vdl=False
, early_stop=False, early_stop_patience=5
)
>
> ```
>
...
...
@@ -286,6 +292,8 @@ paddlex.det.MaskRCNN(num_classes=81, backbone='ResNet50', with_fpn=True, aspect_
> > - **lr_decay_gamma** (float): 默认优化器的学习率衰减率。默认为0.1。
> > - **metric** (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。默认值为None。
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认值为False。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### MaskRCNN评估函数接口
...
...
@@ -350,7 +358,7 @@ paddlex.seg.DeepLabv3p(num_classes=2, backbone='MobileNetV2_x1.0', output_stride
#### DeepLabv3训练函数接口
> ```python
> train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, eval_batch_size=1, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.01, lr_decay_power=0.9, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05):
> train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, eval_batch_size=1, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='IMAGENET', optimizer=None, learning_rate=0.01, lr_decay_power=0.9, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05
, early_stop=False, early_stop_patience=5
):
>
> ```
>
...
...
@@ -370,6 +378,8 @@ paddlex.seg.DeepLabv3p(num_classes=2, backbone='MobileNetV2_x1.0', output_stride
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认False。
> > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
> > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### DeepLabv3评估函数接口
...
...
@@ -427,7 +437,7 @@ paddlex.seg.UNet(num_classes=2, upsample_mode='bilinear', use_bce_loss=False, us
#### Unet训练函数接口
> ```python
> train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, eval_batch_size=1, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='COCO', optimizer=None, learning_rate=0.01, lr_decay_power=0.9, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05):
> train(self, num_epochs, train_dataset, train_batch_size=2, eval_dataset=None, eval_batch_size=1, save_interval_epochs=1, log_interval_steps=2, save_dir='output', pretrain_weights='COCO', optimizer=None, learning_rate=0.01, lr_decay_power=0.9, use_vdl=False, sensitivities_file=None, eval_metric_loss=0.05
, early_stop=False, early_stop_patience=5
):
> ```
>
> **参数:**
...
...
@@ -446,6 +456,8 @@ paddlex.seg.UNet(num_classes=2, upsample_mode='bilinear', use_bce_loss=False, us
> > - **use_vdl** (bool): 是否使用VisualDL进行可视化。默认False。
> > - **sensitivities_file** (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
> > - **eval_metric_loss** (float): 可容忍的精度损失。默认为0.05。
> > - **early_stop** (float): 是否使用提前终止训练策略。默认值为False。
> > - **early_stop_patience** (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内连续下降或持平,则终止训练。默认值为5。
#### Unet评估函数接口
...
...
docs/apis/transforms/cls_transforms.md
浏览文件 @
4c3da33a
...
...
@@ -111,6 +111,8 @@ paddlex.cls.transforms.RandomDistort(brightness_range=0.9, brightness_prob=0.5,
1.
对变换的操作顺序进行随机化操作。
2.
按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。
【注意】该数据增强必须在数据增强Normalize之前使用。
### 参数
*
**brightness_range**
(float): 明亮度因子的范围。默认为0.9。
*
**brightness_prob**
(float): 随机调整明亮度的概率。默认为0.5。
...
...
docs/apis/transforms/det_transforms.md
浏览文件 @
4c3da33a
...
...
@@ -87,6 +87,8 @@ paddlex.det.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5,
1.
对变换的操作顺序进行随机化操作。
2.
按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。
【注意】该数据增强必须在数据增强Normalize之前使用。
### 参数
*
**brightness_range**
(float): 明亮度因子的范围。默认为0.5。
*
**brightness_prob**
(float): 随机调整明亮度的概率。默认为0.5。
...
...
@@ -137,6 +139,8 @@ paddlex.det.transforms.RandomExpand(ratio=4., prob=0.5, fill_value=[123.675, 116
*
**prob**
(float): 随机扩张的概率。默认为0.5。
*
**fill_value**
(list): 扩张图像的初始填充值(0-255)。默认为[123.675, 116.28, 103.53]。
【注意】该数据增强必须在数据增强Resize、ResizeByShort之前使用。
## RandomCrop类
```
python
paddlex
.
det
.
transforms
.
RandomCrop
(
aspect_ratio
=
[.
5
,
2.
],
thresholds
=
[.
0
,
.
1
,
.
3
,
.
5
,
.
7
,
.
9
],
scaling
=
[.
3
,
1.
],
num_attempts
=
50
,
allow_no_crop
=
True
,
cover_all_box
=
False
)
...
...
@@ -154,6 +158,8 @@ paddlex.det.transforms.RandomCrop(aspect_ratio=[.5, 2.], thresholds=[.0, .1, .3,
4.
换算有效真值标注框相对候选裁剪区域的位置坐标。
5.
换算有效分割区域相对候选裁剪区域的位置坐标。
【注意】该数据增强必须在数据增强Resize、ResizeByShort之前使用。
### 参数
*
**aspect_ratio**
(list): 裁剪后短边缩放比例的取值范围,以[min, max]形式表示。默认值为[.5, 2.]。
*
**thresholds**
(list): 判断裁剪候选区域是否有效所需的IoU阈值取值列表。默认值为[.0, .1, .3, .5, .7, .9]。
...
...
docs/apis/transforms/seg_transforms.md
浏览文件 @
4c3da33a
...
...
@@ -155,6 +155,8 @@ paddlex.seg.transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5,
1.
对变换的操作顺序进行随机化操作。
2.
按照1中的顺序以一定的概率对图像在范围[-range, range]内进行随机像素内容变换。
【注意】该数据增强必须在数据增强Normalize之前使用。
### 参数
*
**brightness_range**
(float): 明亮度因子的范围。默认为0.5。
*
**brightness_prob**
(float): 随机调整明亮度的概率。默认为0.5。
...
...
docs/apis/visualize.md
浏览文件 @
4c3da33a
...
...
@@ -40,8 +40,16 @@ paddlex.det.draw_pr_curve(eval_details_file=None, gt=None, pred_bbox=None, pred_
**注意:**
`eval_details_file`
的优先级更高,只要
`eval_details_file`
不为None,就会从
`eval_details_file`
提取真值信息和预测结果做分析。当
`eval_details_file`
为None时,则用
`gt`
、
`pred_mask`
、
`pred_mask`
做分析。
### 使用示例
> 示例一:
点击下载如下示例中的
[
模型
](
https://bj.bcebos.com/paddlex/models/xiaoduxiong_epoch_12.tar.gz
)
和
[
数据集
](
https://bj.bcebos.com/paddlex/datasets/xiaoduxiong_ins_det.tar.gz
)
点击下载如下示例中的
[
模型
](
https://bj.bcebos.com/paddlex/models/insect_epoch_270.zip
)
和
[
数据集
](
https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz
)
> 方式一:分析训练过程中保存的模型文件夹中的评估结果文件`eval_details.json`,例如[模型](https://bj.bcebos.com/paddlex/models/insect_epoch_270.zip)中的`eval_details.json`。
```
import paddlex as pdx
eval_details_file = 'insect_epoch_270/eval_details.json'
pdx.det.draw_pr_curve(eval_details_file, save_dir='./insect')
```
> 方式二:分析模型评估函数返回的评估结果。
```
import
os
#
选择使用
0
号卡
...
...
@@ -50,40 +58,18 @@ os.environ['CUDA_VISIBLE_DEVICES'] = '0'
from
paddlex
.
det
import
transforms
import
paddlex
as
pdx
eval_transforms
=
transforms
.
Compose
([
transforms
.
Normalize
(),
transforms
.
ResizeByShort
(
short_size
=
800
,
max_size
=
1333
),
transforms
.
Padding
(
coarsest_stride
=
32
)
])
eval_dataset
=
pdx
.
datasets
.
CocoDetection
(
data_dir
=
'xiaoduxiong_ins_det/JPEGImages'
,
ann_file
=
'xiaoduxiong_ins_det/val.json'
,
transforms
=
eval_transforms
)
model
=
pdx
.
load_model
(
'xiaoduxiong_epoch_12'
)
metrics
,
evaluate_details
=
model
.
evaluate
(
eval_dataset
,
batch_size
=
1
,
return_details
=
True
)
model
=
pdx
.
load_model
(
'insect_epoch_270'
)
eval_dataset
=
pdx
.
datasets
.
VOCDetection
(
data_dir
=
'insect_det'
,
file_list
=
'insect_det/val_list.txt'
,
label_list
=
'insect_det/labels.txt'
,
transforms
=
model
.
eval_transforms
)
metrics
,
evaluate_details
=
model
.
evaluate
(
eval_dataset
,
batch_size
=
8
,
return_details
=
True
)
gt
=
evaluate_details
[
'gt'
]
bbox
=
evaluate_details
[
'bbox'
]
mask
=
evaluate_details
[
'mask'
]
#
分别可视化
bbox
和
mask
的准召曲线
pdx
.
det
.
draw_pr_curve
(
gt
=
gt
,
pred_bbox
=
bbox
,
pred_mask
=
mask
,
save_dir
=
'./xiaoduxiong'
)
pdx
.
det
.
draw_pr_curve
(
gt
=
gt
,
pred_bbox
=
bbox
,
save_dir
=
'./insect'
)
```
预测框的各个类别的准确率和召回率的对应关系、召回率和置信度阈值的对应关系可视化如下:

.png)
预测mask的各个类别的准确率和召回率的对应关系、召回率和置信度阈值的对应关系可视化如下:

.png)
> 示例二:
使用
[
yolov3_darknet53.py示例代码
](
https://github.com/PaddlePaddle/PaddleX/blob/develop/tutorials/train/detection/yolov3_darknet53.py
)
训练完成后,加载模型评估结果文件进行分析:
```
import paddlex as pdx
eval_details_file = 'output/yolov3_darknet53/best_model/eval_details.json'
pdx.det.draw_pr_curve(eval_details_file, save_dir='./insect')
```
预测框的各个类别的准确率和召回率的对应关系、召回率和置信度阈值的对应关系可视化如下:

.png)
...
...
docs/datasets.md
浏览文件 @
4c3da33a
...
...
@@ -41,8 +41,8 @@ labelA
labelB
...
```
[
点击这里
](
https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz
)
,下载蔬菜分类分类数据集
在PaddleX中,使用
`paddlex.cv.datasets.ImageNet`
(
[
API说明
](
./apis/datasets.html#imagenet
)
)加载分类数据集
[
点击这里
](
https://bj.bcebos.com/paddlex/datasets/vegetables_cls.tar.gz
)
,下载蔬菜分类分类数据集
。
在PaddleX中,使用
`paddlex.cv.datasets.ImageNet`
(
[
API说明
](
./apis/datasets.html#imagenet
)
)加载分类数据集
。
## 目标检测VOC
目标检测VOC数据集包含图像文件夹、标注信息文件夹、标签文件及图像列表文件。
...
...
@@ -81,8 +81,8 @@ labelA
labelB
...
```
[
点击这里
](
https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz
)
,下载昆虫检测数据集
在PaddleX中,使用
`paddlex.cv.datasets.VOCDetection`
(
[
API说明
](
./apis/datasets.html#vocdetection
)
)加载目标检测VOC数据集
[
点击这里
](
https://bj.bcebos.com/paddlex/datasets/insect_det.tar.gz
)
,下载昆虫检测数据集
。
在PaddleX中,使用
`paddlex.cv.datasets.VOCDetection`
(
[
API说明
](
./apis/datasets.html#vocdetection
)
)加载目标检测VOC数据集
。
## 目标检测和实例分割COCO
目标检测和实例分割COCO数据集包含图像文件夹及图像标注信息文件。
...
...
@@ -135,7 +135,7 @@ labelB
]
}
```
每个字段的含义如下所示:
其中,
每个字段的含义如下所示:
| 域名 | 字段名 | 含义 | 数据类型 | 备注 |
|:-----|:--------|:------------|------|:-----|
...
...
@@ -155,8 +155,8 @@ labelB
| categories | supercategory | 类别父类的标签名 | str | |
[
点击这里
](
https://bj.bcebos.com/paddlex/datasets/garbage_ins_det.tar.gz
)
,下载垃圾实例分割数据集
在PaddleX中,使用
`paddlex.cv.datasets.COCODetection`
(
[
API说明
](
./apis/datasets.html#cocodetection
)
)加载COCO格式数据集
[
点击这里
](
https://bj.bcebos.com/paddlex/datasets/garbage_ins_det.tar.gz
)
,下载垃圾实例分割数据集
。
在PaddleX中,使用
`paddlex.cv.datasets.COCODetection`
(
[
API说明
](
./apis/datasets.html#cocodetection
)
)加载COCO格式数据集
。
## 语义分割数据
语义分割数据集包含原图、标注图及相应的文件列表文件。
...
...
@@ -191,13 +191,176 @@ images/xxx2.png annotations/xxx2.png
`labels.txt`
: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
```
background
labelA
labelB
...
```
标注图像为单通道图像,像素值即为对应的类别,像素标注类别需要从0开始递增,
标注图像为单通道图像,像素值即为对应的类别,像素标注类别需要从0开始递增
(一般第一个类别为
`background`
)
,
例如0,1,2,3表示有4种类别,标注类别最多为256类。其中可以指定特定的像素值用于表示该值的像素不参与训练和评估(默认为255)。
[
点击这里
](
https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz
)
,下载视盘语义分割数据集
在PaddleX中,使用
`paddlex.cv.datasets.SegReader`
(
[
API说明
](
./apis/datasets.html#segreader
)
)加载语义分割数据集
[
点击这里
](
https://bj.bcebos.com/paddlex/datasets/optic_disc_seg.tar.gz
)
,下载视盘语义分割数据集。
在PaddleX中,使用
`paddlex.cv.datasets.SegReader`
(
[
API说明
](
./apis/datasets.html#segreader
)
)加载语义分割数据集。
## 图像分类EasyDataCls
图像分类EasyDataCls数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
参考数据文件结构如下:
```
./dataset/ # 数据集根目录
|--easydata # 存放图像和json文件的文件夹
| |--0001.jpg
| |--0001.json
| |--0002.jpg
| |--0002.json
| └--...
|
|--train_list.txt # 训练文件列表文件
|
|--val_list.txt # 验证文件列表文件
|
└--labels.txt # 标签列表文件
```
其中,图像文件名应与json文件名一一对应。
每个json文件存储于
`labels`
相关的信息。如下所示:
```
{"labels": [{"name": "labelA"}]}
```
其中,
`name`
字段代表对应图像的类别。
`train_list.txt`
和
`val_list.txt`
文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
```
easydata/0001.jpg easydata/0001.json
easydata/0002.jpg easydata/0002.json
...
```
`labels.txt`
: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
```
labelA
labelB
...
```
[
点击这里
](
https://ai.baidu.com/easydata/
)
,可以标注图像分类EasyDataCls数据集。
在PaddleX中,使用
`paddlex.cv.datasets.EasyDataCls`
(
[
API说明
](
./apis/datasets.html#easydatacls
)
)加载分类数据集。
## 目标检测和实例分割EasyDataDet
目标检测和实例分割EasyDataDet数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
参考数据文件结构如下:
```
./dataset/ # 数据集根目录ß
|--easydata # 存放图像和json文件的文件夹
| |--0001.jpg
| |--0001.json
| |--0002.jpg
| |--0002.json
| └--...
|
|--train_list.txt # 训练文件列表文件
|
|--val_list.txt # 验证文件列表文件
|
└--labels.txt # 标签列表文件
```
其中,图像文件名应与json文件名一一对应。
每个json文件存储于
`labels`
相关的信息。如下所示:
```
"labels": [{"y1": 18, "x2": 883, "x1": 371, "y2": 404, "name": "labelA",
"mask": "kVfc0`0Zg0<F7J7I5L5K4L4L4L3N3L3N3L3N2N3M2N2N2N2N2N2N1O2N2O1N2N1O2O1N101N1O2O1N101N10001N101N10001N10001O0O10001O000O100000001O0000000000000000000000O1000001O00000O101O000O101O0O101O0O2O0O101O0O2O0O2N2O0O2O0O2N2O1N1O2N2N2O1N2N2N2N2N2N2M3N3M2M4M2M4M3L4L4L4K6K5J7H9E\\iY1"},
{"y1": 314, "x2": 666, "x1": 227, "y2": 676, "name": "labelB",
"mask": "mdQ8g0Tg0:G8I6K5J5L4L4L4L4M2M4M2M4M2N2N2N3L3N2N2N2N2O1N1O2N2N2O1N1O2N2O0O2O1N1O2O0O2O0O2O001N100O2O000O2O000O2O00000O2O000000001N100000000000000000000000000000000001O0O100000001O0O10001N10001O0O101N10001N101N101N101N101N2O0O2N2O0O2N2N2O0O2N2N2N2N2N2N2N2N2N3L3N2N3L3N3L4M2M4L4L5J5L5J7H8H;BUcd<"},
...]}
```
其中,list中的每个元素代表一个标注信息,标注信息中字段的含义如下所示:
| 字段名 | 含义 | 数据类型 | 备注 |
|:--------|:------------|------|:-----|
| x1 | 标注框左下角横坐标 | int | |
| y1 | 标注框左下角纵坐标 | int | |
| x2 | 标注框右上角横坐标 | int | |
| y2 | 标注框右上角纵坐标 | int | |
| name | 标注框中物体类标 | str | |
| mask | 分割区域布尔型numpy编码后的字符串 | str | 该字段可以不存在,当不存在时只能进行目标检测 |
`train_list.txt`
和
`val_list.txt`
文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
```
easydata/0001.jpg easydata/0001.json
easydata/0002.jpg easydata/0002.json
...
```
`labels.txt`
: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
```
labelA
labelB
...
```
[
点击这里
](
https://ai.baidu.com/easydata/
)
,可以标注图像分类EasyDataDet数据集。
在PaddleX中,使用
`paddlex.cv.datasets.EasyDataDet`
(
[
API说明
](
./apis/datasets.html#easydatadet
)
)加载分类数据集。
## 语义分割EasyDataSeg
语义分割EasyDataSeg数据集包含存放图像和json文件的文件夹、标签文件及图像列表文件。
参考数据文件结构如下:
```
./dataset/ # 数据集根目录ß
|--easydata # 存放图像和json文件的文件夹
| |--0001.jpg
| |--0001.json
| |--0002.jpg
| |--0002.json
| └--...
|
|--train_list.txt # 训练文件列表文件
|
|--val_list.txt # 验证文件列表文件
|
└--labels.txt # 标签列表文件
```
其中,图像文件名应与json文件名一一对应。
每个json文件存储于
`labels`
相关的信息。如下所示:
```
"labels": [{"y1": 18, "x2": 883, "x1": 371, "y2": 404, "name": "labelA",
"mask": "kVfc0`0Zg0<F7J7I5L5K4L4L4L3N3L3N3L3N2N3M2N2N2N2N2N2N1O2N2O1N2N1O2O1N101N1O2O1N101N10001N101N10001N10001O0O10001O000O100000001O0000000000000000000000O1000001O00000O101O000O101O0O101O0O2O0O101O0O2O0O2N2O0O2O0O2N2O1N1O2N2N2O1N2N2N2N2N2N2M3N3M2M4M2M4M3L4L4L4K6K5J7H9E\\iY1"},
{"y1": 314, "x2": 666, "x1": 227, "y2": 676, "name": "labelB",
"mask": "mdQ8g0Tg0:G8I6K5J5L4L4L4L4M2M4M2M4M2N2N2N3L3N2N2N2N2O1N1O2N2N2O1N1O2N2O0O2O1N1O2O0O2O0O2O001N100O2O000O2O000O2O00000O2O000000001N100000000000000000000000000000000001O0O100000001O0O10001N10001O0O101N10001N101N101N101N101N2O0O2N2O0O2N2N2O0O2N2N2N2N2N2N2N2N2N3L3N2N3L3N3L4M2M4L4L5J5L5J7H8H;BUcd<"},
...]}
```
其中,list中的每个元素代表一个标注信息,标注信息中字段的含义如下所示:
| 字段名 | 含义 | 数据类型 | 备注 |
|:--------|:------------|------|:-----|
| x1 | 标注框左下角横坐标 | int | |
| y1 | 标注框左下角纵坐标 | int | |
| x2 | 标注框右上角横坐标 | int | |
| y2 | 标注框右上角纵坐标 | int | |
| name | 标注框中物体类标 | str | |
| mask | 分割区域布尔型numpy编码后的字符串 | str | 该字段必须存在 |
`train_list.txt`
和
`val_list.txt`
文本以空格为分割符分为两列,第一列为图像文件相对于dataset的相对路径,第二列为json文件相对于dataset的相对路径。如下所示:
```
easydata/0001.jpg easydata/0001.json
easydata/0002.jpg easydata/0002.json
...
```
`labels.txt`
: 每一行为一个单独的类别,相应的行号即为类别对应的id(行号从0开始),如下所示:
```
labelA
labelB
...
```
[
点击这里
](
https://ai.baidu.com/easydata/
)
,可以标注图像分类EasyDataSeg数据集。
在PaddleX中,使用
`paddlex.cv.datasets.EasyDataSeg`
(
[
API说明
](
./apis/datasets.html#easydataseg
)
)加载分类数据集。
\ No newline at end of file
docs/images/visualized_maskrcnn.jpeg
0 → 100644
浏览文件 @
4c3da33a
188.2 KB
docs/model_zoo.md
浏览文件 @
4c3da33a
...
...
@@ -4,35 +4,38 @@
表中相关模型也可下载好作为相应模型的预训练模型,通过
`pretrain_weights`
指定目录加载使用。
## 图像分类模型
> 表中模型相关指标均为在ImageNet数据集上使用PaddlePaddle Python预测接口测试得到(测试GPU型号为Nvidia Tesla P4),预测速度为每张图片预测用时(不包括预处理和后处理),表中符号`-`表示相关指标暂未测试。
> 表中模型相关指标均为在ImageNet数据集上使用PaddlePaddle Python预测接口测试得到(测试GPU型号为Nvidia Tesla P4
0
),预测速度为每张图片预测用时(不包括预处理和后处理),表中符号`-`表示相关指标暂未测试。
| 模型 | 模型大小 | 预测速度(毫秒) | Top1准确率
| Top5准确率
|
| 模型 | 模型大小 | 预测速度(毫秒) | Top1准确率
(%) | Top5准确率(%)
|
| :----| :------- | :----------- | :--------- | :--------- |
| ResNet18| 46.9MB | 3.456 | 70.98% | 89.92% |
| ResNet34| 87.5MB | 5.668 | 74.57% | 92.14% |
| ResNet50| 102.7MB | 8.787 | 76.50% | 93.00% |
| ResNet101 |179.1MB | 15.447 | 77.56% | 93.64% |
| ResNet50_vd |102.8MB | 9.058 | 79.12% | 94.44% |
| ResNet101_vd| 179.2MB | 15.685 | 80.17% | 94.97% |
| DarkNet53|166.9MB | 11.969 | 78.04% | 94.05% |
| MobileNetV1 | 16.4MB | 2.609 | 70.99% | 89.68% |
| MobileNetV2 | 14.4MB | 4.546 | 72.15% | 90.65% |
| MobileNetV3_large| 22.8MB | - | 75.3% | 75.3% |
| MobileNetV3_small | 12.5MB | 6.809 | 67.46% | 87.12% |
| Xception41 |92.4MB | 13.757 | 79.30% | 94.53% |
| Xception65 | 144.6MB | 19.216 | 81.00% | 95.49% |
| Xception71| 151.9MB | 23.291 | 81.11% | 95.45% |
| DenseNet121 | 32.8MB | 12.437 | 75.66% | 92.58% |
| DenseNet161|116.3MB | 27.717 | 78.57% | 94.14% |
| DenseNet201| 84.6MB | 26.583 | 77.63% | 93.66% |
| ShuffleNetV2 | 10.2MB | 6.101 | 68.8% | 88.5% |
| ResNet18| 46.9MB | 1.499 | 71.0 | 89.9 |
| ResNet34| 87.5MB | 2.272 | 74.6 | 92.1 |
| ResNet50| 102.7MB | 2.939 | 76.5 | 93.0 |
| ResNet101 |179.1MB | 5.314 | 77.6 | 93.6 |
| ResNet50_vd |102.8MB | 3.165 | 79.1 | 94.4 |
| ResNet101_vd| 179.2MB | 5.252 | 80.2 | 95.0 |
| ResNet50_vd_ssld |102.8MB | 3.165 | 82.4 | 96.1 |
| ResNet101_vd_ssld| 179.2MB | 5.252 | 83.7 | 96.7 |
| DarkNet53|166.9MB | 3.139 | 78.0 | 94.1 |
| MobileNetV1 | 16.0MB | 32.523 | 71.0 | 89.7 |
| MobileNetV2 | 14.0MB | 23.318 | 72.2 | 90.7 |
| MobileNetV3_large| 21.0MB | 19.308 | 75.3 | 93.2 |
| MobileNetV3_small | 12.0MB | 6.546 | 68.2 | 88.1 |
| MobileNetV3_large_ssld| 21.0MB | 19.308 | 79.0 | 94.5 |
| MobileNetV3_small_ssld | 12.0MB | 6.546 | 71.3 | 90.1 |
| Xception41 |92.4MB | 4.408 | 79.6 | 94.4 |
| Xception65 | 144.6MB | 6.464 | 80.3 | 94.5 |
| DenseNet121 | 32.8MB | 4.371 | 75.7 | 92.6 |
| DenseNet161|116.3MB | 8.863 | 78.6 | 94.1 |
| DenseNet201| 84.6MB | 8.173 | 77.6 | 93.7 |
| ShuffleNetV2 | 9.0MB | 10.941 | 68.8 | 88.5 |
## 目标检测模型
> 表中模型相关指标均为在MSCOCO数据集上使用PaddlePaddle Python预测接口测试得到(测试GPU型号为Nvidia Tesla V100测试得到,表中符号`-`表示相关指标暂未测试。
| 模型 | 模型大小 | 预测时间(毫秒) | BoxAP |
| 模型 | 模型大小 | 预测时间(毫秒) | BoxAP
(%)
|
|:-------|:-----------|:-------------|:----------|
|FasterRCNN-ResNet50|135.6MB| 78.450 | 35.2 |
|FasterRCNN-ResNet50_vd| 135.7MB | 79.523 | 36.4 |
...
...
@@ -50,7 +53,7 @@
> 表中模型相关指标均为在MSCOCO数据集上测试得到。
| 模型 |模型大小 | 预测时间(毫秒) | BoxAP | SegAP |
| 模型 |模型大小 | 预测时间(毫秒) | BoxAP | SegAP
(%)
|
|:---------|:---------|:----------|:---------|:--------|
|MaskRCNN-ResNet50|51.2MB| 86.096 | 36.5 |32.2|
|MaskRCNN-ResNet50-FPN|184.6MB | 65.859 | 37.9 |34.2|
...
...
paddlex/__init__.py
浏览文件 @
4c3da33a
...
...
@@ -20,6 +20,17 @@ from . import seg
from
.
import
cls
from
.
import
slim
try
:
import
pycocotools
except
:
print
(
"[WARNING] pycocotools is not installed, detection model is not available now."
)
print
(
"[WARNING] pycocotools install: https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/install.md"
)
import
paddlehub
as
hub
if
hub
.
version
.
hub_version
<
'1.6.2'
:
raise
Exception
(
"[ERROR] paddlehub >= 1.6.2 is required"
)
env_info
=
get_environ_info
()
load_model
=
cv
.
models
.
load_model
datasets
=
cv
.
datasets
...
...
paddlex/cv/datasets/__init__.py
浏览文件 @
4c3da33a
...
...
@@ -16,3 +16,6 @@ from .imagenet import ImageNet
from
.voc
import
VOCDetection
from
.coco
import
CocoDetection
from
.seg_dataset
import
SegDataset
from
.easydata_cls
import
EasyDataCls
from
.easydata_det
import
EasyDataDet
from
.easydata_seg
import
EasyDataSeg
\ No newline at end of file
paddlex/cv/datasets/coco.py
浏览文件 @
4c3da33a
...
...
@@ -19,7 +19,6 @@ import random
import
numpy
as
np
import
paddlex.utils.logging
as
logging
import
paddlex
as
pst
from
pycocotools.coco
import
COCO
from
.voc
import
VOCDetection
from
.dataset
import
is_pic
...
...
@@ -35,7 +34,7 @@ class CocoDetection(VOCDetection):
系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的一半。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'
thread
'(Windows和Mac下会强制使用thread,该参数无效)。
线程和'process'进程两种方式。默认为'
process
'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
"""
...
...
@@ -47,6 +46,8 @@ class CocoDetection(VOCDetection):
buffer_size
=
100
,
parallel_method
=
'process'
,
shuffle
=
False
):
from
pycocotools.coco
import
COCO
super
(
VOCDetection
,
self
).
__init__
(
transforms
=
transforms
,
num_workers
=
num_workers
,
...
...
@@ -111,7 +112,7 @@ class CocoDetection(VOCDetection):
im_info
=
{
'im_id'
:
np
.
array
([
img_id
]).
astype
(
'int32'
),
'
origin
_shape'
:
np
.
array
([
im_h
,
im_w
]).
astype
(
'int32'
),
'
image
_shape'
:
np
.
array
([
im_h
,
im_w
]).
astype
(
'int32'
),
}
label_info
=
{
'is_crowd'
:
is_crowd
,
...
...
paddlex/cv/datasets/easydata_cls.py
0 → 100644
浏览文件 @
4c3da33a
# copyright (c) 2020 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.
from
__future__
import
absolute_import
import
os.path
as
osp
import
random
import
copy
import
json
import
paddlex.utils.logging
as
logging
from
.imagenet
import
ImageNet
from
.dataset
import
is_pic
from
.dataset
import
get_encoding
class
EasyDataCls
(
ImageNet
):
"""读取EasyDataCls格式的分类数据集,并对样本进行相应的处理。
Args:
data_dir (str): 数据集所在的目录路径。
file_list (str): 描述数据集图片文件和类别id的文件路径(文本内每行路径为相对data_dir的相对路)。
label_list (str): 描述数据集包含的类别信息文件路径。
transforms (paddlex.cls.transforms): 数据集中每个样本的预处理/增强算子。
num_workers (int|str): 数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据
系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核
数的一半。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
"""
def
__init__
(
self
,
data_dir
,
file_list
,
label_list
,
transforms
=
None
,
num_workers
=
'auto'
,
buffer_size
=
100
,
parallel_method
=
'process'
,
shuffle
=
False
):
super
(
ImageNet
,
self
).
__init__
(
transforms
=
transforms
,
num_workers
=
num_workers
,
buffer_size
=
buffer_size
,
parallel_method
=
parallel_method
,
shuffle
=
shuffle
)
self
.
file_list
=
list
()
self
.
labels
=
list
()
self
.
_epoch
=
0
with
open
(
label_list
,
encoding
=
get_encoding
(
label_list
))
as
f
:
for
line
in
f
:
item
=
line
.
strip
()
self
.
labels
.
append
(
item
)
logging
.
info
(
"Starting to read file list from dataset..."
)
with
open
(
file_list
,
encoding
=
get_encoding
(
file_list
))
as
f
:
for
line
in
f
:
img_file
,
json_file
=
[
osp
.
join
(
data_dir
,
x
)
\
for
x
in
line
.
strip
().
split
()[:
2
]]
if
not
is_pic
(
img_file
):
continue
if
not
osp
.
isfile
(
json_file
):
continue
if
not
osp
.
exists
(
img_file
):
raise
IOError
(
'The image file {} is not exist!'
.
format
(
img_file
))
with
open
(
json_file
,
mode
=
'r'
,
\
encoding
=
get_encoding
(
json_file
))
as
j
:
json_info
=
json
.
load
(
j
)
label
=
json_info
[
'labels'
][
0
][
'name'
]
self
.
file_list
.
append
([
img_file
,
self
.
labels
.
index
(
label
)])
self
.
num_samples
=
len
(
self
.
file_list
)
logging
.
info
(
"{} samples in file {}"
.
format
(
len
(
self
.
file_list
),
file_list
))
\ No newline at end of file
paddlex/cv/datasets/easydata_det.py
0 → 100644
浏览文件 @
4c3da33a
# copyright (c) 2020 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.
from
__future__
import
absolute_import
import
os.path
as
osp
import
random
import
copy
import
json
import
cv2
import
numpy
as
np
import
paddlex.utils.logging
as
logging
from
.voc
import
VOCDetection
from
.dataset
import
is_pic
from
.dataset
import
get_encoding
class
EasyDataDet
(
VOCDetection
):
"""读取EasyDataDet格式的检测数据集,并对样本进行相应的处理。
Args:
data_dir (str): 数据集所在的目录路径。
file_list (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对data_dir的相对路)。
label_list (str): 描述数据集包含的类别信息文件路径。
transforms (paddlex.det.transforms): 数据集中每个样本的预处理/增强算子。
num_workers (int|str): 数据集中样本在预处理过程中的线程或进程数。默认为'auto'。当设为'auto'时,根据
系统的实际CPU核数设置`num_workers`: 如果CPU核数的一半大于8,则`num_workers`为8,否则为CPU核数的
一半。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
"""
def
__init__
(
self
,
data_dir
,
file_list
,
label_list
,
transforms
=
None
,
num_workers
=
'auto'
,
buffer_size
=
100
,
parallel_method
=
'process'
,
shuffle
=
False
):
super
(
VOCDetection
,
self
).
__init__
(
transforms
=
transforms
,
num_workers
=
num_workers
,
buffer_size
=
buffer_size
,
parallel_method
=
parallel_method
,
shuffle
=
shuffle
)
self
.
file_list
=
list
()
self
.
labels
=
list
()
self
.
_epoch
=
0
annotations
=
{}
annotations
[
'images'
]
=
[]
annotations
[
'categories'
]
=
[]
annotations
[
'annotations'
]
=
[]
cname2cid
=
{}
label_id
=
1
with
open
(
label_list
,
encoding
=
get_encoding
(
label_list
))
as
fr
:
for
line
in
fr
.
readlines
():
cname2cid
[
line
.
strip
()]
=
label_id
label_id
+=
1
self
.
labels
.
append
(
line
.
strip
())
logging
.
info
(
"Starting to read file list from dataset..."
)
for
k
,
v
in
cname2cid
.
items
():
annotations
[
'categories'
].
append
({
'supercategory'
:
'component'
,
'id'
:
v
,
'name'
:
k
})
from
pycocotools.mask
import
decode
ct
=
0
ann_ct
=
0
with
open
(
file_list
,
encoding
=
get_encoding
(
file_list
))
as
f
:
for
line
in
f
:
img_file
,
json_file
=
[
osp
.
join
(
data_dir
,
x
)
\
for
x
in
line
.
strip
().
split
()[:
2
]]
if
not
is_pic
(
img_file
):
continue
if
not
osp
.
isfile
(
json_file
):
continue
if
not
osp
.
exists
(
img_file
):
raise
IOError
(
'The image file {} is not exist!'
.
format
(
img_file
))
with
open
(
json_file
,
mode
=
'r'
,
\
encoding
=
get_encoding
(
json_file
))
as
j
:
json_info
=
json
.
load
(
j
)
im_id
=
np
.
array
([
ct
])
im
=
cv2
.
imread
(
img_file
)
im_w
=
im
.
shape
[
1
]
im_h
=
im
.
shape
[
0
]
objs
=
json_info
[
'labels'
]
gt_bbox
=
np
.
zeros
((
len
(
objs
),
4
),
dtype
=
np
.
float32
)
gt_class
=
np
.
zeros
((
len
(
objs
),
1
),
dtype
=
np
.
int32
)
gt_score
=
np
.
ones
((
len
(
objs
),
1
),
dtype
=
np
.
float32
)
is_crowd
=
np
.
zeros
((
len
(
objs
),
1
),
dtype
=
np
.
int32
)
difficult
=
np
.
zeros
((
len
(
objs
),
1
),
dtype
=
np
.
int32
)
gt_poly
=
[
None
]
*
len
(
objs
)
for
i
,
obj
in
enumerate
(
objs
):
cname
=
obj
[
'name'
]
gt_class
[
i
][
0
]
=
cname2cid
[
cname
]
x1
=
max
(
0
,
obj
[
'x1'
])
y1
=
max
(
0
,
obj
[
'y1'
])
x2
=
min
(
im_w
-
1
,
obj
[
'x2'
])
y2
=
min
(
im_h
-
1
,
obj
[
'y2'
])
gt_bbox
[
i
]
=
[
x1
,
y1
,
x2
,
y2
]
is_crowd
[
i
][
0
]
=
0
if
'mask'
in
obj
:
mask_dict
=
{}
mask_dict
[
'size'
]
=
[
im_h
,
im_w
]
mask_dict
[
'counts'
]
=
obj
[
'mask'
].
encode
()
mask
=
decode
(
mask_dict
)
gt_poly
[
i
]
=
self
.
mask2polygon
(
mask
)
annotations
[
'annotations'
].
append
({
'iscrowd'
:
0
,
'image_id'
:
int
(
im_id
[
0
]),
'bbox'
:
[
x1
,
y1
,
x2
-
x1
+
1
,
y2
-
y1
+
1
],
'area'
:
float
((
x2
-
x1
+
1
)
*
(
y2
-
y1
+
1
)),
'segmentation'
:
[[
x1
,
y1
,
x1
,
y2
,
x2
,
y2
,
x2
,
y1
]]
if
gt_poly
[
i
]
is
None
else
gt_poly
[
i
],
'category_id'
:
cname2cid
[
cname
],
'id'
:
ann_ct
,
'difficult'
:
0
})
ann_ct
+=
1
im_info
=
{
'im_id'
:
im_id
,
'image_shape'
:
np
.
array
([
im_h
,
im_w
]).
astype
(
'int32'
),
}
label_info
=
{
'is_crowd'
:
is_crowd
,
'gt_class'
:
gt_class
,
'gt_bbox'
:
gt_bbox
,
'gt_score'
:
gt_score
,
'difficult'
:
difficult
}
if
None
not
in
gt_poly
:
label_info
[
'gt_poly'
]
=
gt_poly
voc_rec
=
(
im_info
,
label_info
)
if
len
(
objs
)
!=
0
:
self
.
file_list
.
append
([
img_file
,
voc_rec
])
ct
+=
1
annotations
[
'images'
].
append
({
'height'
:
im_h
,
'width'
:
im_w
,
'id'
:
int
(
im_id
[
0
]),
'file_name'
:
osp
.
split
(
img_file
)[
1
]
})
if
not
len
(
self
.
file_list
)
>
0
:
raise
Exception
(
'not found any voc record in %s'
%
(
file_list
))
logging
.
info
(
"{} samples in file {}"
.
format
(
len
(
self
.
file_list
),
file_list
))
self
.
num_samples
=
len
(
self
.
file_list
)
from
pycocotools.coco
import
COCO
self
.
coco_gt
=
COCO
()
self
.
coco_gt
.
dataset
=
annotations
self
.
coco_gt
.
createIndex
()
def
mask2polygon
(
self
,
mask
):
contours
,
hierarchy
=
cv2
.
findContours
(
(
mask
).
astype
(
np
.
uint8
),
cv2
.
RETR_TREE
,
cv2
.
CHAIN_APPROX_SIMPLE
)
segmentation
=
[]
for
contour
in
contours
:
contour_list
=
contour
.
flatten
().
tolist
()
if
len
(
contour_list
)
>
4
:
segmentation
.
append
(
contour_list
)
return
segmentation
\ No newline at end of file
paddlex/cv/datasets/easydata_seg.py
0 → 100644
浏览文件 @
4c3da33a
# copyright (c) 2020 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.
from
__future__
import
absolute_import
import
os.path
as
osp
import
random
import
copy
import
json
import
cv2
import
numpy
as
np
import
paddlex.utils.logging
as
logging
from
.dataset
import
Dataset
from
.dataset
import
get_encoding
from
.dataset
import
is_pic
class
EasyDataSeg
(
Dataset
):
"""读取EasyDataSeg语义分割任务数据集,并对样本进行相应的处理。
Args:
data_dir (str): 数据集所在的目录路径。
file_list (str): 描述数据集图片文件和对应标注文件的文件路径(文本内每行路径为相对data_dir的相对路)。
label_list (str): 描述数据集包含的类别信息文件路径。
transforms (list): 数据集中每个样本的预处理/增强算子。
num_workers (int): 数据集中样本在预处理过程中的线程或进程数。默认为4。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'process'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
"""
def
__init__
(
self
,
data_dir
,
file_list
,
label_list
,
transforms
=
None
,
num_workers
=
'auto'
,
buffer_size
=
100
,
parallel_method
=
'process'
,
shuffle
=
False
):
super
(
EasyDataSeg
,
self
).
__init__
(
transforms
=
transforms
,
num_workers
=
num_workers
,
buffer_size
=
buffer_size
,
parallel_method
=
parallel_method
,
shuffle
=
shuffle
)
self
.
file_list
=
list
()
self
.
labels
=
list
()
self
.
_epoch
=
0
from
pycocotools.mask
import
decode
cname2cid
=
{}
label_id
=
0
with
open
(
label_list
,
encoding
=
get_encoding
(
label_list
))
as
fr
:
for
line
in
fr
.
readlines
():
cname2cid
[
line
.
strip
()]
=
label_id
label_id
+=
1
self
.
labels
.
append
(
line
.
strip
())
with
open
(
file_list
,
encoding
=
get_encoding
(
file_list
))
as
f
:
for
line
in
f
:
img_file
,
json_file
=
[
osp
.
join
(
data_dir
,
x
)
\
for
x
in
line
.
strip
().
split
()[:
2
]]
if
not
is_pic
(
img_file
):
continue
if
not
osp
.
isfile
(
json_file
):
continue
if
not
osp
.
exists
(
img_file
):
raise
IOError
(
'The image file {} is not exist!'
.
format
(
img_file
))
with
open
(
json_file
,
mode
=
'r'
,
\
encoding
=
get_encoding
(
json_file
))
as
j
:
json_info
=
json
.
load
(
j
)
im
=
cv2
.
imread
(
img_file
)
im_w
=
im
.
shape
[
1
]
im_h
=
im
.
shape
[
0
]
objs
=
json_info
[
'labels'
]
lable_npy
=
np
.
zeros
([
im_h
,
im_w
]).
astype
(
'uint8'
)
for
i
,
obj
in
enumerate
(
objs
):
cname
=
obj
[
'name'
]
cid
=
cname2cid
[
cname
]
mask_dict
=
{}
mask_dict
[
'size'
]
=
[
im_h
,
im_w
]
mask_dict
[
'counts'
]
=
obj
[
'mask'
].
encode
()
mask
=
decode
(
mask_dict
)
mask
*=
cid
conflict_index
=
np
.
where
(((
lable_npy
>
0
)
&
(
mask
==
cid
))
==
True
)
mask
[
conflict_index
]
=
0
lable_npy
+=
mask
self
.
file_list
.
append
([
img_file
,
lable_npy
])
self
.
num_samples
=
len
(
self
.
file_list
)
logging
.
info
(
"{} samples in file {}"
.
format
(
len
(
self
.
file_list
),
file_list
))
def
iterator
(
self
):
self
.
_epoch
+=
1
self
.
_pos
=
0
files
=
copy
.
deepcopy
(
self
.
file_list
)
if
self
.
shuffle
:
random
.
shuffle
(
files
)
files
=
files
[:
self
.
num_samples
]
self
.
num_samples
=
len
(
files
)
for
f
in
files
:
lable_npy
=
f
[
1
]
sample
=
[
f
[
0
],
None
,
lable_npy
]
yield
sample
paddlex/cv/datasets/imagenet.py
浏览文件 @
4c3da33a
...
...
@@ -35,7 +35,7 @@ class ImageNet(Dataset):
数的一半。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'
thread
'(Windows和Mac下会强制使用thread,该参数无效)。
线程和'process'进程两种方式。默认为'
process
'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
"""
...
...
paddlex/cv/datasets/seg_dataset.py
浏览文件 @
4c3da33a
...
...
@@ -33,7 +33,7 @@ class SegDataset(Dataset):
num_workers (int): 数据集中样本在预处理过程中的线程或进程数。默认为4。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'
thread
'(Windows和Mac下会强制使用thread,该参数无效)。
线程和'process'进程两种方式。默认为'
process
'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
"""
...
...
paddlex/cv/datasets/voc.py
浏览文件 @
4c3da33a
...
...
@@ -17,8 +17,8 @@ import copy
import
os.path
as
osp
import
random
import
numpy
as
np
from
collections
import
OrderedDict
import
xml.etree.ElementTree
as
ET
from
pycocotools.coco
import
COCO
import
paddlex.utils.logging
as
logging
from
.dataset
import
Dataset
from
.dataset
import
is_pic
...
...
@@ -38,7 +38,7 @@ class VOCDetection(Dataset):
一半。
buffer_size (int): 数据集中样本在预处理过程中队列的缓存长度,以样本数为单位。默认为100。
parallel_method (str): 数据集中样本在预处理过程中并行处理的方式,支持'thread'
线程和'process'进程两种方式。默认为'
thread
'(Windows和Mac下会强制使用thread,该参数无效)。
线程和'process'进程两种方式。默认为'
process
'(Windows和Mac下会强制使用thread,该参数无效)。
shuffle (bool): 是否需要对数据集中样本打乱顺序。默认为False。
"""
...
...
@@ -51,6 +51,7 @@ class VOCDetection(Dataset):
buffer_size
=
100
,
parallel_method
=
'process'
,
shuffle
=
False
):
from
pycocotools.coco
import
COCO
super
(
VOCDetection
,
self
).
__init__
(
transforms
=
transforms
,
num_workers
=
num_workers
,
...
...
@@ -66,7 +67,7 @@ class VOCDetection(Dataset):
annotations
[
'categories'
]
=
[]
annotations
[
'annotations'
]
=
[]
cname2cid
=
{}
cname2cid
=
OrderedDict
()
label_id
=
1
with
open
(
label_list
,
'r'
,
encoding
=
get_encoding
(
label_list
))
as
fr
:
for
line
in
fr
.
readlines
():
...
...
@@ -145,7 +146,7 @@ class VOCDetection(Dataset):
im_info
=
{
'im_id'
:
im_id
,
'
origin
_shape'
:
np
.
array
([
im_h
,
im_w
]).
astype
(
'int32'
),
'
image
_shape'
:
np
.
array
([
im_h
,
im_w
]).
astype
(
'int32'
),
}
label_info
=
{
'is_crowd'
:
is_crowd
,
...
...
paddlex/cv/models/base.py
浏览文件 @
4c3da33a
...
...
@@ -24,6 +24,7 @@ import json
import
functools
import
paddlex.utils.logging
as
logging
from
paddlex.utils
import
seconds_to_hms
from
paddlex.utils.utils
import
EarlyStop
import
paddlex
from
collections
import
OrderedDict
from
os
import
path
as
osp
...
...
@@ -334,7 +335,9 @@ class BaseAPI:
save_interval_epochs
=
1
,
log_interval_steps
=
10
,
save_dir
=
'output'
,
use_vdl
=
False
):
use_vdl
=
False
,
early_stop
=
False
,
early_stop_patience
=
5
):
if
not
osp
.
isdir
(
save_dir
):
if
osp
.
exists
(
save_dir
):
os
.
remove
(
save_dir
)
...
...
@@ -396,6 +399,9 @@ class BaseAPI:
train_step_component
=
OrderedDict
()
eval_component
=
OrderedDict
()
thresh
=
0.0001
if
early_stop
:
earlystop
=
EarlyStop
(
early_stop_patience
,
thresh
)
best_accuracy_key
=
""
best_accuracy
=
-
1.0
best_model_epoch
=
1
...
...
@@ -507,3 +513,6 @@ class BaseAPI:
'Current evaluated best model in eval_dataset is epoch_{}, {}={}'
.
format
(
best_model_epoch
,
best_accuracy_key
,
best_accuracy
))
if
eval_dataset
is
not
None
and
early_stop
:
if
earlystop
(
current_accuracy
):
break
paddlex/cv/models/classifier.py
浏览文件 @
4c3da33a
...
...
@@ -110,7 +110,9 @@ class BaseClassifier(BaseAPI):
lr_decay_gamma
=
0.1
,
use_vdl
=
False
,
sensitivities_file
=
None
,
eval_metric_loss
=
0.05
):
eval_metric_loss
=
0.05
,
early_stop
=
False
,
early_stop_patience
=
5
):
"""训练。
Args:
...
...
@@ -132,6 +134,9 @@ class BaseClassifier(BaseAPI):
sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',
则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
eval_metric_loss (float): 可容忍的精度损失。默认为0.05。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises:
ValueError: 模型从inference model进行加载。
...
...
@@ -166,7 +171,9 @@ class BaseClassifier(BaseAPI):
save_interval_epochs
=
save_interval_epochs
,
log_interval_steps
=
log_interval_steps
,
save_dir
=
save_dir
,
use_vdl
=
use_vdl
)
use_vdl
=
use_vdl
,
early_stop
=
early_stop
,
early_stop_patience
=
early_stop_patience
)
def
evaluate
(
self
,
eval_dataset
,
...
...
paddlex/cv/models/deeplabv3p.py
浏览文件 @
4c3da33a
...
...
@@ -232,7 +232,9 @@ class DeepLabv3p(BaseAPI):
lr_decay_power
=
0.9
,
use_vdl
=
False
,
sensitivities_file
=
None
,
eval_metric_loss
=
0.05
):
eval_metric_loss
=
0.05
,
early_stop
=
False
,
early_stop_patience
=
5
):
"""训练。
Args:
...
...
@@ -253,6 +255,9 @@ class DeepLabv3p(BaseAPI):
sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',
则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
eval_metric_loss (float): 可容忍的精度损失。默认为0.05。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises:
ValueError: 模型从inference model进行加载。
...
...
@@ -289,7 +294,9 @@ class DeepLabv3p(BaseAPI):
save_interval_epochs
=
save_interval_epochs
,
log_interval_steps
=
log_interval_steps
,
save_dir
=
save_dir
,
use_vdl
=
use_vdl
)
use_vdl
=
use_vdl
,
early_stop
=
early_stop
,
early_stop_patience
=
early_stop_patience
)
def
evaluate
(
self
,
eval_dataset
,
...
...
paddlex/cv/models/faster_rcnn.py
浏览文件 @
4c3da33a
...
...
@@ -32,7 +32,7 @@ class FasterRCNN(BaseAPI):
Args:
num_classes (int): 包含了背景类的类别数。默认为81。
backbone (str): FasterRCNN的backbone网络,取值范围为['ResNet18', 'ResNet50',
'ResNet50
vd', 'ResNet101', 'ResNet101
vd']。默认为'ResNet50'。
'ResNet50
_vd', 'ResNet101', 'ResNet101_
vd']。默认为'ResNet50'。
with_fpn (bool): 是否使用FPN结构。默认为True。
aspect_ratios (list): 生成anchor高宽比的可选值。默认为[0.5, 1.0, 2.0]。
anchor_sizes (list): 生成anchor大小的可选值。默认为[32, 64, 128, 256, 512]。
...
...
@@ -47,7 +47,7 @@ class FasterRCNN(BaseAPI):
self
.
init_params
=
locals
()
super
(
FasterRCNN
,
self
).
__init__
(
'detector'
)
backbones
=
[
'ResNet18'
,
'ResNet50'
,
'ResNet50
vd'
,
'ResNet101'
,
'ResNet101
vd'
'ResNet18'
,
'ResNet50'
,
'ResNet50
_vd'
,
'ResNet101'
,
'ResNet101_
vd'
]
assert
backbone
in
backbones
,
"backbone should be one of {}"
.
format
(
backbones
)
...
...
@@ -67,7 +67,7 @@ class FasterRCNN(BaseAPI):
elif
backbone_name
==
'ResNet50'
:
layers
=
50
variant
=
'b'
elif
backbone_name
==
'ResNet50vd'
:
elif
backbone_name
==
'ResNet50
_
vd'
:
layers
=
50
variant
=
'd'
norm_type
=
'affine_channel'
...
...
@@ -75,7 +75,7 @@ class FasterRCNN(BaseAPI):
layers
=
101
variant
=
'b'
norm_type
=
'affine_channel'
elif
backbone_name
==
'ResNet101vd'
:
elif
backbone_name
==
'ResNet101
_
vd'
:
layers
=
101
variant
=
'd'
norm_type
=
'affine_channel'
...
...
@@ -165,7 +165,9 @@ class FasterRCNN(BaseAPI):
lr_decay_epochs
=
[
8
,
11
],
lr_decay_gamma
=
0.1
,
metric
=
None
,
use_vdl
=
False
):
use_vdl
=
False
,
early_stop
=
False
,
early_stop_patience
=
5
):
"""训练。
Args:
...
...
@@ -178,7 +180,7 @@ class FasterRCNN(BaseAPI):
log_interval_steps (int): 训练日志输出间隔(单位:迭代次数)。默认为20。
save_dir (str): 模型保存路径。默认值为'output'。
pretrain_weights (str): 若指定为路径时,则加载路径下预训练模型;若为字符串'IMAGENET',
则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为
None
。
则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为
'IMAGENET'
。
optimizer (paddle.fluid.optimizer): 优化器。当该参数为None时,使用默认优化器:
fluid.layers.piecewise_decay衰减策略,fluid.optimizer.Momentum优化方法。
learning_rate (float): 默认优化器的初始学习率。默认为0.0025。
...
...
@@ -188,6 +190,9 @@ class FasterRCNN(BaseAPI):
lr_decay_gamma (float): 默认优化器的学习率衰减率。默认为0.1。
metric (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。默认值为None。
use_vdl (bool): 是否使用VisualDL进行可视化。默认值为False。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises:
ValueError: 评估类型不在指定列表中。
...
...
@@ -196,11 +201,12 @@ class FasterRCNN(BaseAPI):
if
metric
is
None
:
if
isinstance
(
train_dataset
,
paddlex
.
datasets
.
CocoDetection
):
metric
=
'COCO'
elif
isinstance
(
train_dataset
,
paddlex
.
datasets
.
VOCDetection
):
elif
isinstance
(
train_dataset
,
paddlex
.
datasets
.
VOCDetection
)
or
\
isinstance
(
train_dataset
,
paddlex
.
datasets
.
EasyDataDet
):
metric
=
'VOC'
else
:
raise
ValueError
(
"train_dataset should be datasets.VOCDetection or datasets.COCODetection."
"train_dataset should be datasets.VOCDetection or datasets.COCODetection
or datasets.EasyDataDet
."
)
assert
metric
in
[
'COCO'
,
'VOC'
],
"Metric only support 'VOC' or 'COCO'"
self
.
metric
=
metric
...
...
@@ -235,7 +241,9 @@ class FasterRCNN(BaseAPI):
save_interval_epochs
=
save_interval_epochs
,
log_interval_steps
=
log_interval_steps
,
save_dir
=
save_dir
,
use_vdl
=
use_vdl
)
use_vdl
=
use_vdl
,
early_stop
=
early_stop
,
early_stop_patience
=
early_stop_patience
)
def
evaluate
(
self
,
eval_dataset
,
...
...
paddlex/cv/models/mask_rcnn.py
浏览文件 @
4c3da33a
...
...
@@ -32,7 +32,7 @@ class MaskRCNN(FasterRCNN):
Args:
num_classes (int): 包含了背景类的类别数。默认为81。
backbone (str): MaskRCNN的backbone网络,取值范围为['ResNet18', 'ResNet50',
'ResNet50
vd', 'ResNet101', 'ResNet101
vd']。默认为'ResNet50'。
'ResNet50
_vd', 'ResNet101', 'ResNet101_
vd']。默认为'ResNet50'。
with_fpn (bool): 是否使用FPN结构。默认为True。
aspect_ratios (list): 生成anchor高宽比的可选值。默认为[0.5, 1.0, 2.0]。
anchor_sizes (list): 生成anchor大小的可选值。默认为[32, 64, 128, 256, 512]。
...
...
@@ -46,7 +46,7 @@ class MaskRCNN(FasterRCNN):
anchor_sizes
=
[
32
,
64
,
128
,
256
,
512
]):
self
.
init_params
=
locals
()
backbones
=
[
'ResNet18'
,
'ResNet50'
,
'ResNet50
vd'
,
'ResNet101'
,
'ResNet101
vd'
'ResNet18'
,
'ResNet50'
,
'ResNet50
_vd'
,
'ResNet101'
,
'ResNet101_
vd'
]
assert
backbone
in
backbones
,
"backbone should be one of {}"
.
format
(
backbones
)
...
...
@@ -130,7 +130,9 @@ class MaskRCNN(FasterRCNN):
lr_decay_epochs
=
[
8
,
11
],
lr_decay_gamma
=
0.1
,
metric
=
None
,
use_vdl
=
False
):
use_vdl
=
False
,
early_stop
=
False
,
early_stop_patience
=
5
):
"""训练。
Args:
...
...
@@ -153,17 +155,21 @@ class MaskRCNN(FasterRCNN):
lr_decay_gamma (float): 默认优化器的学习率衰减率。默认为0.1。
metric (bool): 训练过程中评估的方式,取值范围为['COCO', 'VOC']。
use_vdl (bool): 是否使用VisualDL进行可视化。默认值为False。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises:
ValueError: 评估类型不在指定列表中。
ValueError: 模型从inference model进行加载。
"""
if
metric
is
None
:
if
isinstance
(
train_dataset
,
paddlex
.
datasets
.
CocoDetection
):
if
isinstance
(
train_dataset
,
paddlex
.
datasets
.
CocoDetection
)
or
\
isinstance
(
train_dataset
,
paddlex
.
datasets
.
EasyDataDet
):
metric
=
'COCO'
else
:
raise
Exception
(
"train_dataset should be datasets.COCODetection."
)
"train_dataset should be datasets.COCODetection
or datasets.EasyDataDet
."
)
assert
metric
in
[
'COCO'
,
'VOC'
],
"Metric only support 'VOC' or 'COCO'"
self
.
metric
=
metric
if
not
self
.
trainable
:
...
...
@@ -201,7 +207,9 @@ class MaskRCNN(FasterRCNN):
save_interval_epochs
=
save_interval_epochs
,
log_interval_steps
=
log_interval_steps
,
save_dir
=
save_dir
,
use_vdl
=
use_vdl
)
use_vdl
=
use_vdl
,
early_stop
=
early_stop
,
early_stop_patience
=
early_stop_patience
)
def
evaluate
(
self
,
eval_dataset
,
...
...
paddlex/cv/models/slim/visualize.py
浏览文件 @
4c3da33a
...
...
@@ -15,9 +15,6 @@
import
os.path
as
osp
import
tqdm
import
numpy
as
np
import
matplotlib
matplotlib
.
use
(
'Agg'
)
import
matplotlib.pyplot
as
plt
from
.prune
import
cal_model_size
from
paddleslim.prune
import
load_sensitivities
...
...
@@ -30,6 +27,10 @@ def visualize(model, sensitivities_file, save_dir='./'):
model (paddlex.cv.models): paddlex中的模型。
sensitivities_file (str): 敏感度文件存储路径。
"""
import
matplotlib
matplotlib
.
use
(
'Agg'
)
import
matplotlib.pyplot
as
plt
program
=
model
.
test_prog
place
=
model
.
places
[
0
]
fig
=
plt
.
figure
()
...
...
paddlex/cv/models/unet.py
浏览文件 @
4c3da33a
...
...
@@ -119,7 +119,9 @@ class UNet(DeepLabv3p):
lr_decay_power
=
0.9
,
use_vdl
=
False
,
sensitivities_file
=
None
,
eval_metric_loss
=
0.05
):
eval_metric_loss
=
0.05
,
early_stop
=
False
,
early_stop_patience
=
5
):
"""训练。
Args:
...
...
@@ -140,12 +142,17 @@ class UNet(DeepLabv3p):
sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',
则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
eval_metric_loss (float): 可容忍的精度损失。默认为0.05。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises:
ValueError: 模型从inference model进行加载。
"""
return
super
(
UNet
,
self
).
train
(
num_epochs
,
train_dataset
,
train_batch_size
,
eval_dataset
,
save_interval_epochs
,
log_interval_steps
,
save_dir
,
pretrain_weights
,
optimizer
,
learning_rate
,
lr_decay_power
,
use_vdl
,
sensitivities_file
,
eval_metric_loss
)
return
super
(
UNet
,
self
).
train
(
num_epochs
,
train_dataset
,
train_batch_size
,
eval_dataset
,
save_interval_epochs
,
log_interval_steps
,
save_dir
,
pretrain_weights
,
optimizer
,
learning_rate
,
lr_decay_power
,
use_vdl
,
sensitivities_file
,
eval_metric_loss
,
early_stop
,
early_stop_patience
)
paddlex/cv/models/utils/visualize.py
浏览文件 @
4c3da33a
...
...
@@ -14,10 +14,8 @@
import
os
import
cv2
import
colorsys
import
numpy
as
np
import
matplotlib.pyplot
as
plt
from
PIL
import
Image
,
ImageDraw
import
paddlex.utils.logging
as
logging
from
.detection_eval
import
fixed_linspace
,
backup_linspace
,
loadRes
...
...
@@ -28,13 +26,13 @@ def visualize_detection(image, result, threshold=0.5, save_dir='./'):
"""
image_name
=
os
.
path
.
split
(
image
)[
-
1
]
image
=
Image
.
open
(
image
).
convert
(
'RGB'
)
image
=
cv2
.
imread
(
image
)
image
=
draw_bbox_mask
(
image
,
result
,
threshold
=
threshold
)
if
save_dir
is
not
None
:
if
not
os
.
path
.
exists
(
save_dir
):
os
.
makedirs
(
save_dir
)
out_path
=
os
.
path
.
join
(
save_dir
,
'visualize_{}'
.
format
(
image_name
))
image
.
save
(
out_path
,
quality
=
95
)
cv2
.
imwrite
(
out_path
,
image
)
logging
.
info
(
'The visualized result is saved as {}'
.
format
(
out_path
))
else
:
return
image
...
...
@@ -123,49 +121,163 @@ def clip_bbox(bbox):
return
xmin
,
ymin
,
xmax
,
ymax
def
draw_bbox_mask
(
image
,
results
,
threshold
=
0.5
,
alpha
=
0.7
):
def
draw_bbox_mask
(
image
,
results
,
threshold
=
0.5
):
import
matplotlib
matplotlib
.
use
(
'Agg'
)
import
matplotlib
as
mpl
import
matplotlib.figure
as
mplfigure
import
matplotlib.colors
as
mplc
from
matplotlib.backends.backend_agg
import
FigureCanvasAgg
# refer to https://github.com/facebookresearch/detectron2/blob/master/detectron2/utils/visualizer.py
def
_change_color_brightness
(
color
,
brightness_factor
):
assert
brightness_factor
>=
-
1.0
and
brightness_factor
<=
1.0
color
=
mplc
.
to_rgb
(
color
)
polygon_color
=
colorsys
.
rgb_to_hls
(
*
mplc
.
to_rgb
(
color
))
modified_lightness
=
polygon_color
[
1
]
+
(
brightness_factor
*
polygon_color
[
1
])
modified_lightness
=
0.0
if
modified_lightness
<
0.0
else
modified_lightness
modified_lightness
=
1.0
if
modified_lightness
>
1.0
else
modified_lightness
modified_color
=
colorsys
.
hls_to_rgb
(
polygon_color
[
0
],
modified_lightness
,
polygon_color
[
2
])
return
modified_color
_SMALL_OBJECT_AREA_THRESH
=
1000
# setup figure
width
,
height
=
image
.
shape
[
1
],
image
.
shape
[
0
]
scale
=
1
fig
=
mplfigure
.
Figure
(
frameon
=
False
)
dpi
=
fig
.
get_dpi
()
fig
.
set_size_inches
(
(
width
*
scale
+
1e-2
)
/
dpi
,
(
height
*
scale
+
1e-2
)
/
dpi
,
)
canvas
=
FigureCanvasAgg
(
fig
)
ax
=
fig
.
add_axes
([
0.0
,
0.0
,
1.0
,
1.0
])
ax
.
axis
(
"off"
)
ax
.
set_xlim
(
0.0
,
width
)
ax
.
set_ylim
(
height
)
default_font_size
=
max
(
np
.
sqrt
(
height
*
width
)
//
90
,
10
//
scale
)
linewidth
=
max
(
default_font_size
/
4
,
1
)
labels
=
list
()
for
dt
in
np
.
array
(
results
):
if
dt
[
'category'
]
not
in
labels
:
labels
.
append
(
dt
[
'category'
])
color_map
=
get_color_map_list
(
len
(
labels
)
)
color_map
=
get_color_map_list
(
256
)
keep_results
=
[]
areas
=
[]
for
dt
in
np
.
array
(
results
):
cname
,
bbox
,
score
=
dt
[
'category'
],
dt
[
'bbox'
],
dt
[
'score'
]
if
score
<
threshold
:
continue
keep_results
.
append
(
dt
)
areas
.
append
(
bbox
[
2
]
*
bbox
[
3
])
areas
=
np
.
asarray
(
areas
)
sorted_idxs
=
np
.
argsort
(
-
areas
).
tolist
()
keep_results
=
[
keep_results
[
k
]
for
k
in
sorted_idxs
]
if
len
(
keep_results
)
>
0
else
[]
for
dt
in
np
.
array
(
keep_results
):
cname
,
bbox
,
score
=
dt
[
'category'
],
dt
[
'bbox'
],
dt
[
'score'
]
xmin
,
ymin
,
w
,
h
=
bbox
xmax
=
xmin
+
w
ymax
=
ymin
+
h
color
=
tuple
(
color_map
[
labels
.
index
(
cname
)])
color
=
tuple
(
color_map
[
labels
.
index
(
cname
)
+
2
])
color
=
[
c
/
255.
for
c
in
color
]
# draw bbox
draw
=
ImageDraw
.
Draw
(
image
)
draw
.
line
([(
xmin
,
ymin
),
(
xmin
,
ymax
),
(
xmax
,
ymax
),
(
xmax
,
ymin
),
(
xmin
,
ymin
)],
width
=
2
,
fill
=
color
)
# draw label
text
=
"{} {:.2f}"
.
format
(
cname
,
score
)
tw
,
th
=
draw
.
textsize
(
text
)
draw
.
rectangle
([(
xmin
+
1
,
ymin
-
th
),
(
xmin
+
tw
+
1
,
ymin
)],
fill
=
color
)
draw
.
text
((
xmin
+
1
,
ymin
-
th
),
text
,
fill
=
(
255
,
255
,
255
))
ax
.
add_patch
(
mpl
.
patches
.
Rectangle
(
(
xmin
,
ymin
),
w
,
h
,
fill
=
False
,
edgecolor
=
color
,
linewidth
=
linewidth
*
scale
,
alpha
=
0.8
,
linestyle
=
"-"
,
))
# draw mask
if
'mask'
in
dt
:
mask
=
dt
[
'mask'
]
color_mask
=
np
.
array
(
color_map
[
labels
.
index
(
dt
[
'category'
])]).
astype
(
'float32'
)
img_array
=
np
.
array
(
image
).
astype
(
'float32'
)
idx
=
np
.
nonzero
(
mask
)
img_array
[
idx
[
0
],
idx
[
1
],
:]
*=
1.0
-
alpha
img_array
[
idx
[
0
],
idx
[
1
],
:]
+=
alpha
*
color_mask
image
=
Image
.
fromarray
(
img_array
.
astype
(
'uint8'
))
return
image
mask
=
np
.
ascontiguousarray
(
mask
)
res
=
cv2
.
findContours
(
mask
.
astype
(
"uint8"
),
cv2
.
RETR_CCOMP
,
cv2
.
CHAIN_APPROX_NONE
)
hierarchy
=
res
[
-
1
]
alpha
=
0.5
if
hierarchy
is
not
None
:
has_holes
=
(
hierarchy
.
reshape
(
-
1
,
4
)[:,
3
]
>=
0
).
sum
()
>
0
res
=
res
[
-
2
]
res
=
[
x
.
flatten
()
for
x
in
res
]
res
=
[
x
for
x
in
res
if
len
(
x
)
>=
6
]
for
segment
in
res
:
segment
=
segment
.
reshape
(
-
1
,
2
)
edge_color
=
mplc
.
to_rgb
(
color
)
+
(
1
,
)
polygon
=
mpl
.
patches
.
Polygon
(
segment
,
fill
=
True
,
facecolor
=
mplc
.
to_rgb
(
color
)
+
(
alpha
,
),
edgecolor
=
edge_color
,
linewidth
=
max
(
default_font_size
//
15
*
scale
,
1
),
)
ax
.
add_patch
(
polygon
)
# draw label
text_pos
=
(
xmin
,
ymin
)
horiz_align
=
"left"
instance_area
=
w
*
h
if
(
instance_area
<
_SMALL_OBJECT_AREA_THRESH
*
scale
or
h
<
40
*
scale
):
if
ymin
>=
height
-
5
:
text_pos
=
(
xmin
,
ymin
)
else
:
text_pos
=
(
xmin
,
ymax
)
height_ratio
=
h
/
np
.
sqrt
(
height
*
width
)
font_size
=
(
np
.
clip
((
height_ratio
-
0.02
)
/
0.08
+
1
,
1.2
,
2
)
*
0.5
*
default_font_size
)
text
=
"{} {:.2f}"
.
format
(
cname
,
score
)
color
=
np
.
maximum
(
list
(
mplc
.
to_rgb
(
color
)),
0.2
)
color
[
np
.
argmax
(
color
)]
=
max
(
0.8
,
np
.
max
(
color
))
color
=
_change_color_brightness
(
color
,
brightness_factor
=
0.7
)
ax
.
text
(
text_pos
[
0
],
text_pos
[
1
],
text
,
size
=
font_size
*
scale
,
family
=
"sans-serif"
,
bbox
=
{
"facecolor"
:
"black"
,
"alpha"
:
0.8
,
"pad"
:
0.7
,
"edgecolor"
:
"none"
},
verticalalignment
=
"top"
,
horizontalalignment
=
horiz_align
,
color
=
color
,
zorder
=
10
,
rotation
=
0
,
)
s
,
(
width
,
height
)
=
canvas
.
print_to_buffer
()
buffer
=
np
.
frombuffer
(
s
,
dtype
=
"uint8"
)
img_rgba
=
buffer
.
reshape
(
height
,
width
,
4
)
rgb
,
alpha
=
np
.
split
(
img_rgba
,
[
3
],
axis
=
2
)
try
:
import
numexpr
as
ne
visualized_image
=
ne
.
evaluate
(
"image * (1 - alpha / 255.0) + rgb * (alpha / 255.0)"
)
except
ImportError
:
alpha
=
alpha
.
astype
(
"float32"
)
/
255.0
visualized_image
=
image
*
(
1
-
alpha
)
+
rgb
*
alpha
visualized_image
=
visualized_image
.
astype
(
"uint8"
)
return
visualized_image
def
draw_pr_curve
(
eval_details_file
=
None
,
...
...
@@ -190,6 +302,9 @@ def draw_pr_curve(eval_details_file=None,
raise
Exception
(
"There is no predicted bbox."
)
if
pred_mask
is
not
None
and
len
(
pred_mask
)
==
0
:
raise
Exception
(
"There is no predicted mask."
)
import
matplotlib
matplotlib
.
use
(
'Agg'
)
import
matplotlib.pyplot
as
plt
from
pycocotools.coco
import
COCO
from
pycocotools.cocoeval
import
COCOeval
coco
=
COCO
()
...
...
paddlex/cv/models/yolo_v3.py
浏览文件 @
4c3da33a
...
...
@@ -164,7 +164,9 @@ class YOLOv3(BaseAPI):
metric
=
None
,
use_vdl
=
False
,
sensitivities_file
=
None
,
eval_metric_loss
=
0.05
):
eval_metric_loss
=
0.05
,
early_stop
=
False
,
early_stop_patience
=
5
):
"""训练。
Args:
...
...
@@ -177,7 +179,7 @@ class YOLOv3(BaseAPI):
log_interval_steps (int): 训练日志输出间隔(单位:迭代次数)。默认为10。
save_dir (str): 模型保存路径。默认值为'output'。
pretrain_weights (str): 若指定为路径时,则加载路径下预训练模型;若为字符串'IMAGENET',
则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为
None
。
则自动下载在ImageNet图片数据上预训练的模型权重;若为None,则不使用预训练模型。默认为
'IMAGENET'
。
optimizer (paddle.fluid.optimizer): 优化器。当该参数为None时,使用默认优化器:
fluid.layers.piecewise_decay衰减策略,fluid.optimizer.Momentum优化方法。
learning_rate (float): 默认优化器的学习率。默认为1.0/8000。
...
...
@@ -190,6 +192,9 @@ class YOLOv3(BaseAPI):
sensitivities_file (str): 若指定为路径时,则加载路径下敏感度信息进行裁剪;若为字符串'DEFAULT',
则自动下载在ImageNet图片数据上获得的敏感度信息进行裁剪;若为None,则不进行裁剪。默认为None。
eval_metric_loss (float): 可容忍的精度损失。默认为0.05。
early_stop (bool): 是否使用提前终止训练策略。默认值为False。
early_stop_patience (int): 当使用提前终止训练策略时,如果验证集精度在`early_stop_patience`个epoch内
连续下降或持平,则终止训练。默认值为5。
Raises:
ValueError: 评估类型不在指定列表中。
...
...
@@ -200,11 +205,12 @@ class YOLOv3(BaseAPI):
if
metric
is
None
:
if
isinstance
(
train_dataset
,
paddlex
.
datasets
.
CocoDetection
):
metric
=
'COCO'
elif
isinstance
(
train_dataset
,
paddlex
.
datasets
.
VOCDetection
):
elif
isinstance
(
train_dataset
,
paddlex
.
datasets
.
VOCDetection
)
or
\
isinstance
(
train_dataset
,
paddlex
.
datasets
.
EasyDataDet
):
metric
=
'VOC'
else
:
raise
ValueError
(
"train_dataset should be datasets.VOCDetection or datasets.COCODetection."
"train_dataset should be datasets.VOCDetection or datasets.COCODetection
or datasets.EasyDataDet
."
)
assert
metric
in
[
'COCO'
,
'VOC'
],
"Metric only support 'VOC' or 'COCO'"
self
.
metric
=
metric
...
...
@@ -240,7 +246,9 @@ class YOLOv3(BaseAPI):
save_interval_epochs
=
save_interval_epochs
,
log_interval_steps
=
log_interval_steps
,
save_dir
=
save_dir
,
use_vdl
=
use_vdl
)
use_vdl
=
use_vdl
,
early_stop
=
early_stop
,
early_stop_patience
=
early_stop_patience
)
def
evaluate
(
self
,
eval_dataset
,
...
...
paddlex/cv/transforms/det_transforms.py
浏览文件 @
4c3da33a
...
...
@@ -58,8 +58,8 @@ class Compose:
im (str/np.ndarray): 图像路径/图像np.ndarray数据。
im_info (dict): 存储与图像相关的信息,dict中的字段如下:
- im_id (np.ndarray): 图像序列号,形状为(1,)。
-
origin
_shape (np.ndarray): 图像原始大小,形状为(2,),
origin_shape[0]为高,origin
_shape[1]为宽。
-
image
_shape (np.ndarray): 图像原始大小,形状为(2,),
image_shape[0]为高,image
_shape[1]为宽。
- mixup (list): list为[im, im_info, label_info],分别对应
与当前图像进行mixup的图像np.ndarray数据、图像相关信息、标注框相关信息;
注意,当前epoch若无需进行mixup,则无该字段。
...
...
@@ -93,9 +93,6 @@ class Compose:
# make default im_info with [h, w, 1]
im_info
[
'im_resize_info'
]
=
np
.
array
(
[
im
.
shape
[
0
],
im
.
shape
[
1
],
1.
],
dtype
=
np
.
float32
)
# copy augment_shape from origin_shape
im_info
[
'augment_shape'
]
=
np
.
array
([
im
.
shape
[
0
],
im
.
shape
[
1
]]).
astype
(
'int32'
)
if
not
self
.
use_mixup
:
if
'mixup'
in
im_info
:
del
im_info
[
'mixup'
]
...
...
@@ -407,16 +404,13 @@ class RandomHorizontalFlip:
raise
TypeError
(
'Cannot do RandomHorizontalFlip! '
+
'Becasuse the im_info and label_info can not be None!'
)
if
'augment_shape'
not
in
im_info
:
raise
TypeError
(
'Cannot do RandomHorizontalFlip! '
+
\
'Becasuse augment_shape is not in im_info!'
)
if
'gt_bbox'
not
in
label_info
:
raise
TypeError
(
'Cannot do RandomHorizontalFlip! '
+
\
'Becasuse gt_bbox is not in label_info!'
)
augment_shape
=
im_info
[
'augment
_shape'
]
image_shape
=
im_info
[
'image
_shape'
]
gt_bbox
=
label_info
[
'gt_bbox'
]
height
=
augment
_shape
[
0
]
width
=
augment
_shape
[
1
]
height
=
image
_shape
[
0
]
width
=
image
_shape
[
1
]
if
np
.
random
.
uniform
(
0
,
1
)
<
self
.
prob
:
im
=
horizontal_flip
(
im
)
...
...
@@ -587,7 +581,7 @@ class MixupImage:
(2)拼接原图像标注框和mixup图像标注框。
(3)拼接原图像标注框类别和mixup图像标注框类别。
(4)原图像标注框混合得分乘以factor,mixup图像标注框混合得分乘以(1-factor),叠加2个结果。
3. 更新im_info中的
augment
_shape信息。
3. 更新im_info中的
image
_shape信息。
Args:
alpha (float): 随机beta分布的下限。默认为1.5。
...
...
@@ -630,7 +624,7 @@ class MixupImage:
当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
存储与标注框相关信息的字典。
其中,im_info更新字段为:
-
augment
_shape (np.ndarray): mixup后的图像高、宽二者组成的np.ndarray,形状为(2,)。
-
image
_shape (np.ndarray): mixup后的图像高、宽二者组成的np.ndarray,形状为(2,)。
im_info删除的字段:
- mixup (list): 与当前字段进行mixup的图像相关信息。
label_info更新字段为:
...
...
@@ -694,7 +688,7 @@ class MixupImage:
label_info
[
'gt_score'
]
=
gt_score
label_info
[
'gt_class'
]
=
gt_class
label_info
[
'is_crowd'
]
=
is_crowd
im_info
[
'
augment
_shape'
]
=
np
.
array
([
im
.
shape
[
0
],
im_info
[
'
image
_shape'
]
=
np
.
array
([
im
.
shape
[
0
],
im
.
shape
[
1
]]).
astype
(
'int32'
)
im_info
.
pop
(
'mixup'
)
if
label_info
is
None
:
...
...
@@ -741,7 +735,7 @@ class RandomExpand:
当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
存储与标注框相关信息的字典。
其中,im_info更新字段为:
-
augment
_shape (np.ndarray): 扩张后的图像高、宽二者组成的np.ndarray,形状为(2,)。
-
image
_shape (np.ndarray): 扩张后的图像高、宽二者组成的np.ndarray,形状为(2,)。
label_info更新字段为:
- gt_bbox (np.ndarray): 随机扩张后真实标注框坐标,形状为(n, 4),
其中n代表真实标注框的个数。
...
...
@@ -754,9 +748,6 @@ class RandomExpand:
raise
TypeError
(
'Cannot do RandomExpand! '
+
'Becasuse the im_info and label_info can not be None!'
)
if
'augment_shape'
not
in
im_info
:
raise
TypeError
(
'Cannot do RandomExpand! '
+
\
'Becasuse augment_shape is not in im_info!'
)
if
'gt_bbox'
not
in
label_info
or
\
'gt_class'
not
in
label_info
:
raise
TypeError
(
'Cannot do RandomExpand! '
+
\
...
...
@@ -764,9 +755,9 @@ class RandomExpand:
if
np
.
random
.
uniform
(
0.
,
1.
)
<
self
.
prob
:
return
(
im
,
im_info
,
label_info
)
augment_shape
=
im_info
[
'augment
_shape'
]
height
=
int
(
augment
_shape
[
0
])
width
=
int
(
augment
_shape
[
1
])
image_shape
=
im_info
[
'image
_shape'
]
height
=
int
(
image
_shape
[
0
])
width
=
int
(
image
_shape
[
1
])
expand_ratio
=
np
.
random
.
uniform
(
1.
,
self
.
ratio
)
h
=
int
(
height
*
expand_ratio
)
...
...
@@ -779,7 +770,7 @@ class RandomExpand:
canvas
*=
np
.
array
(
self
.
fill_value
,
dtype
=
np
.
float32
)
canvas
[
y
:
y
+
height
,
x
:
x
+
width
,
:]
=
im
im_info
[
'
augment
_shape'
]
=
np
.
array
([
h
,
w
]).
astype
(
'int32'
)
im_info
[
'
image
_shape'
]
=
np
.
array
([
h
,
w
]).
astype
(
'int32'
)
if
'gt_bbox'
in
label_info
and
len
(
label_info
[
'gt_bbox'
])
>
0
:
label_info
[
'gt_bbox'
]
+=
np
.
array
([
x
,
y
]
*
2
,
dtype
=
np
.
float32
)
if
'gt_poly'
in
label_info
and
len
(
label_info
[
'gt_poly'
])
>
0
:
...
...
@@ -835,7 +826,9 @@ class RandomCrop:
tuple: 当label_info为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
当label_info不为空时,返回的tuple为(im, im_info, label_info),分别对应图像np.ndarray数据、
存储与标注框相关信息的字典。
其中,label_info更新字段为:
其中,im_info更新字段为:
- image_shape (np.ndarray): 扩裁剪的图像高、宽二者组成的np.ndarray,形状为(2,)。
label_info更新字段为:
- gt_bbox (np.ndarray): 随机裁剪后真实标注框坐标,形状为(n, 4),
其中n代表真实标注框的个数。
- gt_class (np.ndarray): 随机裁剪后每个真实标注框对应的类别序号,形状为(n, 1),
...
...
@@ -850,9 +843,6 @@ class RandomCrop:
raise
TypeError
(
'Cannot do RandomCrop! '
+
'Becasuse the im_info and label_info can not be None!'
)
if
'augment_shape'
not
in
im_info
:
raise
TypeError
(
'Cannot do RandomCrop! '
+
\
'Becasuse augment_shape is not in im_info!'
)
if
'gt_bbox'
not
in
label_info
or
\
'gt_class'
not
in
label_info
:
raise
TypeError
(
'Cannot do RandomCrop! '
+
\
...
...
@@ -861,9 +851,9 @@ class RandomCrop:
if
len
(
label_info
[
'gt_bbox'
])
==
0
:
return
(
im
,
im_info
,
label_info
)
augment_shape
=
im_info
[
'augment
_shape'
]
w
=
augment
_shape
[
1
]
h
=
augment
_shape
[
0
]
image_shape
=
im_info
[
'image
_shape'
]
w
=
image
_shape
[
1
]
h
=
image
_shape
[
0
]
gt_bbox
=
label_info
[
'gt_bbox'
]
thresholds
=
list
(
self
.
thresholds
)
if
self
.
allow_no_crop
:
...
...
@@ -922,7 +912,7 @@ class RandomCrop:
label_info
[
'gt_bbox'
]
=
np
.
take
(
cropped_box
,
valid_ids
,
axis
=
0
)
label_info
[
'gt_class'
]
=
np
.
take
(
label_info
[
'gt_class'
],
valid_ids
,
axis
=
0
)
im_info
[
'
augment
_shape'
]
=
np
.
array
(
im_info
[
'
image
_shape'
]
=
np
.
array
(
[
crop_box
[
3
]
-
crop_box
[
1
],
crop_box
[
2
]
-
crop_box
[
0
]]).
astype
(
'int32'
)
if
'gt_score'
in
label_info
:
...
...
@@ -993,7 +983,7 @@ class ArrangeFasterRCNN:
im_resize_info
=
im_info
[
'im_resize_info'
]
im_id
=
im_info
[
'im_id'
]
im_shape
=
np
.
array
(
(
im_info
[
'
augment_shape'
][
0
],
im_info
[
'augment
_shape'
][
1
],
1
),
(
im_info
[
'
image_shape'
][
0
],
im_info
[
'image
_shape'
][
1
],
1
),
dtype
=
np
.
float32
)
gt_bbox
=
label_info
[
'gt_bbox'
]
gt_class
=
label_info
[
'gt_class'
]
...
...
@@ -1006,7 +996,7 @@ class ArrangeFasterRCNN:
'Becasuse the im_info can not be None!'
)
im_resize_info
=
im_info
[
'im_resize_info'
]
im_shape
=
np
.
array
(
(
im_info
[
'
augment_shape'
][
0
],
im_info
[
'augment
_shape'
][
1
],
1
),
(
im_info
[
'
image_shape'
][
0
],
im_info
[
'image
_shape'
][
1
],
1
),
dtype
=
np
.
float32
)
outputs
=
(
im
,
im_resize_info
,
im_shape
)
return
outputs
...
...
@@ -1086,7 +1076,7 @@ class ArrangeMaskRCNN:
'Becasuse the im_info can not be None!'
)
im_resize_info
=
im_info
[
'im_resize_info'
]
im_shape
=
np
.
array
(
(
im_info
[
'
augment_shape'
][
0
],
im_info
[
'augment
_shape'
][
1
],
1
),
(
im_info
[
'
image_shape'
][
0
],
im_info
[
'image
_shape'
][
1
],
1
),
dtype
=
np
.
float32
)
if
self
.
mode
==
'eval'
:
im_id
=
im_info
[
'im_id'
]
...
...
@@ -1137,7 +1127,7 @@ class ArrangeYOLOv3:
raise
TypeError
(
'Cannot do ArrangeYolov3! '
+
'Becasuse the im_info and label_info can not be None!'
)
im_shape
=
im_info
[
'
augment
_shape'
]
im_shape
=
im_info
[
'
image
_shape'
]
if
len
(
label_info
[
'gt_bbox'
])
!=
len
(
label_info
[
'gt_class'
]):
raise
ValueError
(
"gt num mismatch: bbox and class."
)
if
len
(
label_info
[
'gt_bbox'
])
!=
len
(
label_info
[
'gt_score'
]):
...
...
@@ -1161,7 +1151,7 @@ class ArrangeYOLOv3:
raise
TypeError
(
'Cannot do ArrangeYolov3! '
+
'Becasuse the im_info and label_info can not be None!'
)
im_shape
=
im_info
[
'
augment
_shape'
]
im_shape
=
im_info
[
'
image
_shape'
]
if
len
(
label_info
[
'gt_bbox'
])
!=
len
(
label_info
[
'gt_class'
]):
raise
ValueError
(
"gt num mismatch: bbox and class."
)
im_id
=
im_info
[
'im_id'
]
...
...
@@ -1180,6 +1170,6 @@ class ArrangeYOLOv3:
if
im_info
is
None
:
raise
TypeError
(
'Cannot do ArrangeYolov3! '
+
'Becasuse the im_info can not be None!'
)
im_shape
=
im_info
[
'
augment
_shape'
]
im_shape
=
im_info
[
'
image
_shape'
]
outputs
=
(
im
,
im_shape
)
return
outputs
paddlex/cv/transforms/seg_transforms.py
浏览文件 @
4c3da33a
...
...
@@ -66,8 +66,8 @@ class Compose:
if
self
.
to_rgb
:
im
=
cv2
.
cvtColor
(
im
,
cv2
.
COLOR_BGR2RGB
)
if
label
is
not
None
:
if
not
isinstance
(
label
,
np
.
ndarray
):
label
=
np
.
asarray
(
Image
.
open
(
label
))
for
op
in
self
.
transforms
:
outputs
=
op
(
im
,
im_info
,
label
)
im
=
outputs
[
0
]
...
...
paddlex/utils/utils.py
浏览文件 @
4c3da33a
...
...
@@ -220,3 +220,39 @@ def load_pretrain_weights(exe, main_prog, weights_dir, fuse_bn=False):
len
(
vars_to_load
),
weights_dir
))
if
fuse_bn
:
fuse_bn_weights
(
exe
,
main_prog
,
weights_dir
)
class
EarlyStop
:
def
__init__
(
self
,
patience
,
thresh
):
self
.
patience
=
patience
self
.
counter
=
0
self
.
score
=
None
self
.
max
=
0
self
.
thresh
=
thresh
if
patience
<
1
:
raise
Exception
(
"Argument patience should be a positive integer."
)
def
__call__
(
self
,
current_score
):
if
self
.
score
is
None
:
self
.
score
=
current_score
return
False
elif
current_score
>
self
.
max
:
self
.
counter
=
0
self
.
score
=
current_score
self
.
max
=
current_score
return
False
else
:
if
(
abs
(
self
.
score
-
current_score
)
<
self
.
thresh
or
current_score
<
self
.
score
):
self
.
counter
+=
1
self
.
score
=
current_score
logging
.
debug
(
"EarlyStopping: %i / %i"
%
(
self
.
counter
,
self
.
patience
))
if
self
.
counter
>=
self
.
patience
:
logging
.
info
(
"EarlyStopping: Stop training"
)
return
True
return
False
else
:
self
.
counter
=
0
self
.
score
=
current_score
return
False
setup.py
浏览文件 @
4c3da33a
...
...
@@ -29,7 +29,8 @@ setuptools.setup(
packages
=
setuptools
.
find_packages
(),
setup_requires
=
[
'cython'
,
'numpy'
,
'sklearn'
],
install_requires
=
[
'pycocotools'
,
'pyyaml'
,
'colorama'
,
'tqdm'
,
'visualdl==1.3.0'
,
"pycocotools;platform_system!='Windows'"
,
'pyyaml'
,
'colorama'
,
'tqdm'
,
'visualdl==1.3.0'
,
'paddleslim==1.0.1'
,
'paddlehub>=1.6.2'
],
classifiers
=
[
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录