/* * Copyright (c) 2019, 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.
*/
#if !CONFIG_REALTIME_ONLY staticinlinevoid simple_motion_search_prune_part_features(
AV1_COMP *const cpi, MACROBLOCK *x, SIMPLE_MOTION_DATA_TREE *sms_tree, int mi_row, int mi_col, BLOCK_SIZE bsize, float *features, int features_to_get);
staticbool ext_ml_model_decision_before_none(
AV1_COMP *cpi, constfloat features_from_motion[FEATURE_SIZE_SMS_SPLIT], int *partition_none_allowed, int *partition_horz_allowed, int *partition_vert_allowed, int *do_rectangular_split, int *do_square_split);
staticbool ext_ml_model_decision_before_none_part2(
AV1_COMP *cpi, constfloat features_from_motion[FEATURE_SIZE_SMS_PRUNE_PART], int *prune_horz, int *prune_vert);
staticbool ext_ml_model_decision_after_none(
ExtPartController *const ext_part_controller, constint is_intra_frame, constfloat *const features_after_none, int *do_square_split, int *do_rectangular_split);
staticbool ext_ml_model_decision_after_none_part2(
AV1_COMP *const cpi, constfloat *const features_terminate, int *terminate_partition_search);
staticbool ext_ml_model_decision_after_split(
AV1_COMP *const cpi, constfloat *const features_terminate, int *terminate_partition_search);
staticbool ext_ml_model_decision_after_split_part2(
ExtPartController *const ext_part_controller, constint is_intra_frame, constfloat *const features_prune, int *prune_rect_part_horz, int *prune_rect_part_vert);
staticbool ext_ml_model_decision_after_rect(
ExtPartController *const ext_part_controller, constint is_intra_frame, constfloat *const features_after_rect, int *horza_partition_allowed, int *horzb_partition_allowed, int *verta_partition_allowed, int *vertb_partition_allowed);
staticbool ext_ml_model_decision_after_part_ab(
AV1_COMP *const cpi, MACROBLOCK *const x, BLOCK_SIZE bsize, int part_ctx,
int64_t best_rd, int64_t rect_part_rd[NUM_RECT_PARTS][SUB_PARTITIONS_RECT],
int64_t split_rd[SUB_PARTITIONS_SPLIT], int *const partition_horz4_allowed, int *const partition_vert4_allowed, unsignedint pb_source_variance, int mi_row, int mi_col);
staticinlineint convert_bsize_to_idx(BLOCK_SIZE bsize) { switch (bsize) { case BLOCK_128X128: return 0; case BLOCK_64X64: return 1; case BLOCK_32X32: return 2; case BLOCK_16X16: return 3; case BLOCK_8X8: return 4; default: assert(0 && "Invalid bsize"); return -1;
}
}
char filename[256];
snprintf(filename, sizeof(filename), "%s/%s", path,
get_feature_file_name(id));
FILE *pfile = fopen(filename, "a"); if (pfile == NULL) return; if (!is_test_mode) {
fprintf(pfile, "%d,%d,%d,%d,%d\n", id, (int)bsize, mi_row, mi_col,
feature_size);
} for (int i = 0; i < feature_size; ++i) {
fprintf(pfile, "%.6f", features[i]); if (i < feature_size - 1) fprintf(pfile, ",");
}
fprintf(pfile, "\n");
fclose(pfile);
}
// TODO(chiyotsai@google.com): This is very much a work in progress. We still // need to the following: // -- add support for hdres // -- add support for pruning rectangular partitions // -- use reconstructed pixels instead of source pixels for padding // -- use chroma pixels in addition to luma pixels staticvoid intra_mode_cnn_partition(const AV1_COMMON *const cm, MACROBLOCK *x, int quad_tree_idx, int intra_cnn_based_part_prune_level,
PartitionSearchState *part_state) {
assert(cm->seq_params->sb_size >= BLOCK_64X64 && "Invalid sb_size for intra_cnn!"); const PartitionBlkParams *blk_params = &part_state->part_blk_params; const BLOCK_SIZE bsize = blk_params->bsize;
// Precompute the CNN part and cache the result in MACROBLOCK if (bsize == BLOCK_64X64 && !part_info->cnn_output_valid) { const CNN_CONFIG *cnn_config = &av1_intra_mode_cnn_partition_cnn_config;
if (logits[0] > split_only_thresh) { // As screen contents tend to choose larger partitions, do not prune // PARTITION_NONE when intra_cnn_based_part_prune_level=1. if (intra_cnn_based_part_prune_level != 1) {
part_state->partition_none_allowed = 0;
}
part_state->do_square_split = 1;
av1_disable_rect_partitions(part_state);
}
if (logits[0] < no_split_thresh) {
av1_disable_square_split_partition(part_state);
}
}
staticinlineint get_simple_motion_search_prune_agg(int qindex, int prune_level, int is_rect_part) {
assert(prune_level < TOTAL_AGG_LVLS); if (prune_level == NO_PRUNING) { return -1;
}
// Aggressiveness value for SIMPLE_MOTION_SEARCH_PRUNE_LEVEL except // QIDX_BASED_AGG_LVL constint sms_prune_agg_levels[TOTAL_SIMPLE_AGG_LVLS] = { 0, 1, 2, 3 }; if (prune_level < TOTAL_SIMPLE_AGG_LVLS) { return sms_prune_agg_levels[prune_level];
}
// Map the QIDX_BASED_AGG_LVL to corresponding aggressiveness value. // Aggressive pruning for lower quantizers in non-boosted frames to prune // rectangular partitions. constint qband = is_rect_part ? (qindex <= 90 ? 1 : 0) : 0; constint sms_prune_agg_qindex_based[2] = { 1, 2 }; return sms_prune_agg_qindex_based[qband];
}
// Performs a simple_motion_search with a single reference frame and extract // the variance of residues. Then use the features to determine whether we want // to go straight to splitting without trying PARTITION_NONE staticvoid simple_motion_search_based_split(AV1_COMP *const cpi, MACROBLOCK *x,
SIMPLE_MOTION_DATA_TREE *sms_tree,
PartitionSearchState *part_state) { const AV1_COMMON *const cm = &cpi->common; const PartitionBlkParams *blk_params = &part_state->part_blk_params; constint mi_row = blk_params->mi_row, mi_col = blk_params->mi_col; const BLOCK_SIZE bsize = blk_params->bsize;
constint bsize_idx = convert_bsize_to_idx(bsize); constint is_720p_or_larger = AOMMIN(cm->width, cm->height) >= 720; constint is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480; // res_idx is 0 for res < 480p, 1 for 480p, 2 for 720p+ constint res_idx = is_480p_or_larger + is_720p_or_larger;
// Write features to file
write_features_to_file(cpi->oxcf.partition_info_path,
cpi->ext_part_controller.test_mode, features,
FEATURE_SIZE_SMS_SPLIT, 0, bsize, mi_row, mi_col);
// Note: it is intended to not normalize the features here, to keep it // consistent for all features collected and passed to the external model. if (ext_ml_model_decision_before_none(
cpi, features, &part_state->partition_none_allowed,
&part_state->partition_rect_allowed[HORZ],
&part_state->partition_rect_allowed[VERT],
&part_state->do_rectangular_split, &part_state->do_square_split)) { return;
}
// If the score is very low, prune rectangular split since it is unlikely to // occur. if (cpi->sf.part_sf.simple_motion_search_rect_split) { constfloat scale = res_idx >= 2 ? 3.0f : 2.0f; constfloat rect_split_thresh =
scale * av1_simple_motion_search_no_split_thresh
[cpi->sf.part_sf.simple_motion_search_rect_split][res_idx]
[bsize_idx]; if (score < rect_split_thresh) {
part_state->do_rectangular_split = 0;
}
}
}
// Given a list of ref frames in refs, performs simple_motion_search on each of // the refs and returns the ref with the smallest sse. Returns -1 if none of the // ref in the list is available. Also stores the best sse and var in best_sse, // best_var, respectively. If save_mv is 0, don't update mv_ref_fulls in // sms_tree. If save_mv is 1, update mv_ref_fulls under sms_tree and the // subtrees. staticint simple_motion_search_get_best_ref(
AV1_COMP *const cpi, MACROBLOCK *x, SIMPLE_MOTION_DATA_TREE *sms_tree, int mi_row, int mi_col, BLOCK_SIZE bsize, constint *const refs, int num_refs, int use_subpixel, int save_mv, unsignedint *best_sse, unsignedint *best_var) { const AV1_COMMON *const cm = &cpi->common; int best_ref = -1;
if (mi_col >= cm->mi_params.mi_cols || mi_row >= cm->mi_params.mi_rows) { // If the whole block is outside of the image, set the var and sse to 0.
*best_var = 0;
*best_sse = 0;
return best_ref;
}
// Otherwise do loop through the reference frames and find the one with the // minimum SSE constint num_planes = 1;
if (bsize >= BLOCK_8X8) { for (int r_idx = 0; r_idx < SUB_PARTITIONS_SPLIT; r_idx++) { // Propagate the new motion vectors to a lower level
SIMPLE_MOTION_DATA_TREE *sub_tree = sms_tree->split[r_idx];
sub_tree->start_mvs[ref] = sms_tree->start_mvs[ref];
}
}
}
}
}
return best_ref;
}
// Collects features using simple_motion_search and store them in features. The // features are also cached in SIMPLE_MOTION_DATA_TREE. By default, the features // collected are the sse and var from the subblocks flagged by features_to_get. // Furthermore, if features is not NULL, then 7 more features are appended to // the end of features: // - log(1.0 + dc_q ** 2) // - whether an above macroblock exists // - width of above macroblock // - height of above macroblock // - whether a left marcoblock exists // - width of left macroblock // - height of left macroblock staticinlinevoid simple_motion_search_prune_part_features(
AV1_COMP *const cpi, MACROBLOCK *x, SIMPLE_MOTION_DATA_TREE *sms_tree, int mi_row, int mi_col, BLOCK_SIZE bsize, float *features, int features_to_get) { constint w_mi = mi_size_wide[bsize]; constint h_mi = mi_size_high[bsize];
assert(mi_size_wide[bsize] == mi_size_high[bsize]);
assert(bsize >= BLOCK_8X8);
assert(cpi->ref_frame_flags & av1_ref_frame_flag_list[LAST_FRAME] ||
cpi->ref_frame_flags & av1_ref_frame_flag_list[ALTREF_FRAME]);
// Performs a simple_motion_search with two reference frames and extract // the variance of residues. Then use the features to determine whether we want // to prune some partitions. staticvoid simple_motion_search_prune_rect(AV1_COMP *const cpi, MACROBLOCK *x,
SIMPLE_MOTION_DATA_TREE *sms_tree,
PartitionSearchState *part_state) { const AV1_COMMON *const cm = &cpi->common; const PartitionBlkParams *blk_params = &part_state->part_blk_params; constint mi_row = blk_params->mi_row, mi_col = blk_params->mi_col; const BLOCK_SIZE bsize = blk_params->bsize;
constint bsize_idx = convert_bsize_to_idx(bsize); constint is_720p_or_larger = AOMMIN(cm->width, cm->height) >= 720; constint is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480; // res_idx is 0 for lowres, 1 for 48p, 2 for 720p+ constint res_idx = is_480p_or_larger + is_720p_or_larger;
// Get model parameters const NN_CONFIG *nn_config =
av1_simple_motion_search_prune_rect_nn_config[bsize_idx]; constfloat *ml_mean = av1_simple_motion_search_prune_rect_mean[bsize_idx],
*ml_std = av1_simple_motion_search_prune_rect_std[bsize_idx];
// If there is no valid threshold, return immediately. if (!nn_config || prune_thresh == 0.0f) { return;
}
// Get features float features[FEATURE_SIZE_SMS_PRUNE_PART] = { 0.0f };
simple_motion_search_prune_part_features(cpi, x, sms_tree, mi_row, mi_col,
bsize, features,
FEATURE_SMS_PRUNE_PART_FLAG);
// Note: it is intended to not normalize the features here, to keep it // consistent for all features collected and passed to the external model. if (cpi->sf.part_sf.simple_motion_search_prune_rect &&
!frame_is_intra_only(cm) &&
(part_state->partition_rect_allowed[HORZ] ||
part_state->partition_rect_allowed[VERT]) &&
bsize >= BLOCK_8X8 && !av1_superres_scaled(cm)) { // Write features to file
write_features_to_file(
cpi->oxcf.partition_info_path, cpi->ext_part_controller.test_mode,
features, FEATURE_SIZE_SMS_PRUNE_PART, 1, bsize, mi_row, mi_col);
if (ext_ml_model_decision_before_none_part2(
cpi, features, &part_state->prune_rect_part[HORZ],
&part_state->prune_rect_part[VERT])) { return;
}
}
// Determine if we should prune rectangular partitions. if (probs[PARTITION_HORZ] <= prune_thresh) {
part_state->prune_rect_part[HORZ] = 1;
} if (probs[PARTITION_VERT] <= prune_thresh) {
part_state->prune_rect_part[VERT] = 1;
}
}
// Early terminates PARTITION_NONE using simple_motion_search features and the // rate, distortion, and rdcost of PARTITION_NONE. This is only called when: // - The frame is a show frame // - The frame is not intra only // - The current bsize is > BLOCK_8X8 // - blk_row + blk_height/2 < total_rows and blk_col + blk_width/2 < total_cols void av1_simple_motion_search_early_term_none(
AV1_COMP *const cpi, MACROBLOCK *x, SIMPLE_MOTION_DATA_TREE *sms_tree, const RD_STATS *none_rdc, PartitionSearchState *part_state) { const PartitionBlkParams *blk_params = &part_state->part_blk_params; constint mi_row = blk_params->mi_row, mi_col = blk_params->mi_col; const BLOCK_SIZE bsize = blk_params->bsize;
int result = MAX_NUM_CLASSES_MAX_MIN_PART_PRED - 1; if (cpi->sf.part_sf.auto_max_partition_based_on_simple_motion ==
DIRECT_PRED) {
result = 0; float max_score = scores[0]; for (int i = 1; i < MAX_NUM_CLASSES_MAX_MIN_PART_PRED; ++i) { if (scores[i] > max_score) {
max_score = scores[i];
result = i;
}
} return get_block_size(result);
}
if (cpi->sf.part_sf.auto_max_partition_based_on_simple_motion ==
RELAXED_PRED) { for (result = MAX_NUM_CLASSES_MAX_MIN_PART_PRED - 1; result >= 0;
--result) { if (result < MAX_NUM_CLASSES_MAX_MIN_PART_PRED - 1) {
probs[result] += probs[result + 1];
} if (probs[result] > 0.2) break;
}
} elseif (cpi->sf.part_sf.auto_max_partition_based_on_simple_motion ==
ADAPT_PRED) { const BLOCK_SIZE sb_size = cpi->common.seq_params->sb_size; // TODO(debargha): x->source_variance is unavailable at this point, // so compute. The redundant recomputation later can be removed. constunsignedint source_variance = av1_get_perpixel_variance_facade(
cpi, &x->e_mbd, &x->plane[0].src, sb_size, AOM_PLANE_Y); if (source_variance > 16) { constdouble thresh = source_variance < 128 ? 0.05 : 0.1; for (result = MAX_NUM_CLASSES_MAX_MIN_PART_PRED - 1; result >= 0;
--result) { if (result < MAX_NUM_CLASSES_MAX_MIN_PART_PRED - 1) {
probs[result] += probs[result + 1];
} if (probs[result] > thresh) break;
}
}
}
return get_block_size(result);
}
// Get the minimum partition block width and height(in log scale) under a // SIMPLE_MOTION_DATA_TREE. staticinlinevoid get_min_bsize(const SIMPLE_MOTION_DATA_TREE *sms_tree, int *min_bw, int *min_bh) { if (!sms_tree) return;
// Write features to file
write_features_to_file(cpi->oxcf.partition_info_path,
cpi->ext_part_controller.test_mode, features, FEATURES,
4, bsize, mi_row, mi_col);
if (ext_ml_model_decision_after_split(
cpi, features, &part_state->terminate_partition_search)) { return;
}
float score = 0.0f;
av1_nn_predict(features, nn_config, 1, &score); // Score is indicator of confidence that we should NOT terminate. if (score < thresh) {
part_state->terminate_partition_search = 1;
}
} #undef FEATURES
for (int i = 0; i < SUB_PARTITIONS_SPLIT; i++)
features[5 + i] = (float)split_variance[i] / (float)whole_block_variance;
// Write features to file
write_features_to_file(cpi->oxcf.partition_info_path,
cpi->ext_part_controller.test_mode, features, /*feature_size=*/9, 5, bsize, mi_row, mi_col);
if (ext_ml_model_decision_after_split_part2(
&cpi->ext_part_controller, frame_is_intra_only(&cpi->common),
features, &part_state->prune_rect_part[HORZ],
&part_state->prune_rect_part[VERT])) { return;
}
// 2. Do the prediction and prune 0-2 partitions based on their probabilities float raw_scores[3] = { 0.0f };
av1_nn_predict(features, nn_config, 1, raw_scores); float probs[3] = { 0.0f };
av1_nn_softmax(raw_scores, probs, 3);
// probs[0] is the probability of the fact that both rectangular partitions // are worse than current best_rd if (probs[1] <= cur_thresh) part_state->prune_rect_part[HORZ] = 1; if (probs[2] <= cur_thresh) part_state->prune_rect_part[VERT] = 1;
}
// Use a ML model to predict if horz_a, horz_b, vert_a, and vert_b should be // considered. staticvoid ml_prune_ab_partition(AV1_COMP *const cpi, int part_ctx, int var_ctx, int64_t best_rd,
PartitionSearchState *part_state, int *ab_partitions_allowed) { const PartitionBlkParams blk_params = part_state->part_blk_params; constint mi_row = blk_params.mi_row; constint mi_col = blk_params.mi_col; const BLOCK_SIZE bsize = blk_params.bsize;
if (bsize < BLOCK_8X8 || best_rd >= 1000000000) return; const NN_CONFIG *nn_config = NULL; switch (bsize) { case BLOCK_8X8: nn_config = NULL; break; case BLOCK_16X16: nn_config = &av1_ab_partition_nnconfig_16; break; case BLOCK_32X32: nn_config = &av1_ab_partition_nnconfig_32; break; case BLOCK_64X64: nn_config = &av1_ab_partition_nnconfig_64; break; case BLOCK_128X128: nn_config = &av1_ab_partition_nnconfig_128; break; default: assert(0 && "Unexpected bsize.");
} if (!nn_config) return;
// Generate features. float features[10]; int feature_index = 0;
features[feature_index++] = (float)part_ctx;
features[feature_index++] = (float)var_ctx; constint rdcost = (int)AOMMIN(INT_MAX, best_rd); int sub_block_rdcost[8] = { 0 }; int rd_index = 0; for (int i = 0; i < SUB_PARTITIONS_RECT; ++i) { const int64_t *horz_rd = part_state->rect_part_rd[HORZ]; if (horz_rd[i] > 0 && horz_rd[i] < 1000000000)
sub_block_rdcost[rd_index] = (int)horz_rd[i];
++rd_index;
} for (int i = 0; i < SUB_PARTITIONS_RECT; ++i) { const int64_t *vert_rd = part_state->rect_part_rd[VERT]; if (vert_rd[i] > 0 && vert_rd[i] < 1000000000)
sub_block_rdcost[rd_index] = (int)vert_rd[i];
++rd_index;
} for (int i = 0; i < SUB_PARTITIONS_SPLIT; ++i) { const int64_t *split_rd = part_state->split_rd; if (split_rd[i] > 0 && split_rd[i] < 1000000000)
sub_block_rdcost[rd_index] = (int)split_rd[i];
++rd_index;
} for (int i = 0; i < 8; ++i) { // Ratio between the sub-block RD and the whole-block RD. float rd_ratio = 1.0f; if (sub_block_rdcost[i] > 0 && sub_block_rdcost[i] < rdcost)
rd_ratio = (float)sub_block_rdcost[i] / (float)rdcost;
features[feature_index++] = rd_ratio;
}
assert(feature_index == 10);
// Write features to file if (!frame_is_intra_only(&cpi->common)) {
write_features_to_file(cpi->oxcf.partition_info_path,
cpi->ext_part_controller.test_mode, features, /*feature_size=*/10, 6, bsize, mi_row, mi_col);
}
// Calculate scores using the NN model. float score[16] = { 0.0f };
av1_nn_predict(features, nn_config, 1, score); int int_score[16]; int max_score = -1000; for (int i = 0; i < 16; ++i) {
int_score[i] = (int)(100 * score[i]);
max_score = AOMMAX(int_score[i], max_score);
}
// Make decisions based on the model scores. int thresh = max_score; switch (bsize) { case BLOCK_16X16: thresh -= 150; break; case BLOCK_32X32: thresh -= 100; break; default: break;
}
av1_zero_array(ab_partitions_allowed, NUM_AB_PARTS); for (int i = 0; i < 16; ++i) { if (int_score[i] >= thresh) { if ((i >> 0) & 1) ab_partitions_allowed[HORZ_A] = 1; if ((i >> 1) & 1) ab_partitions_allowed[HORZ_B] = 1; if ((i >> 2) & 1) ab_partitions_allowed[VERT_A] = 1; if ((i >> 3) & 1) ab_partitions_allowed[VERT_B] = 1;
}
}
}
#define FEATURES 18 #define LABELS 4 // Use a ML model to predict if horz4 and vert4 should be considered. void av1_ml_prune_4_partition(AV1_COMP *const cpi, MACROBLOCK *const x, int part_ctx, int64_t best_rd,
PartitionSearchState *part_state, int *part4_allowed, unsignedint pb_source_variance) { const PartitionBlkParams blk_params = part_state->part_blk_params; constint mi_row = blk_params.mi_row; constint mi_col = blk_params.mi_col; const BLOCK_SIZE bsize = blk_params.bsize;
constfloat denom = (float)(pb_source_variance + 1); constfloat low_b = 0.1f; constfloat high_b = 10.0f; for (int i = 0; i < SUB_PARTITIONS_PART4; ++i) { // Ratio between the 4:1 sub-block variance and the whole-block variance. float var_ratio = (float)(horz_4_source_var[i] + 1) / denom; if (var_ratio < low_b) var_ratio = low_b; if (var_ratio > high_b) var_ratio = high_b;
features[feature_index++] = var_ratio;
} for (int i = 0; i < SUB_PARTITIONS_PART4; ++i) { // Ratio between the 1:4 sub-block RD and the whole-block RD. float var_ratio = (float)(vert_4_source_var[i] + 1) / denom; if (var_ratio < low_b) var_ratio = low_b; if (var_ratio > high_b) var_ratio = high_b;
features[feature_index++] = var_ratio;
}
assert(feature_index == FEATURES);
// Write features to file if (!frame_is_intra_only(&cpi->common)) {
write_features_to_file(cpi->oxcf.partition_info_path,
cpi->ext_part_controller.test_mode, features,
FEATURES, 7, bsize, mi_row, mi_col);
}
// Calculate scores using the NN model. float score[LABELS] = { 0.0f };
av1_nn_predict(features, nn_config, 1, score); int int_score[LABELS]; int max_score = -1000; for (int i = 0; i < LABELS; ++i) {
int_score[i] = (int)(100 * score[i]);
max_score = AOMMAX(int_score[i], max_score);
}
// Make decisions based on the model scores. int thresh = max_score; switch (bsize) { case BLOCK_16X16: thresh -= 500; break; case BLOCK_32X32: thresh -= 500; break; case BLOCK_64X64: thresh -= 200; break; default: break;
}
av1_zero_array(part4_allowed, NUM_PART4_TYPES); for (int i = 0; i < LABELS; ++i) { if (int_score[i] >= thresh) { if ((i >> 0) & 1) part4_allowed[HORZ4] = 1; if ((i >> 1) & 1) part4_allowed[VERT4] = 1;
}
}
} #undef FEATURES #undef LABELS
if (!is_edge && block_size_wide[bsize] >= 16) { // If in second pass we used rectangular partition, then do not search for // rectangular partition in the different direction. if (third_pass_part != PARTITION_NONE) { if (third_pass_part == PARTITION_HORZ ||
third_pass_part == PARTITION_HORZ_4 ||
third_pass_part == PARTITION_HORZ_A ||
third_pass_part == PARTITION_HORZ_B) {
part_state->partition_rect_allowed[VERT] = 0;
} elseif (third_pass_part == PARTITION_VERT ||
third_pass_part == PARTITION_VERT_4 ||
third_pass_part == PARTITION_VERT_A ||
third_pass_part == PARTITION_VERT_B) {
part_state->partition_rect_allowed[HORZ] = 0;
}
}
int minSize = AOMMIN(block_size_wide[third_pass_bsize],
block_size_high[third_pass_bsize]); int maxSize = AOMMAX(block_size_wide[third_pass_bsize],
block_size_high[third_pass_bsize]); if (block_size_wide[bsize] < minSize / 4) { // Current partition is too small, just terminate
part_state->terminate_partition_search = 1; return;
} elseif (block_size_wide[bsize] < minSize / 2) { if (third_pass_part != PARTITION_NONE) { // Current partition is very small, and in second pass we used // rectangular partition. Terminate the search here then.
part_state->terminate_partition_search = 1; return;
} else { // Partition is small, but we still check this partition, only disable // further splits. // TODO(any): check why this is not covered by the termination for < // minSize/4.
av1_disable_square_split_partition(part_state);
av1_disable_rect_partitions(part_state); return;
}
} elseif (block_size_wide[bsize] > maxSize) { // Partition is larger than in the second pass. Only allow split.
av1_set_square_split_only(part_state); return;
} elseif (block_size_wide[bsize] >= minSize &&
block_size_wide[bsize] <= maxSize) { // Partition is within a range where it is very likely to find a good // choice, so do not prune anything. return;
}
}
} #endif// CONFIG_THREE_PASS
// Prune rectangular, AB and 4-way partition based on q index and block size if (cpi->sf.part_sf.prune_rectangular_split_based_on_qidx == 1) { if (bsize == BLOCK_8X8 && x->qindex < 35)
av1_disable_rect_partitions(part_state);
// Prune partition // qidx 0 to 85: prune bsize below BLOCK_32X32 // qidx 86 to 170: prune bsize below BLOCK_16X16 // qidx 171 to 255: prune bsize below BLOCK_8X8 if (bsize < max_prune_bsize) {
av1_disable_rect_partitions(part_state);
}
}
if (cpi->sf.part_sf.prune_sub_8x8_partition_level && (bsize == BLOCK_8X8)) { const MACROBLOCKD *const xd = &x->e_mbd; int prune_sub_8x8; if (cpi->sf.part_sf.prune_sub_8x8_partition_level == 2) {
prune_sub_8x8 = 1;
} else {
assert(cpi->sf.part_sf.prune_sub_8x8_partition_level == 1); // Prune if both neighbors are available and either is > BLOCK_8X8
prune_sub_8x8 = xd->left_available && xd->up_available &&
(xd->left_mbmi->bsize > BLOCK_8X8 ||
xd->above_mbmi->bsize > BLOCK_8X8);
} if (prune_sub_8x8) {
av1_disable_all_splits(part_state);
}
}
// A CNN-based speed feature pruning out either split or all non-split // partition in INTRA frame coding. constint try_intra_cnn_based_part_prune =
frame_is_intra_only(cm) &&
cpi->sf.part_sf.intra_cnn_based_part_prune_level &&
cm->seq_params->sb_size >= BLOCK_64X64 && bsize <= BLOCK_64X64 &&
blk_params->bsize_at_least_8x8 &&
av1_is_whole_blk_in_frame(blk_params, mi_params);
if (try_intra_cnn_based_part_prune) {
intra_mode_cnn_partition(&cpi->common, x, x->part_search_info.quad_tree_idx,
cpi->sf.part_sf.intra_cnn_based_part_prune_level,
part_state);
}
// Use simple motion search to prune out split or non-split partitions. This // must be done prior to PARTITION_SPLIT to propagate the initial mvs to a // smaller blocksize. constint try_split_only =
cpi->sf.part_sf.simple_motion_search_split &&
part_state->do_square_split && blk_params->bsize_at_least_8x8 &&
av1_is_whole_blk_in_frame(blk_params, mi_params) &&
!frame_is_intra_only(cm) && !av1_superres_scaled(cm);
if (try_split_only) {
simple_motion_search_based_split(cpi, x, sms_tree, part_state);
}
// Use simple motion search to prune out rectangular partition in some // direction. The results are stored in prune_horz and prune_vert in order to // bypass future related pruning checks if a pruning decision has been made.
// We want to search at least one partition mode, so don't prune if NONE and // SPLIT are disabled. constint non_rect_part_allowed =
part_state->do_square_split || part_state->partition_none_allowed; // Only run the model if the partitions are not already pruned. constint rect_part_allowed = part_state->do_rectangular_split &&
((part_state->partition_rect_allowed[HORZ] &&
!part_state->prune_rect_part[HORZ]) ||
(part_state->partition_rect_allowed[VERT] &&
!part_state->prune_rect_part[VERT]));
void av1_prune_partitions_by_max_min_bsize(SuperBlockEnc *sb_enc,
PartitionSearchState *part_state) {
assert(is_bsize_square(sb_enc->max_partition_size));
assert(is_bsize_square(sb_enc->min_partition_size));
assert(sb_enc->min_partition_size <= sb_enc->max_partition_size); const PartitionBlkParams *blk_params = &part_state->part_blk_params; const BLOCK_SIZE bsize = blk_params->bsize;
assert(is_bsize_square(bsize)); constint max_partition_size_1d = block_size_wide[sb_enc->max_partition_size]; constint min_partition_size_1d = block_size_wide[sb_enc->min_partition_size]; constint bsize_1d = block_size_wide[bsize];
assert(min_partition_size_1d <= max_partition_size_1d); constint is_le_min_sq_part = bsize_1d <= min_partition_size_1d; constint is_gt_max_sq_part = bsize_1d > max_partition_size_1d; if (is_gt_max_sq_part) { // If current block size is larger than max, only allow split.
av1_set_square_split_only(part_state);
} elseif (is_le_min_sq_part) { // If current block size is less or equal to min, only allow none if valid // block large enough; only allow split otherwise.
av1_disable_rect_partitions(part_state);
// only disable square split when current block is not at the picture // boundary. otherwise, inherit the square split flag from previous logic if (av1_blk_has_rows_and_cols(blk_params)) {
part_state->do_square_split = 0;
}
part_state->partition_none_allowed = !(part_state->do_square_split);
}
}
// Decide whether to evaluate the AB partition specified by part_type based on // split and HORZ/VERT info staticint evaluate_ab_partition_based_on_split( const PC_TREE *pc_tree, PARTITION_TYPE rect_part, const RD_RECT_PART_WIN_INFO *rect_part_win_info, int qindex, int split_idx1, int split_idx2) { int num_win = 0; // Threshold for number of winners // Conservative pruning for high quantizers constint num_win_thresh = AOMMIN(3 * (2 * (MAXQ - qindex) / MAXQ), 3); int sub_part_win =
(rect_part_win_info == NULL) ? (pc_tree->partitioning == rect_part)
: (rect_part == PARTITION_HORZ) ? rect_part_win_info->rect_part_win[HORZ]
: rect_part_win_info->rect_part_win[VERT];
num_win += (sub_part_win) ? 1 : 0; if (pc_tree->split[split_idx1]) {
num_win +=
(pc_tree->split[split_idx1]->partitioning == PARTITION_NONE) ? 1 : 0;
} else {
num_win += 1;
} if (pc_tree->split[split_idx2]) {
num_win +=
(pc_tree->split[split_idx2]->partitioning == PARTITION_NONE) ? 1 : 0;
} else {
num_win += 1;
} if (num_win < num_win_thresh) { return 0;
} return 1;
}
void av1_prune_ab_partitions(AV1_COMP *cpi, const MACROBLOCK *x, const PC_TREE *pc_tree, int pb_source_variance,
int64_t best_rdcost, const RD_RECT_PART_WIN_INFO *rect_part_win_info, bool ext_partition_allowed,
PartitionSearchState *part_state, int *ab_partitions_allowed) {
int64_t *horz_rd = part_state->rect_part_rd[HORZ];
int64_t *vert_rd = part_state->rect_part_rd[VERT];
int64_t *split_rd = part_state->split_rd; const PartitionCfg *const part_cfg = &cpi->oxcf.part_cfg; // The standard AB partitions are allowed initially if ext-partition-types are // allowed. int horzab_partition_allowed = ext_partition_allowed &&
part_cfg->enable_ab_partitions &&
part_state->partition_rect_allowed[HORZ]; int vertab_partition_allowed = ext_partition_allowed &&
part_cfg->enable_ab_partitions &&
part_state->partition_rect_allowed[VERT];
// Pruning: pruning out AB partitions on one main direction based on the // current best partition and source variance. if (cpi->sf.part_sf.prune_ext_partition_types_search_level) { if (cpi->sf.part_sf.prune_ext_partition_types_search_level == 1) { // TODO(debargha,huisu@google.com): may need to tune the threshold for // pb_source_variance.
horzab_partition_allowed &= (pc_tree->partitioning == PARTITION_HORZ ||
(pc_tree->partitioning == PARTITION_NONE &&
pb_source_variance < 32) ||
pc_tree->partitioning == PARTITION_SPLIT);
vertab_partition_allowed &= (pc_tree->partitioning == PARTITION_VERT ||
(pc_tree->partitioning == PARTITION_NONE &&
pb_source_variance < 32) ||
pc_tree->partitioning == PARTITION_SPLIT);
} else {
horzab_partition_allowed &= (pc_tree->partitioning == PARTITION_HORZ ||
pc_tree->partitioning == PARTITION_SPLIT);
vertab_partition_allowed &= (pc_tree->partitioning == PARTITION_VERT ||
pc_tree->partitioning == PARTITION_SPLIT);
}
horz_rd[0] = (horz_rd[0] < INT64_MAX ? horz_rd[0] : 0);
horz_rd[1] = (horz_rd[1] < INT64_MAX ? horz_rd[1] : 0);
vert_rd[0] = (vert_rd[0] < INT64_MAX ? vert_rd[0] : 0);
vert_rd[1] = (vert_rd[1] < INT64_MAX ? vert_rd[1] : 0);
split_rd[0] = (split_rd[0] < INT64_MAX ? split_rd[0] : 0);
split_rd[1] = (split_rd[1] < INT64_MAX ? split_rd[1] : 0);
split_rd[2] = (split_rd[2] < INT64_MAX ? split_rd[2] : 0);
split_rd[3] = (split_rd[3] < INT64_MAX ? split_rd[3] : 0);
}
// Pruning: pruning out horz_a or horz_b if the combined rdcost of its // subblocks estimated from previous partitions is much higher than the best // rd so far.
ab_partitions_allowed[HORZ_A] = horzab_partition_allowed;
ab_partitions_allowed[HORZ_B] = horzab_partition_allowed; if (cpi->sf.part_sf.prune_ext_partition_types_search_level) { const int64_t horz_a_rd = horz_rd[1] + split_rd[0] + split_rd[1]; const int64_t horz_b_rd = horz_rd[0] + split_rd[2] + split_rd[3]; switch (cpi->sf.part_sf.prune_ext_partition_types_search_level) { case 1:
ab_partitions_allowed[HORZ_A] &= (horz_a_rd / 16 * 14 < best_rdcost);
ab_partitions_allowed[HORZ_B] &= (horz_b_rd / 16 * 14 < best_rdcost); break; case 2: default:
ab_partitions_allowed[HORZ_A] &= (horz_a_rd / 16 * 15 < best_rdcost);
ab_partitions_allowed[HORZ_B] &= (horz_b_rd / 16 * 15 < best_rdcost); break;
}
}
// Pruning: pruning out vert_a or vert_b if the combined rdcost of its // subblocks estimated from previous partitions is much higher than the best // rd so far.
ab_partitions_allowed[VERT_A] = vertab_partition_allowed;
ab_partitions_allowed[VERT_B] = vertab_partition_allowed; if (cpi->sf.part_sf.prune_ext_partition_types_search_level) { const int64_t vert_a_rd = vert_rd[1] + split_rd[0] + split_rd[2]; const int64_t vert_b_rd = vert_rd[0] + split_rd[1] + split_rd[3]; switch (cpi->sf.part_sf.prune_ext_partition_types_search_level) { case 1:
ab_partitions_allowed[VERT_A] &= (vert_a_rd / 16 * 14 < best_rdcost);
ab_partitions_allowed[VERT_B] &= (vert_b_rd / 16 * 14 < best_rdcost); break; case 2: default:
ab_partitions_allowed[VERT_A] &= (vert_a_rd / 16 * 15 < best_rdcost);
ab_partitions_allowed[VERT_B] &= (vert_b_rd / 16 * 15 < best_rdcost); break;
}
}
// Pruning: pruning out some ab partitions using a DNN taking rd costs of // sub-blocks from previous basic partition types. if (cpi->sf.part_sf.ml_prune_partition && ext_partition_allowed &&
part_state->partition_rect_allowed[HORZ] &&
part_state->partition_rect_allowed[VERT]) { // TODO(huisu@google.com): x->source_variance may not be the current // block's variance. The correct one to use is pb_source_variance. Need to // re-train the model to fix it.
ml_prune_ab_partition(cpi, pc_tree->partitioning,
get_unsigned_bits(x->source_variance), best_rdcost,
part_state, ab_partitions_allowed);
}
// Pruning: pruning AB partitions based on the number of horz/vert wins // in the current block and sub-blocks in PARTITION_SPLIT. if (cpi->sf.part_sf.prune_ext_part_using_split_info >= 2 &&
ab_partitions_allowed[HORZ_A]) {
ab_partitions_allowed[HORZ_A] &= evaluate_ab_partition_based_on_split(
pc_tree, PARTITION_HORZ, rect_part_win_info, x->qindex, 0, 1);
} if (cpi->sf.part_sf.prune_ext_part_using_split_info >= 2 &&
ab_partitions_allowed[HORZ_B]) {
ab_partitions_allowed[HORZ_B] &= evaluate_ab_partition_based_on_split(
pc_tree, PARTITION_HORZ, rect_part_win_info, x->qindex, 2, 3);
} if (cpi->sf.part_sf.prune_ext_part_using_split_info >= 2 &&
ab_partitions_allowed[VERT_A]) {
ab_partitions_allowed[VERT_A] &= evaluate_ab_partition_based_on_split(
pc_tree, PARTITION_VERT, rect_part_win_info, x->qindex, 0, 2);
} if (cpi->sf.part_sf.prune_ext_part_using_split_info >= 2 &&
ab_partitions_allowed[VERT_B]) {
ab_partitions_allowed[VERT_B] &= evaluate_ab_partition_based_on_split(
pc_tree, PARTITION_VERT, rect_part_win_info, x->qindex, 1, 3);
}
}
// Prepare features for the external model. Specifically, features after // ab partition is searched. staticvoid prepare_features_after_part_ab( const AV1_COMP *const cpi, MACROBLOCK *const x, BLOCK_SIZE bsize, int part_ctx, int64_t best_rd,
int64_t rect_part_rd[NUM_RECT_PARTS][SUB_PARTITIONS_RECT],
int64_t split_rd[SUB_PARTITIONS_SPLIT], unsignedint pb_source_variance, int mi_row, int mi_col, aom_partition_features_t *const features) {
int64_t *horz_rd = rect_part_rd[HORZ];
int64_t *vert_rd = rect_part_rd[VERT];
constfloat denom = (float)(pb_source_variance + 1); constfloat low_b = 0.1f; constfloat high_b = 10.0f; for (int i = 0; i < SUB_PARTITIONS_PART4; ++i) { // Ratio between the 4:1 sub-block variance and the whole-block variance. float var_ratio = (float)(horz_4_source_var[i] + 1) / denom; if (var_ratio < low_b) var_ratio = low_b; if (var_ratio > high_b) var_ratio = high_b;
features->after_part_ab.f[feature_index++] = var_ratio;
} for (int i = 0; i < SUB_PARTITIONS_PART4; ++i) { // Ratio between the 1:4 sub-block RD and the whole-block RD. float var_ratio = (float)(vert_4_source_var[i] + 1) / denom; if (var_ratio < low_b) var_ratio = low_b; if (var_ratio > high_b) var_ratio = high_b;
features->after_part_ab.f[feature_index++] = var_ratio;
}
assert(feature_index == 18);
}
// If the external partition model is used, we let it determine partition // decisions before partition none. Specifically, these parameters: // partition_none_allowed // partition_horz_allowed // partition_vert_allowed // do_rectangular_split // do_square_split staticbool ext_ml_model_decision_before_none(
AV1_COMP *cpi, constfloat features_from_motion[FEATURE_SIZE_SMS_SPLIT], int *partition_none_allowed, int *partition_horz_allowed, int *partition_vert_allowed, int *do_rectangular_split, int *do_square_split) {
ExtPartController *const ext_part_controller = &cpi->ext_part_controller; if (!ext_part_controller->ready) returnfalse;
// Setup features.
aom_partition_features_t features;
features.id = AOM_EXT_PART_FEATURE_BEFORE_NONE; for (int i = 0; i < FEATURE_SIZE_SMS_SPLIT; ++i) {
features.before_part_none.f[i] = features_from_motion[i];
}
// Send necessary features to the external model.
av1_ext_part_send_features(ext_part_controller, &features);
// Get partition decisions from the external model.
aom_partition_decision_t decision; constbool valid_decision =
av1_ext_part_get_partition_decision(ext_part_controller, &decision); if (!valid_decision) returnfalse;
// If the external partition model is used, we let it determine partition // decisions before partition none. Specifically, these parameters: // prune_horz // prune_vert staticbool ext_ml_model_decision_before_none_part2(
AV1_COMP *cpi, constfloat features_from_motion[FEATURE_SIZE_SMS_PRUNE_PART], int *prune_horz, int *prune_vert) {
ExtPartController *const ext_part_controller = &cpi->ext_part_controller; if (!ext_part_controller->ready) returnfalse;
// Setup features.
aom_partition_features_t features;
features.id = AOM_EXT_PART_FEATURE_BEFORE_NONE_PART2; for (int i = 0; i < FEATURE_SIZE_SMS_PRUNE_PART; ++i) {
features.before_part_none.f_part2[i] = features_from_motion[i];
}
// Send necessary features to the external model.
av1_ext_part_send_features(ext_part_controller, &features);
// Get partition decisions from the external model.
aom_partition_decision_t decision; constbool valid_decision =
av1_ext_part_get_partition_decision(ext_part_controller, &decision); if (!valid_decision) returnfalse;
// If the external partition model is used, we let it determine partition // decisions after none partition. Specifically, these parameters: // do_square_split // do_rectangular_split bool ext_ml_model_decision_after_none(
ExtPartController *const ext_part_controller, constint is_intra_frame, constfloat *const features_after_none, int *do_square_split, int *do_rectangular_split) { if (!ext_part_controller->ready || is_intra_frame) returnfalse;
// Setup features.
aom_partition_features_t features;
features.id = AOM_EXT_PART_FEATURE_AFTER_NONE; for (int i = 0; i < 4; ++i) {
features.after_part_none.f[i] = features_after_none[i];
}
// Send necessary features to the external model.
av1_ext_part_send_features(ext_part_controller, &features);
// Get partition decisions from the external model.
aom_partition_decision_t decision; constbool valid_decision =
av1_ext_part_get_partition_decision(ext_part_controller, &decision); if (!valid_decision) returnfalse;
// If the external partition model is used, we let it determine partition // decisions after none partition. Specifically, these parameters: // terminate_partition_search bool ext_ml_model_decision_after_none_part2(
AV1_COMP *const cpi, constfloat *const features_terminate, int *terminate_partition_search) {
AV1_COMMON *const cm = &cpi->common;
ExtPartController *const ext_part_controller = &cpi->ext_part_controller; if (!ext_part_controller->ready || frame_is_intra_only(cm)) returnfalse;
// Setup features.
aom_partition_features_t features;
features.id = AOM_EXT_PART_FEATURE_AFTER_NONE_PART2; for (int i = 0; i < FEATURE_SIZE_SMS_TERM_NONE; ++i) {
features.after_part_none.f_terminate[i] = features_terminate[i];
}
// Send necessary features to the external model.
av1_ext_part_send_features(ext_part_controller, &features);
// Get partition decisions from the external model.
aom_partition_decision_t decision; constbool valid_decision =
av1_ext_part_get_partition_decision(ext_part_controller, &decision); if (!valid_decision) returnfalse;
// If the external partition model is used, we let it determine partition // decisions after none partition. Specifically, these parameters: // terminate_partition_search bool ext_ml_model_decision_after_split(AV1_COMP *const cpi, constfloat *const features_terminate, int *terminate_partition_search) { const AV1_COMMON *const cm = &cpi->common;
ExtPartController *const ext_part_controller = &cpi->ext_part_controller; if (frame_is_intra_only(cm) || !cpi->ext_part_controller.ready) { returnfalse;
}
// Setup features.
aom_partition_features_t features;
features.id = AOM_EXT_PART_FEATURE_AFTER_SPLIT; for (int i = 0; i < 31; ++i) {
features.after_part_split.f_terminate[i] = features_terminate[i];
}
// Send necessary features to the external model.
av1_ext_part_send_features(ext_part_controller, &features);
// Get partition decisions from the external model.
aom_partition_decision_t decision; constbool valid_decision =
av1_ext_part_get_partition_decision(ext_part_controller, &decision); if (!valid_decision) returnfalse;
// If the external partition model is used, we let it determine partition // decisions after none partition. Specifically, these parameters: // prune_rect_part[HORZ] // prune_rect_part[VERT] bool ext_ml_model_decision_after_split_part2(
ExtPartController *const ext_part_controller, constint is_intra_frame, constfloat *const features_prune, int *prune_rect_part_horz, int *prune_rect_part_vert) { if (is_intra_frame || !ext_part_controller->ready) { returnfalse;
}
// Setup features.
aom_partition_features_t features;
features.id = AOM_EXT_PART_FEATURE_AFTER_SPLIT_PART2; for (int i = 0; i < 9; ++i) {
features.after_part_split.f_prune_rect[i] = features_prune[i];
}
// Send necessary features to the external model.
av1_ext_part_send_features(ext_part_controller, &features);
// Get partition decisions from the external model.
aom_partition_decision_t decision; constbool valid_decision =
av1_ext_part_get_partition_decision(ext_part_controller, &decision); if (!valid_decision) returnfalse;
// If the external partition model is used, we let it determine partition // decisions after rectangular partition. Specifically, these parameters: // horza_partition_allowed // horzb_partition_allowed // verta_partition_allowed // vertb_partition_allowed staticbool ext_ml_model_decision_after_rect(
ExtPartController *const ext_part_controller, constint is_intra_frame, constfloat *const features_after_rect, int *horza_partition_allowed, int *horzb_partition_allowed, int *verta_partition_allowed, int *vertb_partition_allowed) { if (is_intra_frame || !ext_part_controller->ready) returnfalse;
// Setup features.
aom_partition_features_t features;
features.id = AOM_EXT_PART_FEATURE_AFTER_RECT; for (int i = 0; i < 10; ++i) {
features.after_part_rect.f[i] = features_after_rect[i];
}
// Send necessary features to the external model.
av1_ext_part_send_features(ext_part_controller, &features);
// Get partition decisions from the external model.
aom_partition_decision_t decision; constbool valid_decision =
av1_ext_part_get_partition_decision(ext_part_controller, &decision); if (!valid_decision) returnfalse;
// If the external partition model is used, we let it determine partition // decisions after AB partition. Specifically, these parameters: // partition_vert4_allowed // partition_horz4_allowed staticbool ext_ml_model_decision_after_part_ab(
AV1_COMP *const cpi, MACROBLOCK *const x, BLOCK_SIZE bsize, int part_ctx,
int64_t best_rd, int64_t rect_part_rd[NUM_RECT_PARTS][SUB_PARTITIONS_RECT],
int64_t split_rd[SUB_PARTITIONS_SPLIT], int *const partition_horz4_allowed, int *const partition_vert4_allowed, unsignedint pb_source_variance, int mi_row, int mi_col) { const AV1_COMMON *const cm = &cpi->common;
ExtPartController *const ext_part_controller = &cpi->ext_part_controller;
// Send necessary features to the external model.
av1_ext_part_send_features(ext_part_controller, &features);
// Get partition decisions from the external model.
aom_partition_decision_t decision; constbool valid_decision =
av1_ext_part_get_partition_decision(ext_part_controller, &decision); if (!valid_decision) returnfalse;
// Sets up all the leaf nodes in the tree. for (sms_tree_index = 0; sms_tree_index < leaf_nodes; ++sms_tree_index) {
SIMPLE_MOTION_DATA_TREE *const tree = &sms_tree[sms_tree_index];
tree->block_size = square[0];
}
// Each node has 4 leaf nodes, fill each block_size level of the tree // from leafs to the root. for (nodes = leaf_nodes >> 2; nodes > 0; nodes >>= 2) { for (int i = 0; i < nodes; ++i) {
SIMPLE_MOTION_DATA_TREE *const tree = &sms_tree[sms_tree_index];
tree->block_size = square[square_index]; for (int j = 0; j < 4; j++) tree->split[j] = this_sms++;
++sms_tree_index;
}
++square_index;
}
} else { // Allocation for firstpass/LAP stage // TODO(Mufaddal): refactor square_index to use a common block_size macro // from firstpass.c
SIMPLE_MOTION_DATA_TREE *const tree = &sms_tree[sms_tree_index];
square_index = 2;
tree->block_size = square[square_index];
}
// Set up the root node for the largest superblock size return &sms_tree[tree_nodes - 1];
}
staticvoid write_motion_feature_to_file( constchar *const path, constint sb_counter, constunsignedint *block_sse, constunsignedint *block_var, constint num_blocks, const BLOCK_SIZE bsize, const BLOCK_SIZE fixed_block_size, constint mi_row, constint mi_col) { char filename[256];
snprintf(filename, sizeof(filename), "%s/motion_search_feature_sb%d", path,
sb_counter);
FILE *pfile = fopen(filename, "w");
fprintf(pfile, "%d,%d,%d,%d,%d\n", mi_row, mi_col, bsize,
block_size_wide[fixed_block_size], num_blocks); for (int i = 0; i < num_blocks; ++i) {
fprintf(pfile, "%d", block_sse[i]); if (i < num_blocks - 1) fprintf(pfile, ",");
}
fprintf(pfile, "\n"); for (int i = 0; i < num_blocks; ++i) {
fprintf(pfile, "%d", block_var[i]); if (i < num_blocks - 1) fprintf(pfile, ",");
}
fprintf(pfile, "\n");
fclose(pfile);
}
void av1_init_simple_motion_search_mvs_for_sb(const AV1_COMP *cpi, const TileInfo *tile_info,
MACROBLOCK *x,
SIMPLE_MOTION_DATA_TREE *sms_root, int mi_row, int mi_col) { // Use the NEARESTMV of the sb as the start mv const AV1_COMMON *cm = &cpi->common;
MACROBLOCKD *const xd = &x->e_mbd;
FULLPEL_MV ref_mvs[REF_FRAMES]; const BLOCK_SIZE sb_size = cm->seq_params->sb_size;
av1_zero(ref_mvs); // If tile_info is NULL, assume that the offsets have already been set. if (tile_info) {
av1_set_offsets_without_segment_id(cpi, tile_info, x, mi_row, mi_col,
sb_size);
}
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.