PythonでGPSログをmp4のアニメーション動画に変換する(gpxpy,staticmap,cv2,numpy,pillow)

当サイトではアフィリエイト広告を利用しています

インラインスケート滑走時のGPSログであるgpxファイルに対して、Pythonを使ってmp4形式のアニメーション動画化してみました。

利用したライブラリはgpxpy,staticmap,cv2,numpy,pillowになっています。

GPSログのアニメーション化

普段インラインスケートを行う際は、RunKeeperを用いてGPSログを記録しています。

このGPSログはgpxというフォーマットで記録されており、こちらをPythonで動画化してみることにしました。

利用したライブラリは以下になります。

  • gpxpy: gpxファイルのパース
  • staticmap: OpenStreetMapを使って経路などの画像化
  • pillow: staticmapの戻り値の画像形式
  • cv2: mp4ファイルの生成
  • numpy: pillowとcv2の画像形式変換

プログラムの流れ

  1. gpxpyでGPSログをパース、緯度経度を経路リスト化
  2. 経路リストを100段階に分割
  3. 分割経路をstaticmapで画像化(pillow)
  4. numpyでpillow画像をcv2画像に変換
  5. cv2でmp4動画を生成

なお、経路の場所がわかりやすいよう、あわせて広い区画からズームしていくような動画を前半に作成しています。

Pythonスクリプト

以下が実際に記述したPythonスクリプトになります。

import gpxpy
import staticmap
import cv2
import numpy as np

GPX_FILE = "data/gps-log.gpx"
MP4_FILE = "out/gpx-movie.mp4"
ITER_NUM = 100
ZOOM_START = 3
ZOOM_LEVEL = 13
ZOOM_ITER = 4
IMG_WIDTH = 800
IMG_HEIGHT = 600
MP4_FPS = 10

def pil2cv(imgPIL):
    imgCV_BGR = np.array(imgPIL)[:, :, ::-1]
    return imgCV_BGR

def main():
    gpx_file = open(GPX_FILE, "r")
    gpx = gpxpy.parse(gpx_file)

    map = staticmap.StaticMap(IMG_WIDTH, IMG_HEIGHT)

    coordinates = []  # [(lon, lat)]
    for track in gpx.tracks:
        for segment in track.segments:
            for point in segment.points:
                # print(
                #     f"Point at ({point.latitude},{point.longitude}) -> {point.elevation}"
                # )
                coordinates.append((point.longitude, point.latitude))
    length = len(coordinates)

    # images for movie
    zoom_images = []
    images = []

    # zoom movie
    cen = staticmap.CircleMarker(coordinates[0], "#ffffff00", 1)
    map.add_marker(cen)
    for z in range(ZOOM_START, ZOOM_LEVEL + 1):
        image = map.render(zoom=z)
        zoom_images.append(image)
    map.markers.clear()

    # track movie
    for i in range(ITER_NUM):
        end = int(length * (i + 1) / ITER_NUM)
        coords = coordinates[0:end]
        pos = coords[-1]

        mark_ol = staticmap.CircleMarker(pos, "#ffffff", 18)
        mark = staticmap.CircleMarker(pos, "#0000ff", 16)
        # line_ol = staticmap.Line(coords, "#ffffffaa", 8)
        line = staticmap.Line(coords, "#0000ff80", 6)
        map.add_marker(mark_ol)
        map.add_marker(mark)
        # map.add_line(line_ol)
        map.add_line(line)

        # max zoom=20
        # https://wiki.openstreetmap.org/wiki/Zoom_levels
        image = map.render(zoom=ZOOM_LEVEL)
        images.append(image)

        map.lines.clear()
        map.markers.clear()

    # gen mp4
    codec = cv2.VideoWriter_fourcc(*'mp4v')
    video = cv2.VideoWriter(MP4_FILE, codec, MP4_FPS, (IMG_WIDTH, IMG_HEIGHT))
    for img in zoom_images:
        img = pil2cv(img)
        # Zoom動画は時間延ばすために何回か追加する
        for _ in range(ZOOM_ITER):
            video.write(img)

    for img in images:
        img = pil2cv(img)
        video.write(img)
    video.release()

if __name__ == "__main__":
    main()

動画のスクリーンショット

生成した動画のスクリーンショットは以下になります。

生成した動画を使った例

実際にインラインスケートで取得したGPSログからアニメーション化した動画が、以下の動画の序盤10秒あたりに組み込まれています。

以下の記事で滑走した際のGPSログですね。

まとめ

  • PythonでGPSログ(gpxフォーマット)をmp4動画化
  • gpxpy,staticmap,cv2,numpy,pillowを利用
  • 前半にズーム動画を追加して経路の場所をわかりやすくしている