From 5641a9e0d61951714afeed609a2e787d0a2c1068 Mon Sep 17 00:00:00 2001 From: Stephen Leger Date: Sat, 15 Apr 2017 18:22:52 +0200 Subject: Initial release --- space_clip_editor_refine_solution.py | 197 +++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 space_clip_editor_refine_solution.py (limited to 'space_clip_editor_refine_solution.py') diff --git a/space_clip_editor_refine_solution.py b/space_clip_editor_refine_solution.py new file mode 100644 index 00000000..baed13fc --- /dev/null +++ b/space_clip_editor_refine_solution.py @@ -0,0 +1,197 @@ +# -*- coding:utf-8 -*- + +# ***** GPL LICENSE BLOCK ***** +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# All rights reserved. +# ***** GPL LICENSE BLOCK ***** + +bl_info = { + "name": "Refine tracking solution", + "author": "Stephen Leger", + "license": "GPL", + "version": (1, 1, 1), + "blender": (2, 7, 8), + "location": "Clip Editor > Tools > Solve > Refine Solution", + "description": "Refine motion solution by setting track weight according reprojection error", + "warning": "", + "wiki_url": "https://github.com/s-leger/blenderTracking/wiki", + "tracker_url": "https://github.com/s-leger/blenderTracking/issues", + "support": "COMMUNITY", + "category": "Tools", +} + +import bpy +import math +from mathutils import Vector + +class OP_Tracking_refine_solution(bpy.types.Operator): + """Set track weight by error and solve camera motion""" + bl_idname = "tracking.refine_solution" + bl_label = "Refine" + bl_options = {"UNDO"} + + @classmethod + def poll(cls, context): + return (context.area.spaces.active.clip is not None) + + def execute(self, context): + error = context.window_manager.TrackingTargetError + smooth = context.window_manager.TrackingSmooth + clip = context.area.spaces.active.clip + try: + tracking = clip.tracking + tracks = tracking.tracks + winx = float(clip.size[0]) + winy = float(clip.size[1]) + aspy = 1.0 / tracking.camera.pixel_aspect + start = tracking.reconstruction.cameras[0].frame + end = tracking.reconstruction.cameras[-1].frame + except: + return {'CANCELED'} + + marker_position = Vector() + + for frame in range(start, end): + camera = tracking.reconstruction.cameras.find_frame(frame) + if camera is not None: + imat = camera.matrix.inverted() + projection_matrix = imat.transposed() + else: + continue + + for track in tracking.tracks: + marker = track.markers.find_frame(frame) + if marker is None: + continue + + # weight incomplete tracks on start and end + if frame > start + smooth and frame < end - smooth: + for m in track.markers: + if not m.mute: + tstart = m.frame + break + for m in reversed(track.markers): + if not m.mute: + tend = m.frame + break + dt = min(0.5 * (tend - tstart), smooth) + if dt > 0: + t0 = min(1.0, (frame - tstart) / dt) + t1 = min(1.0, (tend - frame) / dt) + tw = min(t0, t1) + else: + tw = 0.0 + else: + tw = 1.0 + + reprojected_position = track.bundle * projection_matrix + if reprojected_position.z == 0: + track.weight = 0 + track.keyframe_insert("weight", frame=frame) + continue + reprojected_position = reprojected_position / -reprojected_position.z * tracking.camera.focal_length_pixels + reprojected_position = Vector((tracking.camera.principal[0] + reprojected_position[0],tracking.camera.principal[1] * aspy + reprojected_position[1], 0)) + + marker_position[0] = (marker.co[0] + track.offset[0]) * winx + marker_position[1] = (marker.co[1] + track.offset[1]) * winy * aspy + + dp = marker_position - reprojected_position + if dp.length == 0: + track.weight = 1.0 + else: + track.weight = min(1.0, tw * error / dp.length) + track.keyframe_insert("weight", frame=frame) + + + bpy.ops.clip.solve_camera() + return{'FINISHED'} + +class OP_Tracking_reset_solution(bpy.types.Operator): + """Reset track weight and solve camera motion""" + bl_idname = "tracking.reset_solution" + bl_label = "Reset" + bl_options = {"UNDO"} + + @classmethod + def poll(cls, context): + return (context.area.spaces.active.clip is not None) + + def execute(self, context): + clip = context.area.spaces.active.clip + try: + tracking = clip.tracking + tracks = tracking.tracks + start = tracking.reconstruction.cameras[0].frame + end = tracking.reconstruction.cameras[-1].frame + except: + return {'CANCELED'} + start = tracking.reconstruction.cameras[0].frame + end = tracking.reconstruction.cameras[-1].frame + for frame in range(start, end): + camera = tracking.reconstruction.cameras.find_frame(frame) + if camera is None: + continue + for track in tracking.tracks: + marker = track.markers.find_frame(frame) + if marker is None: + continue + track.weight = 1.0 + track.keyframe_insert("weight", frame=frame) + bpy.ops.clip.solve_camera() + return{'FINISHED'} + +class RefineMotionTrackingPanel(bpy.types.Panel): + bl_label = "Refine solution" + bl_space_type = "CLIP_EDITOR" + bl_region_type = "TOOLS" + bl_category = "Solve" + + @classmethod + def poll(cls, context): + return (context.area.spaces.active.clip is not None) + + def draw(self, context): + layout = self.layout + box = layout.box() + row = box.row(align=True) + row.label("Refine") + row = box.row(align=True) + row.prop(context.window_manager, "TrackingTargetError", text="Target error") + row = box.row(align=True) + row.prop(context.window_manager, "TrackingSmooth", text="Smooth transition") + row = box.row(align=True) + row.operator("tracking.refine_solution") + row.operator("tracking.reset_solution") + +def register(): + bpy.types.WindowManager.TrackingTargetError = bpy.props.FloatProperty( + name="error", + description="Refine motion track target error", + default=0.3, + min=0.01) + bpy.types.WindowManager.TrackingSmooth = bpy.props.FloatProperty( + name="Smooth transition", + description="Smooth weight transition on start and end of incomplete tracks", + default=25, + min=1) + bpy.utils.register_module(__name__) + +def unregister(): + bpy.utils.unregister_module(__name__) + del bpy.types.WindowManager.TrackingTargetError + del bpy.types.WindowManager.TrackingSmooth + +if __name__ == "__main__": + register() \ No newline at end of file -- cgit v1.2.3