Open3

mmposeで骨格推定

kun432kun432

https://tech-blog.abeja.asia/entry/advent-2022-day18

上記の記事では、パドックで競走馬が歩いている動画を骨格推定して、過去のパドックと比較して当日の調子を推定する、というもの。

自分がやりたいなーと思っているのは、デビュー前2歳馬の馬体写真から骨格推定して、過去の活躍馬の骨格と比較して、今後活躍する馬をピックアップするというやつ。POG向けに面白そう。

ということで、ここで使用されているmmposeを試してみる。

https://github.com/open-mmlab/mmpose

kun432kun432

Getting StartedのInstallationに従ってデモが動くところまで進める。今回はローカルのJupyterLabで。

https://mmpose.readthedocs.io/en/latest/installation.html

まずmmposeインストールでめちゃめちゃ苦労したので、ポイントだけ。

  • mmposeが依存するmmengine、mmcv、mmdet(mmdetはデモスクリプトで使用されている)のインストールが必要
    • このうちmmcvは使用しているCUDAやPyTorchのバージョンにあわせてインストール時の指定を変える必要がある。
      • ここがうまくできていないとNo module named ‘mmcv._ext’でコケる。
      • mmcvはPyTorchのパッケージ名のバージョンを見ているため、PyTorchのインストールから意識する必要がある
  • mmpose、mmcv、mmdetはバージョンを揃える必要がある。
    • それぞれ大きなバージョンアップにより、それぞれが2系統のバージョンを持っている。
    • mmpose v1.xの場合は、mmcv v2.X、mmdet v3.Xを使用する必要がある
    • 依存パッケージが新しすぎてもダメだったり、とか、依存パッケージが他のパッケージのバージョンに追いついてないとかもある、とかで、どの組み合わせが最適なのかよくわからない。
      • mmdetなんかはAssertionError: MMCV==2.2.0 is used but incompatible. Please install mmcv>=2.0.0rc4, <2.2.0とかでコケる。
      • mmcvを古いバージョンにしてやるとNo module named ‘mmcv._ext’でコケる。
      • mmdetのソースを一部修正、上記のバージョンチェック部分を甘めにして回避した。
  • Colab向けチュートリアルのnotebookがあるが、どうも内容が古そう。
    • 2024/05/23時点でColabのCUDAは12.2だが、notebookは11.8想定、mmcvが対応しているのは12.1までっぽい。
      • cu121に書き換えれば動く
    • mmcvのインストールの箇所で1時間ぐらいかかる。

ただ、あまりにも色々躓いたので記憶がやや不確か&推測も多分に含まれるし、あくまでもこの流れでたまたま動いた一例、というメモなのでご注意。

環境

  • Python-3.11.4
  • CUDA 11.8
  • 仮想環境を作成、その中でJupyterLabを起動して実施。
%%bash
# check NVCC version
nvcc -V
echo

# check GCC version
gcc --version
echo

# check python version
python --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2022 NVIDIA Corporation
Built on Wed_Sep_21_10:33:58_PDT_2022
Cuda compilation tools, release 11.8, V11.8.89
Build cuda_11.8.r11.8/compiler.31833905_0

gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Python 3.11.4

PyTorchのインストール

もう覚えてないけどwheelは確かビルドに必要だったはず。

%%bash
pip install -U pip wheel
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

バージョンを確認する。

!python -c 'import torch;print(torch.__version__);print(torch.version.cuda)'
2.3.0+cu118
11.8

この+cuXXXをmmcvが見ているらしく、普通にPyTorchをインストールすると以下のようになるが、これだとダメらしい。

2.3.0

mmengine/mmcv/mmdetのインストール

推奨されているのはMIMという、OpenMMプロジェクト全体で推奨されているパッケージ/モデル管理ツールでのインストールだけど、これだとうまくいかなかったのでpipでやる。(MIM自体はモデルのダウンロードとかでは便利に使えるので後でインストールする)

重要なのは、使っているCUDA/PyTorchのバージョンに合わせて、mmcvをインストールするというところ。それぞれのバージョンも指定したほうが確実な気がするけど、一応今回はこれで動いたということで。

!pip install mmengine
!pip install mmcv -f https://download.openmmlab.com/mmcv/dist/cu118/torch2.3.0/index.html
!pip install mmdet

実際にインストールされたバージョンは以下。

  • mmengine-0.10.4
  • mmcv-2.2.0
  • mmdet-3.3.0

インストールできたらまずmmcvが動作するかを確認。

from mmcv.ops import get_compiling_cuda_version, get_compiler_version

print('cuda version:', get_compiling_cuda_version())
print('compiler information:', get_compiler_version())

以下のように表示されればOK。ここがダメならNo module named ‘mmcv._ext’になるので、インストール手順が間違っていないかを確認。間違ってなければ、いっそCUDAのバージョンを変えるなりして再度PyTorch/mmcvのインストールをやり直したほうが良いかもしれない。

cuda version: 11.8
compiler information: GCC 9.3

で、mmdet内でmmcvのバージョンを見ているところがある。自分の場合は以下のパスだった。

