FF各位可爱的 OpenMMLab 社区小伙伴儿们,MICCAI 2023 国际会议的头影关键点检测挑战赛 CL-Detection2023 已经开放注册啦!参加此次挑战赛,您将获得 500 欧元的现金奖励;组织方还邀请优秀参赛团队作为 CO-author 一同撰写挑战赛综述论文,投稿到 MedAI 或 TMI 高影响力期刊。
MMPose 作为 OpenMMLab 系列中的姿态估计算法库,已经成为姿态估计领域覆盖算法最多,功能最全的开源算法库之一。为此,我们技术团队协同挑战赛组织方为大家提供了一个简单易用的基线模型 CL-Detection2023-MMPose,助您在挑战赛中取得出色的成绩。
代码基于 MMPose 框架进行构建,开箱即用,并且包含详细的中文代码注释,可放心食用。下面详细内容是手把手的使用教程。
太长不看版:
● QQ交流群:808896849
● 挑战赛官网:https://cl-detection2023.grand-challenge.org/
● 代码仓库链接:https://github.com/szuboy/CL-Detection2023-MMPose
● 挑战赛数据:2D 数据,38 个关键点,无需高配置,8G 显卡,即可轻松参与
● 基线模型性能表现在排行榜上第 10 名,优化空间巨大,SOTA 也不是没可能
前言
这个仓库提供了一个基于 MMPose 框架的的解决方法,让大家都可以更方面进行切换使用流程中的各个组件;同时,该仓库也提供了 Docker 镜象的封装的教程,可以保证大家能够把自己算法模型上传到排行榜进行验证(欢迎加入QQ交流答疑群:808896849)!
大家都知道把大象放冰箱分 3 步,本仓库基线模型的代码共分为了 6 步,每一步都有详细指导,您按照下面的教程来一步一步地复现本仓库结果或者定制自己的模型,完全是轻而易举的事情,因为该踩的坑我们都帮大家踩了,大家直接冲就对了!
第一步 安装使用
(1)配置好相关的依赖环境: 使用 MIM 安装 MMEngine 和 MMCV,保证工程中的 MMPose 的源码自定义安装能够顺利完成安装和使用:
pip install -U openmim
mim install mmengine
mim install "mmcv>=2.0.0"
(2)下载本仓库提供的代码:本仓库使用的是 MMPose V1.0.0(PyTorch 版本 1.8+),并进行了一些适配,可直接通过 Git 克隆本仓库或者是右上角的 Download ZIP 即可,再进行安装操作 MMPose。
本仓库提供的 MMPose 增加了适配挑战赛的数据集 CephalometricDataset 和 CephalometricMetric 类,可以大家的使用体验更加丝滑:
git clone https://github.com/szuboy/CL-Detection2023-MMPose.git
cd CL-Detection2023-MMPose/mmpose_package/mmpose
pip install -r requirements.txt
pip install -v -e .
# "-v" 表示输出更多安装相关的信息
# "-e" 表示以可编辑形式安装,这样可以在不重新安装的情况下,让本地修改直接生效
提醒:MMPose 当前官方版本为 V1.1.0,为了保证实验结果与本教程一致,请大家使用本仓库进行安装和实验。
(3)测试上述安装是否正常: 可执行 step1_test_mmpose.py 测试脚本来进行验证上述操作已经成功执行,能够正常使用 MMPose 进行模型的训练和测试,预期结果将得到对应的版本输出:
import mmpose
print(mmpose.__version__)
# 预期输出结果:1.0.0
第二步 数据下载和预处理
(1)加入 CL-Detection2023 挑战赛:可通过访问 Grand-Challenge 挑战赛平台,然后点击加入我们的挑战赛,平台链接:https://grand-challenge.org/challenges/
(2)发送注册表格到指定邮箱,获取训练数据: 您应该提前下载好挑战赛提供的训练数据的图像和标注的文件,可参考挑战赛官网的详细指导,具体来说:您仅需要发送注册表文件到对应的邮件,拿到数据的可访问链接,下载解压缩挑战赛提供的图像数据 train_stack.mha,含有 400 张头影图像;以及对应的标注文件 train-gt.json,包含 38 个关键点的标注坐标。
(3)数据的预处理:对于 CL-Detection 2023 挑战赛的数据集,组织方考虑到存储的支出和分发的便利,所有图像都进行 0 填充到统一的大小尺度(2400, 2880, 3)。因此,不相关的区域的去除,同时生成 MMPose 框架的要求的 JSON 文件格式,这个预处理脚本的主要功能就是处理这两个问题。您可以执行脚本 step2_prepare_coco_dataset.py 来自动执行上述操作,有以下两种方案进行数据预处理操作,
parser = argparse.ArgumentParser()
# data parameters
parser.add_argument('--mha_file_path', type=str, default='/data/zhangHY/CL-Detection2023/train_stack.mha')
parser.add_argument('--train_gt_path', type=str, default='/data/zhangHY/CL-Detection2023/train-gt.json')
# save processed images dir path
parser.add_argument('--image_save_dir', type=str, default='/data/zhangHY/CL-Detection2023/MMPose')
experiment_config = parser.parse_args()
main(experiment_config)
python step2_prepare_coco_dataset.py \
--mha_file_path='/data/zhangHY/CL-Detection2023/train_stack.mha' \
--train_gt_path='/data/zhangHY/CL-Detection2023/train-gt.json' \
--image_save_dir='/data/zhangHY/CL-Detection2023/MMPose'
代码运行后,您会得到一个去除零填充的图像的文件夹和三个 JSON 文件。此仓库采取训练-验证-测试的模式,模型在训练集上进行训练,在验证集上进行模型训练和超参数的选择,然后在测试集上测试得到最终的模型性能表现,下图是一些图像示例及其对应的关键点:
现有的划分逻辑是将 400 张图像随机划分,其中:训练集 300 张图像,验证集和测试集各 50 张图。当然,您可以按照您的想法来进行划分,不必按照这个模式来操作,比如:只划分训练集和验证集,以此来增加训练集的照片数量,这或许可以提升模型的性能表现。
注意: 为了适配 MMPose 框架的要求,我们需将 CL-Detection2023 挑战赛提供的训练数据及其对应的标注文件转换为采用的类似于 COCO 格式,然后进行解析。其中唯一不同的是:我们在 image 的信息中增加了像素间的距离 spacing 键值,以保证后续的指标计算。同时,目前的生成 JSON 数据文件中的 bbox 键的检测框的坐标值默认是图像的宽高大小,这种设置出发点的主要考虑的因素有两个:
下面是一个 JSON 文件的示例,可以从中观察到:增加的 spacing 键,以及默认的 bbox 和 area 对应的值均为图像大小:
"images": [
{
"id": 104,
"file_name": "104.png",
"width": 1935,
"height": 2400,
"spacing": 0.1
}
...
]
"annotations": [
{
"id": 104,
"image_id": 104,
"category_id": 1,
"keypoints": [...],
"iscrowd": 0,
"bbox": [
0,
0,
1935,
2400
],
"area": 4644000
},
...
]
如果到了这里,恭喜您,你已经成功了一半了。到现在,我们已经有了图像数据和准备好符合 MMPose 的配置文件。接下来就是您的主场啦,您可以随意的在 MMPose 上进行各个模块组合寻优,在排行榜上占据一席之地🔥。
第三步 模型训练
笔者在这里已经提供了一个基于 HRNet 的基线模型的完整配置文件在 cldetection_configs 文件夹中,几乎所有的配置项都在上面任君配置,然后按照您使用 MMPose 的经验来操作使用,简直是无缝切换呀~
放心哈,本仓库提供的 step3_train_and_evaluation.py 文件其实就是 MMPose 下的 tools/train.py 文件,只是重新命名文件名,让大家的逻辑更加清晰,因此,您可以直接这么使用进行训练验证:
CUDA_VISIBLE_DEVICES=0 python step3_train_and_evaluation.py \
cldetection_configs/td-hm_hrnet-w32_udp-8xb64-250e-512x512_KeypointMSELoss.py \
--work-dir='/data/zhangHY/CL-Detection2023/MMPose-checkpoints'
执行完后,你将可能看到如下所示的输出信息,以及模型在验证集数据上的一些评价指标,对应的模型权重将您指定的 --work-dir 保存路径中,可以去查看到对应的权重文件:
06/28 03:16:23 - mmengine - INFO - Epoch(train) [1][ 1/38] lr: 5.000000e-07 eta: 15:19:03 time: 5.805217 data_time: 1.997265 memory: 8801 loss: 0.010580 loss_kpt: 0.010580 acc_pose: 0.000000
06/28 03:16:24 - mmengine - INFO - Epoch(train) [1][ 2/38] lr: 1.501002e-06 eta: 8:38:45 time: 3.277006 data_time: 1.012852 memory: 9545 loss: 0.010586 loss_kpt: 0.010586 acc_pose: 0.003289
06/28 03:16:24 - mmengine - INFO - Epoch(train) [1][ 3/38] lr: 2.502004e-06 eta: 6:23:50 time: 2.425048 data_time: 0.683300 memory: 9545 loss: 0.010585 loss_kpt: 0.010585 acc_pose: 0.000000
...
06/28 03:17:27 - mmengine - INFO - Epoch(train) [2][38/38] lr: 7.557515e-05 eta: 2:24:11 time: 0.868173 data_time: 0.190205 memory: 5290 loss: 0.008559 loss_kpt: 0.008559 acc_pose: 0.960526
06/28 03:17:27 - mmengine - INFO - Saving checkpoint at 2 epochs
06/28 03:17:35 - mmengine - INFO - Epoch(val) [2][ 1/13] eta: 0:00:17 time: 1.422160 data_time: 1.269009 memory: 1233
06/28 03:17:35 - mmengine - INFO - Epoch(val) [2][ 2/13] eta: 0:00:08 time: 0.775162 data_time: 0.637363 memory: 1233
...
06/28 03:17:39 - mmengine - INFO - Evaluating CephalometricMetric...
=> Mean Radial Error : MRE = 5.017 ± 6.976 mm
=> Success Detection Rate : SDR 2.0mm = 19.211% | SDR 2.5mm = 28.789% | SDR 3mm = 39.000% | SDR 4mm = 56.737%
06/28 03:17:39 - mmengine - INFO - Epoch(val) [2][13/13] MRE: 5.017320 SDR 2.0mm: 19.210526 SDR 2.5mm: 28.789474 SDR 3.0mm: 39.000000 SDR 4.0mm: 56.736842data_time: 0.274684 time: 0.42305
如果您想知道更多的配置选择可以参考对应的 MMPose文档,亦可以加入挑战赛的群聊向组织方小助手提出来,请放心哈,小助手会耐心给大家解决问题滴~
注意事项:不过这份配置文件中,有几个重点很值得强调一下,让大家不要进行修改的部分,避免大家掉进坑里面去,如果您不知道背后的原理:
(1)保存模型权重依据: 默认保存模型 checkpoints 的依据是 SDR 2.0mm 评价指标,这是一个自定义的评价指标,官方的 MMPose 中是没有的,但是该仓库的修改后版本中已经带有,可放心食用,对应着下面的这段配置代码:
default_hooks = dict(
timer=dict(type='IterTimerHook'),
logger=dict(type='LoggerHook', interval=1),
param_scheduler=dict(type='ParamSchedulerHook'),
checkpoint=dict(
type='CheckpointHook',
interval=2,
save_best='SDR 2.0mm',
rule='greater'),
sampler_seed=dict(type='DistSamplerSeedHook'),
visualization=dict(type='PoseVisualizationHook', enable=False))
val_evaluator = dict(
type='CephalometricMetric')
SDR 2.0mm 也是挑战赛排行榜上的评价指标,因此我暂时使用它作为 save_best 的依据;当然,您也可以选择另外一个排行榜上的评价指标 MRE,或者其他的 SDR 2.5mm、SDR 3.0mm 和 SDR 4.0mm 作为保存最好模型的依据。 不过注意喔,如果选择了 MRE 作为保存的依据,记得将 rule 修改为 rule='less',因为我们当然是希望误差越来小啦。
(2)数据类型兼容使用:CL-Detection2023 挑战赛为每一张图像都标注了 38 个关键点,MMPose 框架现有的数据配置不兼容,因此,为了大家不折腾这些数据配置信息,本仓库的 MMPose 版本已经进行了兼容实现,可直接使用,具体体现在下面这段配置文件中:
dict(type='PackPoseInputs', meta_keys=('id', 'img_id', 'img_path', 'category_id', 'crowd_index', 'ori_shape',
'img_shape', 'input_size', 'input_center', 'input_scale', 'flip',
'flip_direction', 'flip_indices', 'raw_ann_info', 'spacing'))
dataset_type = 'CephalometricDataset'
data_mode = 'topdown'
data_root = '/data/zhangHY/CL-Detection2023'
重要的事情说三遍,记得每一个 PackPoseInputs 后面的 meta_keys 添加上对应的这些键,特别是 spacing,不然就会报错,导致评价指标将无法进行计算。
好了,现在完全可以开始定制属于自己的配置文件了。由于我们的数据集是基于 COCO 格式的,所以,您可以参考 MMPose 框架下 configs/body_2d_keypoint/topdown_heatmap/coco的自带的配置文件进行任意配置(到了这里,项目进度80%🥳)。
第四步 测试和可视化预测结果
在这里,您可以运行脚本 step4_test_and_visualize.py 来对训练好的模型进行独立的测试,看看性能表现如何,并可视化图片的38个关键点的预测结果。具体来说,您可以终端中进行以下操作:
CUDA_VISIBLE_DEVICES=0 python step4_test_and_visualize.py \
cldetection_configs/td-hm_hrnet-w32_udp-8xb64-250e-512x512_KeypointMSELoss.py \
'/data/zhangHY/CL-Detection2023/MMPose-checkpoints/best_SDR 2.0mm_epoch_40.pth' \
--show-dir='/data/zhangHY/CL-Detection2023/MMPose-visualize'
稍等一会,运行完脚本后,可以观察到模型在独立测试集上的 MRE 和 SDR 一系列指标的性能表现。下面表格是作者尝试的不同配置的性能结果,仓库代码未固定随机种子,如果复现结果与作者提供的实验结果有一点点偏差,这是正常的。同样地,可以在可视化文件夹下观察到一些关键点预测结果的可视化。
放心哈,这个脚本也是 MMPose 框架下 tools/test.py 脚本重命名后的脚本名字,可以让大家更容易的理解和使用,想要知道更多的配置和使用方案,可以参考具体的 MMPose 使用文档,或者是加入我们的官方交流答疑群,保证包教包会。
第五步 预测得到 expected_output.json
挑战赛要求上传的是封装模型的 Docker 文件,为了进行测试 Docker 内部的预测结果和本地模型的预测结果是否一致。因此,我们提供了一个 stack1.mha 的测试文件(其实就是包含两张图的一个文件而已,模拟看不见的测试数据,数据放在了 step6_docker_and_upload/test 文件夹下),要求您加载这个数据,使用训练好的模型进行预测,然后保留下来挑战赛希望的格式。
这整个流程相当于在本地模拟一个完整的预测过程,避免了在 Docker 内部疯狂找 Bugs,那只会疯狂掉头发。下面是 CL-Detection2023 挑战赛要求的结果保存下来的数据格式解释:
{
"name": "Orthodontic landmarks",
"type": "Multiple points",
"points": [
{
"name": "1",
"point": [
831.4453125,
993.75,
1
]
},
{
"name": "2",
"point": [
1473.92578125,
1035.9375,
1
]
},
...
],
"version": {
"major": 1,
"minor": 0
}
}
不同模型得到的 points 内的关键点预测结果是不同的哈,不用和示例的一样,这只是一个参考说明。points 预测列表中的每一个元素必须包含两个键值:
要得到模型在本地的 expected_output.json,同样地,您有两种方法来执行 step5_predict_expected_output.py 脚本操作;但如果您开发的关键点定位流程中补充增加了其他的操作,也还请修改相应的代码进行适配哈:
# config file | 模型的配置文件
parser.add_argument('--config_file', type=str, default='./cldetection_configs/td-hm_hrnet-w32_udp-8xb64-250e-512x512_KeypointMSELoss.py')
# data parameters | 数据文件路径和配置文件的路径
parser.add_argument('--load_mha_path', type=str, default='./step5_docker_and_upload/test/stack1.mha')
parser.add_argument('--save_json_path', type=str, default='./step5_docker_and_upload/test/expected_output.json')
# model load dir path | 最好模型的权重文件路径
parser.add_argument('--load_weight_path', type=str, default='/data/zhangHY/CL-Detection2023/MMPose-checkpoints/best_SDR 2.0mm_epoch_40.pth')
# model test hyper-parameters
parser.add_argument('--cuda_id', type=int, default=0)
experiment_config = parser.parse_args()
main(experiment_config)
python step5_predict_expected_output.py \
--config_file='./cldetection_configs/td-hm_hrnet-w32_udp-8xb64-250e-512x512_KeypointMSELoss.py' \
--load_mha_path='./step5_docker_and_upload/test/stack1.mha' \
--save_json_path='./step5_docker_and_upload/test/expected_output.json' \
--load_weight_path='/data/zhangHY/CL-Detection2023/MMPose-checkpoints/best_SDR 2.0mm_epoch_40.pth'
--cuda_id=0
注意:由于stack1.mha只有两张测试图片,这个脚本将很快运行完,但是不同模型算法的 expected_output.json 是不同的,想要测试自己的模型算法,必须重新执行一遍得到属于您的模型的期待的输出,以保证第六步的代码测试。
第六步 封装为 docker 上传到 Grand-Challenge 平台
首先,需要保证自己的本地计算平台安装了Docker 和 NVIDIA Container Toolkit 这是两个重要的依赖,这是整个算法封装的重要前提。前者是保证可以进行封装,后者是保证Docker中可以调用GPU,一定一定确保自己的系统已经安装配置好! 如果在安装问题中有什么疑问,还请加入挑战赛交流群,小助手依旧会来帮助大家解决滴~
其次,注意修改 requirements.txt 文件中的代码工程的相关依赖(拉取的镜像中已经包含了 torch 模块,请不要重复安装哈),保证预测过程中的相关依赖库都在里面,才能会正确地执行预测代码,得到预测结果。
继而,将 mmpose_package 整个文件夹、模型的权重文件(不要有空格和小数点)和 cldetection_utils.py 工具文件拷贝到 step6_docker_and_upload 目录,保证 Dockerfile 中可以安装到 MMPose 框架,并成功加载到模型权重;同时,别忘了将step5_predict_expected_output.py 脚本预测的 expected_output.json 拷贝到 test 文件夹,最终文件夹结构如下所示:
│ test.sh
│ Dockerfile
│ build.sh
| best_model_weight.pth # 将最好的模型权重文件修改为这个名字,不要包含空格和小数点
│ process.py
│ requirements.txt
│ export.sh
│ .dockerignore
| cldetection_utils.py
| td-hm_hrnet-w32_udp-8xb64-250e-512x512_KeypointMSELoss.py
│
├─test
│ stack1.mha
│ expected_output.json
│
└─mmpose_package
│ mmpose
│ ...
|
如果你有特别拷贝其他的文件或者文件夹,亦或者更新了文件名字,请及时修改下面的这段代码,但都是拷贝到 /opt/algorithm/ 目录下,还请不要修改这个目标目录,不然错误就会找上门了,除非您知道自己在干什么:
# 选择修改:
# 拷贝相关的依赖文件到 /opt/algorithm/ 目录下,包括处理的 python 代码,模型的权重和配置文件等,
# 请注意:docker 有一个拷贝文件夹的bug,只会拷贝文件夹的内容,所以拷贝文件夹后面必须加上根目录
COPY --chown=algorithm:algorithm process.py /opt/algorithm/
COPY --chown=algorithm:algorithm mmpose_package /opt/algorithm/mmpose_package
COPY --chown=algorithm:algorithm cldetection_utils.py /opt/algorithm/
COPY --chown=algorithm:algorithm best_model_weight.pth /opt/algorithm/
COPY --chown=algorithm:algorithm td-hm_hrnet-w32_udp-8xb64-250e-512x512_KeypointMSELoss.py /opt/algorithm/
然后,将自己的算法过程推理测试过程编程在 process.py 文件中的 predict() 函数中,并根据自己的 predict() 返回值更改 save() 函数,其实这一步就是第五步的执行过程,只是一些数据路径被强制规定了。
再而,在终端上通过 sudo ./build.sh 命令来执行 build.sh 脚本(脚本的代码内容一行都不要修改哈)进行构建,检查是否构建成功,进行错误排除;如果一切顺利,您可能会看到类似于以下内容:
[+] Building 298.7s (5/16)
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 4.07kB 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 61B 0.0s
=> [internal] load metadata for docker.io/pytorch/pytorch:1.9.0-cuda11.1-cudnn8-devel 3.2s
=> CANCELED [ 1/12] FROM docker.io/pytorch/pytorch:1.9.0-cuda11.1-cudnn8-devel@sha256:ed167cae955fa654c 295.5s
=> => resolve docker.io/pytorch/pytorch:1.9.0-cuda11.1-cudnn8-devel@sha256:ed167cae955fa654cefc3663fc0c7b 0.0s
...
...
=> => naming to docker.io/library/cldetection_alg_2023 0.0s
最后,执行 sudo ./test.sh 脚本核实 Docker 输出结果和本地预测的结果是否一致,如果一致将看到输入信息:Tests successfully passed...,说明本地的模型的预测结果和 Docker 中的结果是一致的;如若不一致,还请认真排查错误,或者积极向挑战赛组织方积极反馈:
...
}
],
"type": "Multiple points",
"version": {
"major": 1,
"minor": 0
}
}
Tests successfully passed...
cldetection_alg_2023-output-b35388ee544f2a598b5fb5b088494e5c
最后的最后,直接执行 sudo ./export.sh 脚本导出可以上传到挑战赛平台的 CLdetection_Alg_2023.tar.gz 名字的 Docker 文件,可以在 Grand-Challenge 平台上创建算法页进行提交(进度 100%,完美撒花 🌷)。
备注:给各位 OpenMMLab 各位小可爱们的建议
此仓库仅仅只是提供一个基线模型以及一个完整的训练+测试+封装的流程给到各位参赛者,当前基线模型性能的表现并没有很高(然后在排行榜上排名第 10),MMPose 技术团队和组织方可能觉得还可以从以下方向进行优化,仅供参考: