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 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/"
34 "Motion_Tracking/Refine_Track",
35 "category": "Video Tools",
39 from bpy
.types
import (
43 from bpy
.props
import FloatProperty
44 from mathutils
import Vector
47 class TRACKING_OP_refine_solution(Operator
):
48 bl_idname
= "tracking.refine_solution"
50 bl_description
= "Set track weight by error and solve camera motion"
54 def poll(cls
, context
):
55 return (context
.area
and context
.area
.spaces
and
56 hasattr(context
.area
.spaces
.active
, 'clip') and
57 context
.area
.spaces
.active
.clip
is not None
60 def execute(self
, context
):
61 error
= context
.window_manager
.TrackingTargetError
62 smooth
= context
.window_manager
.TrackingSmooth
63 clip
= context
.area
.spaces
.active
.clip
65 tracking
= clip
.tracking
66 tracks
= tracking
.tracks
67 winx
= float(clip
.size
[0])
68 winy
= float(clip
.size
[1])
69 aspy
= 1.0 / tracking
.camera
.pixel_aspect
70 start
= tracking
.reconstruction
.cameras
[0].frame
71 end
= tracking
.reconstruction
.cameras
[-1].frame
75 marker_position
= Vector()
77 for frame
in range(start
, end
):
78 camera
= tracking
.reconstruction
.cameras
.find_frame(frame
=frame
)
79 if camera
is not None:
80 camera_invert
= camera
.matrix
.inverted()
84 for track
in tracking
.tracks
:
85 marker
= track
.markers
.find_frame(frame
)
89 # weight incomplete tracks on start and end
90 if frame
> start
+ smooth
and frame
< end
- smooth
:
91 for m
in track
.markers
:
95 for m
in reversed(track
.markers
):
99 dt
= min(0.5 * (tend
.frame
- tstart
.frame
), smooth
)
101 t0
= min(1.0, (frame
- tstart
.frame
) / dt
)
102 t1
= min(1.0, (tend
.frame
- frame
) / dt
)
109 reprojected_position
= camera_invert
@ track
.bundle
110 if reprojected_position
.z
== 0:
112 track
.keyframe_insert("weight", frame
=frame
)
114 reprojected_position
= reprojected_position
/ -reprojected_position
.z
* \
115 tracking
.camera
.focal_length_pixels
116 reprojected_position
= Vector(
117 (tracking
.camera
.principal
[0] + reprojected_position
[0],
118 tracking
.camera
.principal
[1] * aspy
+ reprojected_position
[1], 0)
121 marker_position
[0] = (marker
.co
[0] + track
.offset
[0]) * winx
122 marker_position
[1] = (marker
.co
[1] + track
.offset
[1]) * winy
* aspy
124 dp
= marker_position
- reprojected_position
128 track
.weight
= min(1.0, tw
* error
/ dp
.length
)
129 track
.keyframe_insert("weight", frame
=frame
)
131 bpy
.ops
.clip
.solve_camera('INVOKE_DEFAULT')
136 class TRACKING_OP_reset_solution(Operator
):
137 bl_idname
= "tracking.reset_solution"
139 bl_description
= "Reset track weight and solve camera motion"
140 bl_options
= {"UNDO"}
143 def poll(cls
, context
):
144 return (context
.area
.spaces
.active
.clip
is not None)
146 def execute(self
, context
):
147 clip
= context
.area
.spaces
.active
.clip
149 tracking
= clip
.tracking
150 tracks
= tracking
.tracks
151 start
= tracking
.reconstruction
.cameras
[0].frame
152 end
= tracking
.reconstruction
.cameras
[-1].frame
156 start
= tracking
.reconstruction
.cameras
[0].frame
157 end
= tracking
.reconstruction
.cameras
[-1].frame
158 for frame
in range(start
, end
):
159 camera
= tracking
.reconstruction
.cameras
.find_frame(frame
=frame
)
162 for track
in tracking
.tracks
:
163 marker
= track
.markers
.find_frame(frame
=frame
)
167 track
.keyframe_insert("weight", frame
=frame
)
169 bpy
.ops
.clip
.solve_camera('INVOKE_DEFAULT')
174 class TRACKING_PT_RefineMotionTracking(Panel
):
175 bl_label
= "Refine solution"
176 bl_space_type
= "CLIP_EDITOR"
177 bl_region_type
= "TOOLS"
178 bl_category
= "Solve"
181 def poll(cls
, context
):
182 return (context
.area
.spaces
.active
.clip
is not None)
184 def draw(self
, context
):
187 col
= layout
.column(align
=True)
188 col
.prop(context
.window_manager
, "TrackingTargetError", text
="Target error")
189 col
.prop(context
.window_manager
, "TrackingSmooth", text
="Smooth transition")
191 sub_box
.scale_y
= 0.25
193 row
= col
.row(align
=True)
194 row
.operator("tracking.refine_solution")
195 row
.operator("tracking.reset_solution")
199 TRACKING_OP_refine_solution
,
200 TRACKING_OP_reset_solution
,
201 TRACKING_PT_RefineMotionTracking
206 bpy
.types
.WindowManager
.TrackingTargetError
= FloatProperty(
208 description
="Refine motion track target error",
212 bpy
.types
.WindowManager
.TrackingSmooth
= FloatProperty(
213 name
="Smooth Transition",
214 description
="Smooth weight transition on start and end of incomplete tracks",
219 bpy
.utils
.register_class(cls
)
223 for cls
in reversed(classes
):
224 bpy
.utils
.unregister_class(cls
)
225 del bpy
.types
.WindowManager
.TrackingTargetError
226 del bpy
.types
.WindowManager
.TrackingSmooth
229 if __name__
== "__main__":