WIP: New warp filter for global motion Do not merge; the highbd filter is not implemented yet. Change-Id: I8f3322f5ab932b0f2e45f3590c135b70d711d915
diff --git a/av1/common/warped_motion.c b/av1/common/warped_motion.c index d7d81db..f473490 100644 --- a/av1/common/warped_motion.c +++ b/av1/common/warped_motion.c
@@ -406,6 +406,117 @@ } } +// For warping, we really use a 6-tap filter, but we do blocks of 8 pixels +// at a time. The zoom/rotation/shear in the model are applied to the +// "fractional" position of each pixel, which therefore varies within +// [-1, 2) * WARPEDPIXEL_PREC_SHIFTS. +// We need an extra 2 taps to fit this in, for a total of 8 taps. +/* clang-format off */ +static const int16_t warped_filter[WARPEDPIXEL_PREC_SHIFTS*3][8] = { + // [-1, 0) + { 0, 0, 128, 0, 0, 0, 0, 0 }, { 0, - 1, 128, 2, - 1, 0, 0, 0 }, + { 1, - 3, 127, 4, - 1, 0, 0, 0 }, { 1, - 4, 126, 6, - 2, 1, 0, 0 }, + { 1, - 5, 126, 8, - 3, 1, 0, 0 }, { 1, - 6, 125, 11, - 4, 1, 0, 0 }, + { 1, - 7, 124, 13, - 4, 1, 0, 0 }, { 2, - 8, 123, 15, - 5, 1, 0, 0 }, + { 2, - 9, 122, 18, - 6, 1, 0, 0 }, { 2, -10, 121, 20, - 6, 1, 0, 0 }, + { 2, -11, 120, 22, - 7, 2, 0, 0 }, { 2, -12, 119, 25, - 8, 2, 0, 0 }, + { 3, -13, 117, 27, - 8, 2, 0, 0 }, { 3, -13, 116, 29, - 9, 2, 0, 0 }, + { 3, -14, 114, 32, -10, 3, 0, 0 }, { 3, -15, 113, 35, -10, 2, 0, 0 }, + { 3, -15, 111, 37, -11, 3, 0, 0 }, { 3, -16, 109, 40, -11, 3, 0, 0 }, + { 3, -16, 108, 42, -12, 3, 0, 0 }, { 4, -17, 106, 45, -13, 3, 0, 0 }, + { 4, -17, 104, 47, -13, 3, 0, 0 }, { 4, -17, 102, 50, -14, 3, 0, 0 }, + { 4, -17, 100, 52, -14, 3, 0, 0 }, { 4, -18, 98, 55, -15, 4, 0, 0 }, + { 4, -18, 96, 58, -15, 3, 0, 0 }, { 4, -18, 94, 60, -16, 4, 0, 0 }, + { 4, -18, 91, 63, -16, 4, 0, 0 }, { 4, -18, 89, 65, -16, 4, 0, 0 }, + { 4, -18, 87, 68, -17, 4, 0, 0 }, { 4, -18, 85, 70, -17, 4, 0, 0 }, + { 4, -18, 82, 73, -17, 4, 0, 0 }, { 4, -18, 80, 75, -17, 4, 0, 0 }, + { 4, -18, 78, 78, -18, 4, 0, 0 }, { 4, -17, 75, 80, -18, 4, 0, 0 }, + { 4, -17, 73, 82, -18, 4, 0, 0 }, { 4, -17, 70, 85, -18, 4, 0, 0 }, + { 4, -17, 68, 87, -18, 4, 0, 0 }, { 4, -16, 65, 89, -18, 4, 0, 0 }, + { 4, -16, 63, 91, -18, 4, 0, 0 }, { 4, -16, 60, 94, -18, 4, 0, 0 }, + { 3, -15, 58, 96, -18, 4, 0, 0 }, { 4, -15, 55, 98, -18, 4, 0, 0 }, + { 3, -14, 52, 100, -17, 4, 0, 0 }, { 3, -14, 50, 102, -17, 4, 0, 0 }, + { 3, -13, 47, 104, -17, 4, 0, 0 }, { 3, -13, 45, 106, -17, 4, 0, 0 }, + { 3, -12, 42, 108, -16, 3, 0, 0 }, { 3, -11, 40, 109, -16, 3, 0, 0 }, + { 3, -11, 37, 111, -15, 3, 0, 0 }, { 2, -10, 35, 113, -15, 3, 0, 0 }, + { 3, -10, 32, 114, -14, 3, 0, 0 }, { 2, - 9, 29, 116, -13, 3, 0, 0 }, + { 2, - 8, 27, 117, -13, 3, 0, 0 }, { 2, - 8, 25, 119, -12, 2, 0, 0 }, + { 2, - 7, 22, 120, -11, 2, 0, 0 }, { 1, - 6, 20, 121, -10, 2, 0, 0 }, + { 1, - 6, 18, 122, - 9, 2, 0, 0 }, { 1, - 5, 15, 123, - 8, 2, 0, 0 }, + { 1, - 4, 13, 124, - 7, 1, 0, 0 }, { 1, - 4, 11, 125, - 6, 1, 0, 0 }, + { 1, - 3, 8, 126, - 5, 1, 0, 0 }, { 1, - 2, 6, 126, - 4, 1, 0, 0 }, + { 0, - 1, 4, 127, - 3, 1, 0, 0 }, { 0, - 1, 2, 128, - 1, 0, 0, 0 }, + + // [0, 1) + { 0, 0, 0, 128, 0, 0, 0, 0 }, { 0, 0, - 1, 128, 2, - 1, 0, 0 }, + { 0, 1, - 3, 127, 4, - 1, 0, 0 }, { 0, 1, - 4, 126, 6, - 2, 1, 0 }, + { 0, 1, - 5, 126, 8, - 3, 1, 0 }, { 0, 1, - 6, 125, 11, - 4, 1, 0 }, + { 0, 1, - 7, 124, 13, - 4, 1, 0 }, { 0, 2, - 8, 123, 15, - 5, 1, 0 }, + { 0, 2, - 9, 122, 18, - 6, 1, 0 }, { 0, 2, -10, 121, 20, - 6, 1, 0 }, + { 0, 2, -11, 120, 22, - 7, 2, 0 }, { 0, 2, -12, 119, 25, - 8, 2, 0 }, + { 0, 3, -13, 117, 27, - 8, 2, 0 }, { 0, 3, -13, 116, 29, - 9, 2, 0 }, + { 0, 3, -14, 114, 32, -10, 3, 0 }, { 0, 3, -15, 113, 35, -10, 2, 0 }, + { 0, 3, -15, 111, 37, -11, 3, 0 }, { 0, 3, -16, 109, 40, -11, 3, 0 }, + { 0, 3, -16, 108, 42, -12, 3, 0 }, { 0, 4, -17, 106, 45, -13, 3, 0 }, + { 0, 4, -17, 104, 47, -13, 3, 0 }, { 0, 4, -17, 102, 50, -14, 3, 0 }, + { 0, 4, -17, 100, 52, -14, 3, 0 }, { 0, 4, -18, 98, 55, -15, 4, 0 }, + { 0, 4, -18, 96, 58, -15, 3, 0 }, { 0, 4, -18, 94, 60, -16, 4, 0 }, + { 0, 4, -18, 91, 63, -16, 4, 0 }, { 0, 4, -18, 89, 65, -16, 4, 0 }, + { 0, 4, -18, 87, 68, -17, 4, 0 }, { 0, 4, -18, 85, 70, -17, 4, 0 }, + { 0, 4, -18, 82, 73, -17, 4, 0 }, { 0, 4, -18, 80, 75, -17, 4, 0 }, + { 0, 4, -18, 78, 78, -18, 4, 0 }, { 0, 4, -17, 75, 80, -18, 4, 0 }, + { 0, 4, -17, 73, 82, -18, 4, 0 }, { 0, 4, -17, 70, 85, -18, 4, 0 }, + { 0, 4, -17, 68, 87, -18, 4, 0 }, { 0, 4, -16, 65, 89, -18, 4, 0 }, + { 0, 4, -16, 63, 91, -18, 4, 0 }, { 0, 4, -16, 60, 94, -18, 4, 0 }, + { 0, 3, -15, 58, 96, -18, 4, 0 }, { 0, 4, -15, 55, 98, -18, 4, 0 }, + { 0, 3, -14, 52, 100, -17, 4, 0 }, { 0, 3, -14, 50, 102, -17, 4, 0 }, + { 0, 3, -13, 47, 104, -17, 4, 0 }, { 0, 3, -13, 45, 106, -17, 4, 0 }, + { 0, 3, -12, 42, 108, -16, 3, 0 }, { 0, 3, -11, 40, 109, -16, 3, 0 }, + { 0, 3, -11, 37, 111, -15, 3, 0 }, { 0, 2, -10, 35, 113, -15, 3, 0 }, + { 0, 3, -10, 32, 114, -14, 3, 0 }, { 0, 2, - 9, 29, 116, -13, 3, 0 }, + { 0, 2, - 8, 27, 117, -13, 3, 0 }, { 0, 2, - 8, 25, 119, -12, 2, 0 }, + { 0, 2, - 7, 22, 120, -11, 2, 0 }, { 0, 1, - 6, 20, 121, -10, 2, 0 }, + { 0, 1, - 6, 18, 122, - 9, 2, 0 }, { 0, 1, - 5, 15, 123, - 8, 2, 0 }, + { 0, 1, - 4, 13, 124, - 7, 1, 0 }, { 0, 1, - 4, 11, 125, - 6, 1, 0 }, + { 0, 1, - 3, 8, 126, - 5, 1, 0 }, { 0, 1, - 2, 6, 126, - 4, 1, 0 }, + { 0, 0, - 1, 4, 127, - 3, 1, 0 }, { 0, 0, - 1, 2, 128, - 1, 0, 0 }, + + // [1, 2) + { 0, 0, 0, 0, 128, 0, 0, 0 }, { 0, 0, 0, - 1, 128, 2, - 1, 0 }, + { 0, 0, 1, - 3, 127, 4, - 1, 0 }, { 0, 0, 1, - 4, 126, 6, - 2, 1 }, + { 0, 0, 1, - 5, 126, 8, - 3, 1 }, { 0, 0, 1, - 6, 125, 11, - 4, 1 }, + { 0, 0, 1, - 7, 124, 13, - 4, 1 }, { 0, 0, 2, - 8, 123, 15, - 5, 1 }, + { 0, 0, 2, - 9, 122, 18, - 6, 1 }, { 0, 0, 2, -10, 121, 20, - 6, 1 }, + { 0, 0, 2, -11, 120, 22, - 7, 2 }, { 0, 0, 2, -12, 119, 25, - 8, 2 }, + { 0, 0, 3, -13, 117, 27, - 8, 2 }, { 0, 0, 3, -13, 116, 29, - 9, 2 }, + { 0, 0, 3, -14, 114, 32, -10, 3 }, { 0, 0, 3, -15, 113, 35, -10, 2 }, + { 0, 0, 3, -15, 111, 37, -11, 3 }, { 0, 0, 3, -16, 109, 40, -11, 3 }, + { 0, 0, 3, -16, 108, 42, -12, 3 }, { 0, 0, 4, -17, 106, 45, -13, 3 }, + { 0, 0, 4, -17, 104, 47, -13, 3 }, { 0, 0, 4, -17, 102, 50, -14, 3 }, + { 0, 0, 4, -17, 100, 52, -14, 3 }, { 0, 0, 4, -18, 98, 55, -15, 4 }, + { 0, 0, 4, -18, 96, 58, -15, 3 }, { 0, 0, 4, -18, 94, 60, -16, 4 }, + { 0, 0, 4, -18, 91, 63, -16, 4 }, { 0, 0, 4, -18, 89, 65, -16, 4 }, + { 0, 0, 4, -18, 87, 68, -17, 4 }, { 0, 0, 4, -18, 85, 70, -17, 4 }, + { 0, 0, 4, -18, 82, 73, -17, 4 }, { 0, 0, 4, -18, 80, 75, -17, 4 }, + { 0, 0, 4, -18, 78, 78, -18, 4 }, { 0, 0, 4, -17, 75, 80, -18, 4 }, + { 0, 0, 4, -17, 73, 82, -18, 4 }, { 0, 0, 4, -17, 70, 85, -18, 4 }, + { 0, 0, 4, -17, 68, 87, -18, 4 }, { 0, 0, 4, -16, 65, 89, -18, 4 }, + { 0, 0, 4, -16, 63, 91, -18, 4 }, { 0, 0, 4, -16, 60, 94, -18, 4 }, + { 0, 0, 3, -15, 58, 96, -18, 4 }, { 0, 0, 4, -15, 55, 98, -18, 4 }, + { 0, 0, 3, -14, 52, 100, -17, 4 }, { 0, 0, 3, -14, 50, 102, -17, 4 }, + { 0, 0, 3, -13, 47, 104, -17, 4 }, { 0, 0, 3, -13, 45, 106, -17, 4 }, + { 0, 0, 3, -12, 42, 108, -16, 3 }, { 0, 0, 3, -11, 40, 109, -16, 3 }, + { 0, 0, 3, -11, 37, 111, -15, 3 }, { 0, 0, 2, -10, 35, 113, -15, 3 }, + { 0, 0, 3, -10, 32, 114, -14, 3 }, { 0, 0, 2, - 9, 29, 116, -13, 3 }, + { 0, 0, 2, - 8, 27, 117, -13, 3 }, { 0, 0, 2, - 8, 25, 119, -12, 2 }, + { 0, 0, 2, - 7, 22, 120, -11, 2 }, { 0, 0, 1, - 6, 20, 121, -10, 2 }, + { 0, 0, 1, - 6, 18, 122, - 9, 2 }, { 0, 0, 1, - 5, 15, 123, - 8, 2 }, + { 0, 0, 1, - 4, 13, 124, - 7, 1 }, { 0, 0, 1, - 4, 11, 125, - 6, 1 }, + { 0, 0, 1, - 3, 8, 126, - 5, 1 }, { 0, 0, 1, - 2, 6, 126, - 4, 1 }, + { 0, 0, 0, - 1, 4, 127, - 3, 1 }, { 0, 0, 0, - 1, 2, 128, - 1, 0 }, +}; +/* clang-format on */ + #if CONFIG_AOM_HIGHBITDEPTH static INLINE void highbd_get_subcolumn(int taps, uint16_t *ref, int32_t *col, int stride, int x, int y_start) { @@ -601,39 +712,11 @@ return error_measure_lut[255 + err]; } -static double warp_erroradv(WarpedMotionParams *wm, uint8_t *ref, int width, - int height, int stride, uint8_t *dst, int p_col, - int p_row, int p_width, int p_height, int p_stride, - int subsampling_x, int subsampling_y, int x_scale, - int y_scale) { - int gm_err = 0, no_gm_err = 0; - int gm_sumerr = 0, no_gm_sumerr = 0; - int i, j; - ProjectPointsFunc projectpoints = get_project_points_type(wm->wmtype); - for (i = p_row; i < p_row + p_height; ++i) { - for (j = p_col; j < p_col + p_width; ++j) { - int in[2], out[2]; - in[0] = j; - in[1] = i; - projectpoints(wm->wmmat, in, out, 1, 2, 2, subsampling_x, subsampling_y); - out[0] = ROUND_POWER_OF_TWO_SIGNED(out[0] * x_scale, 4); - out[1] = ROUND_POWER_OF_TWO_SIGNED(out[1] * y_scale, 4); - gm_err = dst[(j - p_col) + (i - p_row) * p_stride] - - warp_interpolate(ref, out[0], out[1], width, height, stride); - no_gm_err = - dst[(j - p_col) + (i - p_row) * p_stride] - ref[j + i * stride]; - gm_sumerr += error_measure(gm_err); - no_gm_sumerr += error_measure(no_gm_err); - } - } - return (double)gm_sumerr / no_gm_sumerr; -} - -static void warp_plane(WarpedMotionParams *wm, uint8_t *ref, int width, - int height, int stride, uint8_t *pred, int p_col, - int p_row, int p_width, int p_height, int p_stride, - int subsampling_x, int subsampling_y, int x_scale, - int y_scale, int ref_frm) { +static void warp_plane_old(WarpedMotionParams *wm, uint8_t *ref, int width, + int height, int stride, uint8_t *pred, int p_col, + int p_row, int p_width, int p_height, int p_stride, + int subsampling_x, int subsampling_y, int x_scale, + int y_scale, int ref_frm) { int i, j; ProjectPointsFunc projectpoints = get_project_points_type(wm->wmtype); if (projectpoints == NULL) return; @@ -657,6 +740,171 @@ } } +/* The warp filter for ROTZOOM and AFFINE models works as follows: + * Split the input into 8x8 blocks + * For each block, project the point (4, 4) within the block, to get the + overall block position. Split into integer and fractional coordinates, + maintaining full WARPEDMODEL precision + * Filter horizontally: Generate 15 rows of 8 pixels each. Each pixel gets a + variable horizontal offset. This means that, while the rows of the + intermediate buffer align with the rows of the *reference* image, the + columns align with the columns of the *destination* image. + * Filter vertically: Generate the output block (up to 8x8 pixels, but if the + destination is too small we crop the output at this stage). Each pixel has + a variable vertical offset, so that the resulting rows are aligned with + the rows of the destination image. + + To accomplish these alignments, we factor the warp matrix as a + product of two shear / asymmetric zoom matrices: + / a b \ = / 1 0 \ * / 1+alpha beta \ + \ c d / \ gamma 1+delta / \ 0 1 / + where a, b, c, d are wmmat[2], wmmat[3], wmmat[4], wmmat[5] respectively. + The second shear (with alpha and beta) is applied by the horizontal filter, + then the first shear (with gamma and delta) is applied by the vertical + filter. + + The only limitation is that, to fit this in a fixed 8-tap filter size, + the fractional pixel offsets must be at most +-1. Since the horizontal filter + generates 15 rows of 8 columns, and the initial point we project is at (4, 4) + within the block, the parameters must satisfy + 4 * |alpha| + 7 * |beta| <= 1 and 4 * |gamma| + 7 * |delta| <= 1 + for this filter to be applicable. +*/ +static void warp_plane(WarpedMotionParams *wm, uint8_t *ref, int width, + int height, int stride, uint8_t *pred, int p_col, + int p_row, int p_width, int p_height, int p_stride, + int subsampling_x, int subsampling_y, int x_scale, + int y_scale, int ref_frm) { + if (wm->wmtype == ROTZOOM) { + wm->wmmat[5] = wm->wmmat[2]; + wm->wmmat[4] = -wm->wmmat[3]; + } + if (wm->wmtype == ROTZOOM || wm->wmtype == AFFINE) { + int32_t tmp[15 * 8]; + int i, j, k, l, m; + int32_t *mat = wm->wmmat; + + int32_t alpha = mat[2] - (1 << WARPEDMODEL_PREC_BITS); + int32_t beta = mat[3]; + int32_t gamma = ((int64_t)mat[4] << WARPEDMODEL_PREC_BITS) / mat[2]; + int32_t delta = mat[5] - + (((int64_t)mat[3] * mat[4] + (mat[2] / 2)) / mat[2]) - + (1 << WARPEDMODEL_PREC_BITS); + + if ((4 * abs(alpha) + 7 * abs(beta) > (1 << WARPEDMODEL_PREC_BITS)) || + (4 * abs(gamma) + 7 * abs(delta) > (1 << WARPEDMODEL_PREC_BITS))) { + assert(0 && "Warped motion model is incompatible with new warp filter"); + warp_plane_old(wm, ref, width, height, stride, pred, p_col, p_row, + p_width, p_height, p_stride, subsampling_x, subsampling_y, + x_scale, y_scale, ref_frm); + return; + } + + for (i = p_row; i < p_row + p_height; i += 8) { + for (j = p_col; j < p_col + p_width; j += 8) { + int32_t x4, y4, ix4, sx4, iy4, sy4; + if (subsampling_x) + x4 = ROUND_POWER_OF_TWO_SIGNED( + mat[2] * 2 * (j + 4) + mat[3] * 2 * (i + 4) + mat[0] + + (mat[2] + mat[3] - (1 << WARPEDMODEL_PREC_BITS)) / 2, + 1); + else + x4 = mat[2] * (j + 4) + mat[3] * (i + 4) + mat[0]; + + if (subsampling_y) + y4 = ROUND_POWER_OF_TWO_SIGNED( + mat[4] * 2 * (j + 4) + mat[5] * 2 * (i + 4) + mat[1] + + (mat[4] + mat[5] - (1 << WARPEDMODEL_PREC_BITS)) / 2, + 1); + else + y4 = mat[4] * (j + 4) + mat[5] * (i + 4) + mat[1]; + + ix4 = x4 >> WARPEDMODEL_PREC_BITS; + sx4 = x4 & ((1 << WARPEDMODEL_PREC_BITS) - 1); + iy4 = y4 >> WARPEDMODEL_PREC_BITS; + sy4 = y4 & ((1 << WARPEDMODEL_PREC_BITS) - 1); + + // Horizontal filter + for (k = -7; k < 8; ++k) { + int iy = iy4 + k; + if (iy < 0) + iy = 0; + else if (iy > height - 1) + iy = height - 1; + + for (l = -4; l < 4; ++l) { + int ix = ix4 + l; + int sx = ROUND_POWER_OF_TWO_SIGNED(sx4 + alpha * l + beta * k, + WARPEDDIFF_PREC_BITS); + const int16_t *coeffs = warped_filter[sx + WARPEDPIXEL_PREC_SHIFTS]; + int32_t sum = 0; + for (m = 0; m < 8; ++m) { + if (ix + m - 3 < 0) + sum += ref[iy * stride] * coeffs[m]; + else if (ix + m - 3 > width - 1) + sum += ref[iy * stride + width - 1] * coeffs[m]; + else + sum += ref[iy * stride + ix + m - 3] * coeffs[m]; + } + tmp[(k + 7) * 8 + (l + 4)] = sum; + } + } + + // Vertical filter + for (k = -4; k < AOMMIN(4, p_row + p_height - i - 4); ++k) { + for (l = -4; l < AOMMIN(4, p_col + p_width - j - 4); ++l) { + uint8_t *p = + &pred[(i - p_row + k + 4) * p_stride + (j - p_col + l + 4)]; + int sy = ROUND_POWER_OF_TWO_SIGNED(sy4 + gamma * l + delta * k, + WARPEDDIFF_PREC_BITS); + const int16_t *coeffs = warped_filter[sy + WARPEDPIXEL_PREC_SHIFTS]; + int32_t sum = 0; + for (m = 0; m < 8; ++m) { + sum += tmp[(k + m + 4) * 8 + (l + 4)] * coeffs[m]; + } + sum = clip_pixel( + ROUND_POWER_OF_TWO_SIGNED(sum, 2 * WARPEDPIXEL_FILTER_BITS)); + if (ref_frm) + *p = ROUND_POWER_OF_TWO_SIGNED(*p + sum, 1); + else + *p = sum; + } + } + } + } + } else { + warp_plane_old(wm, ref, width, height, stride, pred, p_col, p_row, p_width, + p_height, p_stride, subsampling_x, subsampling_y, x_scale, + y_scale, ref_frm); + } +} + +static double warp_erroradv(WarpedMotionParams *wm, uint8_t *ref, int width, + int height, int stride, uint8_t *dst, int p_col, + int p_row, int p_width, int p_height, int p_stride, + int subsampling_x, int subsampling_y, int x_scale, + int y_scale) { + int gm_err = 0, no_gm_err = 0; + int gm_sumerr = 0, no_gm_sumerr = 0; + int i, j; + uint8_t *tmp = aom_malloc(p_width * p_height); + warp_plane(wm, ref, width, height, stride, tmp, p_col, p_row, p_width, + p_height, p_width, subsampling_x, subsampling_y, x_scale, y_scale, + 0); + + for (i = 0; i < p_height; ++i) { + for (j = 0; j < p_width; ++j) { + gm_err = dst[j + i * p_stride] - tmp[j + i * p_width]; + no_gm_err = dst[j + i * p_stride] - ref[j + i * stride]; + gm_sumerr += error_measure(gm_err); + no_gm_sumerr += error_measure(no_gm_err); + } + } + + aom_free(tmp); + return (double)gm_sumerr / no_gm_sumerr; +} + double av1_warp_erroradv(WarpedMotionParams *wm, #if CONFIG_AOM_HIGHBITDEPTH int use_hbd, int bd,