HumansXドキュメントセンター 公式サイトへ ↗

1.2 MIPI カメラによるリアルタイム検出

サンプルの概要

MIPI カメラによるリアルタイム検出のサンプルは、/app/pydev_demo/08_mipi_camera_sample にある Python インターフェースの開発コード例で、オンボードの MIPI カメラを使ってリアルタイム物体検出を行う方法を示します。このサンプルでは YOLOv5x 物体検出モデルを使用して MIPI カメラで取得したビデオストリームにリアルタイムで推論を行い、検出結果を HDMI で表示して検出枠の情報を出力します。

含まれるサンプル:

root@ubuntu:/app/pydev_demo/08_mipi_camera_sample$ tree
.
├── 01_mipi_camera_yolov5s.py
├── 02_mipi_camera_dump.py
├── 03_mipi_camera_scale.py
├── 04_mipi_camera_crop_scale.py
├── 05_mipi_camera_streamer.py
└── coco_classes.names

実行結果の例

01 リアルタイム物体検出の結果

検出結果の可視化

ディスプレイでリアルタイムのカメラ映像と検出結果の可視化を確認するには、以下が必要です。

  1. 外部ディスプレイ:HDMI ケーブルで開発ボードをディスプレイに接続します。
  2. Desktop 版の特別な処理:Desktop 版のシステムを使用している場合は、まず以下のコマンドを実行してデスクトップサービスを停止する必要があります。

sudo systemctl stop lightdm 3. リモート接続:SSH でボードにリモート接続します。 4. コードの実行:サンプルプログラムを実行すると、接続したディスプレイにリアルタイムの検出結果が表示されます。

02 画像の取得・保存の結果

実行すると、スクリプトと同じディレクトリに YUV 形式の画像ファイルが複数保存されます。デフォルトの解像度は 1920x1080 です。

03 画像リサイズ処理の結果

実行すると、スクリプトと同じディレクトリにリサイズ後の YUV 画像ファイルが保存されます。デフォルトの解像度は 640x360 です。

04 画像のクロップ・リサイズの結果

実行すると、スクリプトと同じディレクトリにクロップ・リサイズ後の YUV 画像ファイル(NV12 形式)が保存されます。デフォルトでは画面中央をクロップ・リサイズします。クロップ位置を調整すると、以下のような YUV 画像が得られます。

05 リアルタイムストリーミング表示の結果

実行すると、HDMI ディスプレイにカメラ映像がリアルタイムで表示されます(ストリーミングテスト)。Desktop 版では、先に sudo systemctl stop lightdm を実行してデスクトップサービスを停止する必要があります。

ハードウェアの準備

ハードウェア接続

  1. RDK 開発ボードを 1 台用意します
  2. 公式対応の MIPI カメラを接続します
  3. HDMI ケーブルでディスプレイと開発ボードを接続します
  4. 電源ケーブルと LAN ケーブルを接続します

クイックスタート

コードとボード上の場所

サンプルファイルは /app/pydev_demo/08_mipi_camera_sample にあります。

コンパイルと実行

Python のサンプルはコンパイル不要で、そのまま実行できます。

01_mipi_camera_yolov5s.py の実行:

cd /app/pydev_demo/08_mipi_camera_sample
python 01_mipi_camera_yolov5s.py

02_mipi_camera_dump.py の実行:

cd /app/pydev_demo/08_mipi_camera_sample
python 02_mipi_camera_dump.py -f 30 -c 10 -w 1920 -h 1080

03_mipi_camera_scale.py の実行:

cd /app/pydev_demo/08_mipi_camera_sample

# 该示例需要 input.yuv 作为输入,这里我们以上个运行示例中的 output0.yuv 作为输入,执行拷贝命令
cp output0.yuv input.yuv

# 然后运行示例
python 03_mipi_camera_scale.py -i input.yuv -o output_640x360.yuv -w 640 -h 360 --iwidth 1920 --iheight 1080

04_mipi_camera_crop_scale.py の実行:

cd /app/pydev_demo/08_mipi_camera_sample
python 04_mipi_camera_crop_scale.py -i input.yuv -o output_640x480.yuv -w 640 -h 480 --iwidth 1920 --iheight 1080 -x 304 -y 304 --crop_w 896 --crop_h 592

05_mipi_camera_streamer.py の実行:

cd /app/pydev_demo/08_mipi_camera_sample
python 05_mipi_camera_streamer.py -w 1920 -h 1080

詳細説明

サンプルプログラムのパラメータオプション

