Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/tracking.c
| Show First 20 Lines • Show All 581 Lines • ▼ Show 20 Lines | MovieTrackingTrack *BKE_tracking_track_add(MovieTracking *tracking, | ||||
| float x, | float x, | ||||
| float y, | float y, | ||||
| int framenr, | int framenr, | ||||
| int width, | int width, | ||||
| int height) | int height) | ||||
| { | { | ||||
| const MovieTrackingSettings *settings = &tracking->settings; | const MovieTrackingSettings *settings = &tracking->settings; | ||||
| MovieTrackingTrack *track = BKE_tracking_track_add_empty(tracking, tracksbase); | |||||
| MovieTrackingMarker marker; | |||||
| const float half_pattern_px = settings->default_pattern_size / 2.0f; | const float half_pattern_px = settings->default_pattern_size / 2.0f; | ||||
| const float half_search_px = settings->default_search_size / 2.0f; | const float half_search_px = settings->default_search_size / 2.0f; | ||||
| const float pattern_size[2] = {half_pattern_px / width, half_pattern_px / height}; | const float pattern_size[2] = {half_pattern_px / width, half_pattern_px / height}; | ||||
| const float search_size[2] = {half_search_px / width, half_search_px / height}; | const float search_size[2] = {half_search_px / width, half_search_px / height}; | ||||
| MovieTrackingTrack *track = BKE_tracking_track_add_empty(tracking, tracksbase); | |||||
| MovieTrackingMarker marker; | |||||
| memset(&marker, 0, sizeof(marker)); | memset(&marker, 0, sizeof(marker)); | ||||
| marker.pos[0] = x; | marker.pos[0] = x; | ||||
| marker.pos[1] = y; | marker.pos[1] = y; | ||||
| marker.framenr = framenr; | marker.framenr = framenr; | ||||
| marker.pattern_corners[0][0] = -pattern_size[0]; | marker.pattern_corners[0][0] = -pattern_size[0]; | ||||
| marker.pattern_corners[0][1] = -pattern_size[1]; | marker.pattern_corners[0][1] = -pattern_size[1]; | ||||
| ▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
| */ | */ | ||||
| void BKE_tracking_track_free(MovieTrackingTrack *track) | void BKE_tracking_track_free(MovieTrackingTrack *track) | ||||
| { | { | ||||
| if (track->markers) { | if (track->markers) { | ||||
| MEM_freeN(track->markers); | MEM_freeN(track->markers); | ||||
| } | } | ||||
| } | } | ||||
| /* Get frame numbers of the very first and last markers. | |||||
| * There is no check on whether the marker is enabled or not. */ | |||||
| void BKE_tracking_track_first_last_frame_get(const MovieTrackingTrack *track, | |||||
| int *r_first_frame, | |||||
| int *r_last_frame) | |||||
| { | |||||
| BLI_assert(track->markersnr > 0); | |||||
| const int last_marker_index = track->markersnr - 1; | |||||
| *r_first_frame = track->markers[0].framenr; | |||||
| *r_last_frame = track->markers[last_marker_index].framenr; | |||||
| } | |||||
| /* Find the minimum starting frame and maximum ending frame within given set of | |||||
| * tracks. | |||||
| */ | |||||
| void BKE_tracking_tracks_first_last_frame_minmax(/*const*/ MovieTrackingTrack **tracks, | |||||
| const int num_tracks, | |||||
| int *r_first_frame, | |||||
| int *r_last_frame) | |||||
| { | |||||
| *r_first_frame = INT_MAX; | |||||
| *r_last_frame = INT_MIN; | |||||
| for (int i = 0; i < num_tracks; ++i) { | |||||
| const struct MovieTrackingTrack *track = tracks[i]; | |||||
| int track_first_frame, track_last_frame; | |||||
| BKE_tracking_track_first_last_frame_get(track, &track_first_frame, &track_last_frame); | |||||
| *r_first_frame = min_ii(*r_first_frame, track_first_frame); | |||||
| *r_last_frame = max_ii(*r_last_frame, track_last_frame); | |||||
| } | |||||
| } | |||||
| int BKE_tracking_count_selected_tracks_in_list(const ListBase *tracks_list) | |||||
| { | |||||
| int num_selected_tracks = 0; | |||||
| LISTBASE_FOREACH (const MovieTrackingTrack *, track, tracks_list) { | |||||
| if (TRACK_SELECTED(track)) { | |||||
| ++num_selected_tracks; | |||||
| } | |||||
| } | |||||
| return num_selected_tracks; | |||||
| } | |||||
| int BKE_tracking_count_selected_tracks_in_active_object(/*const*/ MovieTracking *tracking) | |||||
| { | |||||
| ListBase *tracks_list = BKE_tracking_get_active_tracks(tracking); | |||||
| return BKE_tracking_count_selected_tracks_in_list(tracks_list); | |||||
| } | |||||
| MovieTrackingTrack **BKE_tracking_selected_tracks_in_active_object(MovieTracking *tracking, | |||||
| int *r_num_tracks) | |||||
| { | |||||
| *r_num_tracks = 0; | |||||
| ListBase *tracks_list = BKE_tracking_get_active_tracks(tracking); | |||||
| if (tracks_list == NULL) { | |||||
| return NULL; | |||||
| } | |||||
| /* Initialize input. */ | |||||
| const int num_selected_tracks = BKE_tracking_count_selected_tracks_in_active_object(tracking); | |||||
| if (num_selected_tracks == 0) { | |||||
| return NULL; | |||||
| } | |||||
| MovieTrackingTrack **source_tracks = MEM_malloc_arrayN( | |||||
| num_selected_tracks, sizeof(MovieTrackingTrack *), "selected tracks array"); | |||||
| int source_track_index = 0; | |||||
| LISTBASE_FOREACH (MovieTrackingTrack *, track, tracks_list) { | |||||
| if (!TRACK_SELECTED(track)) { | |||||
| continue; | |||||
| } | |||||
| source_tracks[source_track_index] = track; | |||||
| ++source_track_index; | |||||
| } | |||||
| *r_num_tracks = num_selected_tracks; | |||||
| return source_tracks; | |||||
| } | |||||
| /* Set flag for all specified track's areas. | /* Set flag for all specified track's areas. | ||||
| * | * | ||||
| * area - which part of marker should be selected. see TRACK_AREA_* constants. | * area - which part of marker should be selected. see TRACK_AREA_* constants. | ||||
| * flag - flag to be set for areas. | * flag - flag to be set for areas. | ||||
| */ | */ | ||||
| void BKE_tracking_track_flag_set(MovieTrackingTrack *track, int area, int flag) | void BKE_tracking_track_flag_set(MovieTrackingTrack *track, int area, int flag) | ||||
| { | { | ||||
| if (area == TRACK_AREA_NONE) { | if (area == TRACK_AREA_NONE) { | ||||
| ▲ Show 20 Lines • Show All 237 Lines • ▼ Show 20 Lines | void BKE_tracking_tracks_join(MovieTracking *tracking, | ||||
| dst_track->markersnr = i; | dst_track->markersnr = i; | ||||
| MEM_freeN(markers); | MEM_freeN(markers); | ||||
| BKE_tracking_dopesheet_tag_update(tracking); | BKE_tracking_dopesheet_tag_update(tracking); | ||||
| } | } | ||||
| static void accumulate_marker(MovieTrackingMarker *dst_marker, | |||||
| const MovieTrackingMarker *src_marker) | |||||
| { | |||||
| BLI_assert(dst_marker->framenr == src_marker->framenr); | |||||
| if (src_marker->flag & MARKER_DISABLED) { | |||||
| return; | |||||
| } | |||||
| add_v2_v2(dst_marker->pos, src_marker->pos); | |||||
| for (int corner = 0; corner < 4; ++corner) { | |||||
| add_v2_v2(dst_marker->pattern_corners[corner], src_marker->pattern_corners[corner]); | |||||
| } | |||||
| add_v2_v2(dst_marker->search_min, src_marker->search_min); | |||||
| add_v2_v2(dst_marker->search_max, src_marker->search_max); | |||||
| BLI_assert(is_finite_v2(src_marker->search_min)); | |||||
| BLI_assert(is_finite_v2(src_marker->search_max)); | |||||
| dst_marker->flag &= ~MARKER_DISABLED; | |||||
| if ((src_marker->flag & MARKER_TRACKED) == 0) { | |||||
| dst_marker->flag &= ~MARKER_TRACKED; | |||||
| } | |||||
| } | |||||
| static void multiply_marker(MovieTrackingMarker *marker, const float multiplier) | |||||
| { | |||||
| mul_v2_fl(marker->pos, multiplier); | |||||
| for (int corner = 0; corner < 4; ++corner) { | |||||
| mul_v2_fl(marker->pattern_corners[corner], multiplier); | |||||
| } | |||||
| mul_v2_fl(marker->search_min, multiplier); | |||||
| mul_v2_fl(marker->search_max, multiplier); | |||||
| } | |||||
| /* Helper function for BKE_tracking_tracks_average which takes care of averaging fields of | |||||
| * markers (position, patterns, ...). */ | |||||
| static void tracking_average_markers(MovieTrackingTrack *dst_track, | |||||
| /*const*/ MovieTrackingTrack **src_tracks, | |||||
| const int num_src_tracks) | |||||
| { | |||||
| /* Get global range of frames within which averaging would happen. */ | |||||
| int first_frame, last_frame; | |||||
| BKE_tracking_tracks_first_last_frame_minmax( | |||||
| src_tracks, num_src_tracks, &first_frame, &last_frame); | |||||
| if (last_frame < first_frame) { | |||||
| return; | |||||
| } | |||||
| const int num_frames = last_frame - first_frame + 1; | |||||
| /* Allocate temporary array where averaging will happen into. */ | |||||
| MovieTrackingMarker *accumulator = MEM_calloc_arrayN( | |||||
| num_frames, sizeof(MovieTrackingMarker), "tracks average accumulator"); | |||||
| int *counters = MEM_calloc_arrayN(num_frames, sizeof(int), "tracks accumulator counters"); | |||||
| for (int frame = first_frame; frame <= last_frame; ++frame) { | |||||
| const int frame_index = frame - first_frame; | |||||
| accumulator[frame_index].framenr = frame; | |||||
| accumulator[frame_index].flag |= (MARKER_DISABLED | MARKER_TRACKED); | |||||
| } | |||||
| /* Accumulate track markers. */ | |||||
| for (int track_index = 0; track_index < num_src_tracks; ++track_index) { | |||||
| /*const*/ MovieTrackingTrack *track = src_tracks[track_index]; | |||||
| for (int frame = first_frame; frame <= last_frame; ++frame) { | |||||
| MovieTrackingMarker interpolated_marker; | |||||
| if (!BKE_tracking_marker_get_interpolated(track, frame, &interpolated_marker)) { | |||||
| continue; | |||||
| } | |||||
| const int frame_index = frame - first_frame; | |||||
| accumulate_marker(&accumulator[frame_index], &interpolated_marker); | |||||
| ++counters[frame_index]; | |||||
| } | |||||
| } | |||||
| /* Average and store the result. */ | |||||
| for (int frame = first_frame; frame <= last_frame; ++frame) { | |||||
| /* Average. */ | |||||
| const int frame_index = frame - first_frame; | |||||
| if (!counters[frame_index]) { | |||||
| continue; | |||||
| } | |||||
| const float multiplier = 1.0f / (float)counters[frame_index]; | |||||
| multiply_marker(&accumulator[frame_index], multiplier); | |||||
| /* Store the result. */ | |||||
| BKE_tracking_marker_insert(dst_track, &accumulator[frame_index]); | |||||
| } | |||||
| /* Free memory. */ | |||||
| MEM_freeN(accumulator); | |||||
| MEM_freeN(counters); | |||||
| } | |||||
| /* Helper function for BKE_tracking_tracks_average which takes care of averaging fields of | |||||
| * tracks (track for example, offset). */ | |||||
| static void tracking_average_tracks(MovieTrackingTrack *dst_track, | |||||
| /*const*/ MovieTrackingTrack **src_tracks, | |||||
| const int num_src_tracks) | |||||
| { | |||||
| /* TODO(sergey): Consider averaging weight, stabilization weight, maybe even bundle position. */ | |||||
| zero_v2(dst_track->offset); | |||||
| for (int track_index = 0; track_index < num_src_tracks; track_index++) { | |||||
| add_v2_v2(dst_track->offset, src_tracks[track_index]->offset); | |||||
| } | |||||
| mul_v2_fl(dst_track->offset, 1.0f / num_src_tracks); | |||||
| } | |||||
| void BKE_tracking_tracks_average(MovieTrackingTrack *dst_track, | |||||
| /*const*/ MovieTrackingTrack **src_tracks, | |||||
| const int num_src_tracks) | |||||
| { | |||||
| if (num_src_tracks == 0) { | |||||
| return; | |||||
| } | |||||
| tracking_average_markers(dst_track, src_tracks, num_src_tracks); | |||||
| tracking_average_tracks(dst_track, src_tracks, num_src_tracks); | |||||
| } | |||||
| MovieTrackingTrack *BKE_tracking_track_get_named(MovieTracking *tracking, | MovieTrackingTrack *BKE_tracking_track_get_named(MovieTracking *tracking, | ||||
| MovieTrackingObject *object, | MovieTrackingObject *object, | ||||
| const char *name) | const char *name) | ||||
| { | { | ||||
| ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object); | ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object); | ||||
| MovieTrackingTrack *track = tracksbase->first; | MovieTrackingTrack *track = tracksbase->first; | ||||
| while (track) { | while (track) { | ||||
| ▲ Show 20 Lines • Show All 290 Lines • ▼ Show 20 Lines | MovieTrackingMarker *BKE_tracking_marker_insert(MovieTrackingTrack *track, | ||||
| /* shift array to "free" space for new marker */ | /* shift array to "free" space for new marker */ | ||||
| memmove(track->markers + a + 2, | memmove(track->markers + a + 2, | ||||
| track->markers + a + 1, | track->markers + a + 1, | ||||
| (track->markersnr - a - 2) * sizeof(MovieTrackingMarker)); | (track->markersnr - a - 2) * sizeof(MovieTrackingMarker)); | ||||
| /* put new marker */ | /* put new marker */ | ||||
| track->markers[a + 1] = *marker; | track->markers[a + 1] = *marker; | ||||
| track->last_marker = a + 1; | |||||
| return &track->markers[a + 1]; | return &track->markers[a + 1]; | ||||
| } | } | ||||
| void BKE_tracking_marker_delete(MovieTrackingTrack *track, int framenr) | void BKE_tracking_marker_delete(MovieTrackingTrack *track, int framenr) | ||||
| { | { | ||||
| int a = 0; | int a = 0; | ||||
| while (a < track->markersnr) { | while (a < track->markersnr) { | ||||
| ▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | for (int a = 0; a < 2; a++) { | ||||
| if (marker->search_max[a] < pat_max[a]) { | if (marker->search_max[a] < pat_max[a]) { | ||||
| marker->search_max[a] = pat_max[a]; | marker->search_max[a] = pat_max[a]; | ||||
| marker->search_min[a] = marker->search_max[a] - dim[a]; | marker->search_min[a] = marker->search_max[a] - dim[a]; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Get marker closest to the given frame number. | |||||
| * | |||||
| * If there is maker with exact frame number it returned. | |||||
| * Otherwise, marker with highest frame number but lower than the requested | |||||
| * frame is returned if such marker exists. Otherwise, the marker with lowest | |||||
| * frame number greater than the requested frame number is returned. | |||||
| * | |||||
| * This function has complexity of `O(log number_of_markers)`. | |||||
| */ | |||||
| MovieTrackingMarker *BKE_tracking_marker_get(MovieTrackingTrack *track, int framenr) | MovieTrackingMarker *BKE_tracking_marker_get(MovieTrackingTrack *track, int framenr) | ||||
| { | { | ||||
| int a = track->markersnr - 1; | const int num_markers = track->markersnr; | ||||
| if (!track->markersnr) { | if (num_markers == 0) { | ||||
| BLI_assert(!"Detected degenerated track, should never happen."); | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* approximate pre-first framenr marker with first marker */ | int left_boundary = 0; | ||||
| if (framenr < track->markers[0].framenr) { | int right_boundary = num_markers; | ||||
| return &track->markers[0]; | while (left_boundary < right_boundary) { | ||||
| } | const int median_index = (left_boundary + right_boundary) / 2; | ||||
| MovieTrackingMarker *marker = &track->markers[median_index]; | |||||
| if (track->last_marker < track->markersnr) { | |||||
| a = track->last_marker; | |||||
| } | |||||
| if (track->markers[a].framenr <= framenr) { | if (marker->framenr == framenr) { | ||||
| while (a < track->markersnr && track->markers[a].framenr <= framenr) { | return marker; | ||||
| if (track->markers[a].framenr == framenr) { | |||||
| track->last_marker = a; | |||||
| return &track->markers[a]; | |||||
| } | |||||
| a++; | |||||
| } | } | ||||
| /* if there's no marker for exact position, use nearest marker from left side */ | if (marker->framenr < framenr) { | ||||
| return &track->markers[a - 1]; | left_boundary = median_index + 1; | ||||
| } | } | ||||
| else { | |||||
| while (a >= 0 && track->markers[a].framenr >= framenr) { | BLI_assert(marker->framenr > framenr); | ||||
| if (track->markers[a].framenr == framenr) { | right_boundary = median_index - 1; | ||||
| track->last_marker = a; | |||||
| return &track->markers[a]; | |||||
| } | } | ||||
| a--; | |||||
| } | } | ||||
| /* if there's no marker for exact position, use nearest marker from left side */ | const int closest_index = clamp_i(right_boundary, 0, num_markers - 1); | ||||
| return &track->markers[a]; | |||||
| return NULL; | return &track->markers[closest_index]; | ||||
| } | } | ||||
| MovieTrackingMarker *BKE_tracking_marker_get_exact(MovieTrackingTrack *track, int framenr) | MovieTrackingMarker *BKE_tracking_marker_get_exact(MovieTrackingTrack *track, int framenr) | ||||
| { | { | ||||
| MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); | MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); | ||||
| if (marker->framenr != framenr) { | if (marker->framenr != framenr) { | ||||
| return NULL; | return NULL; | ||||
| Show All 14 Lines | if (marker->framenr != framenr) { | ||||
| BKE_tracking_marker_insert(track, &marker_new); | BKE_tracking_marker_insert(track, &marker_new); | ||||
| marker = BKE_tracking_marker_get(track, framenr); | marker = BKE_tracking_marker_get(track, framenr); | ||||
| } | } | ||||
| return marker; | return marker; | ||||
| } | } | ||||
| static const MovieTrackingMarker *get_usable_marker_for_interpolation( | |||||
| struct MovieTrackingTrack *track, | |||||
| const MovieTrackingMarker *anchor_marker, | |||||
| const int direction) | |||||
| { | |||||
| BLI_assert(direction == -1 || direction == 1); | |||||
| const MovieTrackingMarker *last_marker = track->markers + track->markersnr - 1; | |||||
| const MovieTrackingMarker *current_marker = anchor_marker; | |||||
| while (current_marker >= track->markers && current_marker <= last_marker) { | |||||
| if ((current_marker->flag & MARKER_DISABLED) == 0) { | |||||
| return current_marker; | |||||
| } | |||||
| current_marker += direction; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| bool BKE_tracking_marker_get_interpolated(struct MovieTrackingTrack *track, | |||||
| const int framenr, | |||||
| struct MovieTrackingMarker *r_marker) | |||||
| { | |||||
| const MovieTrackingMarker *closest_marker = BKE_tracking_marker_get(track, framenr); | |||||
| if (closest_marker == NULL) { | |||||
| return false; | |||||
| } | |||||
| if (closest_marker->framenr == framenr && (closest_marker->flag & MARKER_DISABLED) == 0) { | |||||
| *r_marker = *closest_marker; | |||||
| return true; | |||||
| } | |||||
| const MovieTrackingMarker *left_marker = get_usable_marker_for_interpolation( | |||||
| track, closest_marker, -1); | |||||
| if (left_marker == NULL) { | |||||
| return false; | |||||
| } | |||||
| const MovieTrackingMarker *right_marker = get_usable_marker_for_interpolation( | |||||
| track, closest_marker + 1, 1); | |||||
| if (right_marker == NULL) { | |||||
| return false; | |||||
| } | |||||
| if (left_marker == right_marker) { | |||||
| *r_marker = *left_marker; | |||||
| return true; | |||||
| } | |||||
| const float factor = (float)(framenr - left_marker->framenr) / | |||||
| (right_marker->framenr - left_marker->framenr); | |||||
| interp_v2_v2v2(r_marker->pos, left_marker->pos, right_marker->pos, factor); | |||||
| for (int i = 0; i < 4; i++) { | |||||
| interp_v2_v2v2(r_marker->pattern_corners[i], | |||||
| left_marker->pattern_corners[i], | |||||
| right_marker->pattern_corners[i], | |||||
| factor); | |||||
| } | |||||
| interp_v2_v2v2(r_marker->search_min, left_marker->search_min, right_marker->search_min, factor); | |||||
| interp_v2_v2v2(r_marker->search_max, left_marker->search_max, right_marker->search_max, factor); | |||||
| r_marker->framenr = framenr; | |||||
| r_marker->flag = 0; | |||||
| if (framenr == left_marker->framenr) { | |||||
| r_marker->flag = left_marker->flag; | |||||
| } | |||||
| else if (framenr == right_marker->framenr) { | |||||
| r_marker->flag = right_marker->flag; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| void BKE_tracking_marker_pattern_minmax(const MovieTrackingMarker *marker, | void BKE_tracking_marker_pattern_minmax(const MovieTrackingMarker *marker, | ||||
| float min[2], | float min[2], | ||||
| float max[2]) | float max[2]) | ||||
| { | { | ||||
| INIT_MINMAX2(min, max); | INIT_MINMAX2(min, max); | ||||
| minmax_v2v2_v2(min, max, marker->pattern_corners[0]); | minmax_v2v2_v2(min, max, marker->pattern_corners[0]); | ||||
| minmax_v2v2_v2(min, max, marker->pattern_corners[1]); | minmax_v2v2_v2(min, max, marker->pattern_corners[1]); | ||||
| ▲ Show 20 Lines • Show All 278 Lines • ▼ Show 20 Lines | MovieTrackingPlaneMarker *BKE_tracking_plane_marker_insert(MovieTrackingPlaneTrack *plane_track, | ||||
| /* Shift array to "free" space for new marker. */ | /* Shift array to "free" space for new marker. */ | ||||
| memmove(plane_track->markers + a + 2, | memmove(plane_track->markers + a + 2, | ||||
| plane_track->markers + a + 1, | plane_track->markers + a + 1, | ||||
| (plane_track->markersnr - a - 2) * sizeof(MovieTrackingPlaneMarker)); | (plane_track->markersnr - a - 2) * sizeof(MovieTrackingPlaneMarker)); | ||||
| /* Put new marker to an array. */ | /* Put new marker to an array. */ | ||||
| plane_track->markers[a + 1] = *plane_marker; | plane_track->markers[a + 1] = *plane_marker; | ||||
| plane_track->last_marker = a + 1; | |||||
| return &plane_track->markers[a + 1]; | return &plane_track->markers[a + 1]; | ||||
| } | } | ||||
| void BKE_tracking_plane_marker_delete(MovieTrackingPlaneTrack *plane_track, int framenr) | void BKE_tracking_plane_marker_delete(MovieTrackingPlaneTrack *plane_track, int framenr) | ||||
| { | { | ||||
| int a = 0; | int a = 0; | ||||
| ▲ Show 20 Lines • Show All 1,515 Lines • Show Last 20 Lines | |||||