Freenove ESP32-S3 WROOM CAM を使い、乳幼児の うつ伏せ / 非うつ伏せ 学習用写真データを収集するための ESP-IDF プロジェクトです。
既存 Wi-Fi AP に STA 接続し、ESP32 上で Web サーバを起動します。ブラウザからライブストリームを確認し、ラベルを選んで撮影し、SD カードへ保存します。保存済みデータは metadata.csv と画像単位で分割エクスポートでき、必要に応じて SD カード内データを全削除できます。
- YOLOv11n Pose モデルは ESP32-S3 で動かすと 27 秒かかるので現実的でない
- 保証されたうつ伏せ検出モデルが公開されてない
- 顔検出 + α でうつ伏せ検出するのがほとんど
↓
- マイコンで安価に検出したい
- 顔検出だけで検出したい
- ほぼリアルタイムで検出したい
↓
- 顔検出だけでは無理だった
- うつ伏せ・非うつ伏せ状態そのものを個人の範囲で学習させる仕掛けを作りたい
Freenove ESP32-S3 WROOM CAMでカメラ映像を取得するsdkconfigに保存したSSID/PASSWORDで既存 AP に接続する- Web UI でストリーミング映像を表示する
- Web UI で
うつ伏せ / 非うつ伏せを選択して撮影する - JPEG 画像と学習用メタデータを SD カードへ保存する
- 保存済みデータを PC 側へ分割ダウンロードする
- SD カード内のデータセットを初期化する
- 収集機能のみ実装済み
- 推論機能は未実装
- UI/API サーバは
80番ポート - MJPEG ストリームは
81番ポート - 初期フレームサイズは
320 x 240 - 保存形式は JPEG
statusは初回表示時、撮影後、リセット後だけ更新する
学習データ収集インターフェイス
ルート画面 / には以下を配置します。
- カメラストリーミング
- カメラファインダー上のバウンディングボックス表示
- 撮影ボタン
うつ伏せ / 非うつ伏せラジオボタンエクスポートボタンSDカードリセットボタンsubject_id入力欄session_id入力欄location_id入力欄lighting_id入力欄camera_position_id入力欄annotator_id入力欄学習利用可否入力欄除外理由入力欄notes入力欄
- ストリーミング表示はスマホ操作を優先して小さめに表示する
- ストリーミング上で顔を検出したときだけ、検出された顔の bbox を緑色で重ねて表示する
撮影ボタンはストリーミング上部に配置するエクスポートとSDカードリセットは入力欄の下に配置する
SD カード上には dataset/ ディレクトリを作成し、以下を保存します。
dataset/images/*.jpg撮影した JPEG 画像dataset/metadata.csv全画像に対するメタデータ一覧
capture_idtimestamp_mssubject_idsession_idlocation_idlighting_idcamera_position_idannotator_idlabellabel_nameis_usable_for_trainingexclude_reasonnotesimage_pathimage_bytesframe_widthframe_heightpixel_formatjpeg_qualityboard_name
画像だけでは後で偏りや品質問題を潰しにくいので、以下を必須で残します。
subject_id被写体単位分割を行うための最重要キーsession_id同一環境・同一時間帯・同一被写体群を識別するlocation_id撮影場所の偏りを監査するlighting_id照明条件の偏りを監査するcamera_position_id画角と設置位置の偏りを監査するannotator_id注釈担当差分を監査するtimestamp_ms時系列の偏り、連写由来のリーク確認に使うis_usable_for_training曖昧サンプルを学習集合から外すexclude_reason学習利用不可理由を保存するnotes後から判断保留や注意点を監査するframe_width/frame_height前処理条件を再現するjpeg_quality圧縮率差異を追跡するboard_name取得元ハードを識別するimage_bytes異常に小さい破損画像や露光失敗を検知する
エクスポートは巨大アーカイブを 1 回で返さず、以下に分割します。
/api/export/metadatametadata.csvを取得する/api/export/manifest画像一覧をページ単位で取得する/api/export/image?capture_id=...画像を 1 件ずつ取得する
通信断があっても、未取得画像だけ再試行できます。
エクスポートボタンはmetadata.csvを取得する- 続いて
manifest.jsonを生成して取得する - 続いて各 JPEG を 1 件ずつ順次ダウンロードする
- ブラウザ側で複数ダウンロード許可が必要な場合がある
- 大量画像の一括取得はブラウザ依存のため、件数が多い場合は PC 側取得スクリプト化を前提に見直し余地がある
Wi-Fi 認証情報は sdkconfig に保持します。
CONFIG_WIFI_SSIDCONFIG_WIFI_PASSWORD
リポジトリへ実値をコミットしない前提です。
このリポジトリで、管理外のまま運用する前提のファイルやディレクトリのうち、利用者がコマンド実行で生成するものは以下です。 手動で新規作成しないと進めない必須ファイルはありません。
sdkconfig- 用途:
CONFIG_WIFI_SSID,CONFIG_WIFI_PASSWORDなどのローカル設定を保持する - 生成方法:
idf.py menuconfigを実行すると生成または更新される - 補足: Wi-Fi 認証情報はここで設定し、
viなどでの手動新規作成は不要
- 用途:
.venv/- 用途: PC 側パイプライン実行用の Python 仮想環境
- 生成方法:
python3 -m venv .venv - 補足: 生成後に
source .venv/bin/activateとpip install -e .を実行する
artifacts/pc_pipeline/<run_name>/- 用途: PC 側パイプラインの成果物出力先
- 生成方法:
python3 -m pc_pipeline --dataset-root /path/to/exported/datasetを実行すると自動生成される - 補足: 事前に
mkdirや手動作成は不要
managed_components/- 用途:
ESP-IDFのコンポーネント管理で取得された依存物を保持する - 生成方法: 初回の
idf.py buildなどで必要に応じて自動生成される - 補足: 手動作成は不要
- 用途:
build/- 用途:
ESP-IDFビルド生成物を保持する - 生成方法:
idf.py buildを実行すると自動生成される - 補足: 手動作成は不要
- 用途:
.idf-python-env/- 用途:
ESP-IDF v6.0実行用のローカル Python 仮想環境 - 生成方法:
ESP-IDF v6.0の Python 環境をプロジェクト内へ配置したときに生成される - 補足: 本リポジトリでは
v6.0を使い、旧版用の別名ディレクトリは持たない
- 用途:
GET /Web UIGET http://<device-ip>:81/streamMJPEG ストリームGET /api/status状態取得GET /api/face-detections最新の顔検出 bbox 一覧POST /api/capture撮影と保存GET /api/export/manifestページ単位の画像一覧GET /api/export/metadatametadata.csvGET /api/export/image?capture_id=...単一画像取得POST /api/resetデータセット初期化
本リポジトリでは、実装より先に学習データ仕様を閉じます。特に以下を絶対要件とします。
subject_id必須- 画像本体保存必須
is_usable_for_trainingとexclude_reasonによる曖昧サンプル除外subject_id単位分割
source ~/.espressif/v6.0/esp-idf/export.sh
idf.py set-target esp32s3
idf.py menuconfig
idf.py build
idf.py -p <PORT> flash monitorMac 上では python3 を使って PC 側パイプラインを実行します。
最も確実な実行方法は python3 -m pc_pipeline です。
prone-pc-pipeline は pip install -e . 実行後に使えるようになる別名コマンドです。
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
python3 -m pc_pipeline --dataset-root /path/to/exported/datasetpip install -e . の後は、次の別名コマンドでも実行できます。
prone-pc-pipeline --dataset-root /path/to/exported/dataset主な既定値:
- 入力サイズ:
96 x 96 - 分割比率:
train=0.70,val=0.15,test=0.15 - 乱数シード:
42 - 学習条件:
epoch=20,batch_size=32,learning_rate=0.001 - 初期判定閾値:
0.50
成果物は artifacts/pc_pipeline/<run_name>/ に保存します。
config.jsondataset_audit.jsonsplits/train.csv,splits/val.csv,splits/test.csvtraining_dirs/train,training_dirs/val,training_dirs/testcheckpoints/best_model.ptonnx/model.onnxreports/metrics.jsonreports/threshold.jsonreports/quantized_reference.jsonreferences/quantized/train.csv,references/quantized/val.csv,references/quantized/test.csv
ESP-DL 変換コマンドが手元にある場合は、{onnx_path} と {espdl_path} を含む形で渡します。
python3 -m pc_pipeline \
--dataset-root /path/to/exported/dataset \
--espdl-converter-command "espdl_convert --input {onnx_path} --output {espdl_path}"学習用ディレクトリを合わせて作る場合は、次を使います。
python3 -m pc_pipeline \
--dataset-root /path/to/exported/dataset \
--export-training-directories既定では training_dirs/ 配下へ symlink を生成します。実ファイルを複製したい場合は --training-directory-mode copy を指定します。
- 同じ収集セッション中は
session_id,location_id,lighting_id,camera_position_id,annotator_idを毎回変える必要はない - 毎回見直す主な入力は
label,is_usable_for_training,exclude_reason,notes session_idは被写体、時間帯、設置条件が変わったときに切り替えるsubject_idとsession_idは収集前にartifacts/ledger/subject_registry.csv,artifacts/ledger/session_registry.csvへ登録してから使う- 学習利用可否を後から変更した場合は
artifacts/ledger/training_eligibility_history.jsonlへ追記する - 実機確認用に使うセッションは
dataset_role=device_validationとして学習系分割から分離する
