PythonのfitdecodeとOpen CVでGARMINの.fitデータを動画に変換
2021/08/16 categories:Python| tags:Python|fitdecode|Open CV|GARMIN|
Pythonのfitdecodeを使ってGARMINの.fitデータを動画に変換してみました。
PythonのfitdecodeでGARMINの.fitデータをCSVに変換の記事で作成したプログラムを少し変更して、GARMINデバイスで記録したアクティビティのデータを動画に文字で表示するプログラムを作ってみました。
fitデータの読み込みは以前の記事と同様の処理です。その処理で読み込んだデータは数秒毎に記録された時間や距離、速度などのデータになっています。データの一部は下記のような内容になっています。
timestamp | distance | distance_units | enhanced_speed | enhanced_speed_units |
---|---|---|---|---|
2021-08-09 03:51:20+00:00 | 4.88 | m | 4.88 | m/s |
2021-08-09 03:51:21+00:00 | 9.59 | m | 4.703 | m/s |
2021-08-09 03:51:27+00:00 | 36.53 | m | 4.964 | m/s |
2021-08-09 03:51:30+00:00 | 51.74 | m | 5.095 | m/s |
上記を見ると、タイムスタンプが0、1、7、10秒となっています。出力する動画が30fpsの場合、1/30秒ごとのデータが必要になるので、動画のフレーム間のfitデータをnumpyを使用して補完しました。Open CVで画像を作成して、その画像に同じくOpen CVで補完したデータを描画して、動画の1フレームとしています。また、動画の背景はクロマキー合成用に緑色にました。
出力された動画は以下の通りです。
ソースコード
# -*- coding: utf-8 -*-
import cv2
import datetime
import fitdecode
import numpy as np
def main():
datas = {
key : [] for key in [
'timestamp', 'position_lat', 'position_long', 'distance',
'enhanced_speed', 'enhanced_altitude', 'heart_rate',
'cadence', 'temperature'
]
}
with fitdecode.FitReader('Lunch_Ride.fit') as fit:
for frame in fit:
if not isinstance(frame, fitdecode.FitDataMessage):
continue
if not frame.name == 'record':
continue
for key in datas:
if frame.has_field(key):
value = frame.get_value(key)
else:
value = 0
datas[key].append(value)
for key in datas:
datas[key] = np.array( datas[key] )
datas['Timestamp'] = datas.pop('timestamp')
datas['Cadence'] = datas.pop('cadence')
datas['Temperature'] = datas.pop('temperature')
datas['Speed'] = np.round( datas.pop('enhanced_speed') * 0.001 * 3600, 1)
datas['Latitude'] = datas.pop('position_lat') * 0.000000083819031715393
datas['Longitude'] = datas.pop('position_long') * 0.000000083819031715393
datas['Altitude'] = np.round( datas.pop('enhanced_altitude'), 1)
datas['Distance'] = np.round( datas.pop('distance') * 0.001, 1)
datas['Heart rate'] = np.round( datas.pop('heart_rate'), 1)
datas['Time'] = np.array( [ i.seconds for i in datas['Timestamp'] - datas['Timestamp'][0] ] )
width, height = 1280, 720
fps = 30.0
codec = cv2.VideoWriter_fourcc(*'mp4v')
video = cv2.VideoWriter('test01.mp4', codec, fps, (width, height))
for i in range( len(datas['Time']) - 1 ):
t0 = datas['Time'][i]
t1 = datas['Time'][i+1]
frame_count = int( (t1 - t0) * fps )
interp = {
key : np.linspace( datas[key][i], datas[key][i+1], frame_count + 1 )
for key in datas if not key == 'Timestamp'
}
interp['Timestamp'] = [
datetime.datetime.fromtimestamp(t) for t in
np.linspace(
datas['Timestamp'][i].timestamp(),
datas['Timestamp'][i+1].timestamp(),
frame_count + 1
)
]
for f in range(frame_count):
image = np.full( (height, width,3), (0, 255, 0), dtype=np.uint8 )
for k, key in enumerate(interp):
if key == 'Latitude' or key == 'Longitude':
text = '{:s} : {:.3f}'.format( key, 0.0 )
elif key =='Timestamp':
text = key + ' : ' + str(interp[key][f])
else:
text = '{:s} : {:.3f}'.format( key, interp[key][f] )
cv2.putText(
image,
text = text,
org = (10, 55 + 65 * k),
fontFace = cv2.FONT_HERSHEY_DUPLEX,
fontScale = 2,
color = (0, 0, 0),
thickness = 7,
lineType = cv2.LINE_AA
)
cv2.putText(
image,
text = text,
org = (10, 55 + 65 * k),
fontFace = cv2.FONT_HERSHEY_DUPLEX,
fontScale = 2,
color = (255, 255, 255),
thickness = 3,
lineType = cv2.LINE_AA
)
video.write(image)
if i > 10:
break
video.release()
if __name__ == '__main__':
main()