Revert "Initial release"
This reverts commit 754081ae6b100e7bd288da7e72496b4d3768b5ca.
This commit is contained in:
parent
5641a9e0d6
commit
93d779e189
|
@ -1,348 +0,0 @@
|
|||
bl_info = {
|
||||
"name": "Autotrack",
|
||||
"author": "Miika Puustinen, Matti Kaihola",
|
||||
"version": (0, 0, 95),
|
||||
"blender": (2, 78, 0),
|
||||
"location": "Movie clip Editor > Tools Panel > Autotrack",
|
||||
"description": "Motion Tracking with automatic feature detection.",
|
||||
"warning": "",
|
||||
"wiki_url": "https://github.com/miikapuustinen/blender_autotracker",
|
||||
"category": "Motion Tracking",
|
||||
}
|
||||
|
||||
|
||||
import bpy
|
||||
import math
|
||||
|
||||
|
||||
class AutotrackerOperator(bpy.types.Operator):
|
||||
"""Autotrack. Esc to cancel."""
|
||||
bl_idname = "wm.modal_timer_operator"
|
||||
bl_label = "Modal Timer Operator"
|
||||
|
||||
limits = bpy.props.IntProperty(default=0)
|
||||
_timer = None
|
||||
|
||||
def active_clip(self):
|
||||
area = [i for i in bpy.context.screen.areas if i.type == 'CLIP_EDITOR'][0]
|
||||
clip = area.spaces.active.clip.name
|
||||
return clip
|
||||
|
||||
# DETECT FEATURES
|
||||
def auto_features(self, delete_threshold, limits):
|
||||
tracks = []
|
||||
selected = []
|
||||
old = []
|
||||
to_delete = []
|
||||
|
||||
bpy.ops.clip.select_all(action='DESELECT')
|
||||
|
||||
# Detect Features
|
||||
bpy.ops.clip.detect_features(
|
||||
threshold=bpy.context.scene.autotracker_props.df_threshold,
|
||||
min_distance=bpy.context.scene.autotracker_props.df_distance,
|
||||
margin=bpy.context.scene.autotracker_props.df_margin,
|
||||
placement=bpy.context.scene.autotracker_props.placement_list
|
||||
)
|
||||
|
||||
current_frame = bpy.context.scene.frame_current
|
||||
|
||||
tracks = bpy.data.movieclips[self.active_clip()].tracking.tracks
|
||||
for track in tracks:
|
||||
if track.markers.find_frame(current_frame) is not None:
|
||||
if track.select is not True and track.hide is False and track.markers.find_frame(current_frame).mute is False:
|
||||
old.append(track)
|
||||
if track.select is True:
|
||||
selected.append(track)
|
||||
|
||||
# Select overlapping new markers
|
||||
for i in selected:
|
||||
for j in old:
|
||||
i_marker = i.markers.find_frame(current_frame)
|
||||
j_marker = j.markers.find_frame(current_frame)
|
||||
distance = math.sqrt(((i_marker.co[0] - j_marker.co[0])**2) + ((i_marker.co[1] - j_marker.co[1])**2))
|
||||
|
||||
if distance < delete_threshold:
|
||||
to_delete.append(i)
|
||||
break
|
||||
|
||||
# delete short tracks
|
||||
for track in tracks:
|
||||
muted = []
|
||||
active = []
|
||||
# print(track)
|
||||
for marker in track.markers:
|
||||
if marker.mute is True:
|
||||
muted.append(marker)
|
||||
else:
|
||||
active.append(marker)
|
||||
if len(muted) > 3 and len(active) < 1:
|
||||
to_delete.append(track)
|
||||
|
||||
if len(track.markers) > 1 and len(active) == 0:
|
||||
to_delete.append(track)
|
||||
|
||||
# Delete Overlapping Markers
|
||||
bpy.ops.clip.select_all(action='DESELECT')
|
||||
for track in tracks:
|
||||
if track in to_delete:
|
||||
track.select = True
|
||||
|
||||
bpy.ops.clip.delete_track()
|
||||
|
||||
print(str(len(selected)) + "/" + str(len(tracks)) + " tracks tracking.")
|
||||
|
||||
# AUTOTRACK FRAMES
|
||||
def track_frames_backward(self):
|
||||
bpy.ops.clip.track_markers(backwards=True, sequence=False)
|
||||
|
||||
def track_frames_forward(self):
|
||||
bpy.ops.clip.track_markers(backwards=False, sequence=False)
|
||||
|
||||
# REMOVE BAD MARKERS
|
||||
def remove_extra(self, jump_cut, track_backwards):
|
||||
trackers = []
|
||||
|
||||
if track_backwards is True:
|
||||
one_frame = -1
|
||||
two_frames = -2
|
||||
else:
|
||||
one_frame = 1
|
||||
two_frames = 2
|
||||
|
||||
if self.limits >= 3:
|
||||
trackers = bpy.data.movieclips[self.active_clip()].tracking.tracks
|
||||
|
||||
for i in trackers:
|
||||
if len(i.markers) > 5:
|
||||
current_frame = bpy.context.scene.frame_current
|
||||
|
||||
if (i.markers.find_frame(current_frame) is not None and
|
||||
i.markers.find_frame(current_frame - one_frame) is not None and
|
||||
i.markers.find_frame(current_frame - two_frames) is not None):
|
||||
|
||||
key_frame = i.markers.find_frame(current_frame).co
|
||||
prev_frame = i.markers.find_frame(current_frame - one_frame).co
|
||||
distance = math.sqrt(((key_frame[0] - prev_frame[0])**2) + ((key_frame[1] - prev_frame[1])**2))
|
||||
# Jump Cut threshold
|
||||
if distance > jump_cut:
|
||||
if (i.markers.find_frame(current_frame) is not None and
|
||||
i.markers.find_frame(current_frame - one_frame) is not None):
|
||||
|
||||
# create new track to new pos
|
||||
new_track = \
|
||||
bpy.data.movieclips[self.active_clip()].tracking.tracks.new(frame=current_frame)
|
||||
new_track.markers[0].co = i.markers.find_frame(current_frame).co
|
||||
i.markers.find_frame(current_frame).mute = True
|
||||
i.markers.find_frame(current_frame - one_frame).mute = True
|
||||
|
||||
def modal(self, context, event):
|
||||
scene = bpy.context.scene
|
||||
if (event.type in {'ESC'} or scene.frame_current == scene.frame_end + 1 or
|
||||
scene.frame_current == scene.frame_start - 1):
|
||||
self.limits = 0
|
||||
self.cancel(context)
|
||||
return {'FINISHED'}
|
||||
|
||||
if event.type == 'TIMER':
|
||||
|
||||
# PROP VARIABLES
|
||||
delete_threshold = bpy.context.scene.autotracker_props.delete_threshold
|
||||
endframe = bpy.context.scene.frame_end
|
||||
start_frame = bpy.context.scene.frame_start
|
||||
frame_separate = bpy.context.scene.autotracker_props.frame_separation
|
||||
margin = bpy.context.scene.autotracker_props.df_margin
|
||||
distance = bpy.context.scene.autotracker_props.df_distance
|
||||
threshold = bpy.context.scene.autotracker_props.df_threshold
|
||||
jump_cut = bpy.context.scene.autotracker_props.jump_cut
|
||||
track_backwards = bpy.context.scene.autotracker_props.track_backwards
|
||||
|
||||
# Auto features every frame separate step
|
||||
if bpy.context.scene.frame_current % frame_separate == 0 or self.limits == 0:
|
||||
limits = self.limits
|
||||
self.auto_features(delete_threshold, limits)
|
||||
|
||||
# Select all trackers for tracking
|
||||
select_all = bpy.ops.clip.select_all(action='SELECT')
|
||||
tracks = bpy.data.movieclips[self.active_clip()].tracking.tracks
|
||||
active_tracks = []
|
||||
for track in tracks:
|
||||
if track.lock is True:
|
||||
track.select = False
|
||||
else:
|
||||
active_tracks.append(track)
|
||||
|
||||
# Forwards or backwards tracking
|
||||
if track_backwards is True:
|
||||
if len(active_tracks) == 0:
|
||||
print("No new tracks created. Doing nothing.")
|
||||
self.limits = 0
|
||||
self.cancel(context)
|
||||
return {'FINISHED'}
|
||||
else:
|
||||
self.track_frames_backward()
|
||||
else:
|
||||
if len(active_tracks) == 0:
|
||||
print("No new tracks created. Doing nothing.")
|
||||
self.limits = 0
|
||||
self.cancel(context)
|
||||
return {'FINISHED'}
|
||||
else:
|
||||
self.track_frames_forward()
|
||||
|
||||
# Remove bad tracks
|
||||
self.remove_extra(jump_cut, track_backwards)
|
||||
|
||||
self.limits += 1
|
||||
|
||||
return {'PASS_THROUGH'}
|
||||
|
||||
def execute(self, context):
|
||||
wm = context.window_manager
|
||||
self._timer = wm.event_timer_add(time_step=0.5, window=context.window)
|
||||
wm.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def cancel(self, context):
|
||||
wm = context.window_manager
|
||||
wm.event_timer_remove(self._timer)
|
||||
|
||||
|
||||
# UI CREATION #
|
||||
|
||||
class Autotracker_UI(bpy.types.Panel):
|
||||
"""Creates a Panel in the Render Layer properties window"""
|
||||
bl_label = "Autotrack"
|
||||
bl_idname = "autotrack"
|
||||
bl_space_type = 'CLIP_EDITOR'
|
||||
bl_region_type = 'TOOLS'
|
||||
bl_category = "Track"
|
||||
|
||||
# Draw UI
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 1.5
|
||||
|
||||
props = row.operator("wm.modal_timer_operator", text="Autotrack! ", icon='PLAY')
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.autotracker_props, "track_backwards")
|
||||
|
||||
row = layout.row(align=True) # make next row
|
||||
row.prop(context.scene.autotracker_props, "delete_threshold")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.autotracker_props, "frame_separation", text="Frame Separation")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.autotracker_props, "jump_cut", text="Jump Threshold")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="Detect Features Settings:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.autotracker_props, "df_margin", text="Margin:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.autotracker_props, "df_threshold", text="Threshold:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.autotracker_props, "df_distance", text="Distance:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label(text="Feature Placement:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.autotracker_props, "placement_list")
|
||||
|
||||
|
||||
class AutotrackerSettings(bpy.types.PropertyGroup):
|
||||
"""Create properties"""
|
||||
df_margin = bpy.props.IntProperty(
|
||||
name="Detect Features Margin",
|
||||
description="Only features further margin pixels from the image edges are considered.",
|
||||
default=16,
|
||||
min=0,
|
||||
max=2000
|
||||
)
|
||||
df_threshold = bpy.props.FloatProperty(
|
||||
name="Detect Features Threshold",
|
||||
description="Threshold level to concider feature good enough for tracking.",
|
||||
default=0.01,
|
||||
min=0.0,
|
||||
max=1.0
|
||||
)
|
||||
|
||||
df_distance = bpy.props.IntProperty(
|
||||
name="Detect Features Distance",
|
||||
description="Minimal distance accepted between two features.",
|
||||
default=64,
|
||||
min=1,
|
||||
max=300
|
||||
)
|
||||
|
||||
delete_threshold = bpy.props.FloatProperty(
|
||||
name="New Marker Threshold",
|
||||
description="Threshold how near new features can appear during autotracking.",
|
||||
default=0.1,
|
||||
min=0.0,
|
||||
max=1.0
|
||||
)
|
||||
|
||||
frame_separation = bpy.props.IntProperty(
|
||||
name="Frame Separation",
|
||||
description="How often new features are generated.",
|
||||
default=5,
|
||||
min=1,
|
||||
max=100
|
||||
)
|
||||
|
||||
jump_cut = bpy.props.FloatProperty(
|
||||
name="Jump Cut",
|
||||
description="Distance how much a marker can travel before it is considered "
|
||||
"to be a bad track and cut. A new track is added.",
|
||||
default=0.1,
|
||||
min=0.0,
|
||||
max=1.0
|
||||
)
|
||||
|
||||
track_backwards = bpy.props.BoolProperty(
|
||||
name="AutoTrack Backwards",
|
||||
description="Autotrack backwards.",
|
||||
default=False
|
||||
)
|
||||
|
||||
# Dropdown menu
|
||||
list_items = [
|
||||
("FRAME", "Whole Frame", "", 1),
|
||||
("INSIDE_GPENCIL", "Inside Grease Pencil", "", 2),
|
||||
("OUTSIDE_GPENCIL", "Outside Grease Pencil", "", 3),
|
||||
]
|
||||
|
||||
placement_list = bpy.props.EnumProperty(
|
||||
name="",
|
||||
description="Feaure Placement",
|
||||
items=list_items
|
||||
)
|
||||
|
||||
|
||||
# REGISTER BLOCK #
|
||||
def register():
|
||||
bpy.utils.register_class(AutotrackerOperator)
|
||||
bpy.utils.register_class(Autotracker_UI)
|
||||
bpy.utils.register_class(AutotrackerSettings)
|
||||
|
||||
bpy.types.Scene.autotracker_props = \
|
||||
bpy.props.PointerProperty(type=AutotrackerSettings)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(AutotrackerOperator)
|
||||
bpy.utils.unregister_class(Autotracker_UI)
|
||||
bpy.utils.unregister_class(AutotrackerSettings)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
Loading…
Reference in New Issue