5分ウィンドウごとに「ユニーク人数」「行動内訳(移動/滞留)」を簡単に付与できる、軽量なローカルWebアプリです。UI はボタン/ショートカットで高速入力を重視し、出力はガイド準拠のCSVを生成します。
参考ガイド: video_annotation_guidelines_park.md
対応方針(要点)
- 5分単位(任意分に変更可)でウィンドウ分割
- 「移動」= 5分内の累計可視時間 < 120秒、「滞留」= 120秒以上
total_unique = moving_count + staying_count
ディレクトリ構成
webapp/: フロントエンド(静的ファイル: HTML/CSS/JS)server.py: 簡易サーバ + 保存API(Python標準のみ)annotations/: サーバ保存時の出力JSON(自動作成)
必要要件
Python 3.8+(追加の外部ライブラリ不要)- ブラウザ(最新の Chrome / Edge / Firefox / Safari いずれか)
- HEVC/H.265 動画のプレビューは、ブラウザやOSコーデック依存です。Safari では再生可、Chrome/Edge は環境により不可の場合があります。
- (任意)
ffmpeg(ffprobeを含む)- HEVC/H.265 の場合でも、サーバ側で動画長(duration)を取得できるようにするために使用します。
- 再生できない HEVC/H.265 を H.264 に自動変換(トランスコード)してブラウザ再生する機能でも使用します。
任意: 仮想環境
- macOS/Linux:
python3 -m venv .venvsource .venv/bin/activate
- Windows (PowerShell):
py -3 -m venv .venv.venv\Scripts\Activate.ps1
- 本ツールは外部依存がないため、仮想環境は任意です。
起動方法
- サーバ起動(既定ポート:
8000)python3 server.py
- ポート変更
- macOS/Linux:
PORT=8080 python3 server.py - Windows (PowerShell):
$env:PORT=8080; python server.py
- macOS/Linux:
- ブラウザで開く
http://localhost:8000
クイックスタート(実作業に必要な最小手順)
-
- 端末で
python3 server.pyを実行し、ブラウザでhttp://localhost:8000を開く
- 端末で
-
- 画面上部の「動画」からファイルを選択
PYYMMDD_HHMMSS_HHMMSS.*またはPYYMMDD-HHMMSS-HHMMSS.*形式なら開始/終了が自動反映、ウィンドウ自動生成(YYは2000年代の2桁年、例: 25→2025)
-
追加のファイルがある場合は「ファイル追加」ボタンから後から追加可能(5分ウィンドウを再生成)
- 複数ファイルのプレビュー切替は「前の動画」「次の動画」ボタンで可能
- それ以外はメタデータもしくはサーバ側
ffprobeで長さ取得→開始00:00:00/終了に自動反映→ウィンドウ生成
-
- 人数カウントは「未確定」→「移動/滞留」の順で振り分け
A=人数(未確定)+1 →M=未確定→移動 →S=未確定→滞留- 取り消しは
Z、前後移動は←/→またはN
-
- 必要に応じてメモ(notes)を記入
-
- 未確定が残っていないことを確認してから「CSV書き出し」
基本の使い方
- まず動画を読み込む(画面上部「動画」からファイル選択)
- ファイル名が
PYYMMDD_HHMMSS_HHMMSS.*(またはハイフン区切り) の場合:video_id/開始/終了 を自動反映し、ウィンドウも自動生成(YYは2000年代換算) - 上記以外:
video_idはファイル名、開始は00:00:00、終了は動画の長さ(メタデータ or サーバの ffprobe)で自動反映し、ウィンドウも自動生成
- ファイル名が
- ブラウザで再生できない場合はサーバにアップロードして H.264 にトランスコードし、再生/ウィンドウ生成します(
ffmpegが必要)- 再生に失敗した場合は、プレイヤー下部のステータス欄に原因(例:
SRC_NOT_SUPPORTEDなど)と対応方針が表示されます - 変換中はサーバ側の進捗(%)をステータス欄に表示します(1秒ごとに更新)
- 再生に失敗した場合は、プレイヤー下部のステータス欄に原因(例:
- 必要に応じて以下を調整
video_id: 例park_2024-05-01_camA開始時刻/終了時刻:HH:MM:SSまたはMM:SS
- カウント操作(現在ウィンドウに対して)
- まず「人数(未確定)+1」で人物を数える(総人数を先に積み上げ)
- 行動が確定したら「未確定→移動」「未確定→滞留」で振り分け(各 -1 ボタンで未確定へ戻せます)
- 取り消しは「取り消し」
- メモは
notes欄へ(天候/イベント/視認性など)
- ナビゲーション
- サイドバーのウィンドウ一覧をクリック / 「前へ」「次へ」
実時間ベースの5分ウィンドウ(P形式ファイル)
- ファイル名が
PYYMMDD_HHMMSS_HHMMSS.*(またはPYYMMDD-HHMMSS-HHMMSS.*)の場合、ファイル名の開始/終了(実世界の壁時計)からウィンドウを生成します(YYは2000年代の2桁年)。 - 単一ファイルの場合: そのファイルの
開始→終了の範囲で5分刻みのウィンドウを作成(ウィンドウラベルはYYYY-MM-DD HH:MM:SS)。 - 複数ファイルの場合: 複数選択に対応。すべての
P...ファイルの最小開始〜最大終了の連続範囲で5分刻みのウィンドウを一括生成します。- 例:
P250901_120000_122959.mp4とP250901_123000_125959.mp4を同時に選ぶ →12:00:00〜12:59:59の範囲でウィンドウ作成
- 例:
- CSV出力時は
window_start/window_endを壁時計のHH:MM:SSで出力します(跨日をまたぐ場合は日付が変わりますが、CSVには時刻のみを出力)。 - カメラ番号: ツールバーの「カメラ」で番号指定(既定=9)。CSVには
camera列としてnotes/remarksの直前に出力されます。
注意:
- ウィンドウは5分境界(オンタイム)に揃えて生成し、ファイル群で完全にカバーされていない枠は生成されません。
- 不足枠がある場合、ステータス欄に不足枠が一覧表示され、各枠の「この枠を追加」ボタンから手動で不足枠を挿入できます(任意)。
注意:
- 現状、複数ファイルを選んだ場合でも再生プレビューは先頭ファイルのみを表示します(ウィンドウは全範囲で生成されます)。
- ファイル名が
P...形式でない動画は、従来通り「動画長ベース(相対時間)」でウィンドウを作成します。
実作業フロー(例)
-
プロジェクト/動画命名を決める(例:
park_2024-05-01_camA) -
サーバを起動し、動画を読み込む → 自動で開始/終了/ウィンドウが設定される
-
再生しながら該当ウィンドウで
A→M/Sを繰り返し、人数を消化 -
迷いがあれば
notesに状況を残す(天候/イベント/視認性など) -
ウィンドウごとに未確定が0になったら次へ(
→/N) -
最後にCSVを出力(必要なら詳細CSVも)
キーボードショートカット
A: 人数(未確定)+1M: 未確定→移動S: 未確定→滞留Z: 取り消し→またはN: 次のウィンドウ←: 前のウィンドウ
HEVC/H.265 の扱い(再生できない時)
- まずブラウザでのメタデータ取得/再生を試行します。
- 再生不可(例: Chrome/Edge で HEVC)→ サーバへアップロードして H.264 へ自動トランスコードします。
- 変換は非同期で進捗%を表示。完了後は
/transcoded/...のURLで再生可能になります。 ffmpeg/ffprobeが未導入の場合は、サーバUIのステータスにエラーメッセージを表示します。インストール後に再試行してください。
保存・読み込み(任意)
- JSONインポート: 「JSON読込」から
.jsonを選択
トラブルシュート
-
ブラウザで動画が再生されない
- ステータス欄のエラー(例:
SRC_NOT_SUPPORTED)を確認 → 「サーバ変換」を自動試行(ffmpegが必要)
- ステータス欄のエラー(例:
-
動画長が
00:00:00のまま- ブラウザがメタデータ取得不可 → サーバ側
ffprobeで長さを取得(ffmpegの導入が必要)
- ブラウザがメタデータ取得不可 → サーバ側
-
CSVが出力できない
- 未確定の人数が残っています。各ウィンドウで「未確定→移動/滞留」へすべて振り分けてください
-
変換が進まない/失敗する
ffmpegの導入とPATH設定を確認。重い動画は変換に時間がかかります
CSV出力(ガイド準拠)
- 集計CSV(1行=1ウィンドウ)
- ヘッダ:
date,window_start,window_end,total_unique,moving_count,staying_count,camera,notes - 例行(実時間あり):
2025-09-11,00:00,05:00,12,9,3,9,"雨で視界やや悪い" - 実時間(P形式)が無い場合は
dateは空欄になります。
- ヘッダ:
- 詳細CSV(任意の補助出力)
- ヘッダ:
date,window_start,person_local_id,visible_sec,behavior,camera,remarks person_local_idはウィンドウ内での連番(p001など)。visible_secは空欄のまま補助列として出力。
- ヘッダ:
出力ファイル名
- 集計CSV:
YYYYMMDD_HHMMSS_HHMMSS.csv(最初のウィンドウ開始のカレンダー日+最初の開始時刻+最後の終了時刻) - 詳細CSV:
YYYYMMDD_HHMMSS_HHMMSS_detail.csv - 実時間が無い場合(相対時間のみ)は従来どおり
annotations.csv/annotations_detail.csvのようなプレースホルダ名になります。
注意: 未確定の人数が残っているウィンドウがある場合、CSV出力はブロックされます(ガイドの moving_count + staying_count == total_unique を担保するため)。
API(任意利用)
- 保存:
POST /api/annotations- Body(JSON):
{ videoId, windowSeconds, windows, currentIndex } - 保存場所:
annotations/{videoId}.json
- Body(JSON):
- 取得:
GET /api/annotations?video_id={id}
例(保存)
curl -X POST http://localhost:8000/api/annotations -H "Content-Type: application/json" -d @payload.json
例(取得)
curl "http://localhost:8000/api/annotations?video_id=park_2024-05-01_camA"
データ運用の注意
- 変換済み動画は
webapp/transcoded/に保存されます(自動削除はしません)。不要になったら手動で整理してください。
よくある質問
- Q: HEVC/H.265 の動画は使えますか?
- A: 使えます。ブラウザでプレビューできない環境でも、サーバ側で
ffprobeにより動画長を取得し、必要に応じてffmpegで H.264 へ自動トランスコードして再生できます(ffmpegの導入が必要)。プレビュー再生は Safari を推奨します。
- A: 使えます。ブラウザでプレビューできない環境でも、サーバ側で
トランスコードの詳細
-
変換先: MP4 (H.264 + AAC),
-movflags +faststart,-preset veryfast,-crf 23 -
保存先:
webapp/transcoded/に一時保存され、URL は/transcoded/{id}.mp4 -
ストレージ運用: 不要になったファイルは適宜削除してください(自動削除はしていません)。
-
Q: 複数人のグループは?
- A: 各人を個別にカウントしてください。
moving_count + staying_count == total_uniqueを維持します。
- A: 各人を個別にカウントしてください。
開発メモ
- 依存関係: なし(Python標準ライブラリのみ)
- コード場所
webapp/index.html— UIwebapp/styles.css— スタイルwebapp/app.js— ロジックserver.py— 静的配信 + 簡易API
- ポート指定: 環境変数
PORT