Support trapezoidal models for global motion
Adds functinoality for least-squares, RANSAC as well as encoding and
decoding with new constrained homographies that warp blocks to horizontal
and/or vertical trapezoids. This is for future experimentation. None
of the models are actually enabled in the code.
Change-Id: I1936018c6b11587d6fd83c3a2c63548cb641b33f
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 8331e8a..e454200 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -4495,10 +4495,16 @@
&global_motion_types_encodings[type]);
switch (type) {
case HOMOGRAPHY:
- aom_write_primitive_symmetric(
- w, (params->wmmat[6] >> GM_ROW3HOMO_PREC_DIFF), GM_ABS_ROW3HOMO_BITS);
- aom_write_primitive_symmetric(
- w, (params->wmmat[7] >> GM_ROW3HOMO_PREC_DIFF), GM_ABS_ROW3HOMO_BITS);
+ case HORTRAPEZOID:
+ case VERTRAPEZOID:
+ if (type != HORTRAPEZOID)
+ aom_write_primitive_symmetric(
+ w, (params->wmmat[6] >> GM_ROW3HOMO_PREC_DIFF),
+ GM_ABS_ROW3HOMO_BITS);
+ if (type != VERTRAPEZOID)
+ aom_write_primitive_symmetric(
+ w, (params->wmmat[7] >> GM_ROW3HOMO_PREC_DIFF),
+ GM_ABS_ROW3HOMO_BITS);
// fallthrough intended
case AFFINE:
case ROTZOOM:
@@ -4506,11 +4512,13 @@
w,
(params->wmmat[2] >> GM_ALPHA_PREC_DIFF) - (1 << GM_ALPHA_PREC_BITS),
GM_ABS_ALPHA_BITS);
- aom_write_primitive_symmetric(w, (params->wmmat[3] >> GM_ALPHA_PREC_DIFF),
- GM_ABS_ALPHA_BITS);
- if (type == AFFINE || type == HOMOGRAPHY) {
+ if (type != VERTRAPEZOID)
aom_write_primitive_symmetric(
- w, (params->wmmat[4] >> GM_ALPHA_PREC_DIFF), GM_ABS_ALPHA_BITS);
+ w, (params->wmmat[3] >> GM_ALPHA_PREC_DIFF), GM_ABS_ALPHA_BITS);
+ if (type >= AFFINE) {
+ if (type != HORTRAPEZOID)
+ aom_write_primitive_symmetric(
+ w, (params->wmmat[4] >> GM_ALPHA_PREC_DIFF), GM_ABS_ALPHA_BITS);
aom_write_primitive_symmetric(w,
(params->wmmat[5] >> GM_ALPHA_PREC_DIFF) -
(1 << GM_ALPHA_PREC_BITS),
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index e408278..5bc8e60 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -3007,7 +3007,7 @@
#if CONFIG_GLOBAL_MOTION
static int recode_loop_test_global_motion(AV1_COMP *cpi) {
- static const int min_blocks[TRANS_TYPES] = { 0, 60, 120, 180, 240 };
+ static const int min_blocks[TRANS_TYPES] = { 0, 60, 120, 180, 180, 180, 240 };
int i;
int recode = 0;
AV1_COMMON *const cm = &cpi->common;
diff --git a/av1/encoder/global_motion.c b/av1/encoder/global_motion.c
index 8259c45..a213d8d 100644
--- a/av1/encoder/global_motion.c
+++ b/av1/encoder/global_motion.c
@@ -104,7 +104,9 @@
wm->wmmat[2] = 1 << WARPEDMODEL_PREC_BITS;
wm->wmmat[3] = 0;
case ROTZOOM: wm->wmmat[4] = -wm->wmmat[3]; wm->wmmat[5] = wm->wmmat[2];
- case AFFINE: wm->wmmat[6] = wm->wmmat[7] = 0;
+ case AFFINE: wm->wmmat[6] = wm->wmmat[7] = 0; break;
+ case HORTRAPEZOID: wm->wmmat[6] = wm->wmmat[4] = 0; break;
+ case VERTRAPEZOID: wm->wmmat[7] = wm->wmmat[3] = 0; break;
case HOMOGRAPHY: break;
default: assert(0);
}
@@ -119,9 +121,12 @@
uint8_t *ref, int r_width, int r_height,
int r_stride, uint8_t *dst, int d_width,
int d_height, int d_stride, int n_refinements) {
+ static const int max_trans_model_params[TRANS_TYPES] = {
+ 0, 2, 4, 6, 8, 8, 8
+ };
const int border = ERRORADV_BORDER;
int i = 0, p;
- int n_params = n_trans_model_params[wmtype];
+ int n_params = max_trans_model_params[wmtype];
int32_t *param_mat = wm->wmmat;
double step_error;
int32_t step;
@@ -143,6 +148,9 @@
for (i = 0; i < n_refinements; i++, step >>= 1) {
for (p = 0; p < n_params; ++p) {
int step_dir = 0;
+ // Skip searches for parameters that are forced to be 0
+ if (wmtype == HORTRAPEZOID && (p == 4 || p == 6)) continue;
+ if (wmtype == VERTRAPEZOID && (p == 3 || p == 7)) continue;
param = param_mat + p;
curr_param = *param;
best_param = curr_param;
@@ -209,6 +217,8 @@
static INLINE RansacFunc get_ransac_type(TransformationType type) {
switch (type) {
case HOMOGRAPHY: return ransac_homography;
+ case HORTRAPEZOID: return ransac_hortrapezoid;
+ case VERTRAPEZOID: return ransac_vertrapezoid;
case AFFINE: return ransac_affine;
case ROTZOOM: return ransac_rotzoom;
case TRANSLATION: return ransac_translation;
diff --git a/av1/encoder/global_motion.h b/av1/encoder/global_motion.h
index 1f9b5a2..b3893b5 100644
--- a/av1/encoder/global_motion.h
+++ b/av1/encoder/global_motion.h
@@ -23,6 +23,8 @@
0.85, // Translation
0.75, // Rot zoom
0.65, // Affine
+ 0.65, // Hor Trapezoid
+ 0.65, // Ver Trapezoid
0.50, // Homography
};
diff --git a/av1/encoder/ransac.c b/av1/encoder/ransac.c
index 766a1cf..3b06710 100644
--- a/av1/encoder/ransac.c
+++ b/av1/encoder/ransac.c
@@ -22,6 +22,9 @@
#define MAX_DEGENERATE_ITER 10
#define MINPTS_MULTIPLIER 5
+#define INLIER_THRESHOLD 1.0
+#define MIN_TRIALS 20
+
////////////////////////////////////////////////////////////////////////////////
// ransac
typedef int (*IsDegenerateFunc)(double *p);
@@ -76,6 +79,42 @@
}
}
+static void project_points_double_hortrapezoid(double *mat, double *points,
+ double *proj, const int n,
+ const int stride_points,
+ const int stride_proj) {
+ int i;
+ double x, y, Z, Z_inv;
+ for (i = 0; i < n; ++i) {
+ x = *(points++), y = *(points++);
+ Z_inv = mat[7] * y + 1;
+ assert(fabs(Z_inv) > 0.000001);
+ Z = 1. / Z_inv;
+ *(proj++) = (mat[2] * x + mat[3] * y + mat[0]) * Z;
+ *(proj++) = (mat[5] * y + mat[1]) * Z;
+ points += stride_points - 2;
+ proj += stride_proj - 2;
+ }
+}
+
+static void project_points_double_vertrapezoid(double *mat, double *points,
+ double *proj, const int n,
+ const int stride_points,
+ const int stride_proj) {
+ int i;
+ double x, y, Z, Z_inv;
+ for (i = 0; i < n; ++i) {
+ x = *(points++), y = *(points++);
+ Z_inv = mat[6] * x + 1;
+ assert(fabs(Z_inv) > 0.000001);
+ Z = 1. / Z_inv;
+ *(proj++) = (mat[2] * x + mat[0]) * Z;
+ *(proj++) = (mat[4] * x + mat[5] * y + mat[1]) * Z;
+ points += stride_points - 2;
+ proj += stride_proj - 2;
+ }
+}
+
static void project_points_double_homography(double *mat, double *points,
double *proj, const int n,
const int stride_points,
@@ -121,10 +160,8 @@
IsDegenerateFunc is_degenerate,
FindTransformationFunc find_transformation,
ProjectPointsDoubleFunc projectpoints) {
- static const double inlier_threshold = 1.0;
static const double PROBABILITY_REQUIRED = 0.9;
static const double EPS = 1e-12;
- static const int MIN_TRIALS = 20;
int N = 10000, trial_count = 0;
int i;
@@ -223,7 +260,7 @@
double dy = image1_coord[i * 2 + 1] - corners2[i * 2 + 1];
double distance = sqrt(dx * dx + dy * dy);
- inlier_mask[i] = distance < inlier_threshold;
+ inlier_mask[i] = distance < INLIER_THRESHOLD;
if (inlier_mask[i]) {
inlier_set1[num_inliers * 2] = corners1[i * 2];
inlier_set1[num_inliers * 2 + 1] = corners1[i * 2 + 1];
@@ -332,3 +369,19 @@
best_params, 4, is_degenerate_homography, find_homography,
project_points_double_homography);
}
+
+int ransac_hortrapezoid(int *matched_points, int npoints,
+ int *number_of_inliers, int *best_inlier_mask,
+ double *best_params) {
+ return ransac(matched_points, npoints, number_of_inliers, best_inlier_mask,
+ best_params, 4, is_degenerate_homography, find_hortrapezoid,
+ project_points_double_hortrapezoid);
+}
+
+int ransac_vertrapezoid(int *matched_points, int npoints,
+ int *number_of_inliers, int *best_inlier_mask,
+ double *best_params) {
+ return ransac(matched_points, npoints, number_of_inliers, best_inlier_mask,
+ best_params, 4, is_degenerate_homography, find_vertrapezoid,
+ project_points_double_vertrapezoid);
+}
diff --git a/av1/encoder/ransac.h b/av1/encoder/ransac.h
index c45603c..91e6acb 100644
--- a/av1/encoder/ransac.h
+++ b/av1/encoder/ransac.h
@@ -24,11 +24,17 @@
double *best_params);
/* Each of these functions fits a motion model from a set of
-corresponding points in 2 frames using RANSAC.*/
+ corresponding points in 2 frames using RANSAC. */
int ransac_homography(int *matched_points, int npoints, int *number_of_inliers,
int *best_inlier_indices, double *best_params);
int ransac_affine(int *matched_points, int npoints, int *number_of_inliers,
int *best_inlier_indices, double *best_params);
+int ransac_hortrapezoid(int *matched_points, int npoints,
+ int *number_of_inliers, int *best_inlier_indices,
+ double *best_params);
+int ransac_vertrapezoid(int *matched_points, int npoints,
+ int *number_of_inliers, int *best_inlier_indices,
+ double *best_params);
int ransac_rotzoom(int *matched_points, int npoints, int *number_of_inliers,
int *best_inlier_indices, double *best_params);
int ransac_translation(int *matched_points, int npoints, int *number_of_inliers,
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index 8717c0c..52a25bf 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -4911,10 +4911,13 @@
#if CONFIG_GLOBAL_MOTION
static int GLOBAL_MOTION_RATE(const AV1_COMP *const cpi, int ref) {
- static const int gm_amortization_blks[TRANS_TYPES] = { 4, 6, 8, 10, 12 };
+ static const int gm_amortization_blks[TRANS_TYPES] = {
+ 4, 6, 8, 10, 10, 10, 12
+ };
static const int gm_params_cost[TRANS_TYPES] = {
- GM_IDENTITY_BITS, GM_TRANSLATION_BITS, GM_ROTZOOM_BITS,
- GM_AFFINE_BITS, GM_HOMOGRAPHY_BITS,
+ GM_IDENTITY_BITS, GM_TRANSLATION_BITS, GM_ROTZOOM_BITS,
+ GM_AFFINE_BITS, GM_HORTRAPEZOID_BITS, GM_VERTRAPEZOID_BITS,
+ GM_HOMOGRAPHY_BITS,
};
const WarpedMotionParams *gm = &cpi->common.global_motion[(ref)];
assert(gm->wmtype < GLOBAL_TRANS_TYPES);