Raspberry Pi AI Camera の Workout Monitoring サンプルアプリをアレンジしてバーチャル試着アプリを作る #Python

こんにちは。
ソニーセミコンダクタソリューションズの高橋です。

2024年9月に、Raspberry Pi AI Camera が発売されました!
Raspberry Pi に接続可能な、エッジAIのカメラモジュールです。

本記事では、Raspberry Pi AI Camera を使ってアプリ開発を始める方向けに、公式サンプルアプリである「Workout Monitoring」を動かし、さらにそれをアレンジして新たなアプリケーションを作ってみます。

事前準備

機材を用意します。主な機材は下記です。

  • Raspberry Pi 5
    • Raspberry Pi 4 Model B などでも問題ないです
    • Raspberry Pi OS (64-bit) をインストール
  • Raspberry Pi AI Camera
  • Raspberry Pi を操作するための付属品 (モニタ、キーボード、マウスなど)

Raspberry Pi AI Cameraのセットアップ

Raspberry Pi AI Camera をセットアップして、Raspberry Pi 本体と Raspberry Pi AI Camera が適切に通信できるようにします。
詳細な説明は省略しますが、公式ドキュメント他の方の素晴らしい記事 を参考に、セットアップを実施してください。

Raspberry Pi の端末 (ターミナル) で下記コマンドを実行して、正しく映像と物体検出結果が表示されるようになればOKです。

rpicam-hello -t 0s --post-process-file /usr/share/rpi-camera-assets/imx500_mobilenet_ssd.json

Application Module Library のインストール

Application Module Library をインストールします。
Application Module Library とは、ソニーセミコンダクタソリューションズからリリースされている Raspberry Pi AI Camera 向けライブラリです。
これを利用すると、容易にデータ可視化やアプリケーション開発をすることができます。

まず、リポジトリを GitHub から Raspberry Pi へクローンします。

git clone https://github.com/SonySemiconductorSolutions/aitrios-rpi-application-module-library.git
cd aitrios-rpi-application-module-library

続いて、Application Module Library の README に記載されている下記の手順を実行します。

  1. Development environment setup.
  2. Building the Python wheel.

README の Basic example を実行して、Application Module Library を利用した物体検出アプリケーションが動作するまでを確認することをおすすめします。

Workout Monitoring サンプルアプリをそのまま動かしてみる

本記事では、ソニーセミコンダクタソリューションズからリリースされている IMX500 Sample Applications の中の Workout Monitoring サンプルアプリをベースにして、開発を進めていきます。
まずは、Workout Monitoring サンプルアプリをそのまま動かしてみましょう。

uv をインストールします。

cd ~
curl -LsSf https://astral.sh/uv/install.sh | sh

uv のインストールができたら、一度端末 (ターミナル) を再起動してください。

続けて、IMX500 Sample Applications を GitHub からクローンして、Workout Monitoring サンプルアプリのディレクトリへ移動します。

git clone https://github.com/SonySemiconductorSolutions/aitrios-rpi-sample-apps.git
cd aitrios-rpi-sample-apps/examples/workout-monitor

その後、Workout Monitoring サンプルアプリの README に記載されている手順で、アプリケーションをインストールして、実行します。

uv venv --system-site-packages
source .venv/bin/activate
uv run app.py --exercise squat

アプリケーションの実行が成功すれば、無事に人の骨格情報が取得できていることが確認できるはずです!

上記の例では上半身のみを写していますが、全身を写せば顔・下半身を含めた全17個の骨格特徴点 (キーポイント) が取得できます。

この Working Monitoring アプリケーションは、スクワットなどの運動をカウントしてくれる機能を持っています。
ぜひこれを使って身体を鍛えてみてください!

Workout Monitoring サンプルアプリをアレンジしてバーチャル試着アプリを作ってみる

ここからは、前のセクションで動かしてみた Workout Monitoring サンプルアプリをベースにして、バーチャル試着アプリを作っていきます。
今回は人の上半身にTシャツの画像を試着 (重畳) させてみましょう!

画像を重畳する処理を追加

今回は、4つの骨格特徴点 (=右肩、左肩、右腰、左腰) の座標をもとにして、重畳画像の位置・高さ・幅を求めていきます。
algorism_overview.png

では、実際に、Workout Monitoring サンプルアプリの app.py に処理を記述していきます。

まずは、AIモデルから必要な4つの骨格特徴点 (キーポイント) の座標 (0から1の範囲) を受け取り、出力画像の座標に変換します。

scene = frame.image
height, width = scene.shape[:2]

