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

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構造が壊れる」など破綻リスクが爆増します。

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連携まで

  1. ffprobeで動画メタ情報(fps, duration, 撮影開始日時, 解像度, デバイス)を取得→DBに登録
  2. 動画をフレーム分割(OpenCV, ffmpeg等)。frame_num→時刻変換にはfpsとdurationを使う
  3. 各フレームの時刻からGPXのtrack pointをマッチングし、緯度経度を取得
  4. AIアノテ(YOLO等)の推論点を360度座標に逆変換し、フレーム・検出ごとにDB保存
  5. 検出結果のマージ(DBSCANクラスタリング等)で重複排除。代表点のみDBへ
  6. 可視化や再生時は「正確なフレーム時刻と座標・クラス・信頼度・緯度経度」で統合表示

フレーム・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データ基盤としても安心して運用・拡張できます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA