diff options
-rw-r--r-- | extern/libmv/libmv-capi.cc | 11 | ||||
-rw-r--r-- | extern/libmv/libmv-capi.h | 6 | ||||
-rw-r--r-- | extern/libmv/libmv-capi_stub.cc | 10 | ||||
-rw-r--r-- | extern/libmv/libmv/tracking/track_region.cc | 18 | ||||
-rw-r--r-- | extern/libmv/libmv/tracking/track_region.h | 6 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/properties_mask_common.py | 2 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_clip.py | 103 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_tracking.h | 21 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/bpath.c | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/tracking.c | 128 | ||||
-rw-r--r-- | source/blender/editors/mask/mask_edit.c | 1 | ||||
-rw-r--r-- | source/blender/editors/mask/mask_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/mask/mask_shapekey.c | 108 | ||||
-rw-r--r-- | source/blender/editors/space_clip/clip_intern.h | 2 | ||||
-rw-r--r-- | source/blender/editors/space_clip/space_clip.c | 17 | ||||
-rw-r--r-- | source/blender/editors/space_clip/tracking_ops.c | 841 |
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 = ¤t_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 = ¤t_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) |