01_mipi_camera_yolov5s.py のパラメータ

MIPI カメラによるリアルタイム検出のサンプルはコマンドライン引数が不要で、そのまま実行できます。プログラムがオンボードの MIPI カメラを自動的に検出して使用します。

02_mipi_camera_dump.py のパラメータ

パラメータ 説明 型 例
-f フレームレート(FPS) int 30
-c 取得フレーム数(count) int 10
-w 画像の幅 int 1920
-h 画像の高さ int 1080

03_mipi_camera_scale のパラメータ

パラメータ 説明 型 例
-i 入力 YUV ファイルのパス str input.yuv
-o 出力ファイルのパス str output_scale.yuv
-w 出力画像の幅 int 640
-h 出力画像の高さ int 360
--iwidth 入力画像の幅 int 1920
--iheight 入力画像の高さ int 1080

04_mipi_camera_crop_scale のパラメータ

パラメータ 説明 型 例
-i 入力 YUV ファイルのパス str input.yuv
-o 出力ファイルのパス str output_crop_scale.yuv
-w 出力画像の幅 int 640
-h 出力画像の高さ int 480
--iwidth 入力画像の元の幅 int 1920
--iheight 入力画像の元の高さ int 1080
-x クロップ領域の左上の X 座標 int 304
-y クロップ領域の左上の Y 座標 int 304
--crop_w クロップ領域の幅 int 896
--crop_h クロップ領域の高さ int 592

05_mipi_camera_streamer のパラメータ

パラメータ 説明 型 例
-w 出力画像の幅 int 1920
-h 出力画像の高さ int 1080

ソフトウェアアーキテクチャ

ここでは MIPI カメラによるリアルタイム検出のサンプルのソフトウェアアーキテクチャと処理の流れを紹介し、各サンプルプログラムの初期化から実行完了までの一連の実行過程を説明します。コード全体の構造とデータの流れを理解する助けとなります。

リアルタイム物体検出サンプルのソフトウェアアーキテクチャ

  1. モデルの読み込み - hbm_runtime モジュールでモデルファイルを読み込みます
  2. カメラの初期化 - MIPI カメラを初期化します
  3. 表示の初期化 - HDMI 表示を初期化します
  4. デバイスのバインド - カメラの出力を表示にバインドします
  5. 画像の取得 - MIPI カメラからビデオフレームを取得します
  6. 画像の前処理 - 画像をモデルの入力サイズにリサイズし、形式を変換します
  7. モデル推論 - BPU 上で YOLOv5x の順伝播推論を実行します
  8. 結果の後処理 - 出力のデコード、低信頼度の結果の除外、NMS による重複除去、座標のマッピングを行います
  9. 結果の可視化 - 元画像に検出枠とラベルを描画します
  10. 表示出力 - HDMI で結果を表示し、コンソールに FPS と検出情報を出力します

画像取得・保存サンプルのソフトウェアアーキテクチャ

  1. カメラの初期化 - MIPI カメラを初期化します
  2. パラメータの設定 - 取得フレームレート、解像度、取得フレーム数を設定します
  3. 画像の取得 - 指定した数の画像フレームを連続で撮影します
  4. ファイルの保存 - 取得した画像を YUV 形式のファイルとして保存します

画像リサイズ処理サンプルのソフトウェアアーキテクチャ

  1. パラメータの取得 - コマンドライン引数を解析し、入出力ファイルのパスと画像サイズを取得します
  2. VPS の初期化 - VPS オブジェクトを作成し、ハードウェアのリサイズチャネルを開きます
  3. ファイルの読み込み - 入力 YUV ファイルを読み込みます
  4. ハードウェアによるリサイズ - ハードウェア VPS で画像のリサイズを行います
  5. 結果の保存 - リサイズ後の画像を新しい YUV ファイルとして保存します
  6. リソースのクリーンアップ - VPS を閉じ、ハードウェアリソースを解放します

画像クロップ・リサイズサンプルのソフトウェアアーキテクチャ

  1. パラメータの取得 - コマンドライン引数を解析し、入出力ファイルのパス、画像サイズ、クロップ領域の座標を取得します
  2. VPS の初期化 - VPS オブジェクトを作成し、ハードウェアのクロップ・リサイズチャネルを開きます
  3. ファイルの読み込み - 入力 YUV ファイルを読み込みます
  4. ハードウェア処理 - ハードウェア VPS で画像のクロップを行います
  5. 結果の保存 - 処理後の画像を新しい YUV ファイルとして保存します
  6. リソースのクリーンアップ - VPS を閉じ、ハードウェアリソースを解放します

リアルタイムストリーミング表示サンプルのソフトウェアアーキテクチャ

  1. パラメータの取得 - コマンドライン引数を解析し、表示解像度を取得します
  2. ディスプレイの初期化 - 表示オブジェクトを作成し、HDMI 表示レイヤーを初期化します
  3. カメラの初期化 - カメラオブジェクトを作成し、MIPI カメラを開きます
  4. デバイスのバインド - ハードウェアバインドにより、カメラのデータストリームをディスプレイに直接接続します
  5. リアルタイムストリーミング - カメラ映像がハードウェア経路を通じて HDMI ディスプレイに継続的に出力されます
  6. デバイスのバインド解除 - カメラとディスプレイのバインドを解除します
  7. リソースのクリーンアップ - ディスプレイとカメラを閉じ、ハードウェアリソースを解放します

API の流れ

ここではサンプルプログラムで使用している主な API を挙げ、各 API の機能、入力パラメータ、戻り値を説明します。コードの実装の詳細や API の呼び出し方を素早く理解する助けとなります。

リアルタイム物体検出の主な API:

  1. srcampy.Camera()

MIPI カメラオブジェクトを作成します

  1. get_display_res()

HDMI の表示解像度を取得します。戻り値:幅、高さ

  1. cam.open_cam(pipe_id, video_index, fps, width, height, raw_height, rae_width) カメラを開きます。入力:カメラに対応する pipeline チャネル番号、カメラに対応する host 番号、フレームレート、出力幅、出力高さ、元の幅、元の高さ
  2. srcampy.Display()

HDMI 表示オブジェクトを作成します

  1. disp.display(layer, width, height)

表示レイヤーを初期化します。入力:表示レイヤー番号、幅、高さ

  1. srcampy.bind(camera, display)

カメラと表示をバインドします。入力:カメラオブジェクト、表示オブジェクト

  1. hbm_runtime.HB_HBMRuntime(model_path)

モデルを読み込みます。入力:モデルファイルのパス

  1. model.set_scheduling_params(priority, bpu_cores)

モデルのスケジューリングパラメータを設定します。入力:優先度(0-255)、BPU コアのリスト

  1. load_class_names(class_file)

クラス名を読み込みます。入力:クラスファイルのパス。戻り値:クラス名のリスト

  1. cam.get_img(chn, width, height)

カメラの画像フレームを取得します。入力:チャネル番号(デフォルトは 2)、幅、高さ。戻り値:NV12 形式の画像データ

  1. split_nv12_bytes(img, width, height)

NV12 画像の Y 成分と UV 成分を分離します。入力:NV12 画像データ、幅、高さ。戻り値:Y 成分、UV 成分

  1. resize_nv12_yuv(y, uv, target_w, target_h)

NV12 画像をリサイズします。入力:Y 成分、UV 成分、目標の幅、目標の高さ。戻り値:リサイズ後の Y、UV 成分

  1. model.run(input_tensor)

モデル推論を実行します。入力:前処理済みの入力テンソルの辞書。戻り値:モデル出力の辞書

  1. dequantize_outputs(outputs, output_quants)

結果を逆量子化します。入力:モデル出力の辞書、出力の量子化パラメータ。戻り値:float32 型のデータ

  1. decode_outputs(output_names, fp32_outputs, strides, anchors, classes_num)

YOLO モデルの出力をデコードします。入力:出力名のリスト、逆量子化後の出力、ストライド、アンカー、クラス数。戻り値:予測結果

  1. filter_predictions(predictions, score_threshold)

予測結果をフィルタリングします。入力:予測結果、信頼度しきい値。戻り値:検出枠、信頼度、クラス

  1. NMS(boxes, scores, classes, iou_threshold)

非最大値抑制を行います。入力:検出枠、信頼度、クラス、IoU しきい値。戻り値:保持するインデックス

  1. scale_coords_back(boxes, orig_w, orig_h, model_w, model_h, resize_type)

検出枠を元画像のサイズに合わせます。入力:検出枠、元画像の幅と高さ、モデル入力の幅と高さ、リサイズ方式。戻り値:スケーリング後の検出枠

  1. draw_detections_on_disp(display, boxes, cls_ids, scores, class_names, color_map, chn)

表示レイヤーに検出結果を描画します。入力:表示オブジェクト、検出枠、クラス ID、信頼度、クラスリスト、カラーマップ、チャネル番号

  1. srcampy.unbind(camera, display)

カメラと表示のバインドを解除します

  1. cam.close_cam()

