Add --roi parameter

Adds region of interest coding capability.

Works by reading a file of delta QP values which will then be applied
to each frame at LCU level.
This commit is contained in:
Ari Koivula 2017-01-16 08:47:21 +02:00
parent 7ec5f78792
commit 4a0121ac42
8 changed files with 104 additions and 31 deletions

View file

@ -23,7 +23,7 @@ AC_CONFIG_SRCDIR([src/encmain.c])
#
# Here is a somewhat sane guide to lib versioning: http://apr.apache.org/versioning.html
ver_major=3
ver_minor=13
ver_minor=14
ver_release=0
# Prevents configure from adding a lot of defines to the CFLAGS

View file

@ -122,6 +122,10 @@ int kvz_config_init(kvz_config *cfg)
cfg->gop_lp_definition.d = 3;
cfg->gop_lp_definition.t = 1;
cfg->roi.width = 0;
cfg->roi.height = 0;
cfg->roi.dqps = NULL;
return 1;
}
@ -977,6 +981,33 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value)
}
else if OPT("implicit-rdpcm")
cfg->implicit_rdpcm = (bool)atobool(value);
else if OPT("roi") {
// The ROI description is as follows:
// First number is width, second number is height,
// then follows width * height number of dqp values.
FILE* f = fopen(value, "rb");
int width = 0;
int height = 0;
if (!fscanf(f, "%d", &width) || !fscanf(f, "%d", &height)) {
fprintf(stderr, "Failed to read ROI size.\n");
return 0;
}
cfg->roi.width = width;
cfg->roi.height = height;
cfg->roi.dqps = malloc(width * height * sizeof(*cfg->roi.dqps));
if (!cfg->roi.dqps) {
fprintf(stderr, "Failed to allocate memory for ROI table.\n");
return 0;
}
for (int i = 0; i < width * height; ++i) {
int number; // Need a pointer to int for fscanf
if (!fscanf(f, "%d", &number)) {
fprintf(stderr, "Reading ROI file failed.\n");
return 0;
}
cfg->roi.dqps[i] = (uint8_t)number;
}
}
else
return 0;
#undef OPT

View file

@ -118,6 +118,7 @@ static const struct option long_options[] = {
{ "input-format", required_argument, NULL, 0 },
{ "implicit-rdpcm", no_argument, NULL, 0 },
{ "no-implicit-rdpcm", no_argument, NULL, 0 },
{ "roi", required_argument, NULL, 0 },
{0, 0, 0, 0}
};
@ -380,6 +381,13 @@ void print_help(void)
" - none: no constraint\n"
" - frametile: constrain within the tile\n"
" - frametilemargin: constrain even more\n"
" --roi <string> : Use a delta QP map for region of interest\n"
" Read an array of delta QP values from\n"
" a file, where the first two values are the\n"
" width and height, followed by width*height\n"
" delta QP values in raster order.\n"
" The delta QP map can be any size or aspect\n"
" ratio, and will be mapped to LCU's.\n"
"\n"
/* Word wrap to this width to stay under 80 characters (including ") ************/
"Compression tools:\n"

View file