# Convert normalized keypoint coordinates to actual image coordinates
left_shoulder_x  = int(keypoints[5 * 2 + 1] * width)
left_shoulder_y  = int(keypoints[5 * 2] * height)
right_shoulder_x = int(keypoints[6 * 2 + 1] * width)
right_shoulder_y = int(keypoints[6 * 2] * height)
left_hip_x       = int(keypoints[11 * 2 + 1] * width)
left_hip_y       = int(keypoints[11 * 2] * height)
right_hip_x      = int(keypoints[12 * 2 + 1] * width)
right_hip_y      = int(keypoints[12 * 2] * height)

なお、keypoints配列の各要素のインデックスは下の画像のような関係になっているようです。

続いて、4つの骨格特徴点 (キーポイント) から有効な値を選んで取得し、それらをもとに上半身の右上と左下の座標を求めます。

def select_valid_coordinate(coord_a, coord_b):
    if coord_a == 0 and coord_b == 0:
        return 0  
    elif coord_a == 0:
        return coord_b
    else:
        return coord_a

# Determine upper body bounding box coordinates
right_top_x   = select_valid_coordinate(right_shoulder_x, right_hip_x)
right_top_y   = select_valid_coordinate(right_shoulder_y, left_shoulder_y)
left_bottom_x = select_valid_coordinate(left_shoulder_x,  left_hip_x)
left_bottom_y = select_valid_coordinate(right_hip_y,      left_hip_y)

求めた上半身の右上・左下の座標を基準として、それに適切なオフセットや倍率を適用することにより、重畳画像の位置・高さ・幅を求めます。
このオフセットや倍率は、重畳したい画像の大きさによって変化するため、必要に応じて修正してください。

# Constants defenition
OVERLAY_IMG_X_OFFSET_RATIO = 2.0 # Decrease this to increase the leftward offset of the overlay image
OVERLAY_IMG_Y_OFFSET_RATIO = 3.5 # Decrease this to increase the upward offset of the overlay image
OVERLAY_IMG_HEIGHT_RATIO   = 2.2 # Decrease this to reduce the overlay image height
OVERLAY_IMG_WIDTH_RATIO    = 1.5 # Decrease this to reduce the overlay image width
CONFIDENCE_THRETHORLD      = 0.3 # Detection confidence threshold

