GoPro HERO7 で撮影した動画からGPS情報をgpxファイルとして抜き出す方法

Contents

概要

GoPro HERO7 BLACK で撮影した動画(mp4)内には、GPS情報が含まれている。
そのGPS情報gpx形式で抜き出してやると、いろいろ面白いことが出来そうである。

gpx化についての流れは下記が分かりやすい。

GoPro HERO5 Blackで撮影した動画からKMLファイルを抽出してみました

ただし、こちらのサイトで作成されたものはHERO5に対応したものでHERO7で作成した動画に処したところエラーが出て止まってしまうので、HERO7でも使用できるものの作成方法について以後補足する。

(最終的にKML形式に変換しているが、大体のGPS情報入力ソフトはgpxをサポートしているので、途中のgpxの抽出で目標は達成である。)

簡単にまとめると

  1. ffmpegを使用して元動画(mp4)から、GPMF(GoPro Metadata Format もしくは General Purpose Metadata Format)形式で記録されたセンサーデータ(バイナリ)を分離する
  2. gopro2gpxというソフトを使用して分離したデータをgpx形式に変換する

折角なので、どのOSでも使えるようにUnixライクな環境で実行できるように話を進める。

その前に、Windowsユーザーには既にgopro2gpxの実行形式(exe)が配布されているので下記をダウンロードの上、動画からgpxファイルを作成する手順からお読みください。(Linux Shell ベースで書かれているので細かいところは脳内補完で対応してください)

http://tailorandwayne.com/download/GPMD2CSV.zip

Windows Subsystem for Linux (WSL)における環境設定

ここではWindows10上で動作するLinux(Ubuntu)サブシステムを使って環境を構築する方法について触れる。Windowsユーザー以外は読み飛ばしてください。
Windows Subsystem for Linux (WSL) のインストールの方法についてはたくさん情報があるので調べてください。

Go言語のインストール

sudo apt update
sudo apt install golang

・・・以上。

ちなみに、WSL上からWindowsのCドライブやDドライブへの参照はそれぞれ
/mnt/c
/mnt/d
となる。

gopro2gpxの作成

ここでは、Go言語を用いてgopro2gpxの実行形式を作成する手順を紹介する。Macを含むUNIXライクな環境があってGo言語までインストールされているならここから読めばよい。

必要なパッケージをダウンロードするためにGoではgetコマンドを用いる。そのダウンロード先として環境変数GOPATHであらかじめ指定する必要がある。
ここではホームディレクトリ直下にgoというフォルダを作成して、これをダウンロード先とする。

makedir -p ~/go

 

実行ファイルを作成したいディレクトリに移動して、下記を入力すると必要なパッケージをダウンロードして実行ファイル(gopro2gpx)が作成される。

export GOPATH=$HOME/go

go get github.com/JuanIrache/gopro-utils/bin/gopro2gpx
go install github.com/JuanIrache/gopro-utils/bin/gopro2gpx

 

動画からgpxファイルを作成する手順

ffmpeg と gopro2gpx が揃っていればここから読めばよい。

とりあえず作成

話を簡単にするためにgopro2gpx処理する動画(ここではGH010047.MP4とする)はカレントディレクトリにあるとする。

ffmpeg -y -i GH010047.MP4 -codec copy -map 0:3 -f rawvideo GH010047.bin
./gopro2gpx -i GH010047.bin -o GH010047.gpx

いろいろと警告が出てきて、もやっとするが、gpxが作成されていれば成功である。

gopro2gpx のオプションについて補足

Usage of ./gopro2gpx:
  -a int
        Optional: GPS accuracy threshold, defaults to 1000 (default 1000)
  -f int
        Optional: GPS fix state. Defaults to 0 (no fix), can be 2 (2D) or 3 (3D) (default 3)
  -i string
        Required: telemetry file to read
  -o string
        Required: gpx file to write
スイッチ 入力値 内容 初期値
-a <数値> 許容するGPSの精度閾値 小さいほど精度が高い 1000
-f <数値> GPSの測位モード? 3
-i <入力ファイル名> メタデータの記録されているファイル
-0 <出力ファイル名> GPX形式のファイル

使いやすいようにシェルにする(任意)

エディタ等で extract_gpx.sh と名付けて下記を作成
2行目のGOPRO2GPX_FILEに作成したgopro2gpxをフルパスで記入

#!/bin/sh
GOPRO2GPX_FILE=~/bin/gopro2gpx

