mirror of
https://github.com/ultravideo/uvg266.git
synced 2024-11-27 19:24:06 +00:00
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:
parent
7ec5f78792
commit
4a0121ac42
|
@ -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
|
||||
|
|
31
src/cfg.c
31
src/cfg.c
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue