# ISC License # # Copyright 2019 Simon Bruder # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. # # This script is based on “aae-export.py” by Martin Herkt (lachs0r): https://gist.github.com/torque/6453947 bl_info = { 'name': 'Export: CSV Tracking Data', 'description': 'Export motion tracking data to CSV file', 'author': 'Simon Bruder', 'version': (0, 1, 0), 'blender': (2, 80, 0), 'location': 'Movie Clip Editor > Footage > CSV Tracking Data Export', 'warning': '', 'category': 'Import-Export', } import bpy import csv import math import mathutils def write_files(prefix, context): scene = context.scene fps = scene.render.fps / scene.render.fps_base clip = context.edit_movieclip real_scale = clip.real_length_pixels / clip.real_length_meters for trackno, track in enumerate(clip.tracking.tracks): with open(f'{prefix}_{trackno}.csv', 'w') as f: writer = csv.DictWriter( f, fieldnames=[ 'time (frames)', 'time (s)', 'x (px)', 'x (m)', 'y (px)', 'y (m)', 'rotation' ] ) writer.writeheader() startrot = None for marker in [track.markers.find_frame(frameno) for frameno in range(1, clip.frame_duration + 1)]: if not marker or marker.mute: continue coords = marker.co corners = marker.pattern_corners p1 = mathutils.Vector(corners[0]) p2 = mathutils.Vector(corners[1]) mid = (p1 + p2) / 2 diff = mid - mathutils.Vector((0,0)) rotation = math.atan2(diff[0], diff[1]) * 180 / math.pi if startrot == None: startrot = rotation rotation = 0 else: rotation -= startrot - 360 x = coords[0] * clip.size[0] y = (1 - coords[1]) * clip.size[1] writer.writerow({ 'time (frames)': marker.frame, 'time (s)': marker.frame / fps, 'x (px)': x, 'x (m)': x / real_scale, 'y (px)': y, 'y (m)': y / real_scale, 'rotation': rotation % 360 }) return {'FINISHED'} from bpy_extras.io_utils import ExportHelper from bpy.props import StringProperty class ExportCSVKey(bpy.types.Operator, ExportHelper): bl_idname = 'export.csvkey' bl_label = 'Export tracking data to CSV file' filename_ext = '' filter_glob: StringProperty(default='*', options={'HIDDEN'}) def execute(self, context): return write_files(self.filepath, context) class ExportCSVKeyPanel(bpy.types.Panel): bl_idname = 'CLIP_PT_tracking_export' bl_label = 'CSV Tracking Data Export' bl_space_type = 'CLIP_EDITOR' bl_region_type = 'UI' bl_category = 'Footage' bl_options = {'DEFAULT_CLOSED'} def draw(self, context): layout = self.layout col = self.layout.column(align = True) col.prop(context.edit_movieclip, 'real_length_pixels') col.prop(context.edit_movieclip, 'real_length_meters') col.operator('export.csvkey', text='Export') def register(): bpy.utils.register_class(ExportCSVKey) bpy.utils.register_class(ExportCSVKeyPanel) bpy.types.MovieClip.real_length_pixels = bpy.props.IntProperty(name='Real length (px)', default=100) bpy.types.MovieClip.real_length_meters = bpy.props.FloatProperty(name='Real length (m)', default=0.1, precision=3) def unregister(): bpy.utils.unregister_class(ExportCSVKey) del bpy.types.MovieClip.real_length_pixels del bpy.types.MovieClip.real_length_meters if __name__ == '__main__': register()