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()

Share post

Related Posts

コメント