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 #####
24 "name": "Refine tracking solution",
25 "author": "Stephen Leger",
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",
33 "doc_url": "{BLENDER_MANUAL_URL}/addons/video_tools/refine_tracking.html",
34 "category": "Video Tools",
38 from bpy
.types
import (
42 from bpy
.props
import FloatProperty
43 from mathutils
import Vector
46 class TRACKING_OP_refine_solution(Operator
):
47 bl_idname
= "tracking.refine_solution"
49 bl_description
= "Set track weight by error and solve camera motion"
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
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
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()
83 for track
in tracking
.tracks
:
84 marker
= track
.markers
.find_frame(frame
)
88 # weight incomplete tracks on start and end
89 if frame
> start
+ smooth
and frame
< end
- smooth
:
90 for m
in track
.markers
:
94 for m
in reversed(track
.markers
):
98 dt
= min(0.5 * (tend
.frame
- tstart
.frame
), smooth
)
100 t0
= min(1.0, (frame
- tstart
.frame
) / dt
)
101 t1
= min(1.0, (tend
.frame
- frame
) / dt
)
108 reprojected_position
= camera_invert
@ track
.bundle
109 if reprojected_position
.z
== 0:
111 track
.keyframe_insert("weight", frame
=frame
)
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
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')
135 class TRACKING_OP_reset_solution(Operator
):
136 bl_idname
= "tracking.reset_solution"
138 bl_description
= "Reset track weight and solve camera motion"
139 bl_options
= {"UNDO"}
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
148 tracking
= clip
.tracking
149 tracks
= tracking
.tracks
150 start
= tracking
.reconstruction
.cameras
[0].frame
151 end
= tracking
.reconstruction
.cameras
[-1].frame
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
)
161 for track
in tracking
.tracks
:
162 marker
= track
.markers
.find_frame(frame
=frame
)
166 track
.keyframe_insert("weight", frame
=frame
)
168 bpy
.ops
.clip
.solve_camera('INVOKE_DEFAULT')
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"
180 def poll(cls
, context
):
181 return (context
.area
.spaces
.active
.clip
is not None)
183 def draw(self
, context
):
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")
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")
198 TRACKING_OP_refine_solution
,
199 TRACKING_OP_reset_solution
,
200 TRACKING_PT_RefineMotionTracking
205 bpy
.types
.WindowManager
.TrackingTargetError
= FloatProperty(
207 description
="Refine motion track target error",
211 bpy
.types
.WindowManager
.TrackingSmooth
= FloatProperty(
212 name
="Smooth Transition",
213 description
="Smooth weight transition on start and end of incomplete tracks",
218 bpy
.utils
.register_class(cls
)
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__":