旧debug資産の整理

This commit is contained in:
Keisuke Hirata 2026-02-16 16:08:22 +09:00
parent e693f5b694
commit 0d63b2ef6d
5 changed files with 1 additions and 733 deletions

View File

@ -19,7 +19,7 @@
python server/main.py
# サーバーのGPU状態を確認
python test_server_api.py --status
curl -s http://127.0.0.1:8181/status | jq
```
出力例:
@ -40,20 +40,3 @@ python test_server_api.py --status
ROCm Version (HIP): 7.0.51831
======================================================================
```
### 処理プロセスの単体デバッグ
顔検出処理をBlenderから独立してテストできます。
```bash
# 画像ファイルでテスト
python debug_detector.py --image test.jpg
# 動画ファイルでテスト
python debug_detector.py --video test.mp4 --frame 0
# クイックテスト(簡易版)
./test_quick.sh test.jpg
```
詳細は [docs/debugging.md](docs/debugging.md) を参照してください。

View File

@ -1,267 +0,0 @@
#!/usr/bin/env python3
"""
顔検出処理の単体デバッグスクリプト
Usage:
# 画像ファイルで検出をテスト
python debug_detector.py --image path/to/image.jpg
# 動画ファイルで検出をテスト(指定フレームのみ)
python debug_detector.py --video path/to/video.mp4 --frame 100
# 動画ファイルで複数フレームをテスト
python debug_detector.py --video path/to/video.mp4 --start 0 --end 10
# 結果を保存
python debug_detector.py --image test.jpg --output result.jpg
"""
import argparse
import sys
from pathlib import Path
import cv2
import numpy as np
# プロジェクトルートをパスに追加
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
from server.detector import YOLOFaceDetector
def draw_detections(image: np.ndarray, detections, mask=None):
"""
検出結果を画像に描画
Args:
image: 元画像BGR
detections: 検出結果のリスト [(x, y, w, h, conf), ...]
mask: マスク画像オプション
Returns:
描画済み画像
"""
output = image.copy()
# マスクをオーバーレイ
if mask is not None:
# マスクを3チャンネルに変換
mask_colored = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
# 赤色でオーバーレイ(半透明)
mask_overlay = np.zeros_like(output)
mask_overlay[:, :, 2] = mask # 赤チャンネル
output = cv2.addWeighted(output, 1.0, mask_overlay, 0.3, 0)
# バウンディングボックスを描画
for (x, y, w, h, conf) in detections:
# ボックス
cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 信頼度テキスト
label = f"{conf:.2f}"
label_size, baseline = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
y_label = max(y, label_size[1])
cv2.rectangle(
output,
(x, y_label - label_size[1]),
(x + label_size[0], y_label + baseline),
(0, 255, 0),
-1
)
cv2.putText(
output,
label,
(x, y_label),
cv2.FONT_HERSHEY_SIMPLEX,
0.5,
(0, 0, 0),
1
)
return output
def debug_image(args, detector):
"""画像ファイルで検出をデバッグ"""
print(f"画像を読み込み中: {args.image}")
image = cv2.imread(args.image)
if image is None:
print(f"エラー: 画像を読み込めません: {args.image}")
return
print(f"画像サイズ: {image.shape[1]}x{image.shape[0]}")
# 検出実行
print("顔検出を実行中...")
detections = detector.detect(image)
print(f"\n検出結果: {len(detections)}個の顔を検出")
for i, (x, y, w, h, conf) in enumerate(detections):
print(f" [{i+1}] x={x}, y={y}, w={w}, h={h}, conf={conf:.3f}")
# マスク生成
if len(detections) > 0:
mask = detector.generate_mask(
image.shape,
detections,
mask_scale=args.mask_scale,
feather_radius=args.feather_radius
)
else:
mask = None
# 結果を描画
result = draw_detections(image, detections, mask)
# 表示または保存
if args.output:
cv2.imwrite(args.output, result)
print(f"\n結果を保存しました: {args.output}")
if mask is not None and args.save_mask:
mask_path = args.output.replace('.', '_mask.')
cv2.imwrite(mask_path, mask)
print(f"マスクを保存しました: {mask_path}")
else:
cv2.imshow("Detection Result", result)
if mask is not None:
cv2.imshow("Mask", mask)
print("\nキーを押して終了してください...")
cv2.waitKey(0)
cv2.destroyAllWindows()
def debug_video(args, detector):
"""動画ファイルで検出をデバッグ"""
print(f"動画を読み込み中: {args.video}")
cap = cv2.VideoCapture(args.video)
if not cap.isOpened():
print(f"エラー: 動画を開けません: {args.video}")
return
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(f"動画情報: {width}x{height}, {fps:.2f}fps, {total_frames}フレーム")
# フレーム範囲の決定
start_frame = args.start if args.start is not None else args.frame
end_frame = args.end if args.end is not None else args.frame
start_frame = max(0, min(start_frame, total_frames - 1))
end_frame = max(0, min(end_frame, total_frames - 1))
print(f"処理範囲: フレーム {start_frame} - {end_frame}")
# 出力動画の準備
out_writer = None
if args.output:
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out_writer = cv2.VideoWriter(args.output, fourcc, fps, (width, height))
# フレーム処理
for frame_idx in range(start_frame, end_frame + 1):
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_idx)
ret, frame = cap.read()
if not ret:
print(f"警告: フレーム {frame_idx} を読み込めませんでした")
continue
# 検出実行
detections = detector.detect(frame)
# マスク生成
if len(detections) > 0:
mask = detector.generate_mask(
frame.shape,
detections,
mask_scale=args.mask_scale,
feather_radius=args.feather_radius
)
else:
mask = None
# 結果を描画
result = draw_detections(frame, detections, mask)
print(f"フレーム {frame_idx}: {len(detections)}個の顔を検出")
# 保存または表示
if out_writer:
out_writer.write(result)
else:
cv2.imshow(f"Frame {frame_idx}", result)
if mask is not None:
cv2.imshow("Mask", mask)
key = cv2.waitKey(0 if end_frame == start_frame else 30)
if key == ord('q'):
break
cap.release()
if out_writer:
out_writer.release()
print(f"\n結果を保存しました: {args.output}")
else:
cv2.destroyAllWindows()
def main():
parser = argparse.ArgumentParser(
description="顔検出処理の単体デバッグスクリプト",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__
)
# 入力ソース
input_group = parser.add_mutually_exclusive_group(required=True)
input_group.add_argument("--image", type=str, help="テスト用画像ファイル")
input_group.add_argument("--video", type=str, help="テスト用動画ファイル")
# 動画用オプション
parser.add_argument("--frame", type=int, default=0, help="処理する動画フレーム番号(デフォルト: 0")
parser.add_argument("--start", type=int, help="処理開始フレーム(動画のみ)")
parser.add_argument("--end", type=int, help="処理終了フレーム(動画のみ)")
# 検出パラメータ
parser.add_argument("--conf", type=float, default=0.5, help="信頼度閾値(デフォルト: 0.5")
parser.add_argument("--iou", type=float, default=0.45, help="NMS IoU閾値デフォルト: 0.45")
parser.add_argument("--mask-scale", type=float, default=1.5, help="マスクスケール(デフォルト: 1.5")
parser.add_argument("--feather-radius", type=int, default=20, help="マスクぼかし半径(デフォルト: 20")
# 出力オプション
parser.add_argument("--output", "-o", type=str, help="結果画像/動画の保存先")
parser.add_argument("--save-mask", action="store_true", help="マスク画像も保存する(画像のみ)")
# モデル
parser.add_argument("--model", type=str, help="カスタムモデルパス")
args = parser.parse_args()
# 検出器を初期化
print("YOLOFaceDetectorを初期化中...")
detector = YOLOFaceDetector(
model_path=args.model,
conf_threshold=args.conf,
iou_threshold=args.iou
)
# モデルを事前ロード
print("モデルをロード中...")
_ = detector.model
print("準備完了\n")
# デバッグ実行
if args.image:
debug_image(args, detector)
else:
debug_video(args, detector)
if __name__ == "__main__":
main()

