From 5fa46c0b609190908280206de257c5b3f6329086 Mon Sep 17 00:00:00 2001 From: Sarah Parker <sarahparker@google.com> Date: Mon, 11 Jul 2016 11:47:55 -0700 Subject: [PATCH] Add global motion parameters to compressed header Currently nothing is implemented to compute GM parameters, this just adds the capability to send them in the bitstream if they were computed. Still need to implement the reconstruction based on the parameters in reconinter. Change-Id: I72aea3c6a9de9f5a40f96da76c82b54a52781fe2 --- vp10/common/blockd.h | 3 ++ vp10/common/entropymode.h | 3 ++ vp10/common/entropymv.c | 14 +++++++ vp10/common/entropymv.h | 5 +++ vp10/common/mv.h | 61 +++++++++++++++++++++++++++ vp10/common/onyxc_int.h | 3 ++ vp10/decoder/decodeframe.c | 55 ++++++++++++++++++++++++ vp10/decoder/dsubexp.c | 10 +++++ vp10/decoder/dsubexp.h | 5 +++ vp10/encoder/bitstream.c | 55 +++++++++++++++++++++++- vp10/encoder/encodeframe.c | 85 +++++++++++++++++++++++++++++++++++++- vp10/encoder/encoder.h | 3 ++ vp10/encoder/subexp.c | 13 ++++++ vp10/encoder/subexp.h | 7 ++++ 14 files changed, 320 insertions(+), 2 deletions(-) diff --git a/vp10/common/blockd.h b/vp10/common/blockd.h index 6314e0510a..4c46cbb718 100644 --- a/vp10/common/blockd.h +++ b/vp10/common/blockd.h @@ -388,6 +388,9 @@ typedef struct macroblockd { int corrupted; struct vpx_internal_error_info *error_info; +#if CONFIG_GLOBAL_MOTION + Global_Motion_Params *global_motion; +#endif // CONFIG_GLOBAL_MOTION } MACROBLOCKD; static INLINE BLOCK_SIZE get_subsize(BLOCK_SIZE bsize, diff --git a/vp10/common/entropymode.h b/vp10/common/entropymode.h index 71e79d90dc..a898038a83 100644 --- a/vp10/common/entropymode.h +++ b/vp10/common/entropymode.h @@ -118,6 +118,9 @@ typedef struct frame_contexts { vpx_prob ext_intra_probs[PLANE_TYPES]; vpx_prob intra_filter_probs[INTRA_FILTERS + 1][INTRA_FILTERS - 1]; #endif // CONFIG_EXT_INTRA +#if CONFIG_GLOBAL_MOTION + vpx_prob global_motion_types_prob[GLOBAL_MOTION_TYPES - 1]; +#endif // CONFIG_GLOBAL_MOTION } FRAME_CONTEXT; typedef struct FRAME_COUNTS { diff --git a/vp10/common/entropymv.c b/vp10/common/entropymv.c index 87c64219f0..ae6209d00b 100644 --- a/vp10/common/entropymv.c +++ b/vp10/common/entropymv.c @@ -120,6 +120,17 @@ static const uint8_t log_in_base_2[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10 }; +#if CONFIG_GLOBAL_MOTION +const vpx_tree_index vp10_global_motion_types_tree + [TREE_SIZE(GLOBAL_MOTION_TYPES)] = { + -GLOBAL_ZERO, 2, + -GLOBAL_TRANSLATION, -GLOBAL_ROTZOOM +}; + +static const vpx_prob default_global_motion_types_prob + [GLOBAL_MOTION_TYPES - 1] = {224, 128}; +#endif // CONFIG_GLOBAL_MOTION + static INLINE int mv_class_base(MV_CLASS_TYPE c) { return c ? CLASS0_SIZE << (c + 2) : 0; } @@ -277,4 +288,7 @@ void vp10_init_mv_probs(VP10_COMMON *cm) { #else cm->fc->nmvc = default_nmv_context; #endif +#if CONFIG_GLOBAL_MOTION + vp10_copy(cm->fc->global_motion_types_prob, default_global_motion_types_prob); +#endif // CONFIG_GLOBAL_MOTION } diff --git a/vp10/common/entropymv.h b/vp10/common/entropymv.h index f8ade34d57..af69a2daa5 100644 --- a/vp10/common/entropymv.h +++ b/vp10/common/entropymv.h @@ -132,6 +132,11 @@ typedef struct { void vp10_inc_mv(const MV *mv, nmv_context_counts *mvctx, const int usehp); +#if CONFIG_GLOBAL_MOTION +extern const vpx_tree_index vp10_global_motion_types_tree + [TREE_SIZE(GLOBAL_MOTION_TYPES)]; +#endif // CONFIG_GLOBAL_MOTION + #ifdef __cplusplus } // extern "C" #endif diff --git a/vp10/common/mv.h b/vp10/common/mv.h index 8b9348bcad..255a7243cf 100644 --- a/vp10/common/mv.h +++ b/vp10/common/mv.h @@ -13,6 +13,9 @@ #include "vp10/common/common.h" #include "vpx_dsp/vpx_filter.h" +#if CONFIG_GLOBAL_MOTION +#include "vp10/common/warped_motion.h" +#endif // CONFIG_GLOBAL_MOTION #ifdef __cplusplus extern "C" { @@ -33,6 +36,64 @@ typedef struct mv32 { int32_t col; } MV32; +#if CONFIG_GLOBAL_MOTION +// ALPHA here refers to parameters a and b in rotzoom model: +// | a b| +// |-b a| +// +// Anything ending in PREC_BITS is the number of bits of precision +// to maintain when converting from double to integer. +// +// The ABS parameters are used to create an upper and lower bound +// for each parameter. In other words, after a parameter is integerized +// it is clamped between -(1 << ABS_XXX_BITS) and (1 << ABS_XXX_BITS) +// +// XXX_PREC_DIFF and XXX_DECODE_FACTOR are computed once here to +// prevent repetetive computation on the decoder side. These are +// to allow the global motion parameters to be encoded in a lower +// precision than the warped model precision. This means that they +// need to be changed to warped precision when they are decoded. +// +// XX_MIN, XX_MAX are also computed to avoid repeated computation + +#define GM_TRANS_PREC_BITS 3 +#define GM_TRANS_PREC_DIFF (WARPEDMODEL_PREC_BITS - GM_TRANS_PREC_BITS) +#define GM_TRANS_DECODE_FACTOR (1 << GM_TRANS_PREC_DIFF) + +#define GM_ALPHA_PREC_BITS 5 +#define GM_ALPHA_PREC_DIFF (WARPEDMODEL_PREC_BITS - GM_ALPHA_PREC_BITS) +#define GM_ALPHA_DECODE_FACTOR (1 << GM_ALPHA_PREC_DIFF) + +#define GM_ABS_ALPHA_BITS 5 +#define GM_ABS_TRANS_BITS 5 + +#define GM_TRANS_MAX (1 << GM_ABS_TRANS_BITS) +#define GM_ALPHA_MAX (1 << GM_ABS_ALPHA_BITS) +#define GM_TRANS_MIN -GM_TRANS_MAX +#define GM_ALPHA_MIN -GM_ALPHA_MAX + +typedef enum { + GLOBAL_ZERO = 0, + GLOBAL_TRANSLATION = 1, + GLOBAL_ROTZOOM = 2, + GLOBAL_MOTION_TYPES +} GLOBAL_MOTION_TYPE; + +typedef struct { + GLOBAL_MOTION_TYPE gmtype; + WarpedMotionParams motion_params; +} Global_Motion_Params; + +static INLINE GLOBAL_MOTION_TYPE get_gmtype(const Global_Motion_Params *gm) { + if (gm->motion_params.wmmat[2] == 0 && gm->motion_params.wmmat[3] == 0) { + return ((gm->motion_params.wmmat[0] | gm->motion_params.wmmat[1]) ? + GLOBAL_TRANSLATION : GLOBAL_ZERO); + } else { + return GLOBAL_ROTZOOM; + } +} +#endif // CONFIG_GLOBAL_MOTION + #if CONFIG_REF_MV typedef struct candidate_mv { int_mv this_mv; diff --git a/vp10/common/onyxc_int.h b/vp10/common/onyxc_int.h index b2d65b5f9c..37f7631c7e 100644 --- a/vp10/common/onyxc_int.h +++ b/vp10/common/onyxc_int.h @@ -350,6 +350,9 @@ typedef struct VP10Common { // - this is intentionally not placed in FRAME_CONTEXT since it's reset upon // each keyframe and not used afterwards vpx_prob kf_y_prob[INTRA_MODES][INTRA_MODES][INTRA_MODES - 1]; +#if CONFIG_GLOBAL_MOTION + Global_Motion_Params global_motion[MAX_REF_FRAMES]; +#endif BLOCK_SIZE sb_size; // Size of the superblock used for this frame int mib_size; // Size of the superblock in units of MI blocks diff --git a/vp10/decoder/decodeframe.c b/vp10/decoder/decodeframe.c index d4c7d5efa8..66b44a36bd 100644 --- a/vp10/decoder/decodeframe.c +++ b/vp10/decoder/decodeframe.c @@ -3443,6 +3443,55 @@ static void read_supertx_probs(FRAME_CONTEXT *fc, vp10_reader *r) { } #endif // CONFIG_SUPERTX +#if CONFIG_GLOBAL_MOTION +static void read_global_motion_params(Global_Motion_Params *params, + vpx_prob *probs, + vp10_reader *r) { + GLOBAL_MOTION_TYPE gmtype = vp10_read_tree(r, vp10_global_motion_types_tree, + probs); + params->gmtype = gmtype; + switch (gmtype) { + case GLOBAL_ZERO: + break; + case GLOBAL_TRANSLATION: + params->motion_params.wmtype = TRANSLATION; + params->motion_params.wmmat[0] = + vp10_read_primitive_symmetric(r, GM_ABS_TRANS_BITS) * + GM_TRANS_DECODE_FACTOR; + params->motion_params.wmmat[1] = + vp10_read_primitive_symmetric(r, GM_ABS_TRANS_BITS) * + GM_TRANS_DECODE_FACTOR; + break; + case GLOBAL_ROTZOOM: + params->motion_params.wmtype = ROTZOOM; + params->motion_params.wmmat[2] = + vp10_read_primitive_symmetric(r, GM_ABS_TRANS_BITS) * + GM_TRANS_DECODE_FACTOR; + params->motion_params.wmmat[3] = + vp10_read_primitive_symmetric(r, GM_ABS_TRANS_BITS) * + GM_TRANS_DECODE_FACTOR; + params->motion_params.wmmat[0] = + (vp10_read_primitive_symmetric(r, GM_ABS_ALPHA_BITS) * + GM_ALPHA_DECODE_FACTOR) + (1 << WARPEDMODEL_PREC_BITS); + params->motion_params.wmmat[1] = + vp10_read_primitive_symmetric(r, GM_ABS_ALPHA_BITS) * + GM_ALPHA_DECODE_FACTOR; + break; + default: + assert(0); + } +} + +static void read_global_motion(VP10_COMMON *cm, vp10_reader *r) { + int frame; + memset(cm->global_motion, 0, sizeof(cm->global_motion)); + for (frame = LAST_FRAME; frame <= ALTREF_FRAME; ++frame) { + read_global_motion_params( + &cm->global_motion[frame], cm->fc->global_motion_types_prob, r); + } +} +#endif // CONFIG_GLOBAL_MOTION + static int read_compressed_header(VP10Decoder *pbi, const uint8_t *data, size_t partition_size) { VP10_COMMON *const cm = &pbi->common; @@ -3585,6 +3634,9 @@ static int read_compressed_header(VP10Decoder *pbi, const uint8_t *data, if (!xd->lossless[0]) read_supertx_probs(fc, &r); #endif +#if CONFIG_GLOBAL_MOTION + read_global_motion(cm, &r); +#endif // CONFIG_GLOBAL_MOTION } return vp10_reader_has_error(&r); @@ -3714,6 +3766,9 @@ void vp10_decode_frame(VP10Decoder *pbi, init_read_bit_buffer(pbi, &rb, data, data_end, clear_data)); YV12_BUFFER_CONFIG *const new_fb = get_frame_new_buffer(cm); xd->cur_buf = new_fb; +#if CONFIG_GLOBAL_MOTION + xd->global_motion = cm->global_motion; +#endif // CONFIG_GLOBAL_MOTION if (!first_partition_size) { // showing a frame directly diff --git a/vp10/decoder/dsubexp.c b/vp10/decoder/dsubexp.c index 4d53e12c73..49695cc929 100644 --- a/vp10/decoder/dsubexp.c +++ b/vp10/decoder/dsubexp.c @@ -74,3 +74,13 @@ void vp10_diff_update_prob(vp10_reader *r, vpx_prob* p) { *p = (vpx_prob)inv_remap_prob(delp, *p); } } + +int vp10_read_primitive_symmetric(vp10_reader *r, unsigned int mag_bits) { + if (vp10_read_bit(r)) { + int s = vp10_read_bit(r); + int x = vp10_read_literal(r, mag_bits) + 1; + return (s > 0 ? -x : x); + } else { + return 0; + } +} diff --git a/vp10/decoder/dsubexp.h b/vp10/decoder/dsubexp.h index c05ec6e9f4..eaccf1fb26 100644 --- a/vp10/decoder/dsubexp.h +++ b/vp10/decoder/dsubexp.h @@ -24,4 +24,9 @@ void vp10_diff_update_prob(vp10_reader *r, vpx_prob* p); } // extern "C" #endif +// mag_bits is number of bits for magnitude. The alphabet is of size +// 2 * 2^mag_bits + 1, symmetric around 0, where one bit is used to +// indicate 0 or non-zero, mag_bits bits are used to indicate magnitide +// and 1 more bit for the sign if non-zero. +int vp10_read_primitive_symmetric(vp10_reader *r, unsigned int mag_bits); #endif // VP10_DECODER_DSUBEXP_H_ diff --git a/vp10/encoder/bitstream.c b/vp10/encoder/bitstream.c index 3999c94609..9743367b17 100644 --- a/vp10/encoder/bitstream.c +++ b/vp10/encoder/bitstream.c @@ -111,6 +111,9 @@ static struct vp10_token ext_tx_intra_encodings[EXT_TX_SETS_INTRA][TX_TYPES]; #else static struct vp10_token ext_tx_encodings[TX_TYPES]; #endif // CONFIG_EXT_TX +#if CONFIG_GLOBAL_MOTION +static struct vp10_token global_motion_types_encodings[GLOBAL_MOTION_TYPES]; +#endif // CONFIG_GLOBAL_MOTION #if CONFIG_EXT_INTRA static struct vp10_token intra_filter_encodings[INTRA_FILTERS]; #endif // CONFIG_EXT_INTRA @@ -142,6 +145,10 @@ void vp10_encode_token_init(void) { #if CONFIG_OBMC || CONFIG_WARPED_MOTION vp10_tokens_from_tree(motvar_encodings, vp10_motvar_tree); #endif // CONFIG_OBMC || CONFIG_WARPED_MOTION +#if CONFIG_GLOBAL_MOTION + vp10_tokens_from_tree(global_motion_types_encodings, + vp10_global_motion_types_tree); +#endif // CONFIG_GLOBAL_MOTION } static void write_intra_mode(vp10_writer *w, PREDICTION_MODE mode, @@ -3181,6 +3188,50 @@ static void write_uncompressed_header(VP10_COMP *cpi, write_tile_info(cm, wb); } +#if CONFIG_GLOBAL_MOTION +static void write_global_motion_params(Global_Motion_Params *params, + vpx_prob *probs, + vp10_writer *w) { + GLOBAL_MOTION_TYPE gmtype = get_gmtype(params); + vp10_write_token(w, vp10_global_motion_types_tree, probs, + &global_motion_types_encodings[gmtype]); + switch (gmtype) { + case GLOBAL_ZERO: + break; + case GLOBAL_TRANSLATION: + vp10_write_primitive_symmetric(w, params->motion_params.wmmat[0], + GM_ABS_TRANS_BITS); + vp10_write_primitive_symmetric(w, params->motion_params.wmmat[1], + GM_ABS_TRANS_BITS); + break; + case GLOBAL_ROTZOOM: + vp10_write_primitive_symmetric(w, params->motion_params.wmmat[0], + GM_ABS_TRANS_BITS); + vp10_write_primitive_symmetric(w, params->motion_params.wmmat[1], + GM_ABS_TRANS_BITS); + vp10_write_primitive_symmetric(w, params->motion_params.wmmat[2], + GM_ABS_ALPHA_BITS); + vp10_write_primitive_symmetric(w, params->motion_params.wmmat[3], + GM_ABS_ALPHA_BITS); + break; + default: + assert(0); + } +} + +static void write_global_motion(VP10_COMP *cpi, vp10_writer *w) { + VP10_COMMON *const cm = &cpi->common; + int frame; + for (frame = LAST_FRAME; frame <= ALTREF_FRAME; ++frame) { + if (!cpi->global_motion_used[frame]) { + memset(&cm->global_motion[frame], 0, + sizeof(*cm->global_motion)); + } + write_global_motion_params( + &cm->global_motion[frame], cm->fc->global_motion_types_prob, w); + } +} +#endif static uint32_t write_compressed_header(VP10_COMP *cpi, uint8_t *data) { VP10_COMMON *const cm = &cpi->common; @@ -3349,7 +3400,9 @@ static uint32_t write_compressed_header(VP10_COMP *cpi, uint8_t *data) { update_supertx_probs(cm, header_bc); #endif // CONFIG_SUPERTX } - +#if CONFIG_GLOBAL_MOTION + write_global_motion(cpi, header_bc); +#endif // CONFIG_GLOBAL_MOTION #if CONFIG_ANS ans_write_init(&header_ans, data); buf_ans_flush(header_bc, &header_ans); diff --git a/vp10/encoder/encodeframe.c b/vp10/encoder/encodeframe.c index 3119acf284..b91615bf3e 100644 --- a/vp10/encoder/encodeframe.c +++ b/vp10/encoder/encodeframe.c @@ -4536,6 +4536,78 @@ static int input_fpmb_stats(FIRSTPASS_MB_STATS *firstpass_mb_stats, } #endif +#if CONFIG_GLOBAL_MOTION +#define MIN_TRANS_THRESH 8 + +static void convert_translation_to_params( + double *H, Global_Motion_Params *model) { + model->motion_params.wmmat[0] = (int) floor(H[0] * + (1 << GM_TRANS_PREC_BITS) + 0.5); + model->motion_params.wmmat[1] = (int) floor(H[1] * + (1 << GM_TRANS_PREC_BITS) + 0.5); + if (abs(model->motion_params.wmmat[0]) < MIN_TRANS_THRESH && + abs(model->motion_params.wmmat[1]) < MIN_TRANS_THRESH) { + model->motion_params.wmmat[0] = 0; + model->motion_params.wmmat[1] = 0; + } else { + model->motion_params.wmmat[0] = + clamp(model->motion_params.wmmat[0], + GM_TRANS_MIN, GM_TRANS_MAX); + model->motion_params.wmmat[1] = + clamp(model->motion_params.wmmat[1], + GM_TRANS_MIN, GM_TRANS_MAX); + } +} + +static void convert_rotzoom_to_params(double *H, Global_Motion_Params *model) { + model->motion_params.wmmat[0] = (int) floor(H[0] * + (1 << GM_TRANS_PREC_BITS) + 0.5); + model->motion_params.wmmat[1] = (int) floor(H[1] * + (1 << GM_TRANS_PREC_BITS) + 0.5); + model->motion_params.wmmat[0] = + clamp(model->motion_params.wmmat[0], + GM_TRANS_MIN, GM_TRANS_MAX); + model->motion_params.wmmat[1] = + clamp(model->motion_params.wmmat[1], + GM_TRANS_MIN, GM_TRANS_MAX); + + model->motion_params.wmmat[2] = (int) floor(H[2] * + (1 << GM_ALPHA_PREC_BITS) + 0.5) - + (1 << GM_ALPHA_PREC_BITS); + model->motion_params.wmmat[3] = (int) floor(H[3] * + (1 << GM_ALPHA_PREC_BITS) + 0.5); + + model->motion_params.wmmat[2] = clamp(model->motion_params.wmmat[2], + GM_ALPHA_MIN, GM_ALPHA_MAX); + model->motion_params.wmmat[3] = clamp(model->motion_params.wmmat[3], + GM_ALPHA_MIN, GM_ALPHA_MAX); + + if (model->motion_params.wmmat[2] == 0 && + model->motion_params.wmmat[3] == 0) { + if (abs(model->motion_params.wmmat[0]) < MIN_TRANS_THRESH && + abs(model->motion_params.wmmat[1]) < MIN_TRANS_THRESH) { + model->motion_params.wmmat[0] = 0; + model->motion_params.wmmat[1] = 0; + } + } +} + +static void convert_model_to_params(double *H, TransformationType type, + Global_Motion_Params *model) { + switch (type) { + case ROTZOOM: + convert_rotzoom_to_params(H, model); + break; + case TRANSLATION: + convert_translation_to_params(H, model); + break; + default: + break; + } + model->gmtype = get_gmtype(model); +} +#endif // CONFIG_GLOBAL_MOTION + static void encode_frame_internal(VP10_COMP *cpi) { ThreadData *const td = &cpi->td; MACROBLOCK *const x = &td->mb; @@ -4556,6 +4628,18 @@ static void encode_frame_internal(VP10_COMP *cpi) { rdc->m_search_count = 0; // Count of motion search hits. rdc->ex_search_count = 0; // Exhaustive mesh search hits. +#if CONFIG_GLOBAL_MOTION + // TODO(sarahparker) this is a placeholder for gm computation + vpx_clear_system_state(); + vp10_zero(cpi->global_motion_used); + if (cpi->common.frame_type == INTER_FRAME && cpi->Source) { + int frame; + double H[9] = {0, 0, 0, 0, 0, 0, 0, 0, 1}; + for (frame = LAST_FRAME; frame <= ALTREF_FRAME; ++frame) + convert_model_to_params(H, ROTZOOM, &cm->global_motion[frame]); + } +#endif // CONFIG_GLOBAL_MOTION + for (i = 0; i < MAX_SEGMENTS; ++i) { const int qindex = cm->seg.enabled ? vp10_get_qindex(&cm->seg, i, cm->base_qindex) : cm->base_qindex; @@ -4569,7 +4653,6 @@ static void encode_frame_internal(VP10_COMP *cpi) { x->optimize = 0; cm->tx_mode = select_tx_mode(cpi, xd); - vp10_frame_init_quantizer(cpi); vp10_initialize_rd_consts(cpi); diff --git a/vp10/encoder/encoder.h b/vp10/encoder/encoder.h index d7c62b2588..5b07c39517 100644 --- a/vp10/encoder/encoder.h +++ b/vp10/encoder/encoder.h @@ -615,6 +615,9 @@ typedef struct VP10_COMP { int refresh_frame_mask; int existing_fb_idx_to_show; #endif // CONFIG_EXT_REFS +#if CONFIG_GLOBAL_MOTION + int global_motion_used[MAX_REF_FRAMES]; +#endif } VP10_COMP; void vp10_initialize_enc(void); diff --git a/vp10/encoder/subexp.c b/vp10/encoder/subexp.c index 4aaffae795..3a2e09f2fe 100644 --- a/vp10/encoder/subexp.c +++ b/vp10/encoder/subexp.c @@ -286,3 +286,16 @@ int vp10_cond_prob_diff_update_savings(vpx_prob *oldp, upd); return savings; } + +void vp10_write_primitive_symmetric(vp10_writer *w, int word, + unsigned int abs_bits) { + if (word == 0) { + vp10_write_bit(w, 0); + } else { + const int x = abs(word); + const int s = word < 0; + vp10_write_bit(w, 1); + vp10_write_bit(w, s); + vp10_write_literal(w, x - 1, abs_bits); + } +} diff --git a/vp10/encoder/subexp.h b/vp10/encoder/subexp.h index 756b499e53..dbd48ff62e 100644 --- a/vp10/encoder/subexp.h +++ b/vp10/encoder/subexp.h @@ -50,6 +50,13 @@ int vp10_prob_update_search_model_subframe(unsigned int ct[ENTROPY_NODES] int stepsize, int n); #endif // CONFIG_ENTROPY +// +// mag_bits is number of bits for magnitude. The alphabet is of size +// 2 * 2^mag_bits + 1, symmetric around 0, where one bit is used to +// indicate 0 or non-zero, mag_bits bits are used to indicate magnitide +// and 1 more bit for the sign if non-zero. +void vp10_write_primitive_symmetric(vp10_writer *w, int word, + unsigned int mag_bits); #ifdef __cplusplus } // extern "C" #endif -- GitLab