Fix T77568: turnaround camera crashes undoing
[blender-addons.git] / space_clip_editor_refine_solution.py
blob457af2866f65695df5b13f9004d2631c9b91a3d3
1 # -*- coding:utf-8 -*-
3 # ##### BEGIN GPL LICENSE BLOCK #####
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 # All rights reserved.
19 # ##### END GPL LICENSE BLOCK #####
21 # <pep8 compliant>
23 bl_info = {
24 "name": "Refine tracking solution",
25 "author": "Stephen Leger",
26 "license": "GPL",
27 "version": (1, 1, 5),
28 "blender": (2, 80, 0),
29 "location": "Clip Editor > Tools > Solve > Refine Solution",
30 "description": "Refine motion solution by setting track weight according"
31 " to reprojection error",
32 "warning": "",
33 "doc_url": "{BLENDER_MANUAL_URL}/addons/video_tools/refine_tracking.html",
34 "category": "Video Tools",
37 import bpy
38 from bpy.types import (
39 Operator,
40 Panel,
42 from bpy.props import FloatProperty
43 from mathutils import Vector
46 class TRACKING_OP_refine_solution(Operator):
47 bl_idname = "tracking.refine_solution"
48 bl_label = "Refine"
49 bl_description = "Set track weight by error and solve camera motion"
50 bl_options = {"UNDO"}
52 @classmethod
53 def poll(cls, context):
54 return (context.area and context.area.spaces and
55 hasattr(context.area.spaces.active, 'clip') and
56 context.area.spaces.active.clip is not None
59 def execute(self, context):
60 error = context.window_manager.TrackingTargetError
61 smooth = context.window_manager.TrackingSmooth
62 clip = context.area.spaces.active.clip
63 try:
64 tracking = clip.tracking
65 tracks = tracking.tracks
66 winx = float(clip.size[0])
67 winy = float(clip.size[1])
68 aspy = 1.0 / tracking.camera.pixel_aspect
69 start = tracking.reconstruction.cameras[0].frame
70 end = tracking.reconstruction.cameras[-1].frame
71 except:
72 return {'CANCELLED'}
74 marker_position = Vector()
76 for frame in range(start, end):
77 camera = tracking.reconstruction.cameras.find_frame(frame=frame)
78 if camera is not None:
79 camera_invert = camera.matrix.inverted()
80 else:
81 continue
83 for track in tracking.tracks:
84 marker = track.markers.find_frame(frame)
85 if marker is None:
86 continue
88 # weight incomplete tracks on start and end
89 if frame > start + smooth and frame < end - smooth:
90 for m in track.markers:
91 if not m.mute:
92 tstart = m
93 break
94 for m in reversed(track.markers):
95 if not m.mute:
96 tend = m
97 break
98 dt = min(0.5 * (tend.frame - tstart.frame), smooth)
99 if dt > 0:
100 t0 = min(1.0, (frame - tstart.frame) / dt)
101 t1 = min(1.0, (tend.frame - frame) / dt)
102 tw = min(t0, t1)
103 else:
104 tw = 0.0
105 else:
106 tw = 1.0
108 reprojected_position = camera_invert @ track.bundle
109 if reprojected_position.z == 0:
110 track.weight = 0
111 track.keyframe_insert("weight", frame=frame)
112 continue
113 reprojected_position = reprojected_position / -reprojected_position.z * \
114 tracking.camera.focal_length_pixels
115 reprojected_position = Vector(
116 (tracking.camera.principal[0] + reprojected_position[0],
117 tracking.camera.principal[1] * aspy + reprojected_position[1], 0)
120 marker_position[0] = (marker.co[0] + track.offset[0]) * winx
121 marker_position[1] = (marker.co[1] + track.offset[1]) * winy * aspy
123 dp = marker_position - reprojected_position
124 if dp.length == 0:
125 track.weight = 1.0
126 else:
127 track.weight = min(1.0, tw * error / dp.length)
128 track.keyframe_insert("weight", frame=frame)
130 bpy.ops.clip.solve_camera('INVOKE_DEFAULT')
132 return{'FINISHED'}
135 class TRACKING_OP_reset_solution(Operator):
136 bl_idname = "tracking.reset_solution"
137 bl_label = "Reset"
138 bl_description = "Reset track weight and solve camera motion"
139 bl_options = {"UNDO"}
141 @classmethod
142 def poll(cls, context):
143 return (context.area.spaces.active.clip is not None)
145 def execute(self, context):
146 clip = context.area.spaces.active.clip
147 try:
148 tracking = clip.tracking
149 tracks = tracking.tracks
150 start = tracking.reconstruction.cameras[0].frame
151 end = tracking.reconstruction.cameras[-1].frame
152 except:
153 return {'CANCELLED'}
155 start = tracking.reconstruction.cameras[0].frame
156 end = tracking.reconstruction.cameras[-1].frame
157 for frame in range(start, end):
158 camera = tracking.reconstruction.cameras.find_frame(frame=frame)
159 if camera is None:
160 continue
161 for track in tracking.tracks:
162 marker = track.markers.find_frame(frame=frame)
163 if marker is None:
164 continue
165 track.weight = 1.0
166 track.keyframe_insert("weight", frame=frame)
168 bpy.ops.clip.solve_camera('INVOKE_DEFAULT')
170 return{'FINISHED'}
173 class TRACKING_PT_RefineMotionTracking(Panel):
174 bl_label = "Refine solution"
175 bl_space_type = "CLIP_EDITOR"
176 bl_region_type = "TOOLS"
177 bl_category = "Solve"
179 @classmethod
180 def poll(cls, context):
181 return (context.area.spaces.active.clip is not None)
183 def draw(self, context):
184 layout = self.layout
186 col = layout.column(align=True)
187 col.prop(context.window_manager, "TrackingTargetError", text="Target error")
188 col.prop(context.window_manager, "TrackingSmooth", text="Smooth transition")
189 sub_box = col.box()
190 sub_box.scale_y = 0.25
192 row = col.row(align=True)
193 row.operator("tracking.refine_solution")
194 row.operator("tracking.reset_solution")
197 classes =(
198 TRACKING_OP_refine_solution,
199 TRACKING_OP_reset_solution,
200 TRACKING_PT_RefineMotionTracking
204 def register():
205 bpy.types.WindowManager.TrackingTargetError = FloatProperty(
206 name="Target Error",
207 description="Refine motion track target error",
208 default=0.3,
209 min=0.01
211 bpy.types.WindowManager.TrackingSmooth = FloatProperty(
212 name="Smooth Transition",
213 description="Smooth weight transition on start and end of incomplete tracks",
214 default=25,
215 min=1
217 for cls in classes:
218 bpy.utils.register_class(cls)
221 def unregister():
222 for cls in reversed(classes):
223 bpy.utils.unregister_class(cls)
224 del bpy.types.WindowManager.TrackingTargetError
225 del bpy.types.WindowManager.TrackingSmooth
228 if __name__ == "__main__":
229 register()