360度動画AIアノテーションDB管理+GPSマッチング 実践解説

360度動画のAIアノテーション・メタデータ管理を、現場で本当に“止まらずに回せる”仕組みにするには、 ffmpeg/ffprobeによる事前サーチ・GPX(GPS)マッチング・DB設計・全工程自動化が欠かせません。 このページでは、動画処理~GPS位置マッチ~DB登録まで現場仕様で総まとめします。
1. なぜffprobe(ffmpeg)が必須なのか
- ffprobeは「動画ファイルの真の仕様」(fps、総フレーム数、長さ、エンコーダ、EXIF日時、メタデータ等)を取得する唯一の標準ツール
- Pythonからも呼び出せて自動解析が簡単
- 360度カメラやスマホ動画の一部は、撮影位置(緯度経度)や方位も動画メタ内に格納していることがある
- 事前に「動画の実仕様」を全て吸い出し→DBに記録→フレーム処理/再生時もこの情報を正に参照、が鉄則
実践ポイント: ffprobeでの事前サーチがなければ、後で
「フレーム数がズレる」「再生が飛ぶ」「GPS紐付けが失敗」「DB構造が壊れる」など破綻リスクが爆増します。
「フレーム数がズレる」「再生が飛ぶ」「GPS紐付けが失敗」「DB構造が壊れる」など破綻リスクが爆増します。
2. ffprobeによる動画メタデータ取得例(Pythonコード)
import subprocess
import json
def get_video_metadata(video_path):
cmd = [
'ffprobe', '-v', 'error',
'-print_format', 'json',
'-show_format', '-show_streams', video_path
]
result = subprocess.run(cmd, capture_output=True, text=True)
info = json.loads(result.stdout)
return info
info = get_video_metadata('movie_360.mp4')
print(json.dumps(info, indent=2, ensure_ascii=False))
# FPSやdurationの取得例
fps = eval(info['streams'][0]['r_frame_rate']) # '30/1' -> 30
duration = float(info['format']['duration']) # 総秒数
- Pythonからffprobeを叩くことで、jsonで全ての情報が取れる
- r_frame_rateやdurationで正しいFPS・長さを厳密管理
- format[‘tags’] や streams[0][‘tags’] に撮影日時・GPS・方位が入っている場合もある
3. GPX(GPSログ)と動画のマッチング
- 動画ファイルだけでは位置情報が足りない場合、GPX(XML形式の位置ログ)を「時刻マッチング」で使う
- 動画のEXIF日時またはフレーム時刻と、GPX内のtrack point(trkpt)時刻を突合して紐付ける
- 特にドローン・車載・アウトドアカメラでは必須
GPXから時刻付き緯度経度を読み出し、最も近いものを検索(Python例)
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
def get_gpx_points(gpx_file):
tree = ET.parse(gpx_file)
root = tree.getroot()
ns = {'gpx': 'http://www.topografix.com/GPX/1/1'}
points = []
for trkpt in root.findall('.//gpx:trkpt', ns):
lat = float(trkpt.get('lat'))
lon = float(trkpt.get('lon'))
time_str = trkpt.find('gpx:time', ns).text
time_obj = datetime.fromisoformat(time_str.replace('Z', '+00:00'))
points.append({'lat': lat, 'lon': lon, 'time': time_obj})
return points
def get_closest_gps(points, target_time):
min_diff = None
closest = None
for pt in points:
diff = abs((pt['time'] - target_time).total_seconds())
if min_diff is None or diff < min_diff:
min_diff = diff
closest = pt
return closest
# 例: 動画の1フレーム目の時刻取得
video_time = datetime(2024,7,10,14,22,14, tzinfo=timezone.utc)
points = get_gpx_points('track.gpx')
closest_gps = get_closest_gps(points, video_time)
print(closest_gps) # {'lat':..., 'lon':..., 'time':...}
ポイント:
動画の「フレームごとの時刻」と「GPXのトラックポイント」を最短マッチングし、
各フレームの緯度経度に正確に紐付けます。時刻のタイムゾーン補正も厳密に。
各フレームの緯度経度に正確に紐付けます。時刻のタイムゾーン補正も厳密に。
4. DB設計例:動画+フレーム+アノテ+位置情報の正規化
- VideoMaster:動画ID, ファイル名, 撮影者, 開始時刻, FPS, duration, 解像度, デバイス名 など
- Frame:動画ID, フレーム番号, フレーム時刻, 緯度, 経度
- Detection:フレームID, クラス名, x_e, y_e, score(信頼度)
SQLiteで作成する場合(例)
import sqlite3
conn = sqlite3.connect('anno.db')
conn.execute('''CREATE TABLE IF NOT EXISTS VideoMaster (
id INTEGER PRIMARY KEY AUTOINCREMENT,
filename TEXT, shooter TEXT, datetime TEXT,
fps REAL, duration REAL, width INTEGER, height INTEGER, device TEXT)''')
conn.execute('''CREATE TABLE IF NOT EXISTS Frame (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_id INTEGER, frame_num INTEGER, frame_sec REAL,
lat REAL, lon REAL,
FOREIGN KEY(video_id) REFERENCES VideoMaster(id))''')
conn.execute('''CREATE TABLE IF NOT EXISTS Detection (
id INTEGER PRIMARY KEY AUTOINCREMENT,
frame_id INTEGER, class_name TEXT, x REAL, y REAL, score REAL,
FOREIGN KEY(frame_id) REFERENCES Frame(id))''')
conn.commit()
5. ワークフロー例:ffprobe・GPX・DB連携まで
- ffprobeで動画メタ情報(fps, duration, 撮影開始日時, 解像度, デバイス)を取得→DBに登録
- 動画をフレーム分割(OpenCV, ffmpeg等)。frame_num→時刻変換にはfpsとdurationを使う
- 各フレームの時刻からGPXのtrack pointをマッチングし、緯度経度を取得
- AIアノテ(YOLO等)の推論点を360度座標に逆変換し、フレーム・検出ごとにDB保存
- 検出結果のマージ(DBSCANクラスタリング等)で重複排除。代表点のみDBへ
- 可視化や再生時は「正確なフレーム時刻と座標・クラス・信頼度・緯度経度」で統合表示
フレーム・GPS・DB書き込みのイメージ(Python)
# 動画・DBマスタ登録(省略)
video_id = 1
# フレーム処理ループ
for frame_num, frame_img in enumerate(frame_imgs):
frame_sec = frame_num / fps
# 動画開始時刻 + frame_sec → フレーム時刻
frame_time = video_start_time + timedelta(seconds=frame_sec)
gps = get_closest_gps(gpx_points, frame_time)
cur = conn.execute(
'''INSERT INTO Frame (video_id, frame_num, frame_sec, lat, lon)
VALUES (?, ?, ?, ?, ?)''',
(video_id, frame_num, frame_sec, gps['lat'], gps['lon']))
frame_id = cur.lastrowid
# 各物体アノテ(x_e, y_e, class, score)もループでINSERT
for det in detections_in_frame:
conn.execute(
'''INSERT INTO Detection (frame_id, class_name, x, y, score)
VALUES (?, ?, ?, ?, ?)''',
(frame_id, det["class"], det["x_e"], det["y_e"], det["score"]))
conn.commit()
6. よくある課題・克服法
- fps/duration/フレーム数が合わずズレる: ffprobeで取得したfps・durationで厳密に補正。途中でカットやfps変動がある動画は全フレームタイムスタンプを記録
- 緯度経度が無い・ズレる: 動画メタにあればffprobeで直接取得、なければ撮影開始時刻+GPXマッチで最大限補正
- 検出点の重複/ゴミ: DBSCANや信頼度スコアによるフィルタリング。閾値調整も重要
- DB肥大化: 冗長なデータや一時解析結果は定期的にクリーンアップ。代表点のみ本テーブルに格納する構成も可
現場ノウハウ:
- ffprobeによる前処理+GPX連携+DB保存は「運用トラブルの9割」を未然に防ぎます。
- すべての動画・フレームに「タイムスタンプ・座標・緯度経度」を必ず正規化して持たせる。
- 一度DBに入れれば、再生・GIS連携・CSV抽出・API配信すべてスムーズに。
7. 応用・現場カスタマイズ例
- 外部センサ(加速度、方位、気圧)やイベントログも同時DB化 → 総合的な現場管理が可能に
- AI推論の信頼度やラベル分類ごとにアラート・アクションをトリガ
- GISやWebビューワでリアルタイム表示・タイムライン再生(DBから座標・時刻を高速検索)
8. まとめ:この流れを“標準”にする理由
ffprobe+GPX+DB+重複排除+全自動化で「後戻り不可な現場トラブル」を根絶できます。
動画解析×AIアノテ×GIS活用時代は、“止まらない”インフラレベルの運用が要求されます。
この構成を徹底することで、現場のAI・IoTデータ基盤としても安心して運用・拡張できます。