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:
Diffstat (limited to 'source/blender/blenkernel/intern/tracking_stabilize.c')
-rw-r--r--source/blender/blenkernel/intern/tracking_stabilize.c565
1 files changed, 365 insertions, 200 deletions
diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c
index 4d72d851ae9..df42f253fdf 100644
--- a/source/blender/blenkernel/intern/tracking_stabilize.c
+++ b/source/blender/blenkernel/intern/tracking_stabilize.c
@@ -452,6 +452,29 @@ static MovieTrackingMarker *get_tracking_data_point(
}
}
+
+/* Define the reference point for rotation/scale measurement and compensation.
+ * The stabilizator works by assuming the image was distorted by a affine linear
+ * transform, i.e. it was rotated and stretched around this reference point
+ * (pivot point) and then shifted laterally. Any scale and orientation changes
+ * will be picked up relative to this point. And later the image will be
+ * stabilized by rotating around this point. The result can only be as
+ * accurate as this pivot point actually matches the real rotation center
+ * of the actual movements. Thus any scheme to define a pivot point is
+ * always guesswork.
+ *
+ * As a simple default, we use the weighted average of the location markers
+ * of the current frame as pivot point. TODO It is planned to add further
+ * options, like e.g. anchoring the pivot point at the canvas. Moreover,
+ * it is planned to allow for a user controllable offset.
+ */
+static void setup_pivot(const float ref_pos[2], float r_pivot[2])
+{
+ zero_v2(r_pivot); /* TODO: add an animated offset position here. */
+ add_v2_v2(r_pivot, ref_pos);
+}
+
+
/* Calculate the contribution of a single track at the time position (frame) of
* the given marker. Each track has a local reference frame, which is as close
* as possible to the global anchor_frame. Thus the translation contribution is
@@ -508,22 +531,21 @@ static void translation_contribution(TrackStabilizationBase *track_ref,
* in the same framework, we average the scales as logarithms.
*
* aspect is a total aspect ratio of the undistorted image (includes fame and
- * pixel aspect).
+ * pixel aspect). The function returns a quality factor, which can be used
+ * to damp the contributions of points in close proximity to the pivot point,
+ * since such contributions might be dominated by rounding errors and thus
+ * poison the calculated average. When the quality factor goes towards zero,
+ * the weight of this contribution should be reduced accordingly.
*/
-static void rotation_contribution(TrackStabilizationBase *track_ref,
- MovieTrackingMarker *marker,
- float aspect,
- float target_pos[2],
- float averaged_translation_contribution[2],
- float *result_angle,
- float *result_scale)
+static float rotation_contribution(TrackStabilizationBase *track_ref,
+ MovieTrackingMarker *marker,
+ const float aspect,
+ const float pivot[2],
+ float *result_angle,
+ float *result_scale)
{
- float len;
+ float len, quality;
float pos[2];
- float pivot[2];
- copy_v2_fl(pivot, 0.5f); /* Use center of frame as hard wired pivot. */
- add_v2_v2(pivot, averaged_translation_contribution);
- sub_v2_v2(pivot, target_pos);
sub_v2_v2v2(pos, marker->pos, pivot);
pos[0] *= aspect;
@@ -531,9 +553,47 @@ static void rotation_contribution(TrackStabilizationBase *track_ref,
*result_angle = atan2f(pos[1],pos[0]);
- len = len_v2(pos) + SCALE_ERROR_LIMIT_BIAS;
+ len = len_v2(pos);
+
+ /* prevent points very close to the pivot point from poisoning the result */
+ quality = 1 - expf(-len*len / SCALE_ERROR_LIMIT_BIAS*SCALE_ERROR_LIMIT_BIAS);
+ len += SCALE_ERROR_LIMIT_BIAS;
+
*result_scale = len * track_ref->stabilization_scale_base;
BLI_assert(0.0 < *result_scale);
+
+ return quality;
+}
+
+
+/* Workaround to allow for rotation around an arbitrary pivot point.
+ * Currently, the public API functions do not support this flexibility.
+ * Rather, rotation will always be applied around a fixed origin.
+ * As a workaround, we shift the image after rotation to match the
+ * desired rotation centre. And since this offset needs to be applied
+ * after the rotation and scaling, we can collapse it with the
+ * translation compensation, which is also a lateral shift (offset).
+ * The offset to apply is intended_pivot - rotated_pivot
+ */
+static void compensate_rotation_center(const int size, float aspect,
+ const float angle,
+ const float scale,
+ const float pivot[2],
+ float result_translation[2])
+{
+ const float origin[2] = {0.5f*aspect*size, 0.5f*size};
+ float intended_pivot[2], rotated_pivot[2];
+ float rotation_mat[2][2];
+
+ copy_v2_v2(intended_pivot, pivot);
+ copy_v2_v2(rotated_pivot, pivot);
+ rotate_m2(rotation_mat, +angle);
+ sub_v2_v2(rotated_pivot, origin);
+ mul_m2v2(rotation_mat, rotated_pivot);
+ mul_v2_fl(rotated_pivot, scale);
+ add_v2_v2(rotated_pivot, origin);
+ add_v2_v2(result_translation, intended_pivot);
+ sub_v2_v2(result_translation, rotated_pivot);
}
@@ -553,6 +613,7 @@ static bool average_track_contributions(StabContext *ctx,
int framenr,
float aspect,
float r_translation[2],
+ float r_pivot[2],
float *r_angle,
float *r_scale_step)
{
@@ -561,12 +622,15 @@ static bool average_track_contributions(StabContext *ctx,
MovieTrackingTrack *track;
MovieTracking *tracking = ctx->tracking;
MovieTrackingStabilization *stab = &tracking->stabilization;
+ float ref_pos[2];
BLI_assert(stab->flag & TRACKING_2D_STABILIZATION);
zero_v2(r_translation);
*r_scale_step = 0.0f; /* logarithm */
*r_angle = 0.0f;
+ zero_v2(ref_pos);
+
ok = false;
weight_sum = 0.0f;
for (track = tracking->tracks.first; track; track = track->next) {
@@ -586,8 +650,10 @@ static bool average_track_contributions(StabContext *ctx,
float offset[2];
weight_sum += weight;
translation_contribution(stabilization_base, marker, offset);
- mul_v2_fl(offset, weight);
- add_v2_v2(r_translation, offset);
+ r_translation[0] += weight * offset[0];
+ r_translation[1] += weight * offset[1];
+ ref_pos[0] += weight * marker->pos[0];
+ ref_pos[1] += weight * marker->pos[1];
ok |= (weight_sum > EPSILON_WEIGHT);
}
}
@@ -596,8 +662,11 @@ static bool average_track_contributions(StabContext *ctx,
return false;
}
+ ref_pos[0] /= weight_sum;
+ ref_pos[1] /= weight_sum;
r_translation[0] /= weight_sum;
r_translation[1] /= weight_sum;
+ setup_pivot(ref_pos, r_pivot);
if (!(stab->flag & TRACKING_STABILIZE_ROTATION)) {
return ok;
@@ -619,17 +688,15 @@ static bool average_track_contributions(StabContext *ctx,
TrackStabilizationBase *stabilization_base =
access_stabilization_baseline_data(ctx, track);
BLI_assert(stabilization_base != NULL);
- float rotation, scale;
- float target_pos[2];
+ float rotation, scale, quality;
+ quality = rotation_contribution(stabilization_base,
+ marker,
+ aspect,
+ r_pivot,
+ &rotation,
+ &scale);
+ weight *= quality;
weight_sum += weight;
- get_animated_target_pos(ctx, framenr, target_pos);
- rotation_contribution(stabilization_base,
- marker,
- aspect,
- target_pos,
- r_translation,
- &rotation,
- &scale);
*r_angle += rotation * weight;
if (stab->flag & TRACKING_STABILIZE_SCALE) {
*r_scale_step += logf(scale) * weight;
@@ -656,6 +723,75 @@ static bool average_track_contributions(StabContext *ctx,
}
+/* Calculate weight center of location tracks for given frame.
+ * This function performs similar calculations as average_track_contributions(),
+ * but does not require the tracks to be initialized for stabilisation. Moreover,
+ * when there is no usable tracking data for the given frame number, data from
+ * a neighbouring frame is used. Thus this function can be used to calculate
+ * a starting point on initialization.
+ */
+static void average_marker_positions(StabContext *ctx, int framenr, float r_ref_pos[2])
+{
+ bool ok = false;
+ float weight_sum;
+ MovieTrackingTrack *track;
+ MovieTracking *tracking = ctx->tracking;
+
+ zero_v2(r_ref_pos);
+ weight_sum = 0.0f;
+ for (track = tracking->tracks.first; track; track = track->next) {
+ if (track->flag & TRACK_USE_2D_STAB) {
+ float weight = 0.0f;
+ MovieTrackingMarker *marker =
+ get_tracking_data_point(ctx, track, framenr, &weight);
+ if (marker) {
+ weight_sum += weight;
+ r_ref_pos[0] += weight * marker->pos[0];
+ r_ref_pos[1] += weight * marker->pos[1];
+ ok |= (weight_sum > EPSILON_WEIGHT);
+ }
+ }
+ }
+ if (ok) {
+ r_ref_pos[0] /= weight_sum;
+ r_ref_pos[1] /= weight_sum;
+ } else {
+ /* No usable tracking data on any track on this frame.
+ * Use data from neighbouring frames to extrapolate...
+ */
+ int next_lower = MINAFRAME;
+ int next_higher = MAXFRAME;
+ use_values_from_fcurves(ctx, true);
+ for (track = tracking->tracks.first; track; track = track->next) {
+ /* Note: we deliberately do not care if this track
+ * is already initialized for stabilisation */
+ if (track->flag & TRACK_USE_2D_STAB) {
+ int startpoint = search_closest_marker_index(track, framenr);
+ retrieve_next_higher_usable_frame(ctx,
+ track,
+ startpoint,
+ framenr,
+ &next_higher);
+ retrieve_next_lower_usable_frame(ctx,
+ track,
+ startpoint,
+ framenr,
+ &next_lower);
+ }
+ }
+ if (next_lower >= MINFRAME) {
+ /* use next usable frame to the left.
+ * Also default to this frame when we're in a gap */
+ average_marker_positions(ctx, next_lower, r_ref_pos);
+
+ } else if (next_higher < MAXFRAME) {
+ average_marker_positions(ctx, next_higher, r_ref_pos);
+ }
+ use_values_from_fcurves(ctx, false);
+ }
+}
+
+
/* Linear interpolation of data retrieved at two measurement points.
* This function is used to fill gaps in the middle of the covered area,
* at frames without any usable tracks for stabilization.
@@ -670,8 +806,9 @@ static bool interpolate_averaged_track_contributions(StabContext *ctx,
int framenr,
int frame_a,
int frame_b,
- float aspect,
- float translation[2],
+ const float aspect,
+ float r_translation[2],
+ float r_pivot[2],
float *r_angle,
float *r_scale_step)
{
@@ -679,6 +816,7 @@ static bool interpolate_averaged_track_contributions(StabContext *ctx,
float trans_a[2], trans_b[2];
float angle_a, angle_b;
float scale_a, scale_b;
+ float pivot_a[2], pivot_b[2];
bool success = false;
BLI_assert(frame_a <= frame_b);
@@ -688,16 +826,17 @@ static bool interpolate_averaged_track_contributions(StabContext *ctx,
t = ((float)framenr - frame_a) / (frame_b - frame_a);
s = 1.0f - t;
- success = average_track_contributions(ctx, frame_a, aspect, trans_a, &angle_a, &scale_a);
+ success = average_track_contributions(ctx, frame_a, aspect, trans_a, pivot_a, &angle_a, &scale_a);
if (!success) {
return false;
}
- success = average_track_contributions(ctx, frame_b, aspect, trans_b, &angle_b, &scale_b);
+ success = average_track_contributions(ctx, frame_b, aspect, trans_b, pivot_b, &angle_b, &scale_b);
if (!success) {
return false;
}
- interp_v2_v2v2(translation, trans_a, trans_b, t);
+ interp_v2_v2v2(r_translation, trans_a, trans_b, t);
+ interp_v2_v2v2(r_pivot, pivot_a, pivot_b, t);
*r_scale_step = s * scale_a + t * scale_b;
*r_angle = s * angle_a + t * angle_b;
return true;
@@ -802,13 +941,12 @@ static void initialize_track_for_stabilization(StabContext *ctx,
MovieTrackingTrack *track,
int reference_frame,
float aspect,
- const float target_pos[2],
const float average_translation[2],
- float average_angle,
- float average_scale_step)
+ const float pivot[2],
+ const float average_angle,
+ const float average_scale_step)
{
float pos[2], angle, len;
- float pivot[2];
TrackStabilizationBase *local_data =
access_stabilization_baseline_data(ctx, track);
MovieTrackingMarker *marker =
@@ -825,9 +963,6 @@ static void initialize_track_for_stabilization(StabContext *ctx,
marker->pos);
/* Per track baseline value for rotation. */
- copy_v2_fl(pivot, 0.5f); /* Use center of frame as hard wired pivot. */
- add_v2_v2(pivot, average_translation);
- sub_v2_v2(pivot, target_pos);
sub_v2_v2v2(pos, marker->pos, pivot);
pos[0] *= aspect;
@@ -855,10 +990,9 @@ static void initialize_all_tracks(StabContext *ctx, float aspect)
*/
int reference_frame = tracking->stabilization.anchor_frame;
float average_angle=0, average_scale_step=0;
- float average_translation[2];
- float target_pos_at_ref_frame[2];
- zero_v2(target_pos_at_ref_frame);
+ float average_translation[2], average_pos[2], pivot[2];
zero_v2(average_translation);
+ zero_v2(pivot);
/* Initialize private working data. */
for (track = tracking->tracks.first; track != NULL; track = track->next) {
@@ -891,6 +1025,10 @@ static void initialize_all_tracks(StabContext *ctx, float aspect)
goto cleanup;
}
+ /* starting point for pivot, before having initialized any track */
+ average_marker_positions(ctx, reference_frame, average_pos);
+ setup_pivot(average_pos, pivot);
+
for (i = 0; i < track_cnt; ++i) {
track = order[i].data;
if (reference_frame != order[i].reference_frame) {
@@ -899,18 +1037,16 @@ static void initialize_all_tracks(StabContext *ctx, float aspect)
reference_frame,
aspect,
average_translation,
+ pivot,
&average_angle,
&average_scale_step);
- get_animated_target_pos(ctx,
- reference_frame,
- target_pos_at_ref_frame);
}
initialize_track_for_stabilization(ctx,
track,
reference_frame,
aspect,
- target_pos_at_ref_frame,
average_translation,
+ pivot,
average_angle,
average_scale_step);
}
@@ -936,6 +1072,7 @@ static bool stabilization_determine_offset_for_frame(StabContext *ctx,
int framenr,
float aspect,
float r_translation[2],
+ float r_pivot[2],
float *r_angle,
float *r_scale_step)
{
@@ -953,6 +1090,7 @@ static bool stabilization_determine_offset_for_frame(StabContext *ctx,
framenr,
aspect,
r_translation,
+ r_pivot,
r_angle,
r_scale_step);
if (!success) {
@@ -971,6 +1109,7 @@ static bool stabilization_determine_offset_for_frame(StabContext *ctx,
next_higher,
aspect,
r_translation,
+ r_pivot,
r_angle,
r_scale_step);
}
@@ -982,6 +1121,7 @@ static bool stabilization_determine_offset_for_frame(StabContext *ctx,
next_higher,
aspect,
r_translation,
+ r_pivot,
r_angle,
r_scale_step);
}
@@ -991,6 +1131,7 @@ static bool stabilization_determine_offset_for_frame(StabContext *ctx,
next_lower,
aspect,
r_translation,
+ r_pivot,
r_angle,
r_scale_step);
}
@@ -1017,16 +1158,17 @@ static void stabilization_calculate_data(StabContext *ctx,
bool do_compensate,
float scale_step,
float r_translation[2],
+ float r_pivot[2],
float *r_scale,
float *r_angle)
{
- float target_pos[2];
+ float target_pos[2], target_scale;
float scaleinf = get_animated_scaleinf(ctx, framenr);
- *r_scale = (get_animated_target_scale(ctx,framenr) - 1.0f) * scaleinf + 1.0f;
-
if (ctx->stab->flag & TRACKING_STABILIZE_SCALE) {
- *r_scale *= expf(scale_step * scaleinf); /* Averaged in log scale */
+ *r_scale = expf(scale_step * scaleinf); /* Averaged in log scale */
+ } else {
+ *r_scale = 1.0f;
}
mul_v2_fl(r_translation, get_animated_locinf(ctx, framenr));
@@ -1039,10 +1181,17 @@ static void stabilization_calculate_data(StabContext *ctx,
get_animated_target_pos(ctx, framenr, target_pos);
sub_v2_v2(r_translation, target_pos);
*r_angle -= get_animated_target_rot(ctx,framenr);
+ target_scale = get_animated_target_scale(ctx,framenr);
+ if (target_scale != 0.0f) {
+ *r_scale /= target_scale;
+ /* target_scale is an expected/intended reference zoom value */
+ }
/* Convert from relative to absolute coordinates, square pixels. */
r_translation[0] *= (float)size * aspect;
r_translation[1] *= (float)size;
+ r_pivot[0] *= (float)size * aspect;
+ r_pivot[1] *= (float)size;
/* Output measured data, or inverse of the measured values for
* compensation?
@@ -1056,25 +1205,61 @@ static void stabilization_calculate_data(StabContext *ctx,
}
}
+static void stabilization_data_to_mat4(float pixel_aspect,
+ const float pivot[2],
+ const float translation[2],
+ float scale,
+ float angle,
+ float r_mat[4][4])
+{
+ float translation_mat[4][4], rotation_mat[4][4], scale_mat[4][4],
+ pivot_mat[4][4], inv_pivot_mat[4][4],
+ aspect_mat[4][4], inv_aspect_mat[4][4];
+ float scale_vector[3] = {scale, scale, 1.0f};
-/* Determine the inner part of the frame, which is always safe to use.
- * When enlarging the image by the inverse of this factor, any black areas
- * appearing due to frame translation and rotation will be removed.
+ unit_m4(translation_mat);
+ unit_m4(rotation_mat);
+ unit_m4(scale_mat);
+ unit_m4(aspect_mat);
+ unit_m4(pivot_mat);
+ unit_m4(inv_pivot_mat);
+
+ /* aspect ratio correction matrix */
+ aspect_mat[0][0] /= pixel_aspect;
+ invert_m4_m4(inv_aspect_mat, aspect_mat);
+
+ add_v2_v2(pivot_mat[3], pivot);
+ sub_v2_v2(inv_pivot_mat[3], pivot);
+
+ 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_m4_series(r_mat, aspect_mat, translation_mat,
+ pivot_mat, scale_mat, rotation_mat, inv_pivot_mat,
+ inv_aspect_mat);
+}
+
+/* Calculate scale factor necessary to eliminate black image areas
+ * caused by the compensating movements of the stabilizator.
+ * This function visits every frame where stabilisation data is
+ * available and determines the factor for this frame. The overall
+ * largest factor found is returned as result.
*
- * NOTE: When calling this function, basic initialization of tracks must be
- * done already
+ * NOTE: all tracks need to be initialized before calling this function.
*/
-static void stabilization_determine_safe_image_area(StabContext *ctx,
- int size,
- float image_aspect)
+static float calculate_autoscale_factor(StabContext *ctx,
+ int size,
+ float aspect)
{
MovieTrackingStabilization *stab = ctx->stab;
float pixel_aspect = ctx->tracking->camera.pixel_aspect;
+ int height = size, width = aspect*size;
int sfra = INT_MAX, efra = INT_MIN, cfra;
float scale = 1.0f, scale_step = 0.0f;
MovieTrackingTrack *track;
- stab->scale = 1.0f;
/* Calculate maximal frame range of tracks where stabilization is active. */
for (track = ctx->tracking->tracks.first; track; track = track->next) {
@@ -1089,124 +1274,117 @@ static void stabilization_determine_safe_image_area(StabContext *ctx,
}
}
- /* For every frame we calculate scale factor needed to eliminate black border area
- * and choose largest scale factor as final one.
- */
+ use_values_from_fcurves(ctx, true);
for (cfra = sfra; cfra <= efra; cfra++) {
- float translation[2], angle, tmp_scale;
- int i;
+ float translation[2], pivot[2], angle, tmp_scale;
float mat[4][4];
- float points[4][2] = {{0.0f, 0.0f},
- {0.0f, size},
- {image_aspect * size, size},
- {image_aspect * size, 0.0f}};
- float si, co;
- bool do_compensate = true;
-
+ const float points[4][2] = {{0.0f, 0.0f},
+ {0.0f, height},
+ {width, height},
+ {width, 0.0f}};
+ const bool do_compensate = true;
+ /* Calculate stabilization parameters for the current frame. */
stabilization_determine_offset_for_frame(ctx,
cfra,
- image_aspect,
+ aspect,
translation,
+ pivot,
&angle,
&scale_step);
stabilization_calculate_data(ctx,
cfra,
size,
- image_aspect,
+ aspect,
do_compensate,
scale_step,
translation,
+ pivot,
&tmp_scale,
&angle);
-
- BKE_tracking_stabilization_data_to_mat4(size * image_aspect,
- size,
- pixel_aspect,
- translation,
- 1.0f,
- angle,
- mat);
-
- si = sinf(angle);
- co = cosf(angle);
-
+ /* Compose transformation matrix. */
+ /* NOTE: Here we operate in NON-COMPENSATED coordinates, meaning we have
+ * to construct transformation matrix using proper pivot point.
+ * Compensation for that will happen later on.
+ */
+ stabilization_data_to_mat4(pixel_aspect,
+ pivot,
+ translation,
+ tmp_scale,
+ angle,
+ mat);
/* Investigate the transformed border lines for this frame;
* find out, where it cuts the original frame.
*/
- 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_v2_v2(a, points[i]);
- copy_v2_v2(b, points[(i + 1) % 4]);
- a[2] = b[2] = 0.0f;
-
- 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 rot_dx[4][2] = {{1.0f, 0.0f},
- {0.0f, -1.0f},
- {-1.0f, 0.0f},
- {0.0f, 1.0f}};
- const float rot_dy[4][2] = {{0.0f, 1.0f},
- {1.0f, 0.0f},
- {0.0f, -1.0f},
- {-1.0f, 0.0f}};
-
- float dx = translation[0] * rot_dx[j][0] +
- translation[1] * rot_dx[j][1],
- dy = translation[0] * rot_dy[j][0] +
- translation[1] * rot_dy[j][1];
-
- float w, h, E, F, G, H, I, J, K, S;
-
- if (j % 2) {
- w = (float)size / 2.0f;
- h = image_aspect*size / 2.0f;
- }
- else {
- w = image_aspect*size / 2.0f;
- h = (float)size / 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 = (dx * I + dy * J + K) / (-w * I - h * J);
-
- scale = min_ff(scale, S);
+ for (int edge_index = 0; edge_index < 4; edge_index++) {
+ /* Calculate coordinates of stabilized frame edge points.
+ * Use matrix multiplication here so we operate in homogeneous
+ * coordinates.
+ */
+ float stable_edge_p1[3], stable_edge_p2[3];
+ copy_v2_v2(stable_edge_p1, points[edge_index]);
+ copy_v2_v2(stable_edge_p2, points[(edge_index + 1) % 4]);
+ stable_edge_p1[2] = stable_edge_p2[2] = 0.0f;
+ mul_m4_v3(mat, stable_edge_p1);
+ mul_m4_v3(mat, stable_edge_p2);
+ /* Now we iterate over all original frame corners (we call them
+ * 'point' here) to see if there's black area between stabilized
+ * frame edge and original point.
+ */
+ for (int point_index = 0; point_index < 4; point_index++) {
+ const float point[3] = {points[point_index][0],
+ points[point_index][1],
+ 0.0f};
+ /* Calculate vector which goes from first edge point to
+ * second one.
+ */
+ float stable_edge_vec[3];
+ sub_v3_v3v3(stable_edge_vec, stable_edge_p2, stable_edge_p1);
+ /* Calculate vector which connects current frame point to
+ * first edge point.
+ */
+ float point_to_edge_start_vec[3];
+ sub_v3_v3v3(point_to_edge_start_vec, point, stable_edge_p1);
+ /* Use this two vectors to check whether frame point is inside
+ * of the stabilized frame or not.
+ * If the point is inside, there is no black area happening
+ * and no scaling required for it.
+ */
+ if (cross_v2v2(stable_edge_vec, point_to_edge_start_vec) >= 0.0f) {
+ /* We are scaling around motion-compensated pivot point. */
+ float scale_pivot[2];
+ add_v2_v2v2(scale_pivot, pivot, translation);
+ /* Calculate line which goes via `point` and parallel to
+ * the stabilized frame edge. This line is coming via
+ * `point` and `point2` at the end.
+ */
+ float point2[2];
+ add_v2_v2v2(point2, point, stable_edge_vec);
+ /* Calculate actual distance between pivot point and
+ * the stabilized frame edge. Then calculate distance
+ * between pivot point and line which goes via actual
+ * corner and is parallel to the edge.
+ *
+ * Dividing one by another will give us required scale
+ * factor to get rid of black areas.
+ */
+ float real_dist = dist_to_line_v2(scale_pivot,
+ stable_edge_p1,
+ stable_edge_p2);
+ float required_dist = dist_to_line_v2(scale_pivot,
+ point,
+ point2);
+ const float S = required_dist / real_dist;
+ scale = max_ff(scale, S);
}
}
}
}
-
- stab->scale = scale;
-
if (stab->maxscale > 0.0f) {
- stab->scale = max_ff(stab->scale, 1.0f / stab->maxscale);
+ scale = min_ff(scale, stab->maxscale);
}
+ use_values_from_fcurves(ctx, false);
+
+ return scale;
}
@@ -1218,19 +1396,14 @@ static void stabilization_determine_safe_image_area(StabContext *ctx,
* turns out to be tricky, hard to maintain and generally not worth the
* effort. Thus we'll re-initialize on every frame.
*/
-static StabContext *init_stabilizer(MovieClip *clip, int width, int height)
+static StabContext *init_stabilizer(MovieClip *clip, int size, float aspect)
{
- MovieTracking *tracking = &clip->tracking;
- MovieTrackingStabilization *stab = &tracking->stabilization;
- float pixel_aspect = tracking->camera.pixel_aspect;
- float aspect = (float)width * pixel_aspect / height;
- int size = height;
-
StabContext *ctx = initialize_stabilization_working_context(clip);
BLI_assert(ctx != NULL);
initialize_all_tracks(ctx, aspect);
- if (stab->flag & TRACKING_AUTOSCALE) {
- stabilization_determine_safe_image_area(ctx, size, aspect);
+ if (ctx->stab->flag & TRACKING_AUTOSCALE) {
+ ctx->stab->scale = 1.0;
+ ctx->stab->scale = calculate_autoscale_factor(ctx, size, aspect);
}
/* By default, just use values for the global current frame. */
use_values_from_fcurves(ctx, false);
@@ -1275,9 +1448,10 @@ void BKE_tracking_stabilization_data_get(MovieClip *clip,
float pixel_aspect = tracking->camera.pixel_aspect;
float aspect = (float)width * pixel_aspect / height;
int size = height;
+ float pivot[2];
if (enabled) {
- ctx = init_stabilizer(clip, width, height);
+ ctx = init_stabilizer(clip, size, aspect);
}
if (enabled &&
@@ -1285,6 +1459,7 @@ void BKE_tracking_stabilization_data_get(MovieClip *clip,
framenr,
aspect,
translation,
+ pivot,
angle,
&scale_step))
{
@@ -1295,8 +1470,15 @@ void BKE_tracking_stabilization_data_get(MovieClip *clip,
do_compensate,
scale_step,
translation,
+ pivot,
scale,
angle);
+ compensate_rotation_center(size,
+ aspect,
+ *angle,
+ *scale,
+ pivot,
+ translation);
}
else {
zero_v2(translation);
@@ -1426,47 +1608,30 @@ ImBuf *BKE_tracking_stabilize_frame(MovieClip *clip,
void BKE_tracking_stabilization_data_to_mat4(int buffer_width,
int buffer_height,
float pixel_aspect,
- float translation[2], float scale, float angle,
- float mat[4][4])
+ float translation[2],
+ float scale,
+ float angle,
+ float r_mat[4][4])
{
- float translation_mat[4][4], rotation_mat[4][4], scale_mat[4][4],
- pivot_mat[4][4], inv_pivot_mat[4][4],
- aspect_mat[4][4], inv_aspect_mat[4][4];
- float scale_vector[3] = {scale, scale, 1.0f};
-
- float pivot[2]; /* XXX this should be a parameter, it is part of the stabilization data */
-
- /* Use the motion compensated image center as rotation center.
- * This is not 100% correct, but reflects the way the rotation data was
- * measured. Actually we'd need a way to find a good pivot, and use that
- * both for averaging and for compensation.
+ /* Since we cannot receive the real pivot point coordinates (API limitation),
+ * we perform the rotation/scale around the center of frame.
+ * Then we correct by an additional shift, which was calculated in
+ * compensate_rotation_center() and "sneaked in" as additional offset
+ * in the translation parameter. This works, since translation needs to be
+ * applied after rotation/scale anyway. Thus effectively the image gets
+ * rotated around the desired pivot point
*/
/* TODO(sergey) pivot shouldn't be calculated here, rather received
* as a parameter.
*/
- pivot[0] = pixel_aspect * buffer_width / 2.0f - translation[0];
- pivot[1] = (float)buffer_height / 2.0f - translation[1];
-
- unit_m4(translation_mat);
- unit_m4(rotation_mat);
- unit_m4(scale_mat);
- unit_m4(aspect_mat);
- unit_m4(pivot_mat);
- unit_m4(inv_pivot_mat);
-
- /* aspect ratio correction matrix */
- aspect_mat[0][0] /= pixel_aspect;
- invert_m4_m4(inv_aspect_mat, aspect_mat);
-
- add_v2_v2(pivot_mat[3], pivot);
- sub_v2_v2(inv_pivot_mat[3], pivot);
-
- 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_m4_series(mat, aspect_mat, translation_mat,
- pivot_mat, scale_mat, rotation_mat, inv_pivot_mat,
- inv_aspect_mat);
+ float pivot[2];
+ pivot[0] = 0.5f * pixel_aspect * buffer_width;
+ pivot[1] = 0.5f * buffer_height;
+ /* Compose transformation matrix. */
+ stabilization_data_to_mat4(pixel_aspect,
+ pivot,
+ translation,
+ scale,
+ angle,
+ r_mat);
}