View File

@ -1,151 +0,0 @@
# デバッグガイド
## 処理プロセスの単体デバッグ
顔検出処理をBlenderアドオンから独立してテストできます。
### セットアップ
```bash
# 仮想環境をアクティベート
source .venv/bin/activate
# 必要なパッケージがインストールされていることを確認
pip install ultralytics opencv-python torch
```
### 基本的な使い方
#### 画像ファイルで検出をテスト
```bash
# 検出結果を画面に表示
python debug_detector.py --image path/to/image.jpg
# 検出結果を保存
python debug_detector.py --image path/to/image.jpg --output result.jpg
# マスク画像も保存
python debug_detector.py --image path/to/image.jpg --output result.jpg --save-mask
```
#### 動画ファイルで検出をテスト
```bash
# 特定のフレームをテスト
python debug_detector.py --video path/to/video.mp4 --frame 100
# フレーム範囲をテスト(画面表示)
python debug_detector.py --video path/to/video.mp4 --start 0 --end 10
# フレーム範囲を処理して動画保存
python debug_detector.py --video path/to/video.mp4 --start 0 --end 100 --output result.mp4
```
### パラメータ調整
```bash
# 信頼度閾値を調整(デフォルト: 0.5
python debug_detector.py --image test.jpg --conf 0.3
# NMS IoU閾値を調整デフォルト: 0.45
python debug_detector.py --image test.jpg --iou 0.5
# マスクサイズを調整(デフォルト: 1.5
python debug_detector.py --image test.jpg --mask-scale 2.0
# マスクのぼかし半径を調整(デフォルト: 20
python debug_detector.py --image test.jpg --feather-radius 30
```
### カスタムモデルの使用
```bash
python debug_detector.py --image test.jpg --model path/to/custom_model.pt
```
## 推論サーバーの単体起動
推論サーバーを単独で起動してテストすることもできます。
### サーバー起動
```bash
# 仮想環境をアクティベート
source .venv/bin/activate
# サーバーを起動ポート8181
python server/main.py
```
### APIテスト
別のターミナルで:
```bash
# サーバー状態を確認
curl http://127.0.0.1:8181/status
# マスク生成をリクエスト
curl -X POST http://127.0.0.1:8181/generate \
-H "Content-Type: application/json" \
-d '{
"video_path": "/path/to/video.mp4",
"output_dir": "/tmp/masks",
"start_frame": 0,
"end_frame": 10,
"conf_threshold": 0.5,
"iou_threshold": 0.45,
"mask_scale": 1.5
}'
# タスクの状態を確認task_idは上記レスポンスから取得
curl http://127.0.0.1:8181/tasks/{task_id}
# タスクをキャンセル
curl -X POST http://127.0.0.1:8181/tasks/{task_id}/cancel
```
## トラブルシューティング
### GPUROCmが認識されない
```bash
# PyTorchがROCmを認識しているか確認
python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}')"
# ROCm環境変数を確認
echo $ROCM_PATH
echo $HSA_OVERRIDE_GFX_VERSION
```
環境変数が設定されていない場合:
```bash
source .envrc
# または
eval "$(direnv export bash)"
```
### モデルが見つからない
デフォルトモデルは `models/yolov8n-face-lindevs.pt` に配置する必要があります。
```bash
ls -l models/yolov8n-face-lindevs.pt
```
### メモリ不足エラー
大きな動画を処理する場合、メモリ不足になる可能性があります:
- フレーム範囲を小さく分割して処理
- `--conf` 閾値を上げて検出数を減らす
- より小さいモデルを使用
## デバッグのベストプラクティス
1. **まず画像でテスト**: 動画よりも画像の方が早く結果を確認できます
2. **パラメータの影響を理解**: `--conf`、`--mask-scale` などを変えて結果を比較
3. **小さいフレーム範囲から始める**: 動画テストは最初は5-10フレーム程度で
4. **結果を保存して比較**: `--output` オプションで結果を保存し、パラメータごとに比較