./.venv/lib/python3.11/site-packages/mmdet/__init__.py
(snip)
mmcv_minimum_version = '2.0.0rc4'
mmcv_maximum_version = '2.2.0'
(snip)
assert (mmcv_version >= digit_version(mmcv_minimum_version)
        and mmcv_version < digit_version(mmcv_maximum_version)), \
    f'MMCV=={mmcv.__version__} is used but incompatible. ' \
    f'Please install mmcv>={mmcv_minimum_version}, <{mmcv_maximum_version}.'
(snip)

mmcv_maximum_version2.2.1あたりにしておく。

mmposeのインストール

mmposeはソースからインストールする

!git clone https://github.com/open-mmlab/mmpose.git
%cd mmpose
!pip install -r requirements.txt
!pip install -v -e .

mmposeの確認。

# Check MMPose installation
import mmpose

print('mmpose version:', mmpose.__version__)

バージョンが表示されればOK。

mmpose version: 1.3.1

デモ

ではデモを動かす。デモを動かすためにはコンフィグとチェックポイントが必要になる。MIMを使ってダウンロードする。

!pip install -U openmim
!mim download mmpose --config td-hm_hrnet-w48_8xb32-210e_coco-256x192  --dest .

デモで使われている画像はクローンしたレポジトリ内に含まれている。この画像。

デモ用のスクリプトを動かす。

!python demo/image_demo.py \
    tests/data/coco/000000000785.jpg \
    td-hm_hrnet-w48_8xb32-210e_coco-256x192.py \
    td-hm_hrnet-w48_8xb32-210e_coco-256x192-0e67c616_20220913.pth \
    --out-file vis_results.jpg \
    --draw-heatmap

出力されたファイルを表示

from IPython.display import Image

display(Image("./vis_results.jpg"))

Pythonコードだとこう。

from mmpose.apis import inference_topdown, init_model
from mmpose.utils import register_all_modules

register_all_modules()

config_file = 'td-hm_hrnet-w48_8xb32-210e_coco-256x192.py'
checkpoint_file = 'td-hm_hrnet-w48_8xb32-210e_coco-256x192-0e67c616_20220913.pth'
model = init_model(config_file, checkpoint_file, device='cuda:0')  # or device='cpu'

# please prepare an image with person
batch_results = inference_topdown(model, 'tests/data/coco/000000000785.jpg')
print(batch_results)

keypointsが骨格部分のデータっぽい。

[<PoseDataSample(

    META INFORMATION
    ori_shape: (425, 640)
    pad_shape: (256, 192)
    input_center: array([320. , 212.5], dtype=float32)
    input_scale: array([ 800.    , 1066.6666], dtype=float32)
    input_size: (192, 256)
    dataset_name: 'coco'
    flip_indices: [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15]
    img_path: 'tests/data/coco/000000000785.jpg'
    img_shape: (425, 640)
    batch_input_shape: (256, 192)

    DATA FIELDS
    gt_instance_labels: <InstanceData(
        
            META INFORMATION
        
            DATA FIELDS
        ) at 0x7643fa6a1190>
    pred_instances: <InstanceData(
        
            META INFORMATION
        
            DATA FIELDS
            keypoints: array([[[365.83334 ,  83.333336],
                        [374.16666 ,  75.00001 ],
                        [357.5     ,  75.00001 ],
                        [390.83334 ,  75.00001 ],
                        [357.5     ,  83.333336],
                        [407.5     , 108.333336],
                        [365.83334 , 116.66667 ],
                        [440.83334 , 150.      ],
                        [349.16666 , 158.33333 ],
                        [449.16666 , 166.66667 ],
                        [307.5     , 175.      ],
                        [440.83334 , 208.33333 ],
                        [399.16666 , 216.66667 ],
                        [432.5     , 283.33334 ],
                        [374.16666 , 275.      ],
                        [474.16666 , 366.66666 ],
                        [407.5     , 341.66666 ]]], dtype=float32)
            keypoint_scores: array([[0.9376108 , 0.93626845, 0.90840936, 0.95710725, 0.91465086,
                        0.92970216, 0.8663161 , 0.8555557 , 0.9187598 , 0.9389664 ,
                        0.9539192 , 0.883909  , 0.89276993, 0.92668873, 0.88967854,
                        0.8596858 , 0.9045249 ]], dtype=float32)
            keypoints_visible: array([[0.9376108 , 0.93626845, 0.90840936, 0.95710725, 0.91465086,
                        0.92970216, 0.8663161 , 0.8555557 , 0.9187598 , 0.9389664 ,
                        0.9539192 , 0.883909  , 0.89276993, 0.92668873, 0.88967854,
                        0.8596858 , 0.9045249 ]], dtype=float32)
            bbox_scores: array([1.], dtype=float32)
            bboxes: array([[  0.,   0., 640., 425.]], dtype=float32)
        ) at 0x7643f5d82f50>
    gt_instances: <InstanceData(
        
            META INFORMATION
        
            DATA FIELDS
            bboxes: array([[  0.,   0., 640., 425.]], dtype=float32)
            bbox_scales: array([[ 800.    , 1066.6666]], dtype=float32)
            bbox_scores: array([1.], dtype=float32)
        ) at 0x7643fa6a0e50>
) at 0x7643fa6a0790>]

というかこれでまだスタートラインだから。。。