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/common/entropymv.c b/av1/common/entropymv.c
index 866bdb9..91d8d94 100644
--- a/av1/common/entropymv.c
+++ b/av1/common/entropymv.c
@@ -128,30 +128,41 @@
 };
 
 #if CONFIG_GLOBAL_MOTION
-#if GLOBAL_TRANS_TYPES == 5
+#if GLOBAL_TRANS_TYPES == 7  // All models
 const aom_tree_index av1_global_motion_types_tree[TREE_SIZE(
-    GLOBAL_TRANS_TYPES)] = { -IDENTITY, 2, -TRANSLATION, 4,
-                             -ROTZOOM,  6, -AFFINE,      -HOMOGRAPHY };
+    GLOBAL_TRANS_TYPES)] = { -IDENTITY,   2,  -TRANSLATION,  4,
+                             -ROTZOOM,    6,  -AFFINE,       8,
+                             -HOMOGRAPHY, 10, -HORTRAPEZOID, -VERTRAPEZOID };
 
 static const aom_prob default_global_motion_types_prob[GLOBAL_TRANS_TYPES - 1] =
-    { 224, 128, 192, 192 };
+    { 224, 128, 192, 192, 32, 128 };
 
-#elif GLOBAL_TRANS_TYPES == 4
+#elif GLOBAL_TRANS_TYPES == 6  // Do not allow full homography
+const aom_tree_index
+    av1_global_motion_types_tree[TREE_SIZE(GLOBAL_TRANS_TYPES)] = {
+      -IDENTITY,    2, -TRANSLATION, 4, -ROTZOOM, 6, -AFFINE, 8, -HORTRAPEZOID,
+      -VERTRAPEZOID
+    };
+
+static const aom_prob default_global_motion_types_prob[GLOBAL_TRANS_TYPES - 1] =
+    { 224, 128, 192, 192, 128 };
+
+#elif GLOBAL_TRANS_TYPES == 4  // Upto Affine
 const aom_tree_index av1_global_motion_types_tree[TREE_SIZE(
     GLOBAL_TRANS_TYPES)] = { -IDENTITY, 2, -TRANSLATION, 4, -ROTZOOM, -AFFINE };
 
 static const aom_prob default_global_motion_types_prob[GLOBAL_TRANS_TYPES - 1] =
     { 224, 128, 240 };
 
-#elif GLOBAL_TRANS_TYPES == 3
+#elif GLOBAL_TRANS_TYPES == 3  // Upto rotation-zoom
 
 const aom_tree_index av1_global_motion_types_tree[TREE_SIZE(
     GLOBAL_TRANS_TYPES)] = { -IDENTITY, 2, -TRANSLATION, -ROTZOOM };
 
 static const aom_prob default_global_motion_types_prob[GLOBAL_TRANS_TYPES - 1] =
     { 224, 128 };
