| Home | English |
単眼 RGB 画像から、既知形状のマグカップの 6D Pose(位置・姿勢)を推定する。
本プロジェクトでは、単眼 RGB 画像から、既知形状のマグカップの 6D Pose(位置・姿勢)を推定する。
まず Blender 上でカメラおよびマグカップの位置・姿勢をランダムに変化させ、3,000 枚の合成画像と Ground Truth を生成する。この Sim データを使用して、マグカップを画像から検出する物体検知モデルを構築する。
次に、物体検知モデルによって得られた BBox を基準に画像をクロップし、マグカップの 3D モデル上にあらかじめ定義したキーポイントを検出するキーポイントモデルを学習する。
実写画像の推論では、以下の Pipeline を使用する。
RGB Image → Object Detection → BBox Crop → Keypoint Detection → PnP → 6D Pose
PnP では、画像から検出した 2D キーポイントと、対応する既知の 3D キーポイント座標から、カメラ座標系におけるマグカップの位置・姿勢を推定する。
さらに、Sim データのみで学習したキーポイントモデルを 173 枚の実写アノテーションデータで fine-tuning する。最終的に、961 枚の実写画像に対して、
の2モデルを同じ Pipeline で評価し、キーポイント confidence、PnP inlier 数、reprojection error、solvePnPRansac の失敗数を比較する。
さらに、実写キーポイントアノテーションの幾何学的一貫性を改善し、PnP姿勢推定の安定性への影響も確認する。
なお、実写画像には 6D Pose の Ground Truth が存在しないため、本資料では推定 Pose の絶対精度は評価しない。代わりに、キーポイント confidence、PnP inlier 数、reprojection error、RANSAC failure 数、およびフレーム間の姿勢推定の安定性を指標として比較する。
tool-detection-pose/
├── blender
├── cam_calib
├── detection
├── docs
├── keypoint
├── README.md
└── real_dataset
$ cd keypoint/
$ source .venv/bin/activate
(.venv) $ python --version
Python 3.11.2
カメラを動かして、カップのランダムな位置と向きの学習データを作成。
├── blender
│ ├── 0550.txt
│ ├── 0551.txt
│ ├── 0552.txt
│ ├── datagen_random_camera_mug.py
│ ├── synthetic_dataset_0550_randomcam
│ ├── synthetic_dataset_0551_randomcam
│ ├── synthetic_dataset_0552_randomcam
│ └── tool_pose.blend
blender -b tool_pose.blend -P datagen_random_camera_mug.py -- 0550.txt
0550.txt 0551.txt 0552.txt は、カメラとカップの位置と向き。
IMG_0550
#1
Camera
location = (1.10000002, -0.18405022, 1.08624732)
rotation_deg = (75.080479, 1.535211, 92.172959)
Mug_Root
location = (0.43371654, -0.24633931, 0.78921199)
rotation_deg = (90.049760, -331.921048, -66.042895)
...
学習データ総数 3,000 件:
synthetic_dataset_0550_randomcam
├── gt
│ ├── s0550_0000.json
...
│ └── s0550_0999.json
└── images
├── s0550_0000.png
...
└── s0550_0999.png

synthetic_dataset_0551_randomcam
├── gt
│ ├── s0551_0000.json
...
│ └── s0551_0999.json
└── images
├── s0551_0000.png
...
└── s0551_0999.png

synthetic_dataset_0552_randomcam
├── gt
│ ├── s0552_0000.json
...
│ └── s0552_0999.json
└── images
├── s0552_0000.png
...
└── s0552_0999.png

