本文へスキップ
SuiCool

Raspberry Pi 5にCVATを導入(2)— 自動アノテーション機能の追加

Suicool12分

はじめに#

前回の記事(Raspberry Pi 5にCVATを導入(1))では、CVATをRaspberry Pi 5の上で動かすところまで書きました。

ただ、実際に画像へbboxを引き始めるとすぐ気付くのですが、手動アノテーションはかなりしんどいです。100枚を超えたあたりから集中力が切れてきて、500枚目には作業者のメンタルが先に死ぬ、というのが個人的な肌感ですね。

そこで今回は、CVATの自動アノテーション機能(Auto Annotation / AI Tools)をRaspberry Pi 5上にセットアップしてみます。CVATはサーバーレス基盤のNuclio経由で推論関数を呼び出す仕組みになっているので、Nuctl(NuclioのCLI)のインストールと、ARM64で動く推論関数のデプロイがゴールです。

なお、CVAT公式が提供しているserverless functionはほとんどがx86_64前提(pytorch/pytorchやopenvino/ubuntu22_runtimeなど、ARM64イメージがpublishされていないものが多い)で、ARM64でそのまま動くものは限られます。今回はその中でも比較的素直に動くONNXベースのYOLOv7関数を題材にします。

検証時の構成#

項目内容
PCRaspberry Pi 5( 8 GB)
OSRaspberry Pi OS(64bit 6.12.75+rpt-rpi-v8)
CVATv2.64.0
Nuclio1.15.9(CVAT v2.64.0のcomponents/serverless/docker-compose.serverless.ymlで指定されているバージョン)
採用モデルYOLOv7 (ONNX, yolov7-nms-640.onnx)

※1作目で導入したCVAT v2.64.0がそのまま動いていることを前提に進めます。

1.自動アノテーションの仕組み#

CVATの自動アノテーションは、ざっくり下のような3層構成で動いています。

[CVAT UI] ──HTTP──> [CVAT server] ──HTTP──> [Nuclio dashboard / function] ──> [推論モデル本体]
                                              (cvat_cvat ネットワーク内)
  • CVAT serverはUIから「AI Tools → Detectors」のリクエストを受け取ると、対応するNuclio関数のHTTPエンドポイントを叩きに行きます。
  • Nuclioは推論関数(detector / tracker / interactor)を個別のDockerコンテナとして起動・管理するサーバーレスフレームワークですね。
  • 個々のserverless functionは、serverless/<framework>/<author>/<model>/nuclio/ 配下の function.yamlmain.pyDockerfile(あれば)で定義されています。

ARM64で動かす上での要点は、このfunction.yamlが指定するbaseImageがARM64マルチアーキ対応かどうかに尽きます。pytorch/pytorch系はARM64ビルドが事実上提供されていないので、onnxruntimeベースの関数を選んでおくのが無難です。

2.事前準備#

Nuctl(Nuclio CLI)のインストール#

CVAT v2.64.0はcomponents/serverless/docker-compose.serverless.ymlの中でNuclioダッシュボードイメージquay.io/nuclio/dashboard:1.15.9-amd64を指定しているので、CLI側も同じ1.15.9で揃えます。

公式ドキュメントの例はlinux-amd64ですが、Raspberry Pi 5はARM64なので**linux-arm64バイナリ**の方を取ってきます。

# nuctl 1.15.9 の ARM64 バイナリをダウンロード
wget https://github.com/nuclio/nuclio/releases/download/1.15.9/nuctl-1.15.9-linux-arm64

# 実行権限を付与し、/usr/local/bin にシンボリックリンクを張る
sudo chmod +x nuctl-1.15.9-linux-arm64
sudo ln -sf $(pwd)/nuctl-1.15.9-linux-arm64 /usr/local/bin/nuctl

# バージョン確認
nuctl version

Client version: 1.15.9 のように出ればOKです。

CVATをserverless込みで再起動#

docker-compose.yml単体で起動している場合は一度落として、Nuclio入りで起動し直します。

# CVATのリポジトリルートで実行
docker compose -f docker-compose.yml down