@ -452,7 +452,7 @@ static void encoder_state_write_bitstream_pic_parameter_set(bitstream_t* stream,
WRITE_U(stream, 0, 1, "constrained_intra_pred_flag");
WRITE_U(stream, encoder->trskip_enable, 1, "transform_skip_enabled_flag");
if (encoder->cfg->target_bitrate > 0) {
if (encoder->cfg->target_bitrate > 0 || encoder->cfg->roi.dqps != NULL) {
// Use separate QP for each LCU when rate control is enabled.
WRITE_U(stream, 1, 1, "cu_qp_delta_enabled_flag");
WRITE_UE(stream, 0, "diff_cu_qp_delta_depth");

View file

@ -280,7 +280,7 @@ static void encoder_state_worker_encode_lcu(void * opaque)
encoder_state_recdata_to_bufs(state, lcu, state->tile->hor_buf_search, state->tile->ver_buf_search);
if (encoder->deblock_enable) {
if (encoder->cfg->target_bitrate > 0) {
if (encoder->cfg->target_bitrate > 0 || encoder->cfg->roi.dqps != NULL) {
set_cu_qps(state, lcu->position_px.x, lcu->position_px.y, 0, false);
}
@ -329,7 +329,9 @@ static void encoder_state_worker_encode_lcu(void * opaque)
// QP delta is not used when rate control is turned off.
state->must_code_qp_delta = (state->encoder_control->cfg->target_bitrate > 0);
state->must_code_qp_delta = (
state->encoder_control->cfg->target_bitrate > 0
|| state->encoder_control->cfg->roi.dqps != NULL);
//Encode coding tree
kvz_encode_coding_tree(state, lcu->position.x << MAX_DEPTH, lcu->position.y << MAX_DEPTH, 0);

View file

@ -262,7 +262,9 @@ static bool is_on_8x8_grid(int x, int y, edge_dir dir)
static int8_t get_qp_y_pred(const encoder_state_t* state, int x, int y, edge_dir dir)
{
if (state->encoder_control->cfg->target_bitrate <= 0) {
if (state->encoder_control->cfg->target_bitrate <= 0
&& state->encoder_control->cfg->roi.dqps == NULL)
{
return state->qp;
}

View file

@ -319,6 +319,12 @@ typedef struct kvz_config
} gop_lp_definition;
int32_t implicit_rdpcm; /*!< \brief Enable implicit residual DPCM. */
struct {
int32_t width;
int32_t height;
uint8_t *dqps;
} roi; /*!< \since 3.14.0 \brief Map of delta QPs for region of interest coding. */
} kvz_config;
/**

View file

@ -173,6 +173,39 @@ static int8_t lambda_to_qp(const double lambda)
return CLIP(0, 51, qp);
}
static double qp_to_lamba(encoder_state_t * const state, int qp)
{
const encoder_control_t * const ctrl = state->encoder_control;
const int gop_len = ctrl->cfg->gop_len;
const int period = gop_len > 0 ? gop_len : ctrl->cfg->intra_period;
kvz_gop_config const * const gop = &ctrl->cfg->gop[state->frame->gop_offset];
double lambda = pow(2.0, (qp - 12) / 3.0);
if (state->frame->slicetype == KVZ_SLICE_I) {
lambda *= 0.57;
// Reduce lambda for I-frames according to the number of references.
if (period == 0) {
lambda *= 0.5;
} else {
lambda *= 1.0 - CLIP(0.0, 0.5, 0.05 * (period - 1));
}
} else if (gop_len > 0) {
lambda *= gop->qp_factor;
} else {
lambda *= 0.4624;
}
// Increase lambda if not key-frame.
if (period > 0 && state->frame->poc % period != 0) {
lambda *= CLIP(2.0, 4.0, (state->frame->QP - 12) / 6.0);
}
return lambda;
}
/**
* \brief Allocate bits and set lambda and QP for the current picture.
* \param state the main encoder state
@ -206,7 +239,6 @@ void kvz_set_picture_lambda_and_qp(encoder_state_t * const state)
// Rate control disabled
kvz_gop_config const * const gop = &ctrl->cfg->gop[state->frame->gop_offset];
const int gop_len = ctrl->cfg->gop_len;
const int period = gop_len > 0 ? gop_len : ctrl->cfg->intra_period;
state->frame->QP = ctrl->cfg->qp;
@ -214,30 +246,7 @@ void kvz_set_picture_lambda_and_qp(encoder_state_t * const state)
state->frame->QP += gop->qp_offset;
}
double lambda = pow(2.0, (state->frame->QP - 12) / 3.0);
if (state->frame->slicetype == KVZ_SLICE_I) {
lambda *= 0.57;
// Reduce lambda for I-frames according to the number of references.
if (period == 0) {
lambda *= 0.5;
} else {
lambda *= 1.0 - CLIP(0.0, 0.5, 0.05 * (period - 1));
}
} else if (gop_len > 0) {
lambda *= gop->qp_factor;
} else {
lambda *= 0.4624;
}
// Increase lambda if not key-frame.
if (period > 0 && state->frame->poc % period != 0) {
lambda *= CLIP(2.0, 4.0, (state->frame->QP - 12) / 6.0);
}
state->frame->lambda = lambda;
state->frame->lambda = qp_to_lamba(state, state->frame->QP);
}
}
@ -271,7 +280,22 @@ void kvz_set_lcu_lambda_and_qp(encoder_state_t * const state,
{
const encoder_control_t * const ctrl = state->encoder_control;
if (ctrl->cfg->target_bitrate > 0) {
if (ctrl->cfg->roi.dqps != NULL) {
vector2d_t lcu = {
pos.x + state->tile->lcu_offset_x,
pos.y + state->tile->lcu_offset_y
};
vector2d_t roi = {
lcu.x * ctrl->cfg->roi.width / ctrl->in.width_in_lcu,
lcu.y * ctrl->cfg->roi.height / ctrl->in.height_in_lcu
};
int roi_index = roi.x + roi.y * ctrl->cfg->roi.width;
int dqp = ctrl->cfg->roi.dqps[roi_index];
state->qp = state->frame->QP + dqp;
state->lambda = qp_to_lamba(state, state->qp);
state->lambda_sqrt = sqrt(state->frame->lambda);
} else if (ctrl->cfg->target_bitrate > 0) {
lcu_stats_t *lcu = kvz_get_lcu_stats(state, pos.x, pos.y);
const uint32_t pixels = MIN(LCU_WIDTH, state->tile->frame->width - LCU_WIDTH * pos.x) *
MIN(LCU_WIDTH, state->tile->frame->height - LCU_WIDTH * pos.y);