はじめに
前回の記事
![](https://skyrail.tech/wp-content/uploads/2021/11/6195bdae6e082603d043d242.gpx-5151-160x90.png)
で,GoPro HERO10で撮影した動画(mp4)からffmpegを使ってメタデータを取り出しました.取り出したデータは,バイナリ形式のファイルとして保存されますので,このファイルからGPSデータを抽出して使える形にしたいと思います.
データのフォーマット
GPSデータといっても,測位結果が入っているだけ(のはず)なので,Rawデータを読んでリアルタイムで測位演算するよりは簡単(なはず)です.フォーマットは,こちらのサイトでまとめて下さってます(素晴らしい!)ので参考にさせていただきます.
読み進めていきますと,データは下の図のような構造を一つの単位として記録されているようです.
![](https://skyrail.tech/wp-content/uploads/2021/11/dataformat.png)
①:データの塊の頭はASCII文字4文字が入ります(FourCCと呼ばれている)
②:データ型をASCII文字1文字で表現
③:データ構造の1単位(ひとかたまり)の合計バイト数
④:データ構造の1単位の繰返し回数
⑤:データが格納される.データ部は32ビット単位で記録されるので,
終端が32ビットに満たない場合はゼロ埋めされる
以下,GPSデータを読むときに関連しそうなものだけをピックアップしてみました.
DEVC | デバイスから流れてくるデータの1単位.各種データはこの下にネストされる |
DVID | デバイスID.デバイス毎に自動的に振られるユニークな番号 |
DVNM | デバイス名(手元のGoProのデータでは「HERO10 Black」が入っている) |
STRM | 各種telemetry/metadataの開始タグ.各種データはこの下にネストされる |
STNM | Stream名 (GPSなら「GPS (Lat., Long., Alt., 2D speed, 3D speed))」が入っている) |
SCAL | スケールファクタ(除数) |
UNIT | 単位 (GPSならデフォルト設定で “degdegm m/sm/s”)が記録されていた |
GPS5 | 測位結果(別表に詳細) |
GPSU | UTC時刻 (別表に詳細) |
GPSF | FIX状況 (別表に詳細) |
GPSP | DOP値 (別表に詳細) |
データ型 記号 | 定義 | 備考 |
c | char | ASCII文字 |
F | char[4] | ASCII4文字(FourCC) |
U | char[16] | UTC Date Time文字列 |
I (える) | 32-bit signed integer | |
L | 32-bit unsigned integer | |
S | 16-bit unsigned integer | 0 to 65536 |
FourCC | 内容 | 出力頻度 | 備考 |
GPS5 | lat, lon, alt, 2D speed, 3D speed | 約18Hz | 座標系:WGS-84 deg, deg, m, m/s, m/s |
GPSU | UTC Date & Time | 約1Hz | yymmddhhmmss.sss |
GPSF | GPS Fix | 約1Hz | 0: ロック無し 2: 2D測位 3: 3D測位 |
GPSP | 精度劣化指標(DOP) | 約1Hz | 100倍した整数値 |
実際のデータと照らし合わせてみる
ffmpegで抽出したファイルをバイナリエディタで開いて,上のデータフォーマットと照らし合わせてみます.各種データの先頭はASCII文字4文字が入っていますので見やすいです.FourCCを拾いながら構造をみていきますと,以下のようになっていました.
DEVC
DVID
DVNM
STRM
STNM: Accelerometer
加速度関係のデータ色々?(今は無視)
STRM
STNM: Gyroscope
Gyro関係のデータ色々?(今は無視)
|
|
STRM--その他省略
|
|
STRM
STNM: GPS (Lat., Long., Alt., 2D speed, 3D speed)
GPSF
GPSU
UNIT
GPSA
GPS5
|
STRM--その他省略
|
|
DEVC
DVID
DVNM
|
|
以下同じように繰り返し
GPSUの値で想像するに,DEVCは1秒毎に書かれているようです.ホントに1秒かは分かりませんが...いずれにせよ,GPSデータの入っている場所が分かりましたので,その辺りを重点的に読んでみます.
![](https://skyrail.tech/wp-content/uploads/2021/11/GPSF.png)
GPS Fix状態が3であることがわかります
![](https://skyrail.tech/wp-content/uploads/2021/11/GPSU.png)
16バイトのデータが1回繰り返されるので,データ部は16バイトになっています.
UTC時刻が,2011年11月13日01時45分34.634秒ということがわかります
![](https://skyrail.tech/wp-content/uploads/2021/11/GPSP.png)
2バイトのデータが1つなので,データ部の後ろ2バイトはゼロ埋めになっています.
データはDOP×100なので,この時のDOP値は1.43ということがわかります.
DOP値にも色々ありますが,書かれてないので何が出力されているかは分かりません.Fix=3でPDOPかGDOP,Fix=2ならそもそも2次元なのでHDOPですかね...
![](https://skyrail.tech/wp-content/uploads/2021/11/UNIT.png)
char[3]が5回繰り返しです.lat(緯度), lon(経度), alt(高度), 2d speed, 3d speed の単位になります.
![](https://skyrail.tech/wp-content/uploads/2021/11/SCAL.png)
4バイト(int32)を5回繰り返しです.latとlonは10,000,000で割る.altと2D speed は1,000で割る.3D speedは100で割ることになります.
![](https://skyrail.tech/wp-content/uploads/2021/11/GPSA.png)
GPSAは参考にしたサイトでは出ていませんでした.新たに加わったデータでしょうか.
“MSLV”はMean Sea Level(平均海水面)ですかね...高度表現の基準が格納されているものと思われます.
![](https://skyrail.tech/wp-content/uploads/2021/11/GPS5.png)
(int32が5つ=20バイト)が18回繰り返し.一つ目のブロックを読み取って,スケールファクタで割ると,緯度:34.9814846度,経度:135.9636815度,高度:150.790m,2D速度:0.041m/s,3D速度:0.02m/sになります.多分ちゃんと読めてると思います.
例えば緯度ですが,小数点以下7桁まで記録されています.これは,(1e-7)×3600 = 3.6e-4[秒]になり,日本付近では大雑把に緯度1秒が30mくらいですから,分解能としては,(3.6e-4)×30 = 0.01m程度となります.ですので,1cmくらいまでの分解能で記録できているということになります.
GPX,KML形式で書き出す
以上で,データの所在と読み出し方が分かりましたので,あとは繰り返し読み出して,GPXやKMLなど使いやすい形式で出力するだけです.GPXやKMLのフォーマットは色んなデータを埋め込めますが,必要最小限でテストしていきたいと思います.
今回は,GPX,KMLファイルのテンプレートとして次のような構造にしました.
<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="SkyRail - https://skyrail.tech/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/0" >
<metadata>
<time>2021-11-14T08:34:24.774Z</time>
</metadata>
<trk>
<name>Track-01</name>
<trkseg>
<trkpt lat="34.9833524" lon="135.7561149">
<ele>10.733</ele>
<time>2021-11-14T08:35:00.084Z</time>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:speed>0</gpxtpx:speed>
<gpxtpx:dop>3.8</gpxtpx:dop>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="34.9851535" lon="135.7604088">
<ele>69.738</ele>
<time>2021-11-14T08:35:02.119Z</time>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:speed>0.186</gpxtpx:speed>
<gpxtpx:dop>3.8</gpxtpx:dop>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>
GPXの場合は,9~18行目のように,<trkpt>…</trkpt>で囲まれた部分が1つの点(データ)になります.緯度,経度,高度,時刻,速度,DOP値を埋め込んでいます.
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>Demo</name>
<description>Description Demo</description>
<Placemark>
<name>Track Title</name>
<description>Track Description</description>
<Style>
<LineStyle>
<color>FF1400BE</color>
<width>4</width>
</LineStyle>
</Style>
<LineString>
<extrude>1</extrude>
<tessellate>1</tessellate>
<altitudeMode>clampToGround</altitudeMode>
<coordinates>
135.7561149,34.9833524,69.738
135.7604088,34.9851535,59.572
135.7604162,34.9852428,52.853
</coordinates>
</LineString>
</Placemark>
</Document>
</kml>
KMLの場合は,19~23行目のように,<coordinates>…</coordinates>の間に,経度,緯度,高度を1点につき1行で埋め込めばOKです.
18行目で,altitudeModeをclampToGroundに設定しています.”clampToGround”にすると,GoogleEarthでの表示(点を線で繋いだ表示)は高度情報を無視して地表面に張り付いた表示になります.”absolute”にすると,海面からの高さ(標高)で表示されます.詳しくは以下に情報があります.
![](https://skyrail.tech/wp-content/uploads/cocoon-resources/blog-card-cache/0b758b330e112c874d9b949668660cc7.png)
完成版のプログラム
以上をまとめて,次のような流れで処理を行うC#コンソールアプリを作成しました.
- GoProで撮影した動画から
- ffmpegでメタデータを抽出して(バイナリファイルに保存)
- GPSデータをGPXとKML形式で書き出す
全ソースコードはGitHubに置いています.ffprobe.exe,ffmpeg.exe,mp4動画のパス,ファイル名はプログラム内にベタ打ちしてますので適宜変更して下さい.
いつもは,もうちょっと機能追加してからとか,もうちょっとスマートなコードにしてからとか思っている内に,段々熱が冷めて何も公開せずに終わるのですが,今回は不完全でも公開することにしました.日曜プログラミングですので,ヘボい処理があってもご容赦下さい...
追記(GUIアプリ)
コンソール版,しかもファイル名等ベタ打ちはやはり使いにくいので,勢いでGUI付けました.
詳細は別ページにまとめています.C# (wpf, .NET6)で作りました.