1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 "name": "Refine tracking solution",
7 "author": "Stephen Leger",
10 "blender": (2, 80, 0),
11 "location": "Clip Editor > Tools > Solve > Refine Solution",
12 "description": "Refine motion solution by setting track weight according"
13 " to reprojection error",
15 "doc_url": "{BLENDER_MANUAL_URL}/addons/video_tools/refine_tracking.html",
16 "category": "Video Tools",
20 from bpy
.types
import (
24 from bpy
.props
import FloatProperty
25 from mathutils
import Vector
28 class TRACKING_OP_refine_solution(Operator
):
29 bl_idname
= "tracking.refine_solution"
31 bl_description
= "Set track weight by error and solve camera motion"
35 def poll(cls
, context
):
36 return (context
.area
and context
.area
.spaces
and
37 hasattr(context
.area
.spaces
.active
, 'clip') and
38 context
.area
.spaces
.active
.clip
is not None
41 def execute(self
, context
):
42 error
= context
.window_manager
.TrackingTargetError
43 smooth
= context
.window_manager
.TrackingSmooth
44 clip
= context
.area
.spaces
.active
.clip
46 tracking
= clip
.tracking
47 tracks
= tracking
.tracks
48 winx
= float(clip
.size
[0])
49 winy
= float(clip
.size
[1])
50 aspy
= 1.0 / tracking
.camera
.pixel_aspect
51 start
= tracking
.reconstruction
.cameras
[0].frame
52 end
= tracking
.reconstruction
.cameras
[-1].frame
56 marker_position
= Vector()
58 for frame
in range(start
, end
):
59 camera
= tracking
.reconstruction
.cameras
.find_frame(frame
=frame
)
60 if camera
is not None:
61 camera_invert
= camera
.matrix
.inverted()
65 for track
in tracking
.tracks
:
66 marker
= track
.markers
.find_frame(frame
)
70 # weight incomplete tracks on start and end
71 if frame
> start
+ smooth
and frame
< end
- smooth
:
72 for m
in track
.markers
:
76 for m
in reversed(track
.markers
):
80 dt
= min(0.5 * (tend
.frame
- tstart
.frame
), smooth
)
82 t0
= min(1.0, (frame
- tstart
.frame
) / dt
)
83 t1
= min(1.0, (tend
.frame
- frame
) / dt
)
90 reprojected_position
= camera_invert
@ track
.bundle
91 if reprojected_position
.z
== 0:
93 track
.keyframe_insert("weight", frame
=frame
)
95 reprojected_position
= reprojected_position
/ -reprojected_position
.z
* \
96 tracking
.camera
.focal_length_pixels
97 reprojected_position
= Vector(
98 (tracking
.camera
.principal_point_pixels
[0] + reprojected_position
[0],
99 tracking
.camera
.principal_point_pixels
[1] * aspy
+ reprojected_position
[1], 0)
102 marker_position
[0] = (marker
.co
[0] + track
.offset
[0]) * winx
103 marker_position
[1] = (marker
.co
[1] + track
.offset
[1]) * winy
* aspy
105 dp
= marker_position
- reprojected_position
109 track
.weight
= min(1.0, tw
* error
/ dp
.length
)
110 track
.keyframe_insert("weight", frame
=frame
)
112 bpy
.ops
.clip
.solve_camera('INVOKE_DEFAULT')
117 class TRACKING_OP_reset_solution(Operator
):
118 bl_idname
= "tracking.reset_solution"
120 bl_description
= "Reset track weight and solve camera motion"
121 bl_options
= {"UNDO"}
124 def poll(cls
, context
):
125 return (context
.area
.spaces
.active
.clip
is not None)
127 def execute(self
, context
):
128 clip
= context
.area
.spaces
.active
.clip
130 tracking
= clip
.tracking
131 tracks
= tracking
.tracks
132 start
= tracking
.reconstruction
.cameras
[0].frame
133 end
= tracking
.reconstruction
.cameras
[-1].frame
137 start
= tracking
.reconstruction
.cameras
[0].frame
138 end
= tracking
.reconstruction
.cameras
[-1].frame
139 for frame
in range(start
, end
):
140 camera
= tracking
.reconstruction
.cameras
.find_frame(frame
=frame
)
143 for track
in tracking
.tracks
:
144 marker
= track
.markers
.find_frame(frame
=frame
)
148 track
.keyframe_insert("weight", frame
=frame
)
150 bpy
.ops
.clip
.solve_camera('INVOKE_DEFAULT')
155 class TRACKING_PT_RefineMotionTracking(Panel
):
156 bl_label
= "Refine solution"
157 bl_space_type
= "CLIP_EDITOR"
158 bl_region_type
= "TOOLS"
159 bl_category
= "Solve"
162 def poll(cls
, context
):
163 return (context
.area
.spaces
.active
.clip
is not None)
165 def draw(self
, context
):
168 col
= layout
.column(align
=True)
169 col
.prop(context
.window_manager
, "TrackingTargetError", text
="Target error")
170 col
.prop(context
.window_manager
, "TrackingSmooth", text
="Smooth transition")
172 sub_box
.scale_y
= 0.25
174 row
= col
.row(align
=True)
175 row
.operator("tracking.refine_solution")
176 row
.operator("tracking.reset_solution")
180 TRACKING_OP_refine_solution
,
181 TRACKING_OP_reset_solution
,
182 TRACKING_PT_RefineMotionTracking
187 bpy
.types
.WindowManager
.TrackingTargetError
= FloatProperty(
189 description
="Refine motion track target error",
193 bpy
.types
.WindowManager
.TrackingSmooth
= FloatProperty(
194 name
="Smooth Transition",
195 description
="Smooth weight transition on start and end of incomplete tracks",
200 bpy
.utils
.register_class(cls
)
204 for cls
in reversed(classes
):
205 bpy
.utils
.unregister_class(cls
)
206 del bpy
.types
.WindowManager
.TrackingTargetError
207 del bpy
.types
.WindowManager
.TrackingSmooth
210 if __name__
== "__main__":