# serverless 用の compose ファイルを追加して起動
docker compose -f docker-compose.yml \
  -f components/serverless/docker-compose.serverless.yml up -d

docker compose psnuclio という名前のコンテナが増えていればOKです。Nuclioダッシュボードは既定だとホスト側に出ておらず、CVATサーバーは内部ネットワーク(compose上はcvat、Docker上の実名はcvat_cvat)越しにダッシュボード経由で関数を呼び出します。GUIで関数の状態を見たい場合は後述の手順でホストに公開できます。

3.ARM64向けのデプロイ調整#

ここが今回いちばん引っかかるポイントですね。

CVATが用意しているserverless/deploy_cpu.shは、冒頭で必ず docker build -t cvat.openvino.base "$SCRIPT_DIR/openvino/base" を実行します。このopenvino/baseのDockerfileはFROM openvino/ubuntu22_runtime:2023.3.0で始まっていますが、このイメージはARM64版が公開されていません。そのため、スクリプトをそのまま叩くと最初のbuildで落ちてしまいます。

回避策はいくつかありますが、今回はdeploy_cpu.shを使わず、対象関数だけ nuctl deploy を直接叩く方法を採ります。リポジトリ側に手を入れずに済むので個人的にはおすすめです。

Nuclioプロジェクトの作成#

最初の一度だけ、cvat プロジェクトをNuclio側に作っておきます。

nuctl create project cvat --platform local

すでに作成済みだと error: project already exists 等が出ますが、無視して大丈夫です。

YOLOv7 (ONNX) のデプロイ#

ONNX版のYOLOv7関数(serverless/onnx/WongKinYiu/yolov7/nuclio/function.yamlmetadata.name: onnx-wongkinyiu-yolov7)は、baseImageにubuntu:22.04を使い、ビルド時にwgetpython3-pippython-is-python3をaptで入れたうえでonnxruntimeopencv-python-headlesspillowpyyamlをpip installし、https://github.com/WongKinYiu/yolov7/releases/download/v0.1/yolov7-nms-640.onnxをwgetで取ってくる、というかなり素直な構成です。onnxruntimeはARM64用のwheelが公式に提供されているので、追加改変なしでビルドが通ります。

CVATのリポジトリルートで下記を実行してください。

nuctl deploy --project-name cvat \
  --path serverless/onnx/WongKinYiu/yolov7/nuclio \
  --file serverless/onnx/WongKinYiu/yolov7/nuclio/function.yaml \
  --platform local \
  --env CVAT_FUNCTIONS_REDIS_HOST=cvat_redis_ondisk \
  --env CVAT_FUNCTIONS_REDIS_PORT=6666 \
  --platform-config '{"attributes": {"network": "cvat_cvat"}}'

--platform-confignetworkcvat_cvat を指定しているのは、CVATサーバー側のコンテナと同じDockerネットワーク内に関数コンテナを置くためです(deploy_cpu.shが裏でやっているのと同じことを、手で渡しているだけですね)。

本環境ではビルドに[要計測]分ほどかかります。onnxruntime のwheel取得やyolov7-nms-640.onnx(約75MB)のダウンロード時間が支配的なので、ネットワーク回線次第で結構ぶれます。

デプロイ結果の確認#

nuctl get functions --platform local

onnx-wongkinyiu-yolov7STATEready になっていればOKです。

[要スクリーンショット: nuctl get functions --platform local の出力で yolov7 が ready になっている画面]

GUIで状態を確認したい場合は、docker-compose.serverless.override.ymlを作ってダッシュボードをホストに公開できます。

services:
  nuclio:
    ports:
      - '127.0.0.1:8070:8070'
docker compose -f docker-compose.yml \
  -f components/serverless/docker-compose.serverless.yml \
  -f docker-compose.serverless.override.yml up -d

公開後は http://<Raspberry Pi 5のIP>:8070 でアクセスできます。

※Nuclioダッシュボードには認証が無いので、LANに公開する場合でも 127.0.0.1:8070:8070 のままにしておくのが無難です。インターネット側に晒してしまうと、任意の関数をデプロイできる穴になります。