# Adjust overlay image position based on reference points
overlay_img_x = int(right_top_x - (left_bottom_x - right_top_x) // OVERLAY_IMG_X_OFFSET_RATIO)
overlay_img_y = int(right_top_y - (left_bottom_y - right_top_y) // OVERLAY_IMG_Y_OFFSET_RATIO)

# Resize overlay image based on reference points
overlay_img_h = int((left_bottom_y - right_top_y) * OVERLAY_IMG_HEIGHT_RATIO)
overlay_img_w = int((left_bottom_x - right_top_x) * OVERLAY_IMG_WIDTH_RATIO)

最後に、Tシャツの画像を透過成分も含めて読み込み、上記で求めた位置に配置します。

scene = frame.image
overlay_image = cv2.imread("tshirt.png", cv2.IMREAD_UNCHANGED)

resized_overlay = cv2.resize(overlay_image, (overlay_img_w, overlay_img_h))

# Separate alpha channel
alpha_channel = resized_overlay[:, :, 3] / 255.0
rgb_channels  = resized_overlay[:, :, :3]

# Copy the overlay part to the scene
for c in range(0, 3):
    scene[overlay_img_y:overlay_img_y+overlay_img_h, overlay_img_x:overlay_img_x+overlay_img_w, c] = (alpha_channel * rgb_channels[:, :, c] + (1 - alpha_channel) * scene[overlay_img_y:overlay_img_y+overlay_img_h, overlay_img_x:overlay_img_x+overlay_img_w, c])

完成コード

フレーム毎の繰り返しの処理部なども含めた完成形のコードはこちらになります。
GitHub でも公開しています。

import cv2
from modlib.devices import AiCamera
from modlib.models.zoo import Higherhrnet
from modlib.apps.tracker.byte_tracker import BYTETracker

# Constants defenition
OVERLAY_IMG_X_OFFSET_RATIO = 2.0 # Decrease this to increase the leftward offset of the overlay image
OVERLAY_IMG_Y_OFFSET_RATIO = 3.5 # Decrease this to increase the upward offset of the overlay image
OVERLAY_IMG_HEIGHT_RATIO   = 2.2 # Decrease this to reduce the overlay image height
OVERLAY_IMG_WIDTH_RATIO    = 1.5 # Decrease this to reduce the overlay image width
CONFIDENCE_THRETHORLD      = 0.3 # Detection confidence threshold

class BYTETrackerArgs:
    track_thresh: float = 0.25
    track_buffer: int = 30
    match_thresh: float = 0.8
    aspect_ratio_thresh: float = 3.0
    min_box_area: float = 1.0
    mot20: bool = False

def select_valid_coordinate(coord_a, coord_b):
    """
    Select a valid coordinate, prioritizing the first non-zero value
    """
    if coord_a == 0 and coord_b == 0:
        return 0  
    elif coord_a == 0:
        return coord_b
    else:
        return coord_a

def overlay_image_on_upper_body(frame, keypoints, overlay_image):
    """
    Overlay an image on the upper body
    """
    scene = frame.image
    height, width = scene.shape[:2]

    # Convert normalized keypoint coordinates to actual image coordinates
    left_shoulder_x  = int(keypoints[5 * 2 + 1] * width)
    left_shoulder_y  = int(keypoints[5 * 2] * height)
    right_shoulder_x = int(keypoints[6 * 2 + 1] * width)
    right_shoulder_y = int(keypoints[6 * 2] * height)
    left_hip_x       = int(keypoints[11 * 2 + 1] * width)
    left_hip_y       = int(keypoints[11 * 2] * height)
    right_hip_x      = int(keypoints[12 * 2 + 1] * width)
    right_hip_y      = int(keypoints[12 * 2] * height)

    # Determine upper body bounding box coordinates
    right_top_x   = select_valid_coordinate(right_shoulder_x, right_hip_x)
    right_top_y   = select_valid_coordinate(right_shoulder_y, left_shoulder_y)
    left_bottom_x = select_valid_coordinate(left_shoulder_x,  left_hip_x)
    left_bottom_y = select_valid_coordinate(right_hip_y,      left_hip_y)

    # Adjust overlay image position based on reference points
    overlay_img_x = int(right_top_x - (left_bottom_x - right_top_x) // OVERLAY_IMG_X_OFFSET_RATIO)
    overlay_img_y = int(right_top_y - (left_bottom_y - right_top_y) // OVERLAY_IMG_Y_OFFSET_RATIO)

    # Resize overlay image based on reference points
    overlay_img_h = int((left_bottom_y - right_top_y) * OVERLAY_IMG_HEIGHT_RATIO)
    overlay_img_w = int((left_bottom_x - right_top_x) * OVERLAY_IMG_WIDTH_RATIO)

    try:
        resized_overlay = cv2.resize(overlay_image, (overlay_img_w, overlay_img_h))

        # Separate alpha channel
        alpha_channel = resized_overlay[:, :, 3] / 255.0
        rgb_channels  = resized_overlay[:, :, :3]

        # Copy the overlay part to the scene
        for c in range(0, 3):
            scene[overlay_img_y:overlay_img_y+overlay_img_h, overlay_img_x:overlay_img_x+overlay_img_w, c] = (alpha_channel * rgb_channels[:, :, c] + (1 - alpha_channel) * scene[overlay_img_y:overlay_img_y+overlay_img_h, overlay_img_x:overlay_img_x+overlay_img_w, c])
    except:
        pass

    return scene

def start_workout_demo():
    device = AiCamera()
    model = Higherhrnet()
    device.deploy(model)

    # Load the overlay image
    overlay_image = cv2.imread("tshirt.png", cv2.IMREAD_UNCHANGED)

    tracker = BYTETracker(BYTETrackerArgs())

    with device as stream:
        for frame in stream:
            detections = frame.detections[frame.detections.confidence > CONFIDENCE_THRETHORLD]
            detections = tracker.update(frame, detections)

            for k, _, _, _, t in detections:
                frame.image = overlay_image_on_upper_body(frame, k, overlay_image)

            frame.display()

if __name__ == "__main__":
    start_workout_demo()
    exit()

動作例

いらすとや様のTシャツ画像をダウンロードして、それを app.py と同じディレクトリに “tshirt.png” として配置したうえで、実際に動かしてみました!
いい感じに試着できていますね。
virtual-fitting_sample_3sec.gif

もし、記事の途中でうまくいかなかった場合は、お気軽にこの記事へコメントいただくか、下記のサポートページをご活用ください。
コメントのお返事にはお時間を頂く可能性もありますがご了承ください。

今回は、ソニーセミコンダクタソリューションズから提供されている Workout Monitoring サンプルアプリを動作させ、さらにそれをアレンジしてバーチャル試着アプリを開発しました。
利用したAIモデルは、顔や下半身を含めた全17個の骨格特徴点を検出してくれるので、例えば、顔の骨格特徴点を使ってメガネを試着させてみるといったアレンジも考えられそうですね!

また、ソニーセミコンダクタソリューションズは、IMX500 Sample Applications で他にも4つのサンプルアプリを公開しています。
色々なサンプルアプリをベースにして、色々なアレンジを試してみてください!



フラッグシティパートナーズ海外不動産投資セミナー 【DMM FX】入金

Source link