/* * Copyright (c) 2016, Alliance for Open Media. All rights reserved. * * This source code is subject to the terms of the BSD 2 Clause License and * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License * was not distributed with this source code in the LICENSE file, you can * obtain it at www.aomedia.org/license/software. If the Alliance for Open * Media Patent License 1.0 was not distributed with this source code in the * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
staticinlinevoid init_mv_cost_params(MV_COST_PARAMS *mv_cost_params, const MvCosts *mv_costs, const MV *ref_mv, int errorperbit, int sadperbit) {
mv_cost_params->ref_mv = ref_mv;
mv_cost_params->full_ref_mv = get_fullmv_from_mv(ref_mv);
mv_cost_params->mv_cost_type = MV_COST_ENTROPY;
mv_cost_params->error_per_bit = errorperbit;
mv_cost_params->sad_per_bit = sadperbit; // For allintra encoding mode, 'mv_costs' is not allocated. Hence, the // population of mvjcost and mvcost are avoided. In case of IntraBC, these // values are populated from 'dv_costs' in av1_set_ms_to_intra_mode(). if (mv_costs != NULL) {
mv_cost_params->mvjcost = mv_costs->nmv_joint_cost;
mv_cost_params->mvcost[0] = mv_costs->mv_cost_stack[0];
mv_cost_params->mvcost[1] = mv_costs->mv_cost_stack[1];
}
}
// If the absolute SAD difference computed between the pred-to-src of even // and odd rows is small, skip every other row in sad computation. constint odd_to_even_diff_sad =
abs((int)start_mv_sad_even_rows - (int)start_mv_sad_odd_rows); constint mult_thresh = 4; if (odd_to_even_diff_sad * mult_thresh < (int)start_mv_sad_even_rows) {
ms_params->sdf = ms_params->vfp->sdsf;
assert(ms_params->vfp->sdsx4df != NULL);
ms_params->sdx4df = ms_params->vfp->sdsx4df;
ms_params->sdx3df = ms_params->vfp->sdsx4df;
}
}
}
void av1_set_mv_search_range(FullMvLimits *mv_limits, const MV *mv) { // Calculate the outermost full-pixel MVs which are inside the limits set by // av1_set_subpel_mv_search_range(). // // The subpel limits are simply mv->col +/- 8*MAX_FULL_PEL_VAL, and similar // for mv->row. We can then divide by 8 to find the fullpel MV limits. But // we have to be careful about the rounding. We want these bounds to be // at least as tight as the subpel limits, which means that we must round // the minimum values up and the maximum values down when dividing. int col_min = ((mv->col + 7) >> 3) - MAX_FULL_PEL_VAL; int row_min = ((mv->row + 7) >> 3) - MAX_FULL_PEL_VAL; int col_max = (mv->col >> 3) + MAX_FULL_PEL_VAL; int row_max = (mv->row >> 3) + MAX_FULL_PEL_VAL;
// Get intersection of UMV window and valid MV window to reduce # of checks // in diamond search. if (mv_limits->col_min < col_min) mv_limits->col_min = col_min; if (mv_limits->col_max > col_max) mv_limits->col_max = col_max; if (mv_limits->row_min < row_min) mv_limits->row_min = row_min; if (mv_limits->row_max > row_max) mv_limits->row_max = row_max;
int av1_init_search_range(int size) { int sr = 0; // Minimum search size no matter what the passed in value.
size = AOMMAX(16, size);
while ((size << sr) < MAX_FULL_PEL_VAL) sr++;
sr = AOMMIN(sr, MAX_MVSEARCH_STEPS - 2); return sr;
}
// ============================================================================ // Cost of motion vectors // ============================================================================ // TODO(any): Adaptively adjust the regularization strength based on image size // and motion activity instead of using hard-coded values. It seems like we // roughly half the lambda for each increase in resolution // These are multiplier used to perform regularization in motion compensation // when x->mv_cost_type is set to MV_COST_L1. // LOWRES #define SSE_LAMBDA_LOWRES 2 // Used by mv_cost_err_fn #define SAD_LAMBDA_LOWRES 32 // Used by mvsad_err_cost during full pixel search // MIDRES #define SSE_LAMBDA_MIDRES 0 // Used by mv_cost_err_fn #define SAD_LAMBDA_MIDRES 15 // Used by mvsad_err_cost during full pixel search // HDRES #define SSE_LAMBDA_HDRES 1 // Used by mv_cost_err_fn #define SAD_LAMBDA_HDRES 8 // Used by mvsad_err_cost during full pixel search
// Returns the rate of encoding the current motion vector based on the // joint_cost and comp_cost. joint_costs covers the cost of transmitting // JOINT_MV, and comp_cost covers the cost of transmitting the actual motion // vector. staticinlineint mv_cost(const MV *mv, constint *joint_cost, constint *const comp_cost[2]) { return joint_cost[av1_get_mv_joint(mv)] + comp_cost[0][mv->row] +
comp_cost[1][mv->col];
}
#define CONVERT_TO_CONST_MVCOST(ptr) ((constint *const *)(ptr)) // Returns the cost of encoding the motion vector diff := *mv - *ref. The cost // is defined as the rate required to encode diff * weight, rounded to the // nearest 2 ** 7. // This is NOT used during motion compensation. int av1_mv_bit_cost(const MV *mv, const MV *ref_mv, constint *mvjcost, int *const mvcost[2], int weight) { const MV diff = { mv->row - ref_mv->row, mv->col - ref_mv->col }; return ROUND_POWER_OF_TWO(
mv_cost(&diff, mvjcost, CONVERT_TO_CONST_MVCOST(mvcost)) * weight, 7);
}
// Returns the cost of using the current mv during the motion search. This is // used when var is used as the error metric. #define PIXEL_TRANSFORM_ERROR_SCALE 4 staticinlineint mv_err_cost(const MV *mv, const MV *ref_mv, constint *mvjcost, constint *const mvcost[2], int error_per_bit, MV_COST_TYPE mv_cost_type) { const MV diff = { mv->row - ref_mv->row, mv->col - ref_mv->col }; const MV abs_diff = { abs(diff.row), abs(diff.col) };
// Returns the cost of using the current mv during the motion search. This is // only used during full pixel motion search when sad is used as the error // metric staticinlineint mvsad_err_cost(const FULLPEL_MV *mv, const FULLPEL_MV *ref_mv, constint *mvjcost, constint *const mvcost[2], int sad_per_bit, MV_COST_TYPE mv_cost_type) { const MV diff = { GET_MV_SUBPEL(mv->row - ref_mv->row),
GET_MV_SUBPEL(mv->col - ref_mv->col) };
// ============================================================================= // Fullpixel Motion Search: Translational // ============================================================================= #define MAX_PATTERN_SCALES 11 #define MAX_PATTERN_CANDIDATES 8 // max number of candidates per scale #define PATTERN_CANDIDATES_REF 3 // number of refinement candidates
// Search site initialization for DIAMOND / CLAMPED_DIAMOND search methods. // level = 0: DIAMOND, level = 1: CLAMPED_DIAMOND. staticvoid init_dsmotion_compensation(search_site_config *cfg, int stride, int level) { int num_search_steps = 0; int stage_index = MAX_MVSEARCH_STEPS - 1;
// Calculates and returns a sad+mvcost list around an integer best pel during // fullpixel motion search. The resulting list can be used to speed up subpel // motion search later. #define USE_SAD_COSTLIST 1
// calc_int_cost_list uses var to populate the costlist, which is more accurate // than sad but slightly slower. static AOM_FORCE_INLINE void calc_int_cost_list( const FULLPEL_MV best_mv, const FULLPEL_MOTION_SEARCH_PARAMS *ms_params, int *cost_list) { staticconst FULLPEL_MV neighbors[4] = {
{ 0, -1 }, { 1, 0 }, { 0, 1 }, { -1, 0 }
}; constint br = best_mv.row; constint bc = best_mv.col;
// Refresh the costlist it does not contain valid sad if (!costlist_has_sad) {
cost_list[0] = get_mvpred_sad(
ms_params, src, get_buf_from_fullmv(ref, &best_mv), ref_stride);
if (check_bounds(&ms_params->mv_limits, br, bc, 1)) { for (int i = 0; i < 4; i++) { const FULLPEL_MV this_mv = { br + neighbors[i].row,
bc + neighbors[i].col };
cost_list[i + 1] = get_mvpred_sad(
ms_params, src, get_buf_from_fullmv(ref, &this_mv), ref_stride);
}
} else { for (int i = 0; i < 4; i++) { const FULLPEL_MV this_mv = { br + neighbors[i].row,
bc + neighbors[i].col }; if (!av1_is_fullmv_in_range(&ms_params->mv_limits, this_mv)) {
cost_list[i + 1] = INT_MAX;
} else {
cost_list[i + 1] = get_mvpred_sad(
ms_params, src, get_buf_from_fullmv(ref, &this_mv), ref_stride);
}
}
}
}
// Computes motion vector cost and adds to the sad cost. // Then updates the best sad and motion vectors. // Inputs: // this_sad: the sad to be evaluated. // mv: the current motion vector. // mv_cost_params: a structure containing information to compute mv cost. // best_sad: the current best sad. // raw_best_sad (optional): the current best sad without calculating mv cost. // best_mv: the current best motion vector. // second_best_mv (optional): the second best motion vector up to now. // Modifies: // best_sad, raw_best_sad, best_mv, second_best_mv // If the current sad is lower than the current best sad. // Returns: // Whether the input sad (mv) is better than the current best. staticinlineint update_mvs_and_sad(constunsignedint this_sad, const FULLPEL_MV *mv, const MV_COST_PARAMS *mv_cost_params, unsignedint *best_sad, unsignedint *raw_best_sad,
FULLPEL_MV *best_mv,
FULLPEL_MV *second_best_mv) { if (this_sad >= *best_sad) return 0;
// Add the motion vector cost. constunsignedint sad = this_sad + mvsad_err_cost_(mv, mv_cost_params); if (sad < *best_sad) { if (raw_best_sad) *raw_best_sad = this_sad;
*best_sad = sad; if (second_best_mv) *second_best_mv = *best_mv;
*best_mv = *mv; return 1;
} return 0;
}
// Calculate sad4 and update the bestmv information // in FAST_DIAMOND search method. staticinlinevoid calc_sad4_update_bestmv( const FULLPEL_MOTION_SEARCH_PARAMS *ms_params, const MV_COST_PARAMS *mv_cost_params, FULLPEL_MV *best_mv, const FULLPEL_MV center_mv, const uint8_t *center_address, unsignedint *bestsad, unsignedint *raw_bestsad, int search_step, int *best_site, int cand_start, int *cost_list) { conststruct buf_2d *const src = ms_params->ms_buffers.src; conststruct buf_2d *const ref = ms_params->ms_buffers.ref; const search_site *site = ms_params->search_sites->site[search_step];
// Calculate sad and update the bestmv information // in FAST_DIAMOND search method. staticinlinevoid calc_sad_update_bestmv( const FULLPEL_MOTION_SEARCH_PARAMS *ms_params, const MV_COST_PARAMS *mv_cost_params, FULLPEL_MV *best_mv, const FULLPEL_MV center_mv, const uint8_t *center_address, unsignedint *bestsad, unsignedint *raw_bestsad, int search_step, int *best_site, constint num_candidates, int cand_start, int *cost_list) { conststruct buf_2d *const src = ms_params->ms_buffers.src; conststruct buf_2d *const ref = ms_params->ms_buffers.ref; const search_site *site = ms_params->search_sites->site[search_step]; // Loop over number of candidates. for (int i = cand_start; i < num_candidates; i++) { const FULLPEL_MV this_mv = { center_mv.row + site[i].mv.row,
center_mv.col + site[i].mv.col }; if (!av1_is_fullmv_in_range(&ms_params->mv_limits, this_mv)) continue; int thissad = get_mvpred_sad(ms_params, src,
center_address + site[i].offset, ref->stride); if (cost_list) {
cost_list[i + 1] = thissad;
} constint found_better_mv = update_mvs_and_sad(
thissad, &this_mv, mv_cost_params, bestsad, raw_bestsad, best_mv, /*second_best_mv=*/NULL); if (found_better_mv) *best_site = i;
}
}
staticinlinevoid calc_sad_update_bestmv_with_indices( const FULLPEL_MOTION_SEARCH_PARAMS *ms_params, const MV_COST_PARAMS *mv_cost_params, FULLPEL_MV *best_mv, const FULLPEL_MV center_mv, const uint8_t *center_address, unsignedint *bestsad, unsignedint *raw_bestsad, int search_step, int *best_site, constint num_candidates, constint *chkpts_indices, int *cost_list) { conststruct buf_2d *const src = ms_params->ms_buffers.src; conststruct buf_2d *const ref = ms_params->ms_buffers.ref; const search_site *site = ms_params->search_sites->site[search_step]; // Loop over number of candidates. for (int i = 0; i < num_candidates; i++) { int index = chkpts_indices[i]; const FULLPEL_MV this_mv = { center_mv.row + site[index].mv.row,
center_mv.col + site[index].mv.col }; if (!av1_is_fullmv_in_range(&ms_params->mv_limits, this_mv)) { if (cost_list) {
cost_list[index + 1] = INT_MAX;
} continue;
} constint thissad = get_mvpred_sad(
ms_params, src, center_address + site[index].offset, ref->stride); if (cost_list) {
cost_list[index + 1] = thissad;
} constint found_better_mv = update_mvs_and_sad(
thissad, &this_mv, mv_cost_params, bestsad, raw_bestsad, best_mv, /*second_best_mv=*/NULL); if (found_better_mv) *best_site = i;
}
}
// Generic pattern search function that searches over multiple scales. // Each scale can have a different number of candidates and shape of // candidates as indicated in the num_candidates and candidates arrays // passed into this function staticint pattern_search(FULLPEL_MV start_mv, const FULLPEL_MOTION_SEARCH_PARAMS *ms_params, int search_step, constint do_init_search, int *cost_list, FULLPEL_MV *best_mv,
FULLPEL_MV_STATS *best_mv_stats) { staticconstint search_steps[MAX_MVSEARCH_STEPS] = {
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
}; int i, s, t;
conststruct buf_2d *const src = ms_params->ms_buffers.src; conststruct buf_2d *const ref = ms_params->ms_buffers.ref; const search_site_config *search_sites = ms_params->search_sites; constint *num_candidates = search_sites->searches_per_step; constint ref_stride = ref->stride; constint last_is_4 = num_candidates[0] == 4; int br, bc; unsignedint bestsad = UINT_MAX, raw_bestsad = UINT_MAX; int k = -1; const MV_COST_PARAMS *mv_cost_params = &ms_params->mv_cost_params;
search_step = AOMMIN(search_step, MAX_MVSEARCH_STEPS - 1);
assert(search_step >= 0); int best_init_s = search_steps[search_step]; // adjust ref_mv to make sure it is within MV range
clamp_fullmv(&start_mv, &ms_params->mv_limits);
br = start_mv.row;
bc = start_mv.col; if (cost_list != NULL) {
cost_list[0] = cost_list[1] = cost_list[2] = cost_list[3] = cost_list[4] =
INT_MAX;
} int costlist_has_sad = 0;
// Work out the start point for the search
raw_bestsad = get_mvpred_sad(ms_params, src,
get_buf_from_fullmv(ref, &start_mv), ref_stride);
bestsad = raw_bestsad + mvsad_err_cost_(&start_mv, mv_cost_params);
// Search all possible scales up to the search param around the center point // pick the scale of the point that is best as the starting scale of // further steps around it. const uint8_t *center_address = get_buf_from_fullmv(ref, &start_mv); if (do_init_search) {
s = best_init_s;
best_init_s = -1; for (t = 0; t <= s; ++t) { int best_site = -1;
FULLPEL_MV center_mv = { br, bc }; if (check_bounds(&ms_params->mv_limits, br, bc, 1 << t)) { // Call 4-point sad for multiples of 4 candidates. constint no_of_4_cand_loops = num_candidates[t] >> 2; for (i = 0; i < no_of_4_cand_loops; i++) {
calc_sad4_update_bestmv(ms_params, mv_cost_params, best_mv, center_mv,
center_address, &bestsad, &raw_bestsad, t,
&best_site, i * 4, /*cost_list=*/NULL);
} // Rest of the candidates constint remaining_cand = num_candidates[t] % 4;
calc_sad_update_bestmv(ms_params, mv_cost_params, best_mv, center_mv,
center_address, &bestsad, &raw_bestsad, t,
&best_site, remaining_cand,
no_of_4_cand_loops * 4, NULL);
} else {
calc_sad_update_bestmv(ms_params, mv_cost_params, best_mv, center_mv,
center_address, &bestsad, &raw_bestsad, t,
&best_site, num_candidates[t], 0, NULL);
} if (best_site == -1) { continue;
} else {
best_init_s = t;
k = best_site;
}
} if (best_init_s != -1) {
br += search_sites->site[best_init_s][k].mv.row;
bc += search_sites->site[best_init_s][k].mv.col;
center_address += search_sites->site[best_init_s][k].offset;
}
}
// If the center point is still the best, just skip this and move to // the refinement step. if (best_init_s != -1) { constint last_s = (last_is_4 && cost_list != NULL); int best_site = -1;
s = best_init_s;
for (; s >= last_s; s--) { // No need to search all points the 1st time if initial search was used if (!do_init_search || s != best_init_s) {
FULLPEL_MV center_mv = { br, bc }; if (check_bounds(&ms_params->mv_limits, br, bc, 1 << s)) { // Call 4-point sad for multiples of 4 candidates. constint no_of_4_cand_loops = num_candidates[s] >> 2; for (i = 0; i < no_of_4_cand_loops; i++) {
calc_sad4_update_bestmv(ms_params, mv_cost_params, best_mv,
center_mv, center_address, &bestsad,
&raw_bestsad, s, &best_site, i * 4, /*cost_list=*/NULL);
} // Rest of the candidates constint remaining_cand = num_candidates[s] % 4;
calc_sad_update_bestmv(ms_params, mv_cost_params, best_mv, center_mv,
center_address, &bestsad, &raw_bestsad, s,
&best_site, remaining_cand,
no_of_4_cand_loops * 4, NULL);
} else {
calc_sad_update_bestmv(ms_params, mv_cost_params, best_mv, center_mv,
center_address, &bestsad, &raw_bestsad, s,
&best_site, num_candidates[s], 0, NULL);
}
if (best_site == -1) { continue;
} else {
br += search_sites->site[s][best_site].mv.row;
bc += search_sites->site[s][best_site].mv.col;
center_address += search_sites->site[s][best_site].offset;
k = best_site;
}
}
do { int next_chkpts_indices[PATTERN_CANDIDATES_REF];
best_site = -1;
next_chkpts_indices[0] = (k == 0) ? num_candidates[s] - 1 : k - 1;
next_chkpts_indices[1] = k;
next_chkpts_indices[2] = (k == num_candidates[s] - 1) ? 0 : k + 1;
if (best_site != -1) {
k = next_chkpts_indices[best_site];
br += search_sites->site[s][k].mv.row;
bc += search_sites->site[s][k].mv.col;
center_address += search_sites->site[s][k].offset;
}
}
}
}
best_mv->row = br;
best_mv->col = bc;
assert(center_address == get_buf_from_fullmv(ref, best_mv) && "center address is out of sync with best_mv!\n");
// Returns the one-away integer pel cost/sad around the best as follows: // cost_list[0]: cost/sad at the best integer pel // cost_list[1]: cost/sad at delta {0, -1} (left) from the best integer pel // cost_list[2]: cost/sad at delta { 1, 0} (bottom) from the best integer pel // cost_list[3]: cost/sad at delta { 0, 1} (right) from the best integer pel // cost_list[4]: cost/sad at delta {-1, 0} (top) from the best integer pel if (cost_list) { if (USE_SAD_COSTLIST) {
calc_int_sad_list(*best_mv, ms_params, cost_list, costlist_has_sad);
} else {
calc_int_cost_list(*best_mv, ms_params, cost_list);
}
}
// For the following foo_search, the input arguments are: // start_mv: where we are starting our motion search // ms_params: a collection of motion search parameters // search_step: how many steps to skip in our motion search. For example, // a value 3 suggests that 3 search steps have already taken place prior to // this function call, so we jump directly to step 4 of the search process // do_init_search: if on, do an initial search of all possible scales around the // start_mv, and then pick the best scale. // cond_list: used to hold the cost around the best full mv so we can use it to // speed up subpel search later. // best_mv: the best mv found in the motion search staticint hex_search(const FULLPEL_MV start_mv, const FULLPEL_MOTION_SEARCH_PARAMS *ms_params, constint search_step, constint do_init_search, int *cost_list, FULLPEL_MV *best_mv,
FULLPEL_MV_STATS *best_mv_stats) { return pattern_search(start_mv, ms_params, search_step, do_init_search,
cost_list, best_mv, best_mv_stats);
}
int is_off_center = 0; // Number of times that we have stayed in the middle. This is used to skip // search steps in the future if diamond_search_sad is called again. int num_center_steps = 0;
// search_step determines the length of the initial step and hence the number // of iterations. constint tot_steps = cfg->num_search_steps - search_step;
FULLPEL_MV tmp_second_best_mv; if (second_best_mv) {
tmp_second_best_mv = *second_best_mv;
}
*best_mv = start_mv;
// Check the starting position const uint8_t *best_address = get_buf_from_fullmv(ref, &start_mv); unsignedint bestsad = start_mv_sad;
// TODO(chiyotsai@google.com): Implement 4 points search for msdf&sdaf if (ms_params->ms_buffers.second_pred) { for (int step = tot_steps - 1; step >= 0; --step) { const search_site *site = cfg->site[step]; constint num_searches = cfg->searches_per_step[step]; int best_site = 0;
int bestsme = get_mvpred_compound_var_cost(ms_params, best_mv, best_mv_stats);
// If there won't be more n-step search, check to see if refining search is // needed. constint further_steps = cfg->num_search_steps - 1 - step_param; while (n < further_steps) {
++n;
// TODO(chiyotsai@google.com): There is another bug here where the second // best mv gets incorrectly overwritten. Fix it later.
FULLPEL_MV tmp_best_mv;
FULLPEL_MV_STATS tmp_best_mv_stats;
diamond_search_sad(start_mv, start_mv_sad, ms_params, step_param + n,
&num00, &tmp_best_mv, second_best_mv);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.