-#endif  // GLOBAL_TRANS_TYPES
-#endif  // CONFIG_GLOBAL_MOTION
+#endif                         // GLOBAL_TRANS_TYPES
+#endif                         // CONFIG_GLOBAL_MOTION
 
 static INLINE int mv_class_base(MV_CLASS_TYPE c) {
   return c ? CLASS0_SIZE << (c + 2) : 0;
diff --git a/av1/common/mv.h b/av1/common/mv.h
index cd4b4e6..9275c23 100644
--- a/av1/common/mv.h
+++ b/av1/common/mv.h
@@ -53,26 +53,33 @@
 
 /* clang-format off */
 typedef enum {
-  IDENTITY = 0,     // identity transformation, 0-parameter
-  TRANSLATION = 1,  // translational motion 2-parameter
-  ROTZOOM = 2,      // simplified affine with rotation + zoom only, 4-parameter
-  AFFINE = 3,       // affine, 6-parameter
-  HOMOGRAPHY = 4,   // homography, 8-parameter
-  TRANS_TYPES = 5,
+  IDENTITY = 0,      // identity transformation, 0-parameter
+  TRANSLATION = 1,   // translational motion 2-parameter
+  ROTZOOM = 2,       // simplified affine with rotation + zoom only, 4-parameter
+  AFFINE = 3,        // affine, 6-parameter
+  HORTRAPEZOID = 4,  // constrained homography, hor trapezoid, 6-parameter
+  VERTRAPEZOID = 5,  // constrained homography, ver trapezoid, 6-parameter
+  HOMOGRAPHY = 6,    // homography, 8-parameter
+  TRANS_TYPES = 7,
 } TransformationType;
 /* clang-format on */
 
 // Number of types used for global motion (must be >= 3 and <= TRANS_TYPES)
+// The following can be useful:
+// GLOBAL_TRANS_TYPES 3 - up to rotation-zoom
+// GLOBAL_TRANS_TYPES 4 - up to affine
+// GLOBAL_TRANS_TYPES 6 - up to hor/ver trapezoids
+// GLOBAL_TRANS_TYPES 7 - up to full homography
 #define GLOBAL_TRANS_TYPES 3
 
 // number of parameters used by each transformation in TransformationTypes
-static const int n_trans_model_params[TRANS_TYPES] = { 0, 2, 4, 6, 8 };
+static const int trans_model_params[TRANS_TYPES] = { 0, 2, 4, 6, 6, 6, 8 };
 
 // The order of values in the wmmat matrix below is best described
 // by the homography:
-// [x'     (m2 m3 m0   [x
-//  y'  =   m4 m5 m1 *  y
-//  1]      m6 m7 1)    1]
+//      [x'     (m2 m3 m0   [x
+//  z .  y'  =   m4 m5 m1 *  y
+//       1]      m6 m7 1)    1]
 typedef struct {
   TransformationType wmtype;
   int32_t wmmat[8];
@@ -134,6 +141,10 @@
 #define GM_ROTZOOM_BITS (GM_TRANSLATION_BITS + (GM_ABS_ALPHA_BITS + 1) * 2)
 #define GM_AFFINE_BITS (GM_ROTZOOM_BITS + (GM_ABS_ALPHA_BITS + 1) * 2)
 #define GM_HOMOGRAPHY_BITS (GM_AFFINE_BITS + (GM_ABS_ROW3HOMO_BITS + 1) * 2)
+#define GM_HORTRAPEZOID_BITS \
+  (GM_AFFINE_BITS - GM_ABS_ALPHA_BITS + GM_ABS_ROW3HOMO_BITS)
+#define GM_VERTRAPEZOID_BITS \
+  (GM_AFFINE_BITS - GM_ABS_ALPHA_BITS + GM_ABS_ROW3HOMO_BITS)
 
 // Convert a global motion translation vector (which may have more bits than a
 // regular motion vector) into a motion vector
@@ -154,7 +165,11 @@
 }
 
 static INLINE TransformationType get_gmtype(const WarpedMotionParams *gm) {
-  // if (gm->wmmat[6] != 0 || gm->wmmat[7] != 0) return HOMOGRAPHY;
+  if (gm->wmmat[6] != 0 || gm->wmmat[7] != 0) {
+    if (!gm->wmmat[6] && !gm->wmmat[4]) return HORTRAPEZOID;
+    if (!gm->wmmat[7] && !gm->wmmat[3]) return VERTRAPEZOID;
+    return HOMOGRAPHY;
+  }
   if (gm->wmmat[5] == (1 << WARPEDMODEL_PREC_BITS) && !gm->wmmat[4] &&
       gm->wmmat[2] == (1 << WARPEDMODEL_PREC_BITS) && !gm->wmmat[3]) {
     return ((!gm->wmmat[1] && !gm->wmmat[0]) ? IDENTITY : TRANSLATION);
diff --git a/av1/common/warped_motion.c b/av1/common/warped_motion.c
index 9698c5f..86ba46c 100644
--- a/av1/common/warped_motion.c
+++ b/av1/common/warped_motion.c
@@ -178,6 +178,72 @@
   }
 }
 
+void project_points_hortrapezoid(int32_t *mat, int *points, int *proj,
+                                 const int n, const int stride_points,
+                                 const int stride_proj, const int subsampling_x,
+                                 const int subsampling_y) {
+  int i;
+  int64_t x, y, Z;
+  int64_t xp, yp;
+  for (i = 0; i < n; ++i) {
+    x = *(points++), y = *(points++);
+    x = (subsampling_x ? 4 * x + 1 : 2 * x);
+    y = (subsampling_y ? 4 * y + 1 : 2 * y);
+
+    Z = (mat[7] * y + (1 << (WARPEDMODEL_ROW3HOMO_PREC_BITS + 1)));
+    xp = (mat[2] * x + mat[3] * y + 2 * mat[0]) *
+         (1 << (WARPEDPIXEL_PREC_BITS + WARPEDMODEL_ROW3HOMO_PREC_BITS -
+                WARPEDMODEL_PREC_BITS));
+    yp = (mat[5] * y + 2 * mat[1]) *
+         (1 << (WARPEDPIXEL_PREC_BITS + WARPEDMODEL_ROW3HOMO_PREC_BITS -
+                WARPEDMODEL_PREC_BITS));
+
+    xp = xp > 0 ? (xp + Z / 2) / Z : (xp - Z / 2) / Z;
+    yp = yp > 0 ? (yp + Z / 2) / Z : (yp - Z / 2) / Z;
+
+    if (subsampling_x) xp = (xp - (1 << (WARPEDPIXEL_PREC_BITS - 1))) / 2;
+    if (subsampling_y) yp = (yp - (1 << (WARPEDPIXEL_PREC_BITS - 1))) / 2;
+    *(proj++) = xp;
+    *(proj++) = yp;
+
+    points += stride_points - 2;
+    proj += stride_proj - 2;
+  }
+}
+
+void project_points_vertrapezoid(int32_t *mat, int *points, int *proj,
+                                 const int n, const int stride_points,
+                                 const int stride_proj, const int subsampling_x,
+                                 const int subsampling_y) {
+  int i;
+  int64_t x, y, Z;
+  int64_t xp, yp;
+  for (i = 0; i < n; ++i) {
+    x = *(points++), y = *(points++);
+    x = (subsampling_x ? 4 * x + 1 : 2 * x);
+    y = (subsampling_y ? 4 * y + 1 : 2 * y);
+
+    Z = (mat[6] * x + (1 << (WARPEDMODEL_ROW3HOMO_PREC_BITS + 1)));
+    xp = (mat[2] * x + 2 * mat[0]) *
+         (1 << (WARPEDPIXEL_PREC_BITS + WARPEDMODEL_ROW3HOMO_PREC_BITS -
+                WARPEDMODEL_PREC_BITS));
+    yp = (mat[4] * x + mat[5] * y + 2 * mat[1]) *
+         (1 << (WARPEDPIXEL_PREC_BITS + WARPEDMODEL_ROW3HOMO_PREC_BITS -
+                WARPEDMODEL_PREC_BITS));
+
+    xp = xp > 0 ? (xp + Z / 2) / Z : (xp - Z / 2) / Z;
+    yp = yp > 0 ? (yp + Z / 2) / Z : (yp - Z / 2) / Z;
+
+    if (subsampling_x) xp = (xp - (1 << (WARPEDPIXEL_PREC_BITS - 1))) / 2;
+    if (subsampling_y) yp = (yp - (1 << (WARPEDPIXEL_PREC_BITS - 1))) / 2;
+    *(proj++) = xp;
+    *(proj++) = yp;
+
+    points += stride_points - 2;
+    proj += stride_proj - 2;
+  }
+}
+
 void project_points_homography(int32_t *mat, int *points, int *proj,
                                const int n, const int stride_points,
                                const int stride_proj, const int subsampling_x,
@@ -1720,6 +1786,144 @@
   return 0;
 }
 
+int find_vertrapezoid(const int np, double *pts1, double *pts2, double *mat) {
+  const int np3 = np * 3;
+  double *a = (double *)aom_malloc(sizeof(*a) * np3 * 14);
+  double *U = a + np3 * 7;
+  double S[7], V[7 * 7], H[9];
+  int i, mini;
+  double sx, sy, dx, dy;
+  double T1[9], T2[9];
+
+  normalize_homography(pts1, np, T1);
+  normalize_homography(pts2, np, T2);
+
+  for (i = 0; i < np; ++i) {
+    dx = *(pts2++);
+    dy = *(pts2++);
+    sx = *(pts1++);
+    sy = *(pts1++);
+
+    a[i * 3 * 7 + 0] = a[i * 3 * 7 + 1] = 0;
+    a[i * 3 * 7 + 2] = -sx;
+    a[i * 3 * 7 + 3] = -sy;
+    a[i * 3 * 7 + 4] = -1;
+    a[i * 3 * 7 + 5] = dy * sx;
+    a[i * 3 * 7 + 6] = dy;
+
+    a[(i * 3 + 1) * 7 + 0] = sx;
+    a[(i * 3 + 1) * 7 + 1] = 1;
+    a[(i * 3 + 1) * 7 + 2] = a[(i * 3 + 1) * 7 + 3] = a[(i * 3 + 1) * 7 + 4] =
+        0;
+    a[(i * 3 + 1) * 7 + 5] = -dx * sx;
+    a[(i * 3 + 1) * 7 + 6] = -dx;
+
+    a[(i * 3 + 2) * 7 + 0] = -dy * sx;
+    a[(i * 3 + 2) * 7 + 1] = -dy;
+    a[(i * 3 + 2) * 7 + 2] = dx * sx;
+    a[(i * 3 + 2) * 7 + 3] = dx * sy;
+    a[(i * 3 + 2) * 7 + 4] = dx;
+    a[(i * 3 + 2) * 7 + 5] = a[(i * 3 + 2) * 7 + 6] = 0;
+  }
+  if (SVD(U, S, V, a, np3, 7)) {
+    aom_free(a);
+    return 1;
+  } else {
+    double minS = 1e12;
+    mini = -1;
+    for (i = 0; i < 7; ++i) {
+      if (S[i] < minS) {
+        minS = S[i];
+        mini = i;
+      }
+    }
+  }
+  H[1] = H[7] = 0;
+  for (i = 0; i < 1; i++) H[i] = V[i * 7 + mini];
+  for (; i < 6; i++) H[i + 1] = V[i * 7 + mini];
+  for (; i < 7; i++) H[i + 2] = V[i * 7 + mini];
+
+  denormalize_homography_reorder(H, T1, T2);
+  aom_free(a);
+  if (H[8] == 0.0) {
+    return 1;
+  } else {
+    // normalize
+    double f = 1.0 / H[8];
+    for (i = 0; i < 8; i++) mat[i] = f * H[i];
+  }
+  return 0;
+}
+
+int find_hortrapezoid(const int np, double *pts1, double *pts2, double *mat) {
+  const int np3 = np * 3;
+  double *a = (double *)aom_malloc(sizeof(*a) * np3 * 14);
+  double *U = a + np3 * 7;
+  double S[7], V[7 * 7], H[9];
+  int i, mini;
+  double sx, sy, dx, dy;
+  double T1[9], T2[9];
+
+  normalize_homography(pts1, np, T1);
+  normalize_homography(pts2, np, T2);
+
+  for (i = 0; i < np; ++i) {
+    dx = *(pts2++);
+    dy = *(pts2++);
+    sx = *(pts1++);
+    sy = *(pts1++);
+
+    a[i * 3 * 7 + 0] = a[i * 3 * 7 + 1] = a[i * 3 * 7 + 2] = 0;
+    a[i * 3 * 7 + 3] = -sy;
+    a[i * 3 * 7 + 4] = -1;
+    a[i * 3 * 7 + 5] = dy * sy;
+    a[i * 3 * 7 + 6] = dy;
+
+    a[(i * 3 + 1) * 7 + 0] = sx;
+    a[(i * 3 + 1) * 7 + 1] = sy;
+    a[(i * 3 + 1) * 7 + 2] = 1;
+    a[(i * 3 + 1) * 7 + 3] = a[(i * 3 + 1) * 7 + 4] = 0;
+    a[(i * 3 + 1) * 7 + 5] = -dx * sy;
+    a[(i * 3 + 1) * 7 + 6] = -dx;
+
+    a[(i * 3 + 2) * 7 + 0] = -dy * sx;
+    a[(i * 3 + 2) * 7 + 1] = -dy * sy;
+    a[(i * 3 + 2) * 7 + 2] = -dy;
+    a[(i * 3 + 2) * 7 + 3] = dx * sy;
+    a[(i * 3 + 2) * 7 + 4] = dx;
+    a[(i * 3 + 2) * 7 + 5] = a[(i * 3 + 2) * 7 + 6] = 0;
+  }
+
+  if (SVD(U, S, V, a, np3, 7)) {
+    aom_free(a);
+    return 1;
+  } else {
+    double minS = 1e12;
+    mini = -1;
+    for (i = 0; i < 7; ++i) {
+      if (S[i] < minS) {
+        minS = S[i];
+        mini = i;
+      }
+    }
+  }
+  H[3] = H[6] = 0;
+  for (i = 0; i < 3; i++) H[i] = V[i * 7 + mini];
+  for (; i < 5; i++) H[i + 1] = V[i * 7 + mini];
+  for (; i < 7; i++) H[i + 2] = V[i * 7 + mini];
+
+  denormalize_homography_reorder(H, T1, T2);
+  aom_free(a);
+  if (H[8] == 0.0) {
+    return 1;
+  } else {
+    // normalize
+    double f = 1.0 / H[8];
+    for (i = 0; i < 8; i++) mat[i] = f * H[i];
+  }
+  return 0;
+}
+
 int find_homography(const int np, double *pts1, double *pts2, double *mat) {
   // Implemented from Peter Kovesi's normalized implementation
   const int np3 = np * 3;
diff --git a/av1/common/warped_motion.h b/av1/common/warped_motion.h
index c1d8177..b3fb4f2 100644
--- a/av1/common/warped_motion.h
+++ b/av1/common/warped_motion.h
@@ -51,6 +51,14 @@
                            const int stride_points, const int stride_proj,
                            const int subsampling_x, const int subsampling_y);
 
+void project_points_hortrapezoid(int32_t *mat, int *points, int *proj,
+                                 const int n, const int stride_points,
+                                 const int stride_proj, const int subsampling_x,
+                                 const int subsampling_y);
+void project_points_vertrapezoid(int32_t *mat, int *points, int *proj,
+                                 const int n, const int stride_points,
+                                 const int stride_proj, const int subsampling_x,
+                                 const int subsampling_y);
 void project_points_homography(int32_t *mat, int *points, int *proj,
                                const int n, const int stride_points,
                                const int stride_proj, const int subsampling_x,
@@ -85,6 +93,8 @@
 int find_translation(const int np, double *pts1, double *pts2, double *mat);
 int find_rotzoom(const int np, double *pts1, double *pts2, double *mat);
 int find_affine(const int np, double *pts1, double *pts2, double *mat);
+int find_hortrapezoid(const int np, double *pts1, double *pts2, double *mat);
+int find_vertrapezoid(const int np, double *pts1, double *pts2, double *mat);
 int find_homography(const int np, double *pts1, double *pts2, double *mat);
 int find_projection(const int np, double *pts1, double *pts2,
                     WarpedMotionParams *wm_params);
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 6d91cfe..4b98549 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -4449,20 +4449,29 @@
   params->wmtype = type;
   switch (type) {
     case HOMOGRAPHY:
-      params->wmmat[6] = aom_read_primitive_symmetric(r, GM_ABS_ROW3HOMO_BITS) *
-                         GM_ROW3HOMO_DECODE_FACTOR;
-      params->wmmat[7] = aom_read_primitive_symmetric(r, GM_ABS_ROW3HOMO_BITS) *
-                         GM_ROW3HOMO_DECODE_FACTOR;
+    case HORTRAPEZOID:
+    case VERTRAPEZOID:
+      if (type != HORTRAPEZOID)
+        params->wmmat[6] =
+            aom_read_primitive_symmetric(r, GM_ABS_ROW3HOMO_BITS) *
+            GM_ROW3HOMO_DECODE_FACTOR;
+      if (type != VERTRAPEZOID)
+        params->wmmat[7] =
+            aom_read_primitive_symmetric(r, GM_ABS_ROW3HOMO_BITS) *
+            GM_ROW3HOMO_DECODE_FACTOR;
     case AFFINE:
     case ROTZOOM:
       params->wmmat[2] = aom_read_primitive_symmetric(r, GM_ABS_ALPHA_BITS) *
                              GM_ALPHA_DECODE_FACTOR +
                          (1 << WARPEDMODEL_PREC_BITS);
-      params->wmmat[3] = aom_read_primitive_symmetric(r, GM_ABS_ALPHA_BITS) *
-                         GM_ALPHA_DECODE_FACTOR;
-      if (type == AFFINE || type == HOMOGRAPHY) {
-        params->wmmat[4] = aom_read_primitive_symmetric(r, GM_ABS_ALPHA_BITS) *
+      if (type != VERTRAPEZOID)
+        params->wmmat[3] = aom_read_primitive_symmetric(r, GM_ABS_ALPHA_BITS) *
                            GM_ALPHA_DECODE_FACTOR;
+      if (type >= AFFINE) {
+        if (type != HORTRAPEZOID)
+          params->wmmat[4] =
+              aom_read_primitive_symmetric(r, GM_ABS_ALPHA_BITS) *
+              GM_ALPHA_DECODE_FACTOR;
         params->wmmat[5] = aom_read_primitive_symmetric(r, GM_ABS_ALPHA_BITS) *
                                GM_ALPHA_DECODE_FACTOR +
                            (1 << WARPEDMODEL_PREC_BITS);
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);