Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extern/libmv/libmv-capi.cc11
-rw-r--r--extern/libmv/libmv-capi.h6
-rw-r--r--extern/libmv/libmv-capi_stub.cc10
-rw-r--r--extern/libmv/libmv/tracking/track_region.cc18
-rw-r--r--extern/libmv/libmv/tracking/track_region.h6
-rw-r--r--release/scripts/startup/bl_ui/properties_mask_common.py2
-rw-r--r--release/scripts/startup/bl_ui/space_clip.py103
-rw-r--r--source/blender/blenkernel/BKE_tracking.h21
-rw-r--r--source/blender/blenkernel/intern/bpath.c6
-rw-r--r--source/blender/blenkernel/intern/tracking.c128
-rw-r--r--source/blender/editors/mask/mask_edit.c1
-rw-r--r--source/blender/editors/mask/mask_intern.h1
-rw-r--r--source/blender/editors/mask/mask_shapekey.c108
-rw-r--r--source/blender/editors/space_clip/clip_intern.h2
-rw-r--r--source/blender/editors/space_clip/space_clip.c17
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c841
16 files changed, 1229 insertions, 52 deletions
diff --git a/extern/libmv/libmv-capi.cc b/extern/libmv/libmv-capi.cc
index 0585bd3e8ac..d3559cb0e73 100644
--- a/extern/libmv/libmv-capi.cc
+++ b/extern/libmv/libmv-capi.cc
@@ -1094,4 +1094,15 @@ void libmv_homography2DFromCorrespondencesEuc(double (*x1)[2], double (*x2)[2],
memcpy(H, H_mat.data(), 9 * sizeof(double));
}
+void libmv_applyInverseCanonicalHomography(double x, double y,
+ const double *xs, const double *ys,
+ int num_samples_x, int num_samples_y,
+ double *warped_position_x,
+ double *warped_position_y)
+{
+ libmv::ApplyInverseCanonicalHomography(x, y, xs, ys,
+ num_samples_x, num_samples_y,
+ warped_position_x, warped_position_y);
+}
+
#endif
diff --git a/extern/libmv/libmv-capi.h b/extern/libmv/libmv-capi.h
index 13cc3ae8499..ee67ff64fbc 100644
--- a/extern/libmv/libmv-capi.h
+++ b/extern/libmv/libmv-capi.h
@@ -158,6 +158,12 @@ void libmv_cameraIntrinsicsInvert(const libmv_CameraIntrinsicsOptions *libmv_cam
void libmv_homography2DFromCorrespondencesEuc(double (*x1)[2], double (*x2)[2], int num_points, double H[3][3]);
+void libmv_applyInverseCanonicalHomography(double x, double y,
+ const double *xs, const double *ys,
+ int num_samples_x, int num_samples_y,
+ double *warped_position_x,
+ double *warped_position_y);
+
#ifdef __cplusplus
}
#endif
diff --git a/extern/libmv/libmv-capi_stub.cc b/extern/libmv/libmv-capi_stub.cc
index bda9605b422..047bed8bb71 100644
--- a/extern/libmv/libmv-capi_stub.cc
+++ b/extern/libmv/libmv-capi_stub.cc
@@ -286,4 +286,14 @@ void libmv_homography2DFromCorrespondencesEuc(double (* /* x1 */)[2], double (*
H[2][2] = 1.0f;
}
+void libmv_applyInverseCanonicalHomography(double x, double y,
+ const double *xs, const double *ys,
+ int num_samples_x, int num_samples_y,
+ double *warped_position_x,
+ double *warped_position_y)
+{
+ *warped_position_x = (double) num_samples_x * 0.5;
+ *warped_position_y = (double) num_samples_y * 0.5;
+}
+
#endif // ifndef WITH_LIBMV
diff --git a/extern/libmv/libmv/tracking/track_region.cc b/extern/libmv/libmv/tracking/track_region.cc
index 349d84b3817..496ea51ec1e 100644
--- a/extern/libmv/libmv/tracking/track_region.cc
+++ b/extern/libmv/libmv/tracking/track_region.cc
@@ -1560,4 +1560,22 @@ bool SamplePlanarPatch(const FloatImage &image,
return true;
}
+void ApplyInverseCanonicalHomography(const double x, const double y,
+ const double *xs, const double *ys,
+ int num_samples_x, int num_samples_y,
+ double *warped_position_x,
+ double *warped_position_y) {
+ //int num_samples_x, num_samples_y;
+ //PickSampling(xs, ys, xs, ys, &num_samples_x, &num_samples_y);
+
+ // Compute the warp from rectangular coordinates.
+ Mat3 canonical_homography = ComputeCanonicalHomography(xs, ys,
+ num_samples_x,
+ num_samples_y);
+
+ Vec3 warped_position = canonical_homography.inverse() * Vec3(x, y, 1);
+ *warped_position_x = warped_position(0) / warped_position(2);
+ *warped_position_y = warped_position(1) / warped_position(2);
+}
+
} // namespace libmv
diff --git a/extern/libmv/libmv/tracking/track_region.h b/extern/libmv/libmv/tracking/track_region.h
index 6c7218f11d1..c371b95139e 100644
--- a/extern/libmv/libmv/tracking/track_region.h
+++ b/extern/libmv/libmv/tracking/track_region.h
@@ -149,6 +149,12 @@ bool SamplePlanarPatch(const FloatImage &image,
FloatImage *mask, FloatImage *patch,
double *warped_position_x, double *warped_position_y);
+ void ApplyInverseCanonicalHomography(const double x, const double y,
+ const double *xs, const double *ys,
+ int num_samples_x, int num_samples_y,
+ double *warped_position_x,
+ double *warped_position_y);
+
} // namespace libmv
#endif // LIBMV_TRACKING_TRACK_REGION_H_
diff --git a/release/scripts/startup/bl_ui/properties_mask_common.py b/release/scripts/startup/bl_ui/properties_mask_common.py
index 203e5078ee4..579637e6be6 100644
--- a/release/scripts/startup/bl_ui/properties_mask_common.py
+++ b/release/scripts/startup/bl_ui/properties_mask_common.py
@@ -267,6 +267,7 @@ class MASK_PT_tools():
col.operator("mask.shape_key_insert")
col.operator("mask.shape_key_feather_reset")
col.operator("mask.shape_key_rekey")
+ col.operator("mask.shape_key_cleanup")
class MASK_MT_mask(Menu):
@@ -326,6 +327,7 @@ class MASK_MT_animation(Menu):
layout.operator("mask.shape_key_insert")
layout.operator("mask.shape_key_feather_reset")
layout.operator("mask.shape_key_rekey")
+ layout.operator("mask.shape_key_cleanup")
class MASK_MT_select(Menu):
diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py
index 3db8697a457..c5a5c57d815 100644
--- a/release/scripts/startup/bl_ui/space_clip.py
+++ b/release/scripts/startup/bl_ui/space_clip.py
@@ -198,6 +198,54 @@ class CLIP_PT_reconstruction_panel:
return clip and sc.mode == 'RECONSTRUCTION' and sc.view == 'CLIP'
+def _draw_default_tracker_settings(layout, settings):
+ col = layout.column()
+ row = col.row(align=True)
+ label = CLIP_MT_tracking_settings_presets.bl_label
+ row.menu('CLIP_MT_tracking_settings_presets', text=label)
+ row.operator("clip.tracking_settings_preset_add",
+ text="", icon='ZOOMIN')
+ props = row.operator("clip.tracking_settings_preset_add",
+ text="", icon='ZOOMOUT')
+ props.remove_active = True
+
+ col.separator()
+
+ row = col.row(align=True)
+ row.prop(settings, "use_default_red_channel",
+ text="R", toggle=True)
+ row.prop(settings, "use_default_green_channel",
+ text="G", toggle=True)
+ row.prop(settings, "use_default_blue_channel",
+ text="B", toggle=True)
+
+ col.separator()
+
+ sub = col.column(align=True)
+ sub.prop(settings, "default_pattern_size")
+ sub.prop(settings, "default_search_size")
+
+ col.label(text="Tracker:")
+ col.prop(settings, "default_motion_model")
+ col.prop(settings, "use_default_brute")
+ col.prop(settings, "use_default_normalization")
+ col.prop(settings, "use_default_mask")
+ col.prop(settings, "default_correlation_min")
+
+ col.separator()
+
+ sub = col.column(align=True)
+ sub.prop(settings, "default_frames_limit")
+ sub.prop(settings, "default_margin")
+
+ col.label(text="Match:")
+ col.prop(settings, "default_pattern_match", text="")
+
+ col.separator()
+ col.operator("clip.track_settings_as_default",
+ text="Copy From Active Track")
+
+
class CLIP_PT_tools_marker(CLIP_PT_tracking_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'TOOLS'
@@ -221,50 +269,27 @@ class CLIP_PT_tools_marker(CLIP_PT_tracking_panel, Panel):
row.label(text="Tracking Settings")
if settings.show_default_expanded:
- col = box.column()
- row = col.row(align=True)
- label = CLIP_MT_tracking_settings_presets.bl_label
- row.menu('CLIP_MT_tracking_settings_presets', text=label)
- row.operator("clip.tracking_settings_preset_add",
- text="", icon='ZOOMIN')
- row.operator("clip.tracking_settings_preset_add",
- text="", icon='ZOOMOUT').remove_active = True
+ _draw_default_tracker_settings(box, settings)
- col.separator()
- row = col.row(align=True)
- row.prop(settings, "use_default_red_channel",
- text="R", toggle=True)
- row.prop(settings, "use_default_green_channel",
- text="G", toggle=True)
- row.prop(settings, "use_default_blue_channel",
- text="B", toggle=True)
-
- col.separator()
-
- sub = col.column(align=True)
- sub.prop(settings, "default_pattern_size")
- sub.prop(settings, "default_search_size")
-
- col.label(text="Tracker:")
- col.prop(settings, "default_motion_model")
- col.prop(settings, "use_default_brute")
- col.prop(settings, "use_default_normalization")
- col.prop(settings, "use_default_mask")
- col.prop(settings, "default_correlation_min")
-
- col.separator()
+class CLIP_PT_tools_default_tracking_settings(Panel):
+ bl_space_type = 'CLIP_EDITOR'
+ bl_region_type = 'TOOLS'
+ bl_label = "Default Settings"
- sub = col.column(align=True)
- sub.prop(settings, "default_frames_limit")
- sub.prop(settings, "default_margin")
+ @classmethod
+ def poll(cls, context):
+ sc = context.space_data
+ clip = sc.clip
- col.label(text="Match:")
- col.prop(settings, "default_pattern_match", text="")
+ return clip and sc.mode == 'MASK'
- col.separator()
- col.operator("clip.track_settings_as_default",
- text="Copy From Active Track")
+ def draw(self, context):
+ layout = self.layout
+ sc = context.space_data
+ clip = sc.clip
+ settings = clip.tracking.settings
+ _draw_default_tracker_settings(layout, settings)
class CLIP_PT_tools_tracking(CLIP_PT_tracking_panel, Panel):
diff --git a/source/blender/blenkernel/BKE_tracking.h b/source/blender/blenkernel/BKE_tracking.h
index 94e530529ec..cdd21a58d92 100644
--- a/source/blender/blenkernel/BKE_tracking.h
+++ b/source/blender/blenkernel/BKE_tracking.h
@@ -52,6 +52,11 @@ struct rcti;
/* **** Common functions **** */
+void BKE_frame_unified_to_search_pixel(struct MovieTrackingMarker *marker,
+ int frame_width, int frame_height,
+ float unified_x, float unified_y,
+ float *search_x, float *search_y);
+
void BKE_tracking_free(struct MovieTracking *tracking);
void BKE_tracking_settings_init(struct MovieTracking *tracking);
@@ -213,6 +218,22 @@ void BKE_tracking_context_sync_user(const struct MovieTrackingContext *context,
bool BKE_tracking_context_step(struct MovieTrackingContext *context);
void BKE_tracking_context_finish(struct MovieTrackingContext *context);
+bool BKE_tracking_track_region(const struct MovieTrackingTrack *track,
+ const struct MovieTrackingMarker *old_marker,
+ const struct ImBuf *old_search_ibuf,
+ const struct ImBuf *new_search_ibuf,
+ const int frame_width,
+ const int frame_height,
+ float *mask,
+ struct MovieTrackingMarker *new_marker);
+
+void BKE_tracking_apply_inverse_homography(const struct MovieTrackingMarker *marker,
+ int frame_width, int frame_height,
+ float x, float y,
+ int num_samples_x, int num_samples_y,
+ float *warped_position_x,
+ float *warped_position_y);
+
void BKE_tracking_refine_marker(struct MovieClip *clip, struct MovieTrackingTrack *track, struct MovieTrackingMarker *marker, bool backwards);
/* **** Plane tracking **** */
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index b7a6cf4153a..3e83a871c0d 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -579,8 +579,10 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
{
if (SEQ_HAS_PATH(seq)) {
if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_SOUND_RAM)) {
- rewrite_path_fixed_dirfile(seq->strip->dir, seq->strip->stripdata->name,
- visit_cb, absbase, bpath_user_data);
+ if (seq->strip->stripdata) {
+ rewrite_path_fixed_dirfile(seq->strip->dir, seq->strip->stripdata->name,
+ visit_cb, absbase, bpath_user_data);
+ }
}
else if (seq->type == SEQ_TYPE_IMAGE) {
/* might want an option not to loop over all strips */
diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c
index d519b93f963..75e49deaa28 100644
--- a/source/blender/blenkernel/intern/tracking.c
+++ b/source/blender/blenkernel/intern/tracking.c
@@ -463,6 +463,24 @@ static void set_marker_coords_from_tracking(int frame_width, int frame_height, M
marker->pos[1] += marker_unified[1];
}
+void BKE_frame_unified_to_search_pixel(MovieTrackingMarker *marker,
+ int frame_width, int frame_height,
+ float unified_x, float unified_y,
+ float *search_x, float *search_y)
+{
+ float unified_coords[2];
+ float pixel_coords[2];
+
+ unified_coords[0] = unified_x;
+ unified_coords[1] = unified_y;
+
+ marker_unified_to_search_pixel(frame_width, frame_height, marker,
+ unified_coords, pixel_coords);
+
+ *search_x = pixel_coords[0];
+ *search_y = pixel_coords[1];
+}
+
/*********************** clipboard *************************/
/* Free clipboard by freeing memory used by all tracks in it. */
@@ -2674,6 +2692,24 @@ static void uint8_rgba_to_float_gray(const unsigned char *rgba, float *gray, int
}
}
+static float *imbuf_to_grayscale_pixels(const ImBuf *ibuf)
+{
+ float *gray_pixels;
+
+ gray_pixels = MEM_mallocN(ibuf->x * ibuf->y * sizeof(float), "tracking floatBuf");
+
+ if (ibuf->rect_float) {
+ float_rgba_to_gray(ibuf->rect_float, gray_pixels, ibuf->x * ibuf->y,
+ 0.2126f, 0.7152f, 0.0722f);
+ }
+ else {
+ uint8_rgba_to_float_gray((unsigned char *)ibuf->rect, gray_pixels, ibuf->x * ibuf->y,
+ 0.2126f, 0.7152f, 0.0722f);
+ }
+
+ return gray_pixels;
+}
+
/* Get grayscale float search buffer for given marker and frame. */
static float *track_get_search_floatbuf(ImBuf *ibuf, MovieTrackingTrack *track, MovieTrackingMarker *marker,
int *width_r, int *height_r)
@@ -2693,17 +2729,7 @@ static float *track_get_search_floatbuf(ImBuf *ibuf, MovieTrackingTrack *track,
width = searchibuf->x;
height = searchibuf->y;
- gray_pixels = MEM_callocN(width * height * sizeof(float), "tracking floatBuf");
-
- if (searchibuf->rect_float) {
- float_rgba_to_gray(searchibuf->rect_float, gray_pixels, width * height,
- 0.2126f, 0.7152f, 0.0722f);
- }
- else {
- uint8_rgba_to_float_gray((unsigned char *)searchibuf->rect, gray_pixels, width * height,
- 0.2126f, 0.7152f, 0.0722f);
- }
-
+ gray_pixels = imbuf_to_grayscale_pixels(searchibuf);
IMB_freeImBuf(searchibuf);
*width_r = width;
@@ -3233,6 +3259,86 @@ void BKE_tracking_refine_marker(MovieClip *clip, MovieTrackingTrack *track, Movi
IMB_freeImBuf(destination_ibuf);
}
+bool BKE_tracking_track_region(const MovieTrackingTrack *track,
+ const MovieTrackingMarker *old_marker,
+ const ImBuf *old_search_ibuf,
+ const ImBuf *new_search_ibuf,
+ const int frame_width,
+ const int frame_height,
+ float *mask,
+ MovieTrackingMarker *new_marker)
+{
+ /* To convert to the x/y split array format for libmv. */
+ double src_pixel_x[5], src_pixel_y[5];
+ double dst_pixel_x[5], dst_pixel_y[5];
+
+ /* Settings for the tracker */
+ libmv_TrackRegionOptions options = {0};
+ libmv_TrackRegionResult result;
+
+ float *old_search, *new_search;
+ bool tracked;
+
+ /* configure the tracker */
+ tracking_configure_tracker(track, mask, &options);
+
+ /* Convert the marker corners and center into
+ * pixel coordinates in the search/destination images.
+ */
+ get_marker_coords_for_tracking(frame_width, frame_height,
+ old_marker, src_pixel_x, src_pixel_y);
+ get_marker_coords_for_tracking(frame_width, frame_height,
+ new_marker, dst_pixel_x, dst_pixel_y);
+
+ old_search = imbuf_to_grayscale_pixels(old_search_ibuf);
+ new_search = imbuf_to_grayscale_pixels(new_search_ibuf);
+
+ /* run the tracker! */
+ tracked = libmv_trackRegion(&options,
+ old_search,
+ old_search_ibuf->x,
+ old_search_ibuf->y,
+ new_search,
+ new_search_ibuf->x,
+ new_search_ibuf->y,
+ src_pixel_x, src_pixel_y,
+ &result,
+ dst_pixel_x, dst_pixel_y);
+
+ set_marker_coords_from_tracking(frame_width, frame_height,
+ new_marker,
+ dst_pixel_x, dst_pixel_y);
+
+ MEM_freeN(old_search);
+ MEM_freeN(new_search);
+
+ return tracked;
+}
+
+void BKE_tracking_apply_inverse_homography(const MovieTrackingMarker *marker,
+ int frame_width, int frame_height,
+ float x, float y,
+ int num_samples_x, int num_samples_y,
+ float *warped_position_x,
+ float *warped_position_y)
+{
+ double xs[5], ys[5];
+ double warped_position_x_double,
+ warped_position_y_double;
+
+ get_marker_coords_for_tracking(frame_width, frame_height, marker, xs, ys);
+
+ libmv_applyInverseCanonicalHomography(x - 0.5f, y - 0.5f,
+ xs, ys,
+ num_samples_x,
+ num_samples_y,
+ &warped_position_x_double,
+ &warped_position_y_double);
+
+ *warped_position_x = warped_position_x_double;
+ *warped_position_y = warped_position_y_double;
+}
+
/*********************** Plane tracking *************************/
typedef double Vec2[2];
diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c
index 62eb9cc240a..dccca4010b6 100644
--- a/source/blender/editors/mask/mask_edit.c
+++ b/source/blender/editors/mask/mask_edit.c
@@ -450,6 +450,7 @@ void ED_operatortypes_mask(void)
WM_operatortype_append(MASK_OT_shape_key_clear);
WM_operatortype_append(MASK_OT_shape_key_feather_reset);
WM_operatortype_append(MASK_OT_shape_key_rekey);
+ WM_operatortype_append(MASK_OT_shape_key_cleanup);
/* layers */
WM_operatortype_append(MASK_OT_layer_move);
diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h
index 51705aa5837..5fc66659050 100644
--- a/source/blender/editors/mask/mask_intern.h
+++ b/source/blender/editors/mask/mask_intern.h
@@ -114,5 +114,6 @@ void MASK_OT_shape_key_insert(struct wmOperatorType *ot);
void MASK_OT_shape_key_clear(struct wmOperatorType *ot);
void MASK_OT_shape_key_feather_reset(struct wmOperatorType *ot);
void MASK_OT_shape_key_rekey(struct wmOperatorType *ot);
+void MASK_OT_shape_key_cleanup(struct wmOperatorType *ot);
#endif /* __MASK_INTERN_H__ */
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
index d5fbdca5b0a..ec042ffefd0 100644
--- a/source/blender/editors/mask/mask_shapekey.c
+++ b/source/blender/editors/mask/mask_shapekey.c
@@ -451,3 +451,111 @@ int ED_mask_layer_shape_auto_key_select(Mask *mask, const int frame)
return change;
}
+
+/******************** shape key cleanup operator *********************/
+
+static int mask_shape_key_cleanup_exec(bContext *C, wmOperator *op)
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ MaskLayer *mask_layer = BKE_mask_layer_active(mask);
+ MaskLayerShape *current_shape, *next_shape;
+ int removed_count = 0;
+ const float tolerance = RNA_float_get(op->ptr, "tolerance");
+ const float tolerance_squared = tolerance * tolerance;
+ int width, height;
+ float side;
+
+ if (mask_layer == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ ED_mask_get_size(CTX_wm_area(C), &width, &height);
+ side = max_ff(width, height);
+
+ for (current_shape = mask_layer->splines_shapes.first;
+ current_shape;
+ current_shape = next_shape)
+ {
+ MaskLayerShape *previous_shape = current_shape->prev;
+ MaskLayerShapeElem *previous_elements, *current_elements, *next_elements;
+ int i;
+ float interpolation_factor, inv_interpolation_factor;
+ float average_error;
+
+ next_shape = current_shape->next;
+
+ if (previous_shape == NULL || next_shape == NULL) {
+ continue;
+ }
+
+ previous_elements = (MaskLayerShapeElem *) previous_shape->data;
+ current_elements = (MaskLayerShapeElem *) current_shape->data;
+ next_elements = (MaskLayerShapeElem *) next_shape->data;
+
+ if (previous_shape->tot_vert != current_shape->tot_vert ||
+ previous_shape->tot_vert != next_shape->tot_vert ||
+ current_shape->tot_vert != next_shape->tot_vert)
+ {
+ printf("%s: unexpected mistmatch between shapes vertices number.\n", __func__);
+ continue;
+ }
+
+ interpolation_factor = (float)(current_shape->frame - previous_shape->frame) /
+ (float)(next_shape->frame - previous_shape->frame);
+ inv_interpolation_factor = 1.0f - interpolation_factor;
+
+ average_error = 0.0f;
+ for (i = 0; i < current_shape->tot_vert; i++) {
+ int j;
+ for (j = 0; j < MASK_OBJECT_SHAPE_ELEM_SIZE; j++) {
+ float interpolated_value;
+ float current_error;
+ interpolated_value = inv_interpolation_factor * previous_elements[i].value[j] +
+ interpolation_factor * next_elements[i].value[j];
+ current_error = (current_elements[i].value[j] - interpolated_value) * side;
+ average_error += current_error * current_error;
+ }
+ }
+ average_error /= (float) current_shape->tot_vert;
+
+ if (ELEM(current_shape->frame, 3, 9)) {
+ printf("%d %f\n", current_shape->frame, average_error);
+ }
+
+ if (average_error < tolerance_squared) {
+ BLI_remlink(&mask_layer->splines_shapes, current_shape);
+ BKE_mask_layer_shape_free(current_shape);
+ removed_count++;
+ }
+ }
+
+ if (removed_count > 0) {
+ WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
+ DAG_id_tag_update(&mask->id, OB_RECALC_DATA);
+ }
+
+ BKE_reportf(op->reports, RPT_INFO, "Removed %d keys", removed_count);
+
+ return OPERATOR_FINISHED;
+}
+
+void MASK_OT_shape_key_cleanup(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Cleanup Shape Keys";
+ ot->description = "Remove keyframes which doesn't have much affect on mask shape";
+ ot->idname = "MASK_OT_shape_key_cleanup";
+
+ /* api callbacks */
+ ot->exec = mask_shape_key_cleanup_exec;
+ ot->poll = ED_maskedit_mask_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float(ot->srna, "tolerance", 0.8f, -FLT_MAX, FLT_MAX,
+ "Tolerance", "Average distance in pixels which mask is allowed to "
+ "slide off after removing shapekey",
+ -100.0f, 100.0f);
+}
diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h
index 513014a2d0d..24b6de31ddb 100644
--- a/source/blender/editors/space_clip/clip_intern.h
+++ b/source/blender/editors/space_clip/clip_intern.h
@@ -195,6 +195,8 @@ void CLIP_OT_tracking_object_remove(struct wmOperatorType *ot);
void CLIP_OT_copy_tracks(struct wmOperatorType *ot);
void CLIP_OT_paste_tracks(struct wmOperatorType *ot);
+void CLIP_OT_track_mask(struct wmOperatorType *ot);
+
void CLIP_OT_create_plane_track(struct wmOperatorType *ot);
void CLIP_OT_slide_plane_marker(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index adc902bf4ba..d52098a2857 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -519,6 +519,9 @@ static void clip_operatortypes(void)
WM_operatortype_append(CLIP_OT_copy_tracks);
WM_operatortype_append(CLIP_OT_paste_tracks);
+ /* test */
+ WM_operatortype_append(CLIP_OT_track_mask);
+
/* Plane tracker */
WM_operatortype_append(CLIP_OT_create_plane_track);
WM_operatortype_append(CLIP_OT_slide_plane_marker);
@@ -576,6 +579,20 @@ static void clip_keymap(struct wmKeyConfig *keyconf)
RNA_boolean_set(kmi->ptr, "backwards", TRUE);
RNA_boolean_set(kmi->ptr, "sequence", TRUE);
+ /* Mask tracking */
+ kmi = WM_keymap_add_item(keymap, "CLIP_OT_track_mask", LEFTARROWKEY, KM_PRESS, KM_ALT, 0);
+ RNA_boolean_set(kmi->ptr, "backwards", TRUE);
+ RNA_boolean_set(kmi->ptr, "sequence", FALSE);
+ kmi = WM_keymap_add_item(keymap, "CLIP_OT_track_mask", RIGHTARROWKEY, KM_PRESS, KM_ALT, 0);
+ RNA_boolean_set(kmi->ptr, "backwards", FALSE);
+ RNA_boolean_set(kmi->ptr, "sequence", FALSE);
+ kmi = WM_keymap_add_item(keymap, "CLIP_OT_track_mask", TKEY, KM_PRESS, KM_CTRL, 0);
+ RNA_boolean_set(kmi->ptr, "backwards", FALSE);
+ RNA_boolean_set(kmi->ptr, "sequence", TRUE);
+ kmi = WM_keymap_add_item(keymap, "CLIP_OT_track_mask", TKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
+ RNA_boolean_set(kmi->ptr, "backwards", TRUE);
+ RNA_boolean_set(kmi->ptr, "sequence", TRUE);
+
/* mode */
WM_keymap_add_menu(keymap, "CLIP_MT_select_mode", TABKEY, KM_PRESS, 0, 0);
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
index 51d7bc3139a..64f118b0ff0 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -3756,6 +3756,847 @@ void CLIP_OT_paste_tracks(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/********************** Track mask operator *********************/
+
+typedef struct TrackMaskContext {
+ Main *bmain;
+ Scene *scene;
+ Mask *mask;
+
+ /* Layer of a mask for which splines are tracking.
+ * This is an actual mask layer for single-frame
+ * tracking and it's a copy of that layer for sequence
+ * tracking which happens in separate thread.
+ */
+ MaskLayer *mask_layer;
+
+ /* Clip mask is tracking for. */
+ MovieClip *clip;
+
+ /* Runtime user used to acquire original frames from a clip. */
+ MovieClipUser clip_user;
+
+ /* Tracking operator settings. */
+ bool backwards;
+ bool sequence;
+
+ /* In case of sequence tracking indicates how many frames
+ * (or tracking steps) are needed.
+ */
+ int steps;
+
+ /* Last frame number for which keyframe is set for sure. */
+ int last_keyframe_inserted;
+
+ MaskSpline *active_mask_spline;
+ int active_point_index;
+
+ /* Delay between next frame is being tracked. */
+ float delay;
+} TrackMaskContext;
+
+static void configure_dummy_track(const MovieTracking *tracking,
+ MovieTrackingTrack *track)
+{
+ const MovieTrackingSettings *settings = &tracking->settings;
+
+ /* Fill track's settings from default tracking settings. */
+ track->motion_model = settings->default_motion_model;
+ track->minimum_correlation = settings->default_minimum_correlation;
+ track->margin = settings->default_margin;
+ track->pattern_match = settings->default_pattern_match;
+ track->frames_limit = settings->default_frames_limit;
+ track->flag = settings->default_flag;
+ track->algorithm_flag = settings->default_algorithm_flag;
+
+ /* We always using mask for the track. */
+ track->algorithm_flag |= TRACK_ALGORITHM_FLAG_USE_MASK;
+}
+
+static void compute_spline_points_boundbox(MaskSpline *mask_spline,
+ float (*diff_points)[2],
+ unsigned int tot_diff_point,
+ float corner_min[2],
+ float corner_max[2])
+{
+ MaskSplinePoint *spline_points, *current_spline_point;
+ int i;
+ float *current_diff_point;
+
+ INIT_MINMAX2(corner_min, corner_max);
+
+ spline_points = mask_spline->points;
+ for (i = 0, current_spline_point = spline_points;
+ i < mask_spline->tot_point;
+ i++, current_spline_point++)
+ {
+ BezTriple *bezt = &current_spline_point->bezt;
+
+ DO_MINMAX2(bezt->vec[0], corner_min, corner_max);
+ DO_MINMAX2(bezt->vec[1], corner_min, corner_max);
+ DO_MINMAX2(bezt->vec[2], corner_min, corner_max);
+ }
+
+ for (i = 0, current_diff_point = diff_points[0];
+ i < tot_diff_point;
+ i++, current_diff_point += 2)
+ {
+ DO_MINMAX2(current_diff_point, corner_min, corner_max);
+ }
+}
+
+static bool configure_dummy_marker(MovieClip *clip, MovieClipUser *user,
+ MaskSpline *mask_spline,
+ MovieTrackingMarker *marker)
+{
+ float (*diff_points)[2];
+ unsigned int tot_diff_point;
+ float spline_corner_min[2], spline_corner_max[2];
+
+ diff_points = BKE_mask_spline_differentiate(mask_spline,
+ &tot_diff_point);
+
+ if (diff_points == NULL) {
+ /* Failed to rasterize spline, or spline is ampty. */
+ return false;
+ }
+
+ /* Compute bounding box of the spline */
+ /* TODO(sergey): consider taking feather into account */
+ compute_spline_points_boundbox(mask_spline,
+ diff_points,
+ tot_diff_point,
+ spline_corner_min,
+ spline_corner_max);
+
+ /* Convert corner coorinates from mask's square space
+ * to movie clip frame space.
+ */
+ BKE_mask_coord_to_movieclip(clip, user,
+ spline_corner_min,
+ spline_corner_min);
+
+ BKE_mask_coord_to_movieclip(clip, user,
+ spline_corner_max,
+ spline_corner_max);
+
+ /* Marker position is at the center of spline bounding box. */
+ interp_v2_v2v2(marker->pos,
+ spline_corner_min,
+ spline_corner_max,
+ 0.5f);
+
+ /* Marker corners matches bounding box.
+ *
+ * Could be useful to store previous homography
+ * for more accurate tracking.
+ */
+ sub_v2_v2v2(marker->pattern_corners[0],
+ spline_corner_min,
+ marker->pos);
+
+ sub_v2_v2v2(marker->pattern_corners[2],
+ spline_corner_max,
+ marker->pos);
+
+ marker->pattern_corners[1][0] = marker->pattern_corners[2][0];
+ marker->pattern_corners[1][1] = marker->pattern_corners[0][1];
+
+ marker->pattern_corners[3][0] = marker->pattern_corners[0][0];
+ marker->pattern_corners[3][1] = marker->pattern_corners[2][1];
+
+ /* Search is 1.5 times larger than pattern for now. */
+ mul_v2_v2fl(marker->search_min, marker->pattern_corners[0], 1.5f);
+ mul_v2_v2fl(marker->search_max, marker->pattern_corners[2], 1.5f);
+
+ MEM_freeN(diff_points);
+
+ return true;
+}
+
+static void convert_delta_to_mask_space(MovieClip *clip, MovieClipUser *user,
+ const float old_pos[2], const float new_pos[2],
+ float delta[2])
+{
+ float old_pos_in_mask_space[2], new_pos_in_mask_space[2];
+
+ BKE_mask_coord_from_movieclip(clip, user, old_pos_in_mask_space, old_pos);
+ BKE_mask_coord_from_movieclip(clip, user, new_pos_in_mask_space, new_pos);
+
+ sub_v2_v2v2(delta, new_pos_in_mask_space, old_pos_in_mask_space);
+}
+
+static void configure_dummy_mask(const Mask *orig_mask,
+ MaskSpline *mask_spline,
+ Mask *dummy_mask)
+{
+ MaskLayer *dummy_mask_layer;
+ MaskSpline *dummy_mask_spline;
+
+ /* Use original mask as a reference for all the settings. */
+ *dummy_mask = *orig_mask;
+
+ /* And use own layer with single spline only. */
+ dummy_mask->masklayers.first = dummy_mask->masklayers.last = NULL;
+
+ dummy_mask_layer = BKE_mask_layer_new(dummy_mask, "Dummy Layer");
+ dummy_mask_spline = BKE_mask_spline_copy(mask_spline);
+
+ BLI_addtail(&dummy_mask_layer->splines, dummy_mask_spline);
+}
+
+static void free_dummy_mask(Mask *dummy_mask)
+{
+ BKE_mask_layer_free_list(&dummy_mask->masklayers);
+}
+
+static ImBuf *get_rasterized_mask_spline_buffer(const Mask *mask,
+ MaskSpline *mask_spline,
+ int frame_width,
+ int frame_height)
+{
+ Mask dummy_mask;
+ MaskRasterHandle *mask_rasterizer_handle;
+ ImBuf *rasterized_ibuf;
+ float *rasterized_buffer;
+ int i;
+
+ configure_dummy_mask(mask, mask_spline, &dummy_mask);
+
+ rasterized_buffer = MEM_mallocN(sizeof(float) * frame_width * frame_height,
+ "rasterized spline buffer");
+
+ mask_rasterizer_handle = BKE_maskrasterize_handle_new();
+
+ BKE_maskrasterize_handle_init(mask_rasterizer_handle, &dummy_mask,
+ frame_width, frame_height,
+ TRUE, TRUE, TRUE);
+
+ BKE_maskrasterize_buffer(mask_rasterizer_handle,
+ frame_width, frame_height,
+ rasterized_buffer);
+
+ BKE_maskrasterize_handle_free(mask_rasterizer_handle);
+
+ rasterized_ibuf = IMB_allocImBuf(frame_width, frame_height,
+ 32, IB_rectfloat);
+
+ for (i = 0; i < frame_width * frame_height; i++) {
+ float *pixel = rasterized_ibuf->rect_float + 4 * i;
+
+ pixel[0] = pixel[1] = pixel[2] = rasterized_buffer[i];
+ pixel[3] = 1.0f;
+ }
+
+ MEM_freeN(rasterized_buffer);
+ free_dummy_mask(&dummy_mask);
+
+ return rasterized_ibuf;
+}
+
+static float *tracking_mask_from_ibuf(ImBuf *ibuf)
+{
+ float *mask = MEM_mallocN(sizeof(float) * ibuf->x * ibuf->y, "mask tracking mask");
+ int i;
+
+ for (i = 0; i < ibuf->x * ibuf->y; i++) {
+ float *pixel = ibuf->rect_float + ibuf->channels * i;
+
+ mask[i] = pixel[0];
+ }
+
+ return mask;
+}
+
+static void deform_spline_points_by_marker(MovieClip *clip, MovieClipUser *user,
+ MaskSpline *mask_spline,
+ MovieTrackingMarker *old_marker,
+ MovieTrackingMarker *new_marker)
+{
+ MaskSplinePoint *spline_points, *current_spline_point;
+ int i, frame_width, frame_height;
+ float corners_delta[4][2];
+
+ BKE_movieclip_get_size(clip, user, &frame_width, &frame_height);
+
+ /* First we compute delta of all pattern corners in mask space. */
+ for (i = 0; i < 4; i++) {
+ float old_corner[2], new_corner[2];
+
+ add_v2_v2v2(old_corner,
+ old_marker->pattern_corners[i],
+ old_marker->pos);
+
+ add_v2_v2v2(new_corner,
+ new_marker->pattern_corners[i],
+ new_marker->pos);
+
+ convert_delta_to_mask_space(clip, user,
+ old_corner, new_corner,
+ corners_delta[i]);
+ }
+
+ /* Then we iterate all mask spline points and moving them
+ * by an interpolated coordinate delta.
+ */
+ spline_points = mask_spline->points;
+ for (i = 0, current_spline_point = spline_points;
+ i < mask_spline->tot_point;
+ i++, current_spline_point++)
+ {
+ BezTriple *bezt = &current_spline_point->bezt;
+ int j;
+
+ for (j = 0; j < 3; j++) {
+ float delta[2];
+ float pos_in_marker_space[2];
+ float pos_in_search_space[2];
+ float pos_in_homography_space[2];
+ float left_side_point[2],
+ right_side_point[2];
+
+ /* Compute spline point coordinate relative to marker's center.
+ * This is still in normalized space.
+ */
+ BKE_mask_coord_to_movieclip(clip, user,
+ pos_in_marker_space,
+ bezt->vec[j]);
+ sub_v2_v2(pos_in_marker_space, old_marker->pos);
+
+ /* Then convert coordinate from normalized space to a pixel
+ * space relative to marker search area origin.
+ */
+ BKE_frame_unified_to_search_pixel(old_marker,
+ frame_width, frame_height,
+ pos_in_marker_space[0],
+ pos_in_marker_space[1],
+ &pos_in_search_space[0],
+ &pos_in_search_space[1]);
+
+ /* And finally apply an inverse homography to get spline point
+ * coordinate in pattern patch coordinates.
+ * Using number of samples of 1 so this coordinates could be
+ * used as weights for bilinear interpolation.
+ */
+ BKE_tracking_apply_inverse_homography(old_marker,
+ frame_width, frame_height,
+ pos_in_search_space[0],
+ pos_in_search_space[1],
+ 1, 1,
+ &pos_in_homography_space[0],
+ &pos_in_homography_space[1]);
+
+ /* Do bilinear interpolation. */
+ interp_v2_v2v2(left_side_point,
+ corners_delta[0],
+ corners_delta[1],
+ pos_in_homography_space[1]);
+
+ interp_v2_v2v2(right_side_point,
+ corners_delta[1],
+ corners_delta[2],
+ pos_in_homography_space[1]);
+
+ interp_v2_v2v2(delta,
+ left_side_point,
+ right_side_point,
+ pos_in_homography_space[0]);
+
+ /* Apply delta on bezier point/handle. */
+ add_v2_v2(bezt->vec[j], delta);
+ }
+ }
+
+ /* Need to update handles after points were deformed. */
+ for (i = 0; i < mask_spline->tot_point; i++) {
+ BKE_mask_calc_handle_point(mask_spline, &spline_points[i]);
+ }
+}
+
+static bool mask_track_context_init(const bContext *C,
+ const wmOperator *op,
+ bool duplicate_layer,
+ TrackMaskContext *track_mask_context)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+ Mask *mask = CTX_data_edit_mask(C);
+ MaskLayer *mask_layer = BKE_mask_layer_active(mask);
+ Scene *scene = CTX_data_scene(C);
+
+ if (mask == NULL || mask_layer == NULL) {
+ return false;
+ }
+
+ BKE_mask_evaluate(mask, sc->user.framenr, TRUE);
+
+ if (mask_layer->act_spline && mask_layer->act_point) {
+ track_mask_context->active_point_index =
+ mask_layer->act_point - mask_layer->act_spline->points;
+ }
+ else {
+ track_mask_context->active_point_index = -1;
+ }
+
+ if (duplicate_layer) {
+ int active_spline_index =
+ BLI_findindex(&mask_layer->splines, mask_layer->act_spline);
+
+ mask_layer = BKE_mask_layer_copy(mask_layer);
+
+ track_mask_context->active_mask_spline =
+ BLI_findlink(&mask_layer->splines, active_spline_index);
+ }
+ else {
+ track_mask_context->active_mask_spline = mask_layer->act_spline;
+ }
+
+ /* We don't need deformed spline hanging around.
+ *
+ * This is because differentiation will use deformed
+ * points if they present and this is something we
+ * don't want to happen (sine deformed points are
+ * not being updated during tracking).
+ */
+ BKE_mask_layer_free_deform(mask_layer);
+
+ track_mask_context->bmain = CTX_data_main(C);
+ track_mask_context->scene = scene;
+ track_mask_context->mask = mask;
+ track_mask_context->mask_layer = mask_layer;
+ track_mask_context->clip = ED_space_clip_get_clip(sc);
+ track_mask_context->clip_user = sc->user;
+ track_mask_context->backwards = RNA_boolean_get(op->ptr, "backwards");
+ track_mask_context->sequence = RNA_boolean_get(op->ptr, "sequence");
+ track_mask_context->delay = 1.0f / scene->r.frs_sec * 1000.0f;
+
+ if (track_mask_context->sequence) {
+ if (track_mask_context->backwards) {
+ track_mask_context->steps = CFRA - SFRA;
+ }
+ else {
+ track_mask_context->steps = EFRA - CFRA;
+ }
+ }
+ else {
+ track_mask_context->steps = 1;
+ }
+
+ return true;
+}
+
+static bool track_mask_step(TrackMaskContext *track_mask_context)
+{
+ Mask *mask = track_mask_context->mask;
+ MaskLayer *mask_layer = track_mask_context->mask_layer;
+ MaskSpline *mask_spline;
+ MovieClip *clip = track_mask_context->clip;
+ MovieClipUser *user = &track_mask_context->clip_user;
+ MovieTracking *tracking = &clip->tracking;
+ ImBuf *old_ibuf, *new_ibuf;
+ int frame_delta = track_mask_context->backwards ? -1 : 1;
+ int clip_flag = clip->flag & MCLIP_TIMECODE_FLAGS;
+
+ /* Image buffer we're tracking from */
+ old_ibuf = BKE_movieclip_get_ibuf_flag(clip, user, clip_flag,
+ MOVIECLIP_CACHE_SKIP);
+
+ if (!old_ibuf) {
+ return false;
+ }
+
+ /* Image buffer we're tracking on */
+ user->framenr += frame_delta;
+ new_ibuf = BKE_movieclip_get_ibuf_flag(clip, user, clip_flag,
+ MOVIECLIP_CACHE_SKIP);
+ if (!new_ibuf) {
+ IMB_freeImBuf(old_ibuf);
+ return false;
+ }
+
+ for (mask_spline = mask_layer->splines.first;
+ mask_spline;
+ mask_spline = mask_spline->next)
+ {
+ if (mask_spline->flag & SELECT) {
+ MovieTrackingMarker marker = {{0}};
+ ImBuf *old_search_ibuf, *new_search_ibuf;
+
+ BLI_assert(mask_spline->points_deform == NULL);
+
+ /* Try creating dummy marker for tracking from current spline. */
+ if (configure_dummy_marker(clip, user, mask_spline, &marker)) {
+ MovieTrackingTrack track = {0};
+ MovieTrackingMarker new_marker = {{0}};
+ ImBuf *rasterized_spline_ibuf, *mask_ibuf;
+ bool tracked;
+ float *tracking_mask;
+
+ /* Configure dummy track. */
+ configure_dummy_track(tracking, &track);
+
+ /* Create image buffer for search area at old and new frames. */
+ old_search_ibuf =
+ BKE_tracking_get_search_imbuf(old_ibuf,
+ &track, &marker,
+ FALSE, FALSE);
+
+ new_search_ibuf =
+ BKE_tracking_get_search_imbuf(new_ibuf,
+ &track, &marker,
+ FALSE, FALSE);
+
+ /* Compute mask. */
+ rasterized_spline_ibuf =
+ get_rasterized_mask_spline_buffer(mask,
+ mask_spline,
+ old_ibuf->x,
+ old_ibuf->y);
+
+ mask_ibuf =
+ BKE_tracking_get_search_imbuf(rasterized_spline_ibuf,
+ &track, &marker,
+ FALSE, FALSE);
+
+ tracking_mask = tracking_mask_from_ibuf(mask_ibuf);
+
+ /* Use old position as an initial guess. */
+ new_marker = marker;
+
+ /* Run the tracker. */
+ tracked = BKE_tracking_track_region(&track, &marker,
+ old_search_ibuf,
+ new_search_ibuf,
+ old_ibuf->x, old_ibuf->y,
+ tracking_mask,
+ &new_marker);
+
+ if (tracked) {
+ MaskLayerShape *mask_layer_shape;
+
+ /* Move the mask to new position. */
+ deform_spline_points_by_marker(clip, user, mask_spline,
+ &marker, &new_marker);
+
+ /* Create shape key for tracked mask position. */
+ mask_layer_shape =
+ BKE_mask_layer_shape_varify_frame(mask_layer,
+ user->framenr);
+
+ BKE_mask_layer_shape_from_mask(mask_layer,
+ mask_layer_shape);
+
+ /* We store frame number of latest keyframes inserted to
+ * deal with threading synchronization between main thread
+ * and tracking thread.
+ * Basically, main thread need to set current clip editor's
+ * frame to latest tracked frame, which is not always equal to
+ * context user's frame due to latency between altering
+ * that frame number and actual keyframe is being created here.
+ */
+ track_mask_context->last_keyframe_inserted = user->framenr;
+ }
+
+ /* Free memory used. */
+ IMB_freeImBuf(old_search_ibuf);
+ IMB_freeImBuf(new_search_ibuf);
+ IMB_freeImBuf(mask_ibuf);
+ IMB_freeImBuf(rasterized_spline_ibuf);
+
+ MEM_freeN(tracking_mask);
+ }
+ }
+ }
+
+ IMB_freeImBuf(old_ibuf);
+ IMB_freeImBuf(new_ibuf);
+
+ return true;
+}
+
+static bool track_mask_do_locked(bContext *C, wmOperator *op)
+{
+ TrackMaskContext track_mask_context;
+ Scene *scene;
+ Mask *mask;
+ MovieClip *clip;
+ int i, framenr;
+
+ if (!mask_track_context_init(C, op, false, &track_mask_context)) {
+ return false;
+ }
+
+ scene = track_mask_context.scene;
+ mask = track_mask_context.mask;
+ clip = track_mask_context.clip;
+
+ for (i = 0; i < track_mask_context.steps; i++) {
+ if (!track_mask_step(&track_mask_context)) {
+ break;
+ }
+ }
+
+ /* Update scene current frame. */
+ framenr = track_mask_context.clip_user.framenr;
+ scene->r.cfra =
+ BKE_movieclip_remap_clip_to_scene_frame(clip, framenr);
+
+ WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
+ WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+ DAG_id_tag_update(&mask->id, 0);
+
+ return true;
+}
+
+static int track_mask_testbreak(void)
+{
+ return G.is_break;
+}
+
+static void track_mask_startjob(void *custom_data, short *stop,
+ short *do_update, float *progress)
+{
+ TrackMaskContext *track_mask_context = custom_data;
+ int i;
+
+ for (i = 0; i < track_mask_context->steps; i++) {
+ double start_time = PIL_check_seconds_timer();
+
+ if (*stop || track_mask_testbreak())
+ break;
+
+ if (track_mask_step(track_mask_context)) {
+ double exec_time = PIL_check_seconds_timer() - start_time;
+
+ if (track_mask_context->delay > (float)exec_time)
+ PIL_sleep_ms(track_mask_context->delay - (float)exec_time);
+
+ *progress = (float) i / track_mask_context->steps;
+ *do_update = 1;
+ }
+ else {
+ break;
+ }
+ }
+}
+
+static void update_space_clip_user(Main *bmain, int framenr)
+{
+ bScreen *scr;
+
+ for (scr = bmain->screen.first; scr; scr = scr->id.next) {
+ ScrArea *area;
+ for (area = scr->areabase.first; area; area = area->next) {
+ SpaceLink *sl;
+ for (sl = area->spacedata.first; sl; sl = sl->next) {
+ switch (sl->spacetype) {
+ case SPACE_CLIP:
+ {
+ SpaceClip *space_clip = (SpaceClip *)sl;
+ space_clip->user.framenr = framenr;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void mask_layer_set_data_from_other(const TrackMaskContext *track_mask_context,
+ const MaskLayer *mask_layer_from,
+ MaskLayer *mask_layer_to)
+{
+ MaskSpline *current_spline;
+ MaskLayerShape *current_shape;
+
+ /* Free old data. */
+ BKE_mask_layer_free_shapes(mask_layer_to);
+ BKE_mask_spline_free_list(&mask_layer_to->splines);
+
+ mask_layer_to->act_spline = NULL;
+ mask_layer_to->act_point = NULL;
+
+ /* Copy splines. */
+ for (current_spline = mask_layer_from->splines.first;
+ current_spline;
+ current_spline = current_spline->next)
+ {
+ MaskSpline *new_spline = BKE_mask_spline_copy(current_spline);
+ BLI_addtail(&mask_layer_to->splines, new_spline);
+ if (current_spline == track_mask_context->active_mask_spline) {
+ mask_layer_to->act_spline = new_spline;
+ }
+ }
+
+ /* Update active spline point pointer. */
+ if (mask_layer_to->act_spline &&
+ track_mask_context->active_point_index > 0)
+ {
+ mask_layer_to->act_point =
+ mask_layer_to->act_spline->points +
+ track_mask_context->active_point_index;
+ }
+
+ /* Copy shapes. */
+ for (current_shape = mask_layer_from->splines_shapes.first;
+ current_shape;
+ current_shape = current_shape->next)
+ {
+ MaskLayerShape *new_shape =
+ BKE_mask_layer_shape_duplicate(current_shape);
+ BLI_addtail(&mask_layer_to->splines_shapes, new_shape);
+ }
+}
+
+static void mask_layer_merge_to_origin(const TrackMaskContext *track_mask_context)
+{
+ Mask *mask = track_mask_context->mask;
+ MaskLayer *mask_layer = track_mask_context->mask_layer;
+ MaskLayer *current_mask_layer;
+
+ for (current_mask_layer = mask->masklayers.first;
+ current_mask_layer;
+ current_mask_layer = current_mask_layer->next)
+ {
+ if (!strcmp(current_mask_layer->name, mask_layer->name)) {
+ mask_layer_set_data_from_other(track_mask_context,
+ mask_layer,
+ current_mask_layer);
+
+ break;
+ }
+ }
+}
+
+static void track_mask_updatejob(void *custom_data)
+{
+ TrackMaskContext *track_mask_context = custom_data;
+ Main *bmain = track_mask_context->bmain;
+ Mask *mask = track_mask_context->mask;
+ int framenr = track_mask_context->last_keyframe_inserted;
+
+ mask_layer_merge_to_origin(track_mask_context);
+ update_space_clip_user(bmain, framenr);
+
+ WM_main_add_notifier(NC_MASK | NA_EDITED, mask);
+ DAG_id_tag_update(&mask->id, 0);
+}
+
+static void track_mask_freejob(void *custom_data)
+{
+ TrackMaskContext *track_mask_context = custom_data;
+ Scene *scene = track_mask_context->scene;
+ Mask *mask = track_mask_context->mask;
+ MovieClip *clip = track_mask_context->clip;
+ int framenr = track_mask_context->clip_user.framenr;
+
+ scene->r.cfra =
+ BKE_movieclip_remap_clip_to_scene_frame(clip, framenr);
+
+ BKE_mask_layer_free(track_mask_context->mask_layer);
+ MEM_freeN(track_mask_context);
+
+ WM_main_add_notifier(NC_SCENE | ND_FRAME, scene);
+ WM_main_add_notifier(NC_MASK | NA_EDITED, mask);
+}
+
+static int track_mask_invoke(bContext *C, wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ wmWindowManager *window_manager = CTX_wm_manager(C);
+ wmWindow *window = CTX_wm_window(C);
+ ScrArea *screen_area = CTX_wm_area(C);
+ TrackMaskContext *track_mask_context;
+ bool sequence = RNA_boolean_get(op->ptr, "sequence");
+ wmJob *wm_job;
+
+ if (sequence == false) {
+ if (track_mask_do_locked(C, op)) {
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ if (WM_jobs_test(window_manager, screen_area, WM_JOB_TYPE_ANY)) {
+ /* Only one tracking is allowed at a time. */
+ return OPERATOR_CANCELLED;
+ }
+
+ track_mask_context = MEM_mallocN(sizeof(TrackMaskContext),
+ "mask tracking context");
+
+ if (!mask_track_context_init(C, op, true, track_mask_context)) {
+ MEM_freeN(track_mask_context);
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Setup job. */
+ wm_job = WM_jobs_get(window_manager, window, screen_area, "Track Mask",
+ WM_JOB_PROGRESS, WM_JOB_TYPE_CLIP_TRACK_MARKERS);
+ WM_jobs_customdata_set(wm_job, track_mask_context, track_mask_freejob);
+ WM_jobs_timer(wm_job, track_mask_context->delay / 1000.0f,
+ NC_MASK | ND_DATA, 0);
+ WM_jobs_callbacks(wm_job, track_mask_startjob, NULL,
+ track_mask_updatejob, NULL);
+
+ G.is_break = FALSE;
+
+ WM_jobs_start(window_manager, wm_job);
+
+ /* add modal handler for ESC */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int track_mask_modal(bContext *C, wmOperator *UNUSED(op),
+ const wmEvent *event)
+{
+ /* no running tracking, remove handler and pass through */
+ if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_wm_area(C), WM_JOB_TYPE_ANY))
+ return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
+
+ /* running tracking */
+ switch (event->type) {
+ case ESCKEY:
+ return OPERATOR_RUNNING_MODAL;
+ break;
+ }
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+static int track_mask_exec(bContext *C, wmOperator *op)
+{
+ if (track_mask_do_locked(C, op)) {
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void CLIP_OT_track_mask(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Track Mask";
+ ot->description = "Track Mask";
+ ot->idname = "CLIP_OT_track_mask";
+
+ /* api callbacks */
+ ot->invoke = track_mask_invoke;
+ ot->exec = track_mask_exec;
+ ot->modal = track_mask_modal;
+ ot->poll = ED_space_clip_maskedit_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_boolean(ot->srna, "backwards", 0, "Backwards", "Do backwards tracking");
+ RNA_def_boolean(ot->srna, "sequence", 0, "Track Sequence", "Track marker during image sequence rather than single image");
+}
+
/********************** Create plane track operator *********************/
static int create_plane_track_tracks_exec(bContext *C, wmOperator *op)