View File

@ -1,73 +0,0 @@
#!/bin/bash
# クイックテストスクリプト
# 処理プロセスが正常に動作するか確認
set -e
echo "=== 顔検出処理のクイックテスト ==="
echo ""
# 仮想環境の確認
if [ ! -d ".venv" ]; then
echo "エラー: .venv が見つかりません"
echo "仮想環境を作成してください: python -m venv .venv"
exit 1
fi
# 環境変数の読み込み
if [ -f ".env" ]; then
echo "環境変数を読み込み中..."
export $(cat .env | grep -v '^#' | xargs)
fi
# 仮想環境をアクティベート
source .venv/bin/activate
# モデルの確認
MODEL_PATH="models/yolov8n-face-lindevs.pt"
if [ ! -f "$MODEL_PATH" ]; then
echo "警告: モデルファイルが見つかりません: $MODEL_PATH"
echo "デフォルトモデルをダウンロードしてください"
fi
# テスト画像の確認
if [ $# -eq 0 ]; then
echo "使い方: $0 <画像ファイルまたは動画ファイル>"
echo ""
echo "例:"
echo " $0 test.jpg"
echo " $0 test.mp4"
exit 1
fi
INPUT_FILE="$1"
if [ ! -f "$INPUT_FILE" ]; then
echo "エラー: ファイルが見つかりません: $INPUT_FILE"
exit 1
fi
# ファイルタイプの判定
EXT="${INPUT_FILE##*.}"
EXT_LOWER=$(echo "$EXT" | tr '[:upper:]' '[:lower:]')
echo "入力ファイル: $INPUT_FILE"
echo ""
# GPU情報の表示
echo "=== GPU情報 ==="
python -c "import torch; print(f'PyTorch CUDA available: {torch.cuda.is_available()}'); print(f'Device: {torch.cuda.get_device_name(0) if torch.cuda.is_available() else \"CPU\"}') if torch.cuda.is_available() else None" 2>/dev/null || echo "PyTorchが見つかりません"
echo ""
# テスト実行
echo "=== 検出テストを開始 ==="
if [[ "$EXT_LOWER" == "mp4" || "$EXT_LOWER" == "avi" || "$EXT_LOWER" == "mov" ]]; then
# 動画の場合は最初の1フレームのみテスト
python debug_detector.py --video "$INPUT_FILE" --frame 0
else
# 画像の場合
python debug_detector.py --image "$INPUT_FILE"
fi
echo ""
echo "=== テスト完了 ==="

View File

@ -1,224 +0,0 @@
#!/usr/bin/env python3
"""
推論サーバーAPIのテストスクリプト
Usage:
# サーバーの状態確認
python test_server_api.py --status
# マスク生成のテスト
python test_server_api.py --video test.mp4 --output /tmp/masks --start 0 --end 10
"""
import argparse
import json
import time
import urllib.request
import urllib.error
from pathlib import Path
import sys
SERVER_URL = "http://127.0.0.1:8181"
def check_status():
"""サーバーの状態を確認"""
try:
with urllib.request.urlopen(f"{SERVER_URL}/status", timeout=2) as response:
data = json.loads(response.read().decode('utf-8'))
print("✓ サーバーは稼働中です")
print(f" Status: {data.get('status')}")
print(f" GPU Available: {data.get('gpu_available')}")
if data.get('gpu_device'):
print(f" GPU Device: {data.get('gpu_device')}")
if data.get('gpu_count'):
print(f" GPU Count: {data.get('gpu_count')}")
if data.get('rocm_version'):
print(f" ROCm Version: {data.get('rocm_version')}")
return True
except (urllib.error.URLError, ConnectionRefusedError, TimeoutError) as e:
print("✗ サーバーに接続できません")
print(f" エラー: {e}")
print("\nサーバーを起動してください:")
print(" ./run_server.sh")
return False
def submit_task(video_path, output_dir, start_frame, end_frame, conf, iou, mask_scale):
"""マスク生成タスクを送信"""
data = {
"video_path": video_path,
"output_dir": output_dir,
"start_frame": start_frame,
"end_frame": end_frame,
"conf_threshold": conf,
"iou_threshold": iou,
"mask_scale": mask_scale,
}
req = urllib.request.Request(
f"{SERVER_URL}/generate",
data=json.dumps(data).encode('utf-8'),
headers={'Content-Type': 'application/json'},
method='POST'
)
try:
with urllib.request.urlopen(req) as response:
result = json.loads(response.read().decode('utf-8'))
return result
except urllib.error.HTTPError as e:
error_msg = e.read().decode('utf-8')
raise RuntimeError(f"サーバーエラー: {error_msg}")
def get_task_status(task_id):
"""タスクの状態を取得"""
try:
with urllib.request.urlopen(f"{SERVER_URL}/tasks/{task_id}") as response:
return json.loads(response.read().decode('utf-8'))
except urllib.error.HTTPError:
return {"status": "unknown"}
def cancel_task(task_id):
"""タスクをキャンセル"""
try:
req = urllib.request.Request(
f"{SERVER_URL}/tasks/{task_id}/cancel",
method='POST'
)
with urllib.request.urlopen(req):
pass
return True
except urllib.error.HTTPError:
return False
def monitor_task(task_id, poll_interval=0.5):
"""タスクの進行状況を監視"""
print(f"\nタスクID: {task_id}")
print("進行状況を監視中...\n")
last_progress = -1
while True:
status = get_task_status(task_id)
state = status.get('status')
progress = status.get('progress', 0)
total = status.get('total', 0)
# 進行状況の表示
if progress != last_progress and total > 0:
percentage = (progress / total) * 100
bar_length = 40
filled = int(bar_length * progress / total)
bar = '=' * filled + '-' * (bar_length - filled)
print(f"\r[{bar}] {progress}/{total} ({percentage:.1f}%)", end='', flush=True)
last_progress = progress
# 終了状態のチェック
if state == "completed":
print("\n\n✓ 処理が完了しました")
print(f" 出力先: {status.get('result_path')}")
print(f" メッセージ: {status.get('message')}")
return True
elif state == "failed":
print("\n\n✗ 処理が失敗しました")
print(f" エラー: {status.get('message')}")
return False
elif state == "cancelled":
print("\n\n- 処理がキャンセルされました")
return False
time.sleep(poll_interval)
def main():
parser = argparse.ArgumentParser(
description="推論サーバーAPIのテストスクリプト"
)
# 操作モード
mode_group = parser.add_mutually_exclusive_group(required=True)
mode_group.add_argument("--status", action="store_true", help="サーバーの状態を確認")
mode_group.add_argument("--video", type=str, help="処理する動画ファイル")
# タスクパラメータ
parser.add_argument("--output", type=str, default="/tmp/masks", help="マスク出力先ディレクトリ")
parser.add_argument("--start", type=int, default=0, help="開始フレーム")
parser.add_argument("--end", type=int, default=10, help="終了フレーム")
parser.add_argument("--conf", type=float, default=0.5, help="信頼度閾値")
parser.add_argument("--iou", type=float, default=0.45, help="NMS IoU閾値")
parser.add_argument("--mask-scale", type=float, default=1.5, help="マスクスケール")
# その他のオプション
parser.add_argument("--no-wait", action="store_true", help="タスク送信後、完了を待たない")
args = parser.parse_args()
# 状態確認モード
if args.status:
check_status()
return
# マスク生成モード
print("=== 推論サーバーAPIテスト ===\n")
# サーバーの確認
if not check_status():
sys.exit(1)
# 動画ファイルの確認
if not Path(args.video).exists():
print(f"\n✗ 動画ファイルが見つかりません: {args.video}")
sys.exit(1)
video_path = str(Path(args.video).absolute())
output_dir = str(Path(args.output).absolute())
print(f"\n動画: {video_path}")
print(f"出力先: {output_dir}")
print(f"フレーム範囲: {args.start} - {args.end}")
print(f"パラメータ: conf={args.conf}, iou={args.iou}, mask_scale={args.mask_scale}")
# タスクを送信
print("\nタスクを送信中...")
try:
result = submit_task(
video_path,
output_dir,
args.start,
args.end,
args.conf,
args.iou,
args.mask_scale
)
except Exception as e:
print(f"\n✗ タスク送信失敗: {e}")
sys.exit(1)
task_id = result['id']
print(f"✓ タスクが送信されました (ID: {task_id})")
# 完了待機
if not args.no_wait:
try:
success = monitor_task(task_id)
sys.exit(0 if success else 1)
except KeyboardInterrupt:
print("\n\n中断されました")
print("タスクをキャンセル中...")
if cancel_task(task_id):
print("✓ タスクをキャンセルしました")
sys.exit(130)
else:
print("\nタスクの状態を確認するには:")
print(f" python test_server_api.py --task-status {task_id}")
if __name__ == "__main__":
main()