# ローカル一括API処理

指定フォルダに置いた動画とCSVを読み、動画から「高画質かつブレが少なく、湯気・白飛びが少ない」代表カットを1枚抽出し、BytePlus Responses APIへ送信します。返却JSON、抽出フレーム、CSVメタデータをSQLite DBと学習用JSONL/CSVに保存します。

評価方針は `APIテスト/豚山スープ画像評価プロンプト方針.md` を読み込んで使います。方針文書と異なるプロンプトを別管理しない設計です。

## 入力フォルダ

フォルダ内に以下を置きます。

- 動画: `.mp4`, `.mov`, `.m4v`, `.avi`, `.mkv`
- CSV: 添付サンプルと同じ列を想定
  - `テキスト`
  - `提出者`
  - `時間帯`
  - `スープ濃度`
  - `添付ファイル`
  - `スープのみの点数`
  - `濃度込みの点数`
  - `評価備考`
  - `通知するブロック長`

CSVがない場合は、フォルダ内の動画を全件処理します。

## セットアップ

```powershell
pip install -r requirements.txt
Copy-Item local_batch_api\config.example.yaml local_batch_api\config.yaml
```

動画抽出はVPS本番では `ffmpeg` / `ffprobe` の利用を推奨します。

- Ubuntu/Debian: `sudo apt-get update && sudo apt-get install -y ffmpeg`
- Windows検証環境: `winget install --id Gyan.FFmpeg -e`

初回疎通テストのように代表カット精度より速度を優先する場合は、`processing.extraction_strategy: "ffmpeg_middle"` を使うと動画長の指定位置から1フレームだけ抽出します。

## Brixなし濃度推定用の特徴量作成

将来 `brix_raw` が現場入力されなくなる前提では、`brix_raw` / `スープ濃度` は学習時の教師ラベルとしてだけ使い、本番推論の入力には使いません。

動画のみから濃度推定器を作るためのローカル画像特徴量は以下で作成します。

```powershell
python local_batch_api\build_density_features.py `
  --csv "学習データ\ギフトホールディングス様→ラーメン画像AI評価 コピー_ai-request-table_グリッド 10.csv" `
  --video-dir "学習データ" `
  --output "local_batch_api\density_features\density_features.csv" `
  --save-frames
```

この処理はAPI送信を行いません。動画から複数フレームを抽出し、白濁度、透明感代理値、茶色領域比率、クリーム色領域比率、白領域比率、エッジ量などをCSV/JSONLに保存します。

### スープ領域だけを使う改善ルート

現在の最有力ルートは、参照プロジェクト `AIラーメン開発\ramen-soup-ai-suite\03_soup-density-ai` のRF-DETR-Segモデルで、鍋・背景・金属・ザル・湯気の影響を減らしたスープ領域画像を作ってから特徴量化する方法です。

まず保存済みフレームを作ります。

```powershell
python local_batch_api\build_density_features.py `
  --csv "学習データ\ギフトホールディングス様→ラーメン画像AI評価 コピー_ai-request-table_グリッド 10.csv" `
  --video-dir "学習データ" `
  --output "local_batch_api\density_features\density_features_source.csv" `
  --frames-dir "local_batch_api\density_features\frames_for_masks" `
  --save-frames
```

次に参照プロジェクトの学習済みチェックポイントでスープ領域を切り抜きます。実行には `rfdetr` / `torch` / `torchvision` が必要です。

```powershell
python local_batch_api\extract_soup_masks.py `
  --input-dir "local_batch_api\density_features\frames_for_masks" `
  --output-dir "local_batch_api\density_features\soup_crops" `
  --report "local_batch_api\density_features\soup_crops_report.csv" `
  --output-kind crop
```

切り抜き画像から特徴量を作ります。

```powershell
python local_batch_api\build_density_features_from_images.py `
  --csv "学習データ\ギフトホールディングス様→ラーメン画像AI評価 コピー_ai-request-table_グリッド 10.csv" `
  --image-dir "local_batch_api\density_features\soup_crops" `
  --output "local_batch_api\density_features\density_features_soup_crops.csv"
```

最後に学習評価します。

```powershell
python local_batch_api\train_density_estimator.py `
  --features "local_batch_api\density_features\density_features_soup_crops.csv" `
  --folds 5 `
  --output "local_batch_api\density_features\density_eval_soup_crops_5class.json"
```

2026-06-18時点の全520件・非セグメンテーション特徴量の基準値は、5段階完全一致 `0.4904`、±1段階許容 `0.8769`、3分類完全一致 `0.5442` です。参照プロジェクト側の過去結果では、スープ切り抜き済み画像を使った古典特徴量で5段階完全一致がおよそ `0.535` から `0.563` まで出ているため、次の改善対象はこのセグメンテーション経路です。

APIキーはどちらかで設定します。

```powershell
$env:ARK_API_KEY="ark_xxx"
```

または `secrets/byteplus.key` に1行で保存します。

## 実行

```powershell
python local_batch_api\batch_process.py --input "C:\path\to\folder"
```

API送信なしでフレーム抽出とDB登録だけ確認する場合:

```powershell
python local_batch_api\batch_process.py --input "C:\path\to\folder" --dry-run
```

## 並列処理とAPI制限

動画からの代表カット抽出とAPI送信はパイプライン並列で動きます。API応答待ちの間も、次の動画のフレーム抽出を進めます。

`local_batch_api/config.yaml` の以下で調整します。

```yaml
concurrency:
  frame_workers: 2
  api_workers: 1
  requests_per_minute: 20
```

- `frame_workers`: 動画フレーム抽出の並列数
- `api_workers`: API送信の並列数
- `requests_per_minute`: API送信の上限。ModelArkのエンドポイント `RateLimit.Rpm` 以下に設定してください

安全運用では、最初は `api_workers: 1`、`requests_per_minute` は実際のRPM上限の50〜70%程度から始めます。

`429`、`500`、`502`、`503`、`504` は一時的な制限・混雑として扱い、`api.max_retries` と `api.retry_base_seconds` に従ってバックオフ再試行します。`Retry-After` ヘッダーが返る場合はその秒数を優先します。

## 出力

既定では `local_batch_api/output/` に保存します。

- `frames/`: 抽出した代表カット
- `ramen_soup_evaluations.sqlite3`: 処理履歴DB
- `training_dataset.jsonl`: 学習用JSONL
- `training_dataset.csv`: 学習用CSV

## GPU/NPUについて

OpenCVのOpenCLが使える環境では、`processing.use_opencl: true` によりフレーム評価処理でOpenCLを有効化します。API型LLMの推論はローカルGPU/NPUではなくAPI提供側で実行されます。NPUを使うローカルVLM推論へ切り替える場合は、別途ONNX/OpenVINO等のローカルモデル実装が必要です。

## 代表カット選定

代表カットは以下をスコア化して選びます。

- `sharpness`: Laplacian分散。高いほどブレ・ピンぼけが少ない
- `motion_blur_score`: 低シャープネスと方向性の強いブレのペナルティ
- `blowout_ratio`: 白飛び率
- `steam_haze_score`: 湯気・白かぶりの代理指標
- `contrast`: 液面の見えやすさ

`processing.min_sharpness` 未満の候補は原則除外します。全候補が下回る場合のみ、最良候補をフォールバックとして使います。