物体検知には PyTorch / TorchVision の ssdlite320_mobilenet_v3_large を使用する。
本モデルは後段のキーポイント検知のために、マグカップ領域を検出・クロップする目的で使用する。
train.py dataset.py を使用、models/ へ
(.venv) detection$ python train.py
Device: cuda
Train samples: 3000
Epoch 001/100 train_loss=3.324669
Epoch 002/100 train_loss=1.868230
Epoch 003/100 train_loss=1.370852
Epoch 004/100 train_loss=1.174247
Epoch 005/100 train_loss=1.005588
saved: models/ssdlite320_sim0550_0552_epoch_005.pth
Epoch 006/100 train_loss=0.940396
...
50 epoch 学習し、5 epoch 毎に重みファイルを保存:
(.venv) rinda@P1G7 detection$ ls models/
ssdlite320_sim0550_0552_epoch_005.pth ssdlite320_sim0550_0552_epoch_020.pth ssdlite320_sim0550_0552_epoch_035.pth ssdlite320_sim0550_0552_epoch_050.pth
ssdlite320_sim0550_0552_epoch_010.pth ssdlite320_sim0550_0552_epoch_025.pth ssdlite320_sim0550_0552_epoch_040.pth
ssdlite320_sim0550_0552_epoch_015.pth ssdlite320_sim0550_0552_epoch_030.pth ssdlite320_sim0550_0552_epoch_045.pth
各重みファイルで実写データを推論:
(.venv) detection$ python infer_real_multi_pth.py
推論結果:
results_real_randomcam_multi_pth
├── all_epochs_scene_summary.csv
├── epoch_010
│ ├── frame_results.csv
│ ├── scene_summary.csv
│ └── vis
├── epoch_020
│ ├── frame_results.csv
│ ├── scene_summary.csv
│ └── vis
├── epoch_030
│ ├── frame_results.csv
│ ├── scene_summary.csv
│ └── vis
├── epoch_040
│ ├── frame_results.csv
│ ├── scene_summary.csv
│ └── vis
└── epoch_050
├── frame_results.csv
├── scene_summary.csv
└── vis
epoch_030 の重みファイルを検知モデルとして採用。以下 epoch_030/scene_summary.csv から:
| Object | Frames | Success | Low Confidence | Total Detected | Detection Rate | Miss |
|---|---|---|---|---|---|---|
| Mug | 961 | 961 | 0 | 961 | 100.0% | 0 |
| Combination Wrench | 961 | 449 | 208 | 657 | 68.4% | 304 |
Mug の検知は 100% で、今回の実写データはシミュレーションモデルで検知可能と判断。
Combination Wrench の検知率は低い。原因として、シミュレーション上の Combination Wrench と実物の外観差が大きいことが考えられる。
以降では 6D Pose 推定の対象である Mug のみを扱い、Combination Wrench については言及しない。
| [Synthetic-to-Real Object Detection | Blender + PyTorch on YouTube](https://youtube.com/shorts/THVDTcLsF0w) |
キーポイント検知には Heatmap Regression ベースのモデルを使用する。
各キーポイントはマグカップの既知 3D モデル上の点と対応しており、検出した 2D 座標と既知の 3D 座標を PnP に使用する。
keypoint/
├── dataset
├── model
├── output
├── training
datasetフォルダ:
dataset/
├── real
│ ├── detection
│ ├── gt
│ ├── gt_visuals
│ ├── images
│ └── metadata.csv
├── real_anno
│ ├── IMG_0538_00008.jpg
│ ├── IMG_0538_00008.json
...
│ ├── IMG_0552_00088.jpg
│ └── IMG_0552_00088.json
└── sim
├── detection
├── gt
├── gt_visuals
├── images
└── metadata.csv
real_anno:実写データをアノテーションしたデータ。
sim:Sim データ、datagen_keypoint_sim.py で作成。
real:Sim モデルを fine-tuning する際の実写データ、datagen_keypoint_real.py で作成。
datagen_keypoint_sim.py
SOURCE_DIRS = [
Path("../blender/synthetic_dataset_0550_randomcam"),
Path("../blender/synthetic_dataset_0551_randomcam"),
Path("../blender/synthetic_dataset_0552_randomcam"),
]
MODEL_PATH = Path(
"../detection/models/"
"ssdlite320_sim0550_0552_epoch_030.pth"
)
CROP_SIZE = 420
OUTPUT_SIZE = 420
Sim データ SOURCE_DIRS を検知モデル MODEL_PATH で推論した結果を元に、BBox からクロップした画像を 420x420 として出力(BBox を十分に囲むサイズ)。
出力:
dataset/sim
├── detection
├── gt
├── gt_visuals
├── images
└── metadata.csv
train.py model.py dataset.py を使用して構築:
(.venv) keypoint$ python training/train.py
Device: cuda
Train samples: 2971
Epoch 001 | train loss=0.003636 mean=147.95px max=497.90px
[BEST] epoch=1 train_mean=147.95px
Epoch 002 | train loss=0.000607
Epoch 003 | train loss=0.000579
Epoch 004 | train loss=0.000573
Epoch 005 | train loss=0.000569
Epoch 006 | train loss=0.000567
Epoch 007 | train loss=0.000567
Epoch 008 | train loss=0.000565
Epoch 009 | train loss=0.000562
Epoch 010 | train loss=0.000558 mean=129.44px max=429.86px
[BEST] epoch=10 train_mean=129.44px
Epoch 011 | train loss=0.000550
...
Epoch 200 | train loss=0.000007 mean= 1.57px max=104.14px
============================================================
TRAINING COMPLETE
============================================================
Best epoch : 190
Best train error : 1.56px
Best train mean : 1.56px
Train per-keypoint error:
K0: mean= 1.56px max= 3.89px
K1: mean= 1.57px max= 4.30px
K2: mean= 1.56px max= 4.53px
K3: mean= 1.57px max= 4.70px
T0: mean= 1.53px max= 4.03px
T1: mean= 1.59px max= 4.23px
T2: mean= 1.57px max= 4.27px
T3: mean= 1.61px max= 71.67px
K4: mean= 1.52px max= 4.06px
K5: mean= 1.56px max= 4.45px
K6: mean= 1.53px max= 39.06px
Best model : model/sim/best_model.pth
History : model/sim/training_history.csv
Visuals : model/sim/train_visuals
Best model (epoch 190) の学習データ上の平均キーポイント誤差は 1.56 px だった。
該当画像を確認すると、T3 / K6 はいずれも他の物体によってキーポイント位置が遮蔽されていた。

このため、大きな誤差は通常の可視キーポイントの検出失敗ではなく、occlusion により直接観測できないキーポイントの推定で発生している。
200 epoch 学習し、10 epoch 毎に重みファイルを保存:
(.venv) keypoint$ ls model/sim
best_model.pth epoch_020.pth epoch_050.pth epoch_080.pth epoch_110.pth epoch_140.pth epoch_170.pth epoch_200.pth
epoch_001.pth epoch_030.pth epoch_060.pth epoch_090.pth epoch_120.pth epoch_150.pth epoch_180.pth training_history.csv
epoch_010.pth epoch_040.pth epoch_070.pth epoch_100.pth epoch_130.pth epoch_160.pth epoch_190.pth train_visuals
best_model.pth は学習中に train mean error が最小となった checkpoint の model weights を保存したもので、今回の学習では epoch 190 の weights が選択された。
961 枚の実写データの 173 枚にキーポイントアノテーションを GT として定義。

datagen_keypoint_real.py
DEFAULT_SOURCE_DIR = Path("dataset/real_anno")
DEFAULT_OUTPUT_DIR = Path("dataset/real")
DEFAULT_MODEL_PATH = Path(
"../detection/models/"
"ssdlite320_sim0550_0552_epoch_030.pth"
)
NUM_CLASSES = 3
MUG_LABEL = 2
SCORE_THRESHOLD = 0.5
CROP_SIZE = 420
OUTPUT_SIZE = 420
KEYPOINT_NAMES = [
"K0",
"K1",
"K2",
"K3",
"T0",
"T1",
"T2",
"T3",
"K4",
"K5",
"K6",
]
実写をアノテーションしたデータ DEFAULT_SOURCE_DIR から、fine-tuning のための学習データ DEFAULT_OUTPUT_DIR を作成。物体検知モデル DEFAULT_MODEL_PATH を使用、サイズ 420x420 の画像を作成。DEFAULT_SOURCE_DIR から KEYPOINT_NAMES 等の GT を作成。
出力:
dataset/real
├── detection
├── gt
├── gt_visuals
├── images
└── metadata.csv
train_real_fit.py model.py dataset_real.py を使用して構築。
dataset_real.py
DATASET_ROOT = (
"dataset/real"
)
BASE_MODEL = Path(
"model/sim/best_model.pth"
)
OUTPUT_DIR = Path(
"model/real"
)
EPOCHS = 300
学習データ DATASET_ROOT、fine-tuningするモデル BASE_MODEL、出力先 OUTPUT_DIR、epoch 300
(.venv) keypoint$ python training/train_real_fit.py
Device: cuda
Train samples: 173
Skipped: {'missing_image': 0, 'no_keypoint': 0, 'invalid_gt': 0}
Base model: model/sim/best_model.pth
Base epoch: 190
Base synthetic train mean: 1.56px
Before fine-tuning | mean=126.23px max=425.33px valid=1414
Before fine-tuning per-keypoint error:
K0: n=109 mean=156.66px max=299.01px
K1: n=108 mean=185.39px max=404.57px
K2: n=125 mean=136.62px max=348.14px
K3: n= 91 mean=155.22px max=276.81px
T0: n=145 mean= 85.96px max=359.21px
T1: n=128 mean=116.61px max=411.70px
T2: n=135 mean=161.65px max=390.46px
T3: n=166 mean=145.10px max=385.73px
K4: n=118 mean=108.26px max=344.40px
K5: n=116 mean= 81.79px max=286.29px
K6: n=173 mean= 84.57px max=425.33px
Epoch 001 | train loss=0.000556 mean=116.90px max=422.06px valid=1414
[BEST] epoch=1 train_mean=116.90px
Epoch 002 | train loss=0.000529
Epoch 003 | train loss=0.000506
Epoch 004 | train loss=0.000481
Epoch 005 | train loss=0.000453
Epoch 006 | train loss=0.000420
Epoch 007 | train loss=0.000386
Epoch 008 | train loss=0.000353
Epoch 009 | train loss=0.000325
Epoch 010 | train loss=0.000300 mean= 33.57px max=420.29px valid=1414
[BEST] epoch=10 train_mean=33.57px
Epoch 011 | train loss=0.000278
...
Epoch 299 | train loss=0.000003
Epoch 300 | train loss=0.000003 mean= 1.47px max= 3.00px valid=1414
[BEST] epoch=300 train_mean=1.47px
============================================================
REAL FINE-TUNING COMPLETE
============================================================
Best epoch : 300
Best train error : 1.47px
Best train mean : 1.47px
Best train max : 3.00px
Best real-fit per-keypoint error:
K0: n=109 mean= 1.42px max= 2.81px
K1: n=108 mean= 1.48px max= 2.54px
K2: n=125 mean= 1.45px max= 2.82px
K3: n= 91 mean= 1.41px max= 2.60px
T0: n=145 mean= 1.54px max= 2.65px
T1: n=128 mean= 1.44px max= 3.00px
T2: n=135 mean= 1.43px max= 2.58px
T3: n=166 mean= 1.49px max= 2.73px
K4: n=118 mean= 1.55px max= 2.85px
K5: n=116 mean= 1.54px max= 2.56px
K6: n=173 mean= 1.45px max= 2.85px
Best model : model/real/best_model.pth
History : model/real/training_history.csv
Visuals : model/real/train_visuals
(.venv) rinda@P1G7 keypoint$ df -k
Filesystem 1K-blocks Used Available Use% Mounted on
tmpfs 3234836 3136 3231700 1% /run
/dev/nvme0n1p2 490048472 368155536 96926332 80% /
tmpfs 16174168 71580 16102588 1% /dev/shm
tmpfs 5120 8 5112 1% /run/lock
efivarfs 172 80 88 48% /sys/firmware/efi/efivars
/dev/nvme0n1p1 1098632 6288 1092344 1% /boot/efi
tmpfs 3234832 148 3234684 1% /run/user/1000
epoch 300 における学習データ上の平均キーポイント誤差は 1.47 px となった。
run_pipeline_real.py
DEFAULT_FRAMES_ROOT = Path(
"../real_dataset/frames"
)
DEFAULT_DETECTOR_PATH = Path(
"../detection/models/ssdlite320_sim0550_0552_epoch_030.pth"
)
DEFAULT_KEYPOINT_MODEL_PATH = Path(
"model/sim/best_model.pth"
# "model/real/best_model.pth"
)
DEFAULT_OUTPUT_DIR = Path(
"output/pipeline_keypoint_real_simonly"
# "output/pipeline_keypoint_real"
)
RANSAC_REPROJ_ERROR = 12.0 #8.0 #5.0
FINAL_INLIER_ERROR = 12.0 #8.0 #5.0
DEFAULT_DETECTOR_PATH:検知モデル、Sim モデルを使用。
DEFAULT_KEYPOINT_MODEL_PATH:キーポイント検知モデル。
DEFAULT_OUTPUT_DIR:姿勢推定結果。
実写アノテーションを用いた検証では数 px 程度のアノテーション誤差が確認されたため、複数の閾値を試した結果、本評価では 12.0 px を採用した。
なお PnP では、3Dモデル上に定義した11点のキーポイントと、Keypoint model が推定した画像上の2D点を対応させる。3D keypoints は以下の座標を使用した。単位は meter。
| Point | X | Y | Z |
|---|---:|---:|---:|
| K0 | 0.036510 | -0.000531 | 0.001519 |
| K1 | -0.036406 | -0.000531 | 0.001519 |
| K2 | -0.000755 | -0.035998 | 0.001519 |
| K3 | -0.000755 | 0.036682 | 0.001519 |
| T0 | 0.036510 | -0.000531 | 0.088068 |
| T1 | -0.036406 | -0.000531 | 0.088068 |
| T2 | -0.000755 | -0.035998 | 0.088068 |
| T3 | -0.000755 | 0.036682 | 0.088068 |
| K4 | 0.037754 | -0.000025 | 0.078986 |
| K5 | 0.037754 | -0.000025 | 0.024736 |
| K6 | 0.071656 | 0.000064 | 0.061815 |
model/sim/best_model.pth を使用。
(.venv) rinda@P1G7 keypoint$ python training/run_pipeline_real.py
Device : cuda
Frames root : ../real_dataset/frames
Detector : ../detection/models/ssdlite320_sim0550_0552_epoch_030.pth
Keypoint model : model/sim/best_model.pth
Canonical crop : 420x420
KP input : 420x420
KP heatmap : 112x112
KP checkpoint : epoch=190
Processed: 1/961
...
Processed: 950/961
============================================================
UNIFIED REAL PIPELINE COMPLETE
============================================================
Images : 961
Processed : 961
No detection : 0
Output : output/pipeline_keypoint_real_simonly
CSV log : output/pipeline_keypoint_real_simonly/pipeline_log.csv
pipeline_keypoint_real_simonly
├── IMG_0538
│ ├── IMG_0538_00001_crop.png
│ ├── IMG_0538_00001_detection.png
│ ├── IMG_0538_00001_keypoints_crop.png
│ ├── IMG_0538_00001_keypoints_original.png
│ ├── IMG_0538_00001_pnp.png
│ ├── IMG_0538_00002_crop.png
│ ├── IMG_0538_00002_detection.png
│ ├── IMG_0538_00002_keypoints_crop.png
│ ├── IMG_0538_00002_keypoints_original.png
│ ├── IMG_0538_00002_pnp.png
...
├── IMG_0539
├── IMG_0540
├── IMG_0541
├── IMG_0542
├── IMG_0543
├── IMG_0544
├── IMG_0546
├── IMG_0547
├── IMG_0548
├── IMG_0549
├── IMG_0550
├── IMG_0551
├── IMG_0552
└── pipeline_log.csv
model/real/best_model.pth Real モデルを使用。
(.venv) keypoint$ python training/run_pipeline_real.py
Device : cuda
Frames root : ../real_dataset/frames
Detector : ../detection/models/ssdlite320_sim0550_0552_epoch_030.pth
Keypoint model : model/real/best_model.pth
Canonical crop : 420x420
KP input : 420x420
KP heatmap : 112x112
KP checkpoint : epoch=300
Processed: 1/961
Processed: 50/961
...
Processed: 900/961
Processed: 950/961
============================================================
UNIFIED REAL PIPELINE COMPLETE
============================================================
Images : 961
Processed : 961
No detection : 0
Output : output/pipeline_keypoint_real
CSV log : output/pipeline_keypoint_real/pipeline_log.csv
pipeline_keypoint_real
├── IMG_0538
│ ├── IMG_0538_00001_crop.png
│ ├── IMG_0538_00001_detection.png
│ ├── IMG_0538_00001_keypoints_crop.png
│ ├── IMG_0538_00001_keypoints_original.png
│ ├── IMG_0538_00001_pnp.png
│ ├── IMG_0538_00002_crop.png
│ ├── IMG_0538_00002_detection.png
│ ├── IMG_0538_00002_keypoints_crop.png
│ ├── IMG_0538_00002_keypoints_original.png
│ ├── IMG_0538_00002_pnp.png
...
├── IMG_0539
├── IMG_0540
├── IMG_0541
├── IMG_0542
├── IMG_0543
├── IMG_0544
├── IMG_0546
├── IMG_0547
├── IMG_0548
├── IMG_0549
├── IMG_0550
├── IMG_0551
├── IMG_0552
└── pipeline_log.csv
IMG_0538_00001_detection.png
IMG_0538_00001_crop.png
IMG_0538_00001_keypoints_crop.png
IMG_0538_00001_keypoints_original.png
IMG_0538_00001_pnp.png

実写データ 961 枚に対して、Sim モデルと Real fine-tuning モデルを使用して キーポイント検知および PnP を実行した結果を比較する。
| 指標 | Real Fine-tuning | Sim Only |
|---|---|---|
| Frames | 961 | 961 |
| Mean Keypoint Confidence | 0.604 | 0.104 |
| Mean PnP Final Inliers | 7.10 | 2.22 |
| Mean PnP All RMSE | 59.3 px | 134.3 px |
| solvePnPRansac Failed | 21 | 538 |
Real fine-tuning モデルでは、Sim Only モデルと比較してキーポイントの confidence が高く、PnP の final inlier 数も増加した。特に solvePnPRansac の失敗は、Sim Only の 538 件に対して Real Fine-tuning では 21 件まで減少した。
この結果から、Real Fine-tuning により実写画像に対するキーポイント推定の confidence と幾何的一貫性が改善し、その結果として PnP の安定性が大きく向上したと判断する。ただし、実写画像には 6D Pose の Ground Truth が存在しないため、ここで評価しているのは姿勢推定の絶対精度ではなく、推定処理の安定性である。
6D Pose Estimation – Before Real-World Fine-Tuning (Sim Only) on YouTube 6D Pose Estimation – After Real-World Fine-Tuning on YouTube
学習および評価に使用したUbuntu + CUDA環境と、Raspberry Pi上で同一の推論パイプラインを実行し、推論結果と処理速度を比較した。
| 項目 | 内容 |
|---|---|
| Device | Raspberry Pi 5 Model B Rev 1.0 |
| CPU | ARM Cortex-A76, 4 cores, max 2.4 GHz |
| Memory | 4 GB |
| OS | Debian GNU/Linux 12 (bookworm) |
| ONNX Runtime | 1.29.0 |
| Execution Provider | CPUExecutionProvider |
学習済みの物体検知モデルとキーポイント推定モデルをONNX形式に変換し、Raspberry Pi上で推論パイプラインを実行した。
使用したモデルを以下に示す。
| モデル | ONNXモデル |
|---|---|
| 物体検知 | ssdlite320_sim0550_0552_epoch_030.onnx |
| キーポイント推定 | best_model.onnx |
推論パイプラインは以下の構成とした。
物体検知 → クロップ → キーポイント推定 → PnP姿勢推定
| 項目 | 内容 |
|---|---|
| CPU | Intel Core Ultra 7 155H, 16 cores / 22 threads, max 4.8 GHz |
| GPU | NVIDIA RTX 2000 Ada Generation, 8 GB |
| Memory | 30 GB |
| OS | Ubuntu 24.04.4 LTS |
| NVIDIA Driver | 580.126.09 |
| CUDA | 13.0 |
| Inference | PyTorch / CUDA |
Raspberry Piと同一の入力画像を使用し、同一構成の推論パイプラインを実行した。
同一の961枚の実写画像を使用し、Raspberry Pi 5とUbuntu + CUDA環境で推論結果および処理速度を比較した。
| Platform | Detection | Keypoint | PnP | Total | FPS |
|---|---|---|---|---|---|
| Raspberry Pi 5 / CPU | 70.36 ms | 56.06 ms | 5.34 ms | 132.12 ms | 7.60 |
| Ubuntu / RTX 2000 Ada / CUDA | 60.25 ms | 21.87 ms | 5.18 ms | 87.71 ms | 11.60 |
Ubuntu + CUDA環境では、パイプライン全体の処理速度はRaspberry Pi 5の約1.5倍となった。特にキーポイント推定では、56.06 msから21.87 msとなり、約2.6倍高速であった。
一方、推論結果は両環境でほぼ一致した。物体検知スコアは全961画像で一致し、BBox座標差も最大0.001 px未満であった。また、11点のキーポイント座標は全961画像で一致した。
PnPの判定結果も全体としてほぼ一致したが、RANSACの非決定性により、961画像中2画像でRANSAC_FAILとなるフレームに違いが見られた。
6D Pose Estimation: CUDA vs Raspberry Pi 5 on YouTube
手動アノテーションでは、画像上で正確なキーポイント位置を定義することが難しい。特に今回のマグカップのように、画像上から3D形状上の対応点を正確に判断しにくい物体では、アノテーションそのものに誤差や幾何学的な不整合が生じる。
その結果、fine-tuning 後のキーポイント推論が一見良好であっても、後段の PnP による姿勢推定では推定座標軸の向きや位置が不安定になるケースが確認された。
実写画像を Blender の背景画像として配置し、対応する3Dオブジェクトを実写画像上の物体に重ね合わせる。これにより、3Dモデル上で定義したキーポイントを画像平面へ投影し、3D / 2D の対応関係が一貫したアノテーションを作成する。
実写の物体と3Dオブジェクトが一致するように調整する。


3Dオブジェクトを Wireframe 表示し、実写画像上の物体と一致するように位置・姿勢を調整する。

位置合わせ後、.blend ファイルを保存して以下のスクリプトを実行する。
/Applications/Blender5_1_2.app/Contents/MacOS/Blender \
-b tool_pose.blend -P save_source_gt.py
Note: 本アノテーション作りは Mac で実施 。
出力:source_GT.jsonl 実写映像に対する、対象物の3D/2D情報を保存。
スクリプト:posegt_project_source.py
入力 :posegt_source_GT.jsonl
出力 :posegt_projected_GT.jsonl
blender$ blender5 -b tool_pose.blend -P posegt_project_source.py
スクリプト:posegt_datagen_real.py
DEFAULT_SOURCE_GT = Path(
"../blender/posegt_projected_GT.jsonl"
)
DEFAULT_OUTPUT_DIR = Path(
"dataset/real_posegt"
)
REAL_IMAGE_DIR = Path(
"dataset/real_anno"
)
DEFAULT_SOURCE_GT REAL_IMAGE_DIR を入力として学習データを作成:
(.venv) keypoint$ python training/posegt_datagen_real.py
学習スクリプト:train_real_fit.py
DATASET_ROOT = (
# "dataset/real"
"dataset/real_posegt"
)
BASE_MODEL = Path(
"model/sim/best_model.pth"
)
OUTPUT_DIR = Path(
# "model/real"
"model/real_posegt"
)
(.venv) keypoint$ python training/train_real_fit.py
推論スクリプト:run_pipeline_real.py
DEFAULT_KEYPOINT_MODEL_PATH = Path(
# "model/sim/best_model.pth"
# "model/real/best_model.pth"
"model/real_posegt/best_model.pth"
)
DEFAULT_OUTPUT_DIR = Path(
# "output/pipeline_keypoint_real_simonly"
"output/pipeline_keypoint_posegt"
)
(.venv) keypoint$ python training/run_pipeline_real.py
今回は、推論結果の動画で使用した以下のシーンの 59 枚を再アノテーション:
"IMG_0540",
"IMG_0541",
"IMG_0543",
"IMG_0546",
"IMG_0548",
"IMG_0549",
"IMG_0550",
"IMG_0552",
アノテーション改善後は、改善前に見られた 推定座標軸の大きな反転や急激な変動が減少し、フレーム間でより一貫した姿勢推定結果が得られた。定量的な Pose GT は存在しないため、ここでは主に可視化結果から時間方向の安定性を確認している。