if [ $# -lt 1 ]; then
    echo "Usage: $0 <input_movie_file> [<output_dir>]"
    exit 1
fi

IN_FILE_EXT=$1
IN_FILE=${IN_FILE_EXT##*/}
IN_BASE=${IN_FILE%.*}
OUT_DIR=${IN_FILE_EXT%/*}

if [ $# -ge 2 ]; then
    OUT_DIR=$2
fi

ffmpeg -y -i "${IN_FILE_EXT}" -codec copy -map 0:3 -f rawvideo "${OUT_DIR}/${IN_BASE}.bin"
${GOPRO2GPX_FILE} -i "${OUT_DIR}/${IN_BASE}.bin" -o "${OUT_DIR}/${IN_BASE}.gpx"

使用方法:
./extract_gpx.sh <元動画ファイル> [<出力先フォルダ>]
使用例:
./extract_gpx.sh “/mnt/d/Videos/GoPro/20181015/GH010047.MP4” “/mnt/d/GPX”

作成したgpxの確認

得られたgpxは下記のサイトにて正しく抽出できたか確認できる。

GPS Visualizer

技術参考

  1. Parser for GPMF™ formatted telemetry data used within GoPro® cameras.
  2. Tools to parse metadata from GoPro Hero 5 cameras
  3. EXTRACT SENSOR DATA FROM GOPRO VIDEOS

 

Ubuntu 18.04 LTS で ネットワークインターフェース名を昔の表記(eth0など)に変更する方法

Ubuntu 18.04LTSを導入したところ、ネットワークインターフェース名が「ens192」という表示になった。
udevのバージョン197以降、デフォルトの動作では予測可能なネットワークインタフェース名がつけられるように仕様が変更された結果である。
そうは言ってもやはり慣れ親しんだ「eth0」等の表記に戻したいと考えている人は少なくないのではないだろうか。

dmesg を見てみると、ブート時に一度eth0を認識した後、わざわざens192に変更されているのが分かる。

bang@steinsgate:~$ dmesg | grep -i eth
[    1.923038] vmxnet3 0000:0b:00.0 eth0: NIC Link is Up 10000 Mbps
[    1.954736] vmxnet3 0000:13:00.0 eth1: NIC Link is Up 10000 Mbps
[    2.179409] vmxnet3 0000:0b:00.0 ens192: renamed from eth0
[    2.279580] vmxnet3 0000:13:00.0 ens224: renamed from eth1

要はこの動作を無効にすれば良い。これはカーネルの起動オプションで
net.ifnames=0 biosdevname=0
を追加することにより可能となる。

grub に設定を登録

下記は grubからの起動時にカーネル起動オプションを永続的に追加する方法である。/etc/default/grub を開き GRUB_CMDLINE_LINUX に追加のカーネル起動オプションを設定する。

sudo vi /etc/default/grub
GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0"

grubに設定を反映させる。

sudo grub2-mkconfig -o /boot/grub2/grub.cfg

インターフェースの変更をNetplanにも反映

次に起動時にインターフェースにIP等が設定されるようにNetplanのデバイス名も変えておく。

sudo vi /etc/netplan/50-cloud-init.yaml
network:
    ethernets:
        eth0:
            addresses: []
            dhcp4: true
        eth1:
            addresses: []
            dhcp4: true
    version: 2
参考
  1. CentOS7におけるNIC命名ルール

SharedPreferences の putStringSet()メソッドの不具合

Stringのリストがうまく永続化できない!

SharedPreferences を用いて Stringのリストを保存しようと思い下記のようにした。

// プリファレンスから、保存している値を得る
val pref = getSharedPreferences("prefs", Context.MODE_PRIVATE)
val stringSet = pref.getStringSet("key1", mutableSetOf())

// 適当な値をリストに追加
stringSet.add(stringSet.size.toString())

// プリファレンスに保存する
pref.edit().putStringSet("key1", stringSet).apply()

ところが次のような不思議な現象に遭遇した。

  • アプリの動作中は問題ない
  • アプリをいったん終了し、再度立ち上げると、一番最初に記録した値のみが再現され、その後に追加した値はなかったことになっていた

どうやら、(環境により異なるが)
/data/data/(applicationId)/shared_prefs
辺りに生成される「prefs.xml」(SharedPreferencesの実態)が最初の書き込み以降更新されていないようなのだ。
putString() とか putInt() で同じようにしたときには意図したとおりに動作するのに、なぜか putStringSet() ではそのようになる。

これはバグなのだろうか?調べてみたところJavaでも同じことが起こっているようだ。

解決策として、いくつか考えてみた。

putString()等でダミーを一緒に書き込む

// プリファレンスに保存する
pref.edit().putString("dummy", "dummy")
pref.edit().putStringSet("key1", stringSet).apply()

これは putString() ならうまくxmlファイルの更新がかかるのでそれを利用した。冗長なデータを書き込むのであまり気分はよくない。

一旦、全てをクリアしてしまう

// プリファレンスに保存する
pref.edit().clear()
pref.edit().putStringSet("key1", stringSet).apply()

気分一新、全ての内容をクリアしてから再度上書きする。この場合はこれでもなんとかなってしまうのだが、このデータベースに他の情報が一緒に記録されていれば(というかその可能性は高い)目も当てられないことになる。

該当する情報だけ一旦消してから書き直す

// プリファレンスに保存する
pref.edit().remove("key1").apply()
pref.edit().putStringSet("key1", stringSet).apply()

該当情報だけ消して書き直す。一番角が立たない方法だろう。
remove() で消去したあと、apply() するのがポイント!これを忘れるとやっぱり最初の現象と同じになってしまう。