カメラを閉じます

  1. disp.close()

表示を閉じます

画像取得・保存の主な API:

  1. libsrcampy.Camera()

MIPI カメラオブジェクトを作成します

  1. cam.open_cam(pipe_id, video_index, fps, width, height,)

カメラを開きます。入力:カメラに対応する pipeline チャネル番号、カメラに対応する host 番号、フレームレート、幅、高さ

  1. cam.get_img(chn)

画像を取得します。入力:画像を取得するモジュール。戻り値:YUV 形式の画像データ

  1. file.write(data)

ファイルにデータを書き込みます。入力:画像データ

  1. cam.close_cam()

カメラを閉じます

画像リサイズ処理の主な API:

  1. libsrcampy.Camera()

VPS(ビデオ処理システム)オブジェクトを作成します

  1. vps.open_vps(grp_id, chn_id, input_w, input_h, output_w, output_h)

VPS チャネルを開きます。入力:グループ ID、チャネル ID、入力の幅と高さ、出力の幅と高さ

  1. file.read()

ファイルデータを読み込みます。戻り値:画像データ(バイト列)

  1. vps.set_img(img_data)

入力画像データを設定します。入力:YUV 画像データ(バイト列)

  1. vps.get_img(chn, width, height)

処理後の画像を取得します。入力:チャネル番号、幅、高さ。戻り値:処理後の YUV 画像データ(バイト列)

  1. file.write(data)

処理後の画像データを書き込みます。入力:画像データ(バイト列)

  1. vps.close_cam()

VPS を閉じます

画像クロップ・リサイズの主な API:

  1. libsrcampy.Camera()

VPS(ビデオ処理システム)オブジェクトを作成します

  1. vps.open_vps(grp_id, chn_id, input_w, input_h, output_w, output_h, crop_rect)

VPS チャネルを開いてクロップ領域を設定します。入力:グループ ID、チャネル ID、入力の幅と高さ、出力の幅と高さ、クロップ領域 [x, y, w, h]

  1. file.read()

ファイルデータを読み込みます。戻り値:画像データ(バイト列)

  1. vps.set_img(img_data)

入力画像データを設定します。入力:YUV 画像データ(バイト列)

  1. vps.get_img(chn, width, height)

処理後の画像を取得します。入力:チャネル番号、幅、高さ。戻り値:処理後の YUV 画像データ(NV12 形式、バイト列)

  1. file.write(data)

処理後の画像データを書き込みます。入力:画像データ(バイト列)

  1. vps.close_cam()

VPS を閉じます

リアルタイムストリーミング表示の主な API:

  1. libsrcampy.Display()

HDMI 表示オブジェクトを作成します

  1. disp.display(layer, width, height)

表示レイヤーを初期化します。入力:表示レイヤー番号、幅、高さ

  1. libsrcampy.Camera()

MIPI カメラオブジェクトを作成します

  1. cam.open_cam(pipe_id, video_index, fps, width, height,)

カメラを開きます。入力:カメラに対応する pipeline チャネル番号、カメラに対応する host 番号、フレームレート、幅、高さ

  1. libsrcampy.bind(camera, display)

カメラと表示をバインドします。入力:カメラオブジェクト、表示オブジェクト。戻り値:バインド結果

  1. libsrcampy.unbind(camera, display)

カメラと表示のバインドを解除します

  1. disp.close()

表示を閉じます

  1. cam.close_cam()

カメラを閉じます

FAQ

Q: サンプル実行時にカメラの初期化失敗と表示される場合は? A: MIPI カメラが正しく接続されているか、カメラドライバーが正しく読み込まれているかを確認してください。デバイスを再起動してみることもできます。

Q: HDMI の表示が正常でない、または何も出力されない場合は? A: HDMI の接続を確認し、表示サービスが停止していること(systemctl stop lightdm を使用するなど)を確認してください。

Q: 検出しきい値を調整するには? A: コード内の --score-thres の値を変更します。例えば 0.5 に変更すると、信頼度の低い検出結果が除外され、誤検出を減らせます。

Q: 表示の解像度を変更するには? A: コード内の sensor_width と sensor_height 変数を変更します。ただし、表示デバイスがその解像度に対応しているかに注意してください。

Q: サンプル実行時のフレームレートが低い場合は? A: より軽量なモデルを使用するか、カメラの取得解像度を調整してみてください。

Q: 検出結果の画像を保存するには? A: コードに画像保存の処理を追加できます。例えば cv2.imwrite() を使って処理後の画像を保存します。