[要スクリーンショット: Nuclioダッシュボードで yolov7 関数が ready になった状態]

4.CVATから自動アノテーションを実行#

ここまで来れば、あとはCVAT UI上の操作だけですね。

  1. CVATにログインし、画像が登録されたタスクを開きます(タスクが無い場合は1作目の手順を参考に適当なデータセットを登録してください)。
  2. アノテーション画面の左サイドバー上部にあるMagic wandアイコン(CVAT公式ドキュメント上の呼称)をクリック。
  3. Detectorsタブを選び、Modelドロップダウンから YOLO v7 を選びます(この表示名はfunction.yamlannotations.nameに由来します)。
  4. CVATのラベルとモデル側のCOCOクラス(person, bicycle, car, ...)をマッピングし、必要ならThresholdを調整したうえでAnnotate ボタンを押します。
  5. しばらく待つと、検出結果がbboxとして画像に反映されます。

[要スクリーンショット: CVATのアノテーション画面でAI Tools → Detectorsを開き、YOLO v7を選択したダイアログ]

[要スクリーンショット: 自動アノテーション実行後、人物や車両にbboxが付与された状態の画像]

本環境では1枚あたり[要計測]秒程度で推論が返ってきます。Raspberry Pi 5のCPUは4コアCortex-A76 2.4GHzなので、入力解像度640×640のYOLOv7-onnxならギリギリ実用、というくらいの肌感です。

動作確認の小ネタ#

関数単体で叩いて応答を確認したい場合は、CVATの公式ドキュメントにもある nuctl invoke が使えます。

image=$(curl https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png --output - | base64 | tr -d '\n')
cat << EOF > /tmp/input.json
{"image": "$image"}
EOF
cat /tmp/input.json | nuctl invoke onnx-wongkinyiu-yolov7 -c 'application/json'

status: 200 OK と検出結果のJSONが返ってくれば、関数単体としては問題なく動いています。

5.停止・クリーンアップ#

CVAT本体ごと停止するときは、起動時と同じファイル群を指定してdownしてください。

docker compose -f docker-compose.yml \
  -f components/serverless/docker-compose.serverless.yml down

特定の関数だけ消したい場合は下記の通り。

# 関数を削除
nuctl delete function onnx-wongkinyiu-yolov7 --platform local

# プロジェクトごと削除する場合(cvat配下の全関数が消えます)
nuctl delete project cvat --platform local

ビルド時に作られた中間イメージ(cvat.onnx.wongkinyiu.yolov7 など)も、ストレージが厳しい場合は docker image prune で掃除しておくと良いと思います。

6.さいごに#

Raspberry Pi 5上のCVATに、ONNX版YOLOv7を載せた自動アノテーションをひと通り通すことができました。お疲れ様でした。

実用上のボトルネックは、概ね下の2点くらいでしょうか。

  • 推論レイテンシ:入力1枚あたり[要計測]秒。バッチで数千枚を流すような用途には正直つらいですが、「自分で引いた仮ラベルの叩き台」くらいの使い方なら十分実用範囲です。
  • メモリ:Nuclio関数コンテナ+CVATスタックでメモリを結構食うので、8GBモデルでないと厳しいですね。

なお、pytorch系の関数(SAMやFaster R-CNNなど)をARM64で動かすにはbaseImageの差し替えとwheelの差し替えが必要で、function単位での移植作業が発生します。このあたりはまた別の記事で扱えればと思います。

ARM64対応で詰まりやすいのは結局のところ deploy_cpu.sh 冒頭のopenvino baseイメージビルドの一点なので、そこさえ回避できればONNX系関数は意外と素直に動くものですね。

参考文献#

対象

ソフトウェア
CVAT on Raspberry Pi 5

タグ

管理者

Suicool

本業では画像検査ソフトウェアを開発しているソフトウェアエンジニア。
プライベートで実施した検証の内容をブログにまとめています。

Next reading

関連するベンチや運用ログ、深掘り記事を続けて読めます。