#if !CONFIG_REALTIME_ONLY static inline void 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, const float 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, const float 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, const int is_intra_frame, const float *const features_after_none, int *do_square_split,
int *do_rectangular_split);
staticbool ext_ml_model_decision_after_split_part2(
ExtPartController *const ext_part_controller, const int is_intra_frame, const float *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, const int is_intra_frame, const float *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, unsigned int pb_source_variance,
int mi_row, int mi_col);
static inline int convert_bsize_to_idx(BLOCK_SIZE bsize) { switch (bsize) { case BLOCK_128X128: return0; case BLOCK_64X64: return1; case BLOCK_32X32: return2; case BLOCK_16X16: return3; case BLOCK_8X8: return4; default: assert(0 && "Invalid bsize"); return -1;
}
}
staticvoid write_features_to_file(const char *const path, constbool is_test_mode, const float *features, const int feature_size, const int id, const BLOCK_SIZE bsize, const int mi_row, const int mi_col) {
if (!WRITE_FEATURE_TO_FILE && !is_test_mode) return;
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;
const int bsize_idx = convert_bsize_to_idx(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);
}
}
static inline int 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 const int sms_prune_agg_levels[TOTAL_SIMPLE_AGG_LVLS] = { 0, 1, 2, 3, 4, 5 };
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. const int qband = is_rect_part ? (qindex <= 90 ? 1 : 0) : 0; const int sms_prune_agg_qindex_based[2] = { 3, 4 }; 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; const int mi_row = blk_params->mi_row, mi_col = blk_params->mi_col; const BLOCK_SIZE bsize = blk_params->bsize;
const int bsize_idx = convert_bsize_to_idx(bsize); const int is_720p_or_larger = AOMMIN(cm->width, cm->height) >= 720; const int is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480; // res_idx is 0 for res < 480p, 1 for 480p, 2 for 720p+ const int 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) { const float scale = res_idx >= 2 ? 3.0f : 2.0f; const float rect_split_thresh =
scale * av1_simple_motion_search_no_split_thresh[SIMPLE_AGG_LVL3]
[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. static int 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, const int *const refs,
int num_refs, int use_subpixel, int save_mv, unsigned int *best_sse, unsigned int *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 const int num_planes = 1;
*best_sse = INT_MAX;
for (int ref_idx = 0; ref_idx < num_refs; ref_idx++) { const int ref = refs[ref_idx];
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 static inline void 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) { const int w_mi = mi_size_wide[bsize]; const int 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]);
// Setting up motion search const int ref_list[] = { cpi->rc.is_src_frame_alt_ref ? ALTREF_FRAME
: LAST_FRAME }; const int num_refs = 1; const int use_subpixel = 1;
// Doing whole block first to update the mv
if (!sms_tree->sms_none_valid && features_to_get & FEATURE_SMS_NONE_FLAG) {
simple_motion_search_get_best_ref(cpi, x, sms_tree, mi_row, mi_col, bsize,
ref_list, num_refs, use_subpixel, 1,
&sms_tree->sms_none_feat[0],
&sms_tree->sms_none_feat[1]);
sms_tree->sms_none_valid = 1;
}
// 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; const int mi_row = blk_params->mi_row, mi_col = blk_params->mi_col; const BLOCK_SIZE bsize = blk_params->bsize;
const int bsize_idx = convert_bsize_to_idx(bsize); const int is_720p_or_larger = AOMMIN(cm->width, cm->height) >= 720; const int is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480; // res_idx is 0 for lowres, 1 for 48p, 2 for 720p+ const int 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]; const float *ml_mean = av1_simple_motion_search_prune_rect_mean[bsize_idx],
*ml_std = av1_simple_motion_search_prune_rect_std[bsize_idx];
const int agg = get_simple_motion_search_prune_agg(
x->qindex, cpi->sf.part_sf.simple_motion_search_prune_agg, 1);
if (agg < 0) { return;
}
// 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; const int 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;
}
} else if (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. constunsigned int 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. static inline void 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
int split_variance[SUB_PARTITIONS_SPLIT]; const BLOCK_SIZE subsize = get_partition_subsize(bsize, PARTITION_SPLIT); struct buf_2d buf;
buf.stride = x->plane[0].src.stride; const int bw = block_size_wide[bsize];
for (int i = 0; i < SUB_PARTITIONS_SPLIT; ++i) { const int x_idx = (i & 1) * bw / 2; const int y_idx = (i >> 1) * bw / 2;
buf.buf = x->plane[0].src.buf + x_idx + y_idx * buf.stride;
split_variance[i] =
av1_get_perpixel_variance_facade(cpi, xd, &buf, subsize, AOM_PLANE_Y);
}
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; const int mi_row = blk_params.mi_row; const int 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; const int 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 #define NEW_LABELS 3 // 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, unsigned int pb_source_variance) { const AV1_COMMON *const cm = &cpi->common; const PartitionBlkParams blk_params = part_state->part_blk_params; const int mi_row = blk_params.mi_row; const int mi_col = blk_params.mi_col; const BLOCK_SIZE bsize = blk_params.bsize;
const int is_720p_or_larger = AOMMIN(cm->width, cm->height) >= 720; const int is_480p_or_larger = AOMMIN(cm->width, cm->height) >= 480; // res_idx is 0 for res < 480p, 1 for 480p, 2 for 720p+ const int res_idx = is_480p_or_larger + is_720p_or_larger;
const int bsize_idx = convert_bsize_to_idx(bsize);
if (bsize_idx < 0) return; const float *ml_mean = av1_partition4_nn_mean[bsize_idx]; const float *ml_std = av1_partition4_nn_std[bsize_idx];
int ml_model_index = (cpi->sf.part_sf.ml_4_partition_search_level_index < 3);
const NN_CONFIG *nn_config = NULL; // 4-way partitions are only allowed for these three square block sizes. switch (bsize) { case BLOCK_16X16:
nn_config = &av1_4_partition_nnconfig_16[ml_model_index]; break; case BLOCK_32X32:
nn_config = &av1_4_partition_nnconfig_32[ml_model_index]; break; case BLOCK_64X64:
nn_config = &av1_4_partition_nnconfig_64[ml_model_index]; break; default: assert(0 && "Unexpected bsize.");
}
if (!nn_config || !ml_mean || !ml_std) return;
// 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);
}
if (ml_model_index == 0) { // 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;
}
}
} else { // Calculate scores using the NN model.
float score[NEW_LABELS] = { 0.0f };
float probs[NEW_LABELS] = { 0.0f };
av1_nn_predict(features, nn_config, 1, score);
av1_nn_softmax(score, probs, NEW_LABELS);
// Make decisions based on the model scores. const float search_thresh = av1_partition4_search_thresh
[cpi->sf.part_sf.ml_4_partition_search_level_index][res_idx][bsize_idx]; const float not_search_thresh = av1_partition4_not_search_thresh
[cpi->sf.part_sf.ml_4_partition_search_level_index][res_idx][bsize_idx];
for (int i = 1; i < NEW_LABELS; ++i) {
if (probs[i] >= search_thresh) {
if (i == 1) part4_allowed[HORZ4] = 1;
if (i == 2) part4_allowed[VERT4] = 1;
}
if (probs[i] < not_search_thresh) {
if (i == 1) part4_allowed[HORZ4] = 0;
if (i == 2) part4_allowed[VERT4] = 0;
}
}
}
} #undef FEATURES #undef LABELS #undef NEW_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;
} else if (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;
} else if (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;
}
} else if (block_size_wide[bsize] > maxSize) { // Partition is larger than in the second pass. Only allow split.
av1_set_square_split_only(part_state); return;
} else if (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);
} else if (cpi->sf.part_sf.prune_rectangular_split_based_on_qidx == 2) { // Enumeration difference between two square partitions const int sqr_bsize_step = BLOCK_32X32 - BLOCK_16X16;
int max_bsize =
BLOCK_32X32 - (x->qindex * 3 / QINDEX_RANGE) * sqr_bsize_step;
max_bsize = AOMMAX(max_bsize, BLOCK_4X4); const BLOCK_SIZE max_prune_bsize =
(BLOCK_SIZE)AOMMIN(max_bsize, BLOCK_32X32);
// 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. const int 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) {
assert(x->e_mbd.error_info->setjmp);
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. const int 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. const int 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. const int 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)); const int max_partition_size_1d = block_size_wide[sb_enc->max_partition_size]; const int min_partition_size_1d = block_size_wide[sb_enc->min_partition_size]; const int bsize_1d = block_size_wide[bsize];
assert(min_partition_size_1d <= max_partition_size_1d); const int is_le_min_sq_part = bsize_1d <= min_partition_size_1d; const int 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);
} else if (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 static int 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 const int 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) { return0;
} return1;
}
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) { case1:
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; case2: 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) { case1:
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; case2: 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_cfg->enable_ab_partitions &&
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], unsigned int 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];
const float denom = (float)(pb_source_variance + 1); const float low_b = 0.1f; const float 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, const float 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) return false;
// 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) return false;
// 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, const float 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) return false;
// 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) return false;
// 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, const int is_intra_frame, const float *const features_after_none, int *do_square_split,
int *do_rectangular_split) {
if (!ext_part_controller->ready || is_intra_frame) return false;
// 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) return false;
// 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, const float *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)) return false;
// 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) return false;
// 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, const float *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) { return false;
}
// 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) return false;
// 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, const int is_intra_frame, const float *const features_prune, int *prune_rect_part_horz,
int *prune_rect_part_vert) {
if (is_intra_frame || !ext_part_controller->ready) { return false;
}
// 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) return false;
// 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, const int is_intra_frame, const float *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) return false;
// 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) return false;
// 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, unsigned int 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) return false;
// This function resembles "av1_setup_sms_tree()" in context_tree.c // with function signature change. static SIMPLE_MOTION_DATA_TREE *setup_sms_tree(
AV1_COMP *const cpi, SIMPLE_MOTION_DATA_TREE *sms_tree) {
AV1_COMMON *const cm = &cpi->common; const int stat_generation_stage = is_stat_generation_stage(cpi); const int is_sb_size_128 = cm->seq_params->sb_size == BLOCK_128X128; const int tree_nodes =
av1_get_pc_tree_nodes(is_sb_size_128, stat_generation_stage);
int sms_tree_index = 0;
SIMPLE_MOTION_DATA_TREE *this_sms;
int square_index = 1;
int nodes;
this_sms = &sms_tree[0];
if (!stat_generation_stage) { const int leaf_factor = is_sb_size_128 ? 4 : 1; const int leaf_nodes = 256 * leaf_factor;
// 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( const char *const path, const int sb_counter, constunsigned int *block_sse, constunsigned int *block_var, const int num_blocks, const BLOCK_SIZE bsize, const BLOCK_SIZE fixed_block_size, const int mi_row, const int 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_collect_motion_search_features_sb(AV1_COMP *const cpi, ThreadData *td,
TileDataEnc *tile_data, const int mi_row, const int mi_col, const BLOCK_SIZE bsize,
aom_partition_features_t *features) { const AV1_COMMON *const cm = &cpi->common;
if (frame_is_intra_only(cm)) return;
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.