diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2013-12-30 15:03:59 +0400 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2014-01-01 20:32:48 +0400 |
commit | 2785e8e73d3473cf481ba65a6b50a50c194e63d8 (patch) | |
tree | 179263a95aa25ccf8ecd383786dd535419aeabe4 /source/blender/blenkernel/intern/tracking_stabilize.c | |
parent | 5933b2455c409963580ea616047f2f2ee332ff71 (diff) |
Split tracking.c into several files
File tracking.c became rather huge and annoying to
maintain and it really contains several independent
areas of motrack pipeline.
Now we've got:
* tracking.c: general-purpose functions which are used
by blender, clip editor, RNA and so.
* tracking_detect.c: feature detection functions
(blender-side, logic is still in libmv).
* tracking_plane_tracker.c: blender-side 2D tracking logic.
* tracking_plane_tracker.c: plane track tracker.
* tracking_solver.c: functions for camera solving.
* tracking_stabilize.c: 2D stabilization functions.
* tracking_util.c: utility functions for all those files
and which shouldn't be public.
Diffstat (limited to 'source/blender/blenkernel/intern/tracking_stabilize.c')
-rw-r--r-- | source/blender/blenkernel/intern/tracking_stabilize.c | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c new file mode 100644 index 00000000000..21550f411fc --- /dev/null +++ b/source/blender/blenkernel/intern/tracking_stabilize.c @@ -0,0 +1,445 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2011 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * Keir Mierle + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/tracking_stabilize.c + * \ingroup bke + * + * This file contains implementation of 2D frame stabilization. + */ + +#include <limits.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_movieclip_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_tracking.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" + +/* Calculate median point of markers of tracks marked as used for + * 2D stabilization. + * + * NOTE: frame number should be in clip space, not scene space + */ +static bool stabilization_median_point_get(MovieTracking *tracking, int framenr, float median[2]) +{ + bool ok = false; + float min[2], max[2]; + MovieTrackingTrack *track; + + INIT_MINMAX2(min, max); + + track = tracking->tracks.first; + while (track) { + if (track->flag & TRACK_USE_2D_STAB) { + MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); + + minmax_v2v2_v2(min, max, marker->pos); + + ok = true; + } + + track = track->next; + } + + median[0] = (max[0] + min[0]) / 2.0f; + median[1] = (max[1] + min[1]) / 2.0f; + + return ok; +} + +/* Calculate stabilization data (translation, scale and rotation) from + * given median of first and current frame medians, tracking data and + * frame number. + * + * NOTE: frame number should be in clip space, not scene space + */ +static void stabilization_calculate_data(MovieTracking *tracking, int framenr, int width, int height, + float firstmedian[2], float median[2], + float translation[2], float *scale, float *angle) +{ + MovieTrackingStabilization *stab = &tracking->stabilization; + + *scale = (stab->scale - 1.0f) * stab->scaleinf + 1.0f; + *angle = 0.0f; + + translation[0] = (firstmedian[0] - median[0]) * width * (*scale); + translation[1] = (firstmedian[1] - median[1]) * height * (*scale); + + mul_v2_fl(translation, stab->locinf); + + if ((stab->flag & TRACKING_STABILIZE_ROTATION) && stab->rot_track && stab->rotinf) { + MovieTrackingMarker *marker; + float a[2], b[2]; + float x0 = (float)width / 2.0f, y0 = (float)height / 2.0f; + float x = median[0] * width, y = median[1] * height; + + marker = BKE_tracking_marker_get(stab->rot_track, 1); + sub_v2_v2v2(a, marker->pos, firstmedian); + a[0] *= width; + a[1] *= height; + + marker = BKE_tracking_marker_get(stab->rot_track, framenr); + sub_v2_v2v2(b, marker->pos, median); + b[0] *= width; + b[1] *= height; + + *angle = -atan2f(a[0] * b[1] - a[1] * b[0], a[0] * b[0] + a[1] * b[1]); + *angle *= stab->rotinf; + + /* convert to rotation around image center */ + translation[0] -= (x0 + (x - x0) * cosf(*angle) - (y - y0) * sinf(*angle) - x) * (*scale); + translation[1] -= (y0 + (x - x0) * sinf(*angle) + (y - y0) * cosf(*angle) - y) * (*scale); + } +} + +/* Calculate factor of a scale, which will eliminate black areas + * appearing on the frame caused by frame translation. + */ +static float stabilization_calculate_autoscale_factor(MovieTracking *tracking, int width, int height) +{ + float firstmedian[2]; + MovieTrackingStabilization *stab = &tracking->stabilization; + float aspect = tracking->camera.pixel_aspect; + + /* Early output if stabilization data is already up-to-date. */ + if (stab->ok) + return stab->scale; + + /* See comment in BKE_tracking_stabilization_data_get about first frame. */ + if (stabilization_median_point_get(tracking, 1, firstmedian)) { + int sfra = INT_MAX, efra = INT_MIN, cfra; + float scale = 1.0f; + MovieTrackingTrack *track; + + stab->scale = 1.0f; + + /* Calculate frame range of tracks used for stabilization. */ + track = tracking->tracks.first; + while (track) { + if (track->flag & TRACK_USE_2D_STAB || + ((stab->flag & TRACKING_STABILIZE_ROTATION) && track == stab->rot_track)) + { + sfra = min_ii(sfra, track->markers[0].framenr); + efra = max_ii(efra, track->markers[track->markersnr - 1].framenr); + } + + track = track->next; + } + + /* For every frame we calculate scale factor needed to eliminate black + * aread and choose largest scale factor as final one. + */ + for (cfra = sfra; cfra <= efra; cfra++) { + float median[2]; + float translation[2], angle, tmp_scale; + int i; + float mat[4][4]; + float points[4][2] = {{0.0f, 0.0f}, {0.0f, height}, {width, height}, {width, 0.0f}}; + float si, co; + + stabilization_median_point_get(tracking, cfra, median); + + stabilization_calculate_data(tracking, cfra, width, height, firstmedian, median, translation, &tmp_scale, &angle); + + BKE_tracking_stabilization_data_to_mat4(width, height, aspect, translation, 1.0f, angle, mat); + + si = sinf(angle); + co = cosf(angle); + + for (i = 0; i < 4; i++) { + int j; + float a[3] = {0.0f, 0.0f, 0.0f}, b[3] = {0.0f, 0.0f, 0.0f}; + + copy_v3_v3(a, points[i]); + copy_v3_v3(b, points[(i + 1) % 4]); + + mul_m4_v3(mat, a); + mul_m4_v3(mat, b); + + for (j = 0; j < 4; j++) { + float point[3] = {points[j][0], points[j][1], 0.0f}; + float v1[3], v2[3]; + + sub_v3_v3v3(v1, b, a); + sub_v3_v3v3(v2, point, a); + + if (cross_v2v2(v1, v2) >= 0.0f) { + const float rotDx[4][2] = {{1.0f, 0.0f}, {0.0f, -1.0f}, {-1.0f, 0.0f}, {0.0f, 1.0f}}; + const float rotDy[4][2] = {{0.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, -1.0f}, {-1.0f, 0.0f}}; + + float dx = translation[0] * rotDx[j][0] + translation[1] * rotDx[j][1], + dy = translation[0] * rotDy[j][0] + translation[1] * rotDy[j][1]; + + float w, h, E, F, G, H, I, J, K, S; + + if (j % 2) { + w = (float)height / 2.0f; + h = (float)width / 2.0f; + } + else { + w = (float)width / 2.0f; + h = (float)height / 2.0f; + } + + E = -w * co + h * si; + F = -h * co - w * si; + + if ((i % 2) == (j % 2)) { + G = -w * co - h * si; + H = h * co - w * si; + } + else { + G = w * co + h * si; + H = -h * co + w * si; + } + + I = F - H; + J = G - E; + K = G * F - E * H; + + S = (-w * I - h * J) / (dx * I + dy * J + K); + + scale = max_ff(scale, S); + } + } + } + } + + stab->scale = scale; + + if (stab->maxscale > 0.0f) + stab->scale = min_ff(stab->scale, stab->maxscale); + } + else { + stab->scale = 1.0f; + } + + stab->ok = TRUE; + + return stab->scale; +} + +/* Get stabilization data (translation, scaling and angle) for a given frame. + * + * NOTE: frame number should be in clip space, not scene space + */ +void BKE_tracking_stabilization_data_get(MovieTracking *tracking, int framenr, int width, int height, + float translation[2], float *scale, float *angle) +{ + float firstmedian[2], median[2]; + MovieTrackingStabilization *stab = &tracking->stabilization; + + /* Early output if stabilization is disabled. */ + if ((stab->flag & TRACKING_2D_STABILIZATION) == 0) { + zero_v2(translation); + *scale = 1.0f; + *angle = 0.0f; + + return; + } + + /* Even if tracks does not start at frame 1, their position will + * be estimated at this frame, which will give reasonable result + * in most of cases. + * + * However, it's still better to replace this with real first + * frame number at which tracks are appearing. + */ + if (stabilization_median_point_get(tracking, 1, firstmedian)) { + stabilization_median_point_get(tracking, framenr, median); + + if ((stab->flag & TRACKING_AUTOSCALE) == 0) + stab->scale = 1.0f; + + if (!stab->ok) { + if (stab->flag & TRACKING_AUTOSCALE) + stabilization_calculate_autoscale_factor(tracking, width, height); + + stabilization_calculate_data(tracking, framenr, width, height, firstmedian, median, + translation, scale, angle); + + stab->ok = TRUE; + } + else { + stabilization_calculate_data(tracking, framenr, width, height, firstmedian, median, + translation, scale, angle); + } + } + else { + zero_v2(translation); + *scale = 1.0f; + *angle = 0.0f; + } +} + +/* Stabilize given image buffer using stabilization data for + * a specified frame number. + * + * NOTE: frame number should be in clip space, not scene space + */ +ImBuf *BKE_tracking_stabilize_frame(MovieTracking *tracking, int framenr, ImBuf *ibuf, + float translation[2], float *scale, float *angle) +{ + float tloc[2], tscale, tangle; + MovieTrackingStabilization *stab = &tracking->stabilization; + ImBuf *tmpibuf; + int width = ibuf->x, height = ibuf->y; + float aspect = tracking->camera.pixel_aspect; + float mat[4][4]; + int j, filter = tracking->stabilization.filter; + void (*interpolation)(struct ImBuf *, struct ImBuf *, float, float, int, int) = NULL; + int ibuf_flags; + + if (translation) + copy_v2_v2(tloc, translation); + + if (scale) + tscale = *scale; + + /* Perform early output if no stabilization is used. */ + if ((stab->flag & TRACKING_2D_STABILIZATION) == 0) { + if (translation) + zero_v2(translation); + + if (scale) + *scale = 1.0f; + + if (angle) + *angle = 0.0f; + + return ibuf; + } + + /* Allocate frame for stabilization result. */ + ibuf_flags = 0; + if (ibuf->rect) + ibuf_flags |= IB_rect; + if (ibuf->rect_float) + ibuf_flags |= IB_rectfloat; + + tmpibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, ibuf_flags); + + /* Calculate stabilization matrix. */ + BKE_tracking_stabilization_data_get(tracking, framenr, width, height, tloc, &tscale, &tangle); + BKE_tracking_stabilization_data_to_mat4(ibuf->x, ibuf->y, aspect, tloc, tscale, tangle, mat); + invert_m4(mat); + + if (filter == TRACKING_FILTER_NEAREST) + interpolation = nearest_interpolation; + else if (filter == TRACKING_FILTER_BILINEAR) + interpolation = bilinear_interpolation; + else if (filter == TRACKING_FILTER_BICUBIC) + interpolation = bicubic_interpolation; + else + /* fallback to default interpolation method */ + interpolation = nearest_interpolation; + + /* This function is only used for display in clip editor and + * sequencer only, which would only benefit of using threads + * here. + * + * But need to keep an eye on this if the function will be + * used in other cases. + */ +#pragma omp parallel for if (tmpibuf->y > 128) + for (j = 0; j < tmpibuf->y; j++) { + int i; + for (i = 0; i < tmpibuf->x; i++) { + float vec[3] = {i, j, 0.0f}; + + mul_v3_m4v3(vec, mat, vec); + + interpolation(ibuf, tmpibuf, vec[0], vec[1], i, j); + } + } + + if (tmpibuf->rect_float) + tmpibuf->userflags |= IB_RECT_INVALID; + + if (translation) + copy_v2_v2(translation, tloc); + + if (scale) + *scale = tscale; + + if (angle) + *angle = tangle; + + return tmpibuf; +} + +/* Get 4x4 transformation matrix which corresponds to + * stabilization data and used for easy coordinate + * transformation. + * + * NOTE: The reason it is 4x4 matrix is because it's + * used for OpenGL drawing directly. + */ +void BKE_tracking_stabilization_data_to_mat4(int width, int height, float aspect, + float translation[2], float scale, float angle, + float mat[4][4]) +{ + float translation_mat[4][4], rotation_mat[4][4], scale_mat[4][4], + center_mat[4][4], inv_center_mat[4][4], + aspect_mat[4][4], inv_aspect_mat[4][4]; + float scale_vector[3] = {scale, scale, scale}; + + unit_m4(translation_mat); + unit_m4(rotation_mat); + unit_m4(scale_mat); + unit_m4(center_mat); + unit_m4(aspect_mat); + + /* aspect ratio correction matrix */ + aspect_mat[0][0] = 1.0f / aspect; + invert_m4_m4(inv_aspect_mat, aspect_mat); + + /* image center as rotation center + * + * Rotation matrix is constructing in a way rotaion happens around image center, + * and it's matter of calculating trasnlation in a way, that applying translation + * after rotation would make it so rotation happens around median point of tracks + * used for translation stabilization. + */ + center_mat[3][0] = (float)width / 2.0f; + center_mat[3][1] = (float)height / 2.0f; + invert_m4_m4(inv_center_mat, center_mat); + + size_to_mat4(scale_mat, scale_vector); /* scale matrix */ + add_v2_v2(translation_mat[3], translation); /* translation matrix */ + rotate_m4(rotation_mat, 'Z', angle); /* rotation matrix */ + + /* compose transformation matrix */ + mul_serie_m4(mat, translation_mat, center_mat, aspect_mat, rotation_mat, inv_aspect_mat, + scale_mat, inv_center_mat, NULL); +} |