Skip to content
Snippets Groups Projects
Select Git revision
  • b3600e4e5bcee6e2c25c7442c49f612a7182111f
  • master default protected
  • blender-v3.6-release
  • main
  • blender-v4.1-release
  • blender-v4.0-release
  • blender-v3.3-release
  • asset-shelf
  • blender-v3.5-release
  • brush-assets-project
  • blender-v2.93-release
  • blender-v3.4-release
  • xr-dev
  • bholodeck-v3.3
  • blender-v3.2-release
  • temp-xr-tracker
  • blender-v3.1-release
  • screenshots-manual
  • gltf_vtree
  • blender-v2.83-release
  • blender-v3.0-release
  • v3.6.18
  • v3.6.19
  • v3.6.20
  • v3.6.21
  • v3.6.22
  • v3.6.23
  • v4.1.1
  • v4.1.0
  • v3.6.10
  • v3.6.11
  • v3.6.12
  • v3.6.13
  • v3.6.14
  • v3.6.15
  • v3.6.16
  • v3.6.17
  • v3.6.9
  • v3.3.16
  • v3.6.8
  • v3.3.15
41 results

space_clip_editor_refine_solution.py

Blame
  • space_clip_editor_refine_solution.py 7.74 KiB
    # -*- coding:utf-8 -*-
    
    # ##### BEGIN 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 <http://www.gnu.org/licenses/>.
    #  All rights reserved.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    # <pep8 compliant>
    
    bl_info = {
        "name": "Refine tracking solution",
        "author": "Stephen Leger",
        "license": "GPL",
        "version": (1, 1, 5),
        "blender": (2, 80, 0),
        "location": "Clip Editor > Tools > Solve > Refine Solution",
        "description": "Refine motion solution by setting track weight according"
                       " to reprojection error",
        "warning": "",
        "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
                    "video_tools/refine_tracking.html",
        "category": "Video Tools",
    }
    
    import bpy
    from bpy.types import (
            Operator,
            Panel,
            )
    from bpy.props import FloatProperty
    from mathutils import Vector
    
    
    class TRACKING_OP_refine_solution(Operator):
        bl_idname = "tracking.refine_solution"
        bl_label = "Refine"
        bl_description = "Set track weight by error and solve camera motion"
        bl_options = {"UNDO"}
    
        @classmethod
        def poll(cls, context):
            return (context.area and context.area.spaces and
                    hasattr(context.area.spaces.active, 'clip') and
                    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 {'CANCELLED'}
    
            marker_position = Vector()
    
            for frame in range(start, end):
                camera = tracking.reconstruction.cameras.find_frame(frame=frame)
                if camera is not None:
                    camera_invert = camera.matrix.inverted()
                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
                                break
                        for m in reversed(track.markers):
                            if not m.mute:
                                tend = m
                                break
                        dt = min(0.5 * (tend.frame - tstart.frame), smooth)
                        if dt > 0:
                            t0 = min(1.0, (frame - tstart.frame) / dt)
                            t1 = min(1.0, (tend.frame - frame) / dt)
                            tw = min(t0, t1)
                        else:
                            tw = 0.0
                    else:
                        tw = 1.0
    
                    reprojected_position = camera_invert @ track.bundle
                    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('INVOKE_DEFAULT')
    
            return{'FINISHED'}
    
    
    class TRACKING_OP_reset_solution(Operator):
        bl_idname = "tracking.reset_solution"
        bl_label = "Reset"
        bl_description = "Reset track weight and solve camera motion"
        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 {'CANCELLED'}
    
            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=frame)
                if camera is None:
                    continue
                for track in tracking.tracks:
                    marker = track.markers.find_frame(frame=frame)
                    if marker is None:
                        continue
                    track.weight = 1.0
                    track.keyframe_insert("weight", frame=frame)
    
            bpy.ops.clip.solve_camera('INVOKE_DEFAULT')
    
            return{'FINISHED'}
    
    
    class TRACKING_PT_RefineMotionTracking(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
    
            col = layout.column(align=True)
            col.prop(context.window_manager, "TrackingTargetError", text="Target error")
            col.prop(context.window_manager, "TrackingSmooth", text="Smooth transition")
            sub_box = col.box()
            sub_box.scale_y = 0.25
    
            row = col.row(align=True)
            row.operator("tracking.refine_solution")
            row.operator("tracking.reset_solution")
    
    
    classes =(
        TRACKING_OP_refine_solution,
        TRACKING_OP_reset_solution,
        TRACKING_PT_RefineMotionTracking
        )
    
    
    def register():
        bpy.types.WindowManager.TrackingTargetError = FloatProperty(
                name="Target Error",
                description="Refine motion track target error",
                default=0.3,
                min=0.01
                )
        bpy.types.WindowManager.TrackingSmooth = FloatProperty(
                name="Smooth Transition",
                description="Smooth weight transition on start and end of incomplete tracks",
                default=25,
                min=1
                )
        for cls in classes:
            bpy.utils.register_class(cls)
    
    
    def unregister():
        for cls in reversed(classes):
            bpy.utils.unregister_class(cls)
        del bpy.types.WindowManager.TrackingTargetError
        del bpy.types.WindowManager.TrackingSmooth
    
    
    if __name__ == "__main__":
        register()