mirror of
https://github.com/ultravideo/uvg266.git
synced 2024-11-23 18:14:06 +00:00
Merge branch 'sao-mode-cost'
This commit is contained in:
commit
6e24ba0a5f
331
src/sao.c
331
src/sao.c
|
@ -40,6 +40,70 @@ static const vector2d g_sao_edge_offsets[SAO_NUM_EO][2] = {
|
|||
{ { 1, -1 }, { -1, 1 } }
|
||||
};
|
||||
|
||||
// Mapping of edge_idx values to eo-classes.
|
||||
static const unsigned g_sao_eo_idx_to_eo_category[] = { 1, 2, 0, 3, 4 };
|
||||
// Mapping relationships between a, b and c to eo_idx.
|
||||
#define EO_IDX(a, b, c) (2 + SIGN3((c) - (a)) + SIGN3((c) - (b)))
|
||||
|
||||
|
||||
|
||||
int sao_band_ddistortion(const pixel *orig_data, const pixel *rec_data,
|
||||
int block_width, int block_height,
|
||||
int band_pos, int sao_bands[4])
|
||||
{
|
||||
int y, x;
|
||||
int shift = g_bitdepth-5;
|
||||
int sum = 0;
|
||||
|
||||
for (y = 0; y < block_height; ++y) {
|
||||
for (x = 0; x < block_width; ++x) {
|
||||
int band = (rec_data[y * block_width + x] >> shift) - band_pos;
|
||||
int offset = 0;
|
||||
if (band >= 0 && band < 4) {
|
||||
offset = sao_bands[band];
|
||||
}
|
||||
if (offset != 0) {
|
||||
int diff = orig_data[y * block_width + x] - rec_data[y * block_width + x];
|
||||
// Offset is applied to reconstruction, so it is subtracted from diff.
|
||||
sum += (diff - offset) * (diff - offset) - diff * diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
int sao_edge_ddistortion(const pixel *orig_data, const pixel *rec_data,
|
||||
int block_width, int block_height,
|
||||
int eo_class, int offsets[NUM_SAO_EDGE_CATEGORIES])
|
||||
{
|
||||
int y, x;
|
||||
int sum = 0;
|
||||
vector2d a_ofs = g_sao_edge_offsets[eo_class][0];
|
||||
vector2d b_ofs = g_sao_edge_offsets[eo_class][1];
|
||||
|
||||
for (y = 1; y < block_height - 1; ++y) {
|
||||
for (x = 1; x < block_width - 1; ++x) {
|
||||
const pixel *c_data = &rec_data[y * block_width + x];
|
||||
pixel a = c_data[a_ofs.y * block_width + a_ofs.x];
|
||||
pixel c = c_data[0];
|
||||
pixel b = c_data[b_ofs.y * block_width + b_ofs.x];
|
||||
|
||||
int eo_idx = EO_IDX(a, b, c);
|
||||
int eo_cat = g_sao_eo_idx_to_eo_category[eo_idx];
|
||||
int offset = offsets[eo_cat];
|
||||
|
||||
if (offset != 0) {
|
||||
int diff = orig_data[y * block_width + x] - c;
|
||||
// Offset is applied to reconstruction, so it is subtracted from diff.
|
||||
sum += (diff - offset) * (diff - offset) - diff * diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
void init_sao_info(sao_info *sao) {
|
||||
|
@ -48,10 +112,6 @@ void init_sao_info(sao_info *sao) {
|
|||
sao->merge_up_flag = 0;
|
||||
}
|
||||
|
||||
// Mapping of edge_idx values to eo-classes.
|
||||
static const unsigned g_sao_eo_idx_to_eo_category[] = { 1, 2, 0, 3, 4 };
|
||||
// Mapping relationships between a, b and c to eo_idx.
|
||||
#define EO_IDX(a, b, c) (2 + SIGN3((c) - (a)) + SIGN3((c) - (b)))
|
||||
|
||||
/**
|
||||
* \brief Check merge conditions
|
||||
|
@ -60,14 +120,18 @@ static int sao_check_merge(const sao_info *sao_candidate, int type,
|
|||
int offsets[NUM_SAO_EDGE_CATEGORIES],
|
||||
int band_position, int eo_class)
|
||||
{
|
||||
if (sao_candidate->type == type) {
|
||||
if (sao_candidate && sao_candidate->type == type) {
|
||||
if (type == SAO_TYPE_NONE) {
|
||||
return 1;
|
||||
}
|
||||
if (offsets[1] == sao_candidate->offsets[1] &&
|
||||
offsets[2] == sao_candidate->offsets[2] &&
|
||||
offsets[3] == sao_candidate->offsets[3] &&
|
||||
offsets[4] == sao_candidate->offsets[4]) {
|
||||
// Type must be BAND or EDGE
|
||||
if ((type == SAO_TYPE_BAND && band_position == sao_candidate->band_position) ||
|
||||
(type == SAO_TYPE_EDGE && eo_class == sao_candidate->eo_class)) {
|
||||
(type == SAO_TYPE_EDGE && eo_class == sao_candidate->eo_class))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +139,122 @@ static int sao_check_merge(const sao_info *sao_candidate, int type,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int sao_mode_bits_none(sao_info *sao_top, sao_info *sao_left)
|
||||
{
|
||||
int mode_bits = 0;
|
||||
|
||||
// FL coded merges.
|
||||
if (sao_left != NULL) {
|
||||
mode_bits += 1;
|
||||
if (sao_check_merge(sao_left, SAO_TYPE_NONE, 0, 0, 0)) {
|
||||
return mode_bits;
|
||||
}
|
||||
}
|
||||
if (sao_top != NULL) {
|
||||
mode_bits += 1;
|
||||
if (sao_check_merge(sao_top, SAO_TYPE_NONE, 0, 0, 0)) {
|
||||
return mode_bits;
|
||||
}
|
||||
}
|
||||
|
||||
// TR coded type_idx_, none = 0
|
||||
mode_bits += 1;
|
||||
|
||||
return mode_bits;
|
||||
}
|
||||
|
||||
|
||||
static int sao_mode_bits_edge(int edge_class, int offsets[NUM_SAO_EDGE_CATEGORIES],
|
||||
sao_info *sao_top, sao_info *sao_left)
|
||||
{
|
||||
int mode_bits = 0;
|
||||
|
||||
// FL coded merges.
|
||||
if (sao_left != NULL) {
|
||||
mode_bits += 1;
|
||||
if (sao_check_merge(sao_left, SAO_TYPE_EDGE, offsets, 0, edge_class)) {
|
||||
return mode_bits;
|
||||
}
|
||||
}
|
||||
if (sao_top != NULL) {
|
||||
mode_bits += 1;
|
||||
if (sao_check_merge(sao_top, SAO_TYPE_EDGE, offsets, 0, edge_class)) {
|
||||
return mode_bits;
|
||||
}
|
||||
}
|
||||
|
||||
// TR coded type_idx_, edge = 2 = cMax
|
||||
mode_bits += 1;
|
||||
|
||||
// TR coded offsets.
|
||||
{
|
||||
sao_eo_cat edge_cat;
|
||||
for (edge_cat = SAO_EO_CAT1; edge_cat <= SAO_EO_CAT4; ++edge_cat) {
|
||||
int abs_offset = abs(offsets[edge_cat]);
|
||||
if (abs_offset == 0 || abs_offset == SAO_ABS_OFFSET_MAX) {
|
||||
mode_bits += abs_offset + 1;
|
||||
} else {
|
||||
mode_bits += abs_offset + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TR coded sao_eo_class_
|
||||
if (edge_class == SAO_EO0 || edge_class == SAO_EO3) {
|
||||
mode_bits += 1;
|
||||
} else {
|
||||
mode_bits += 2;
|
||||
}
|
||||
|
||||
return mode_bits;
|
||||
}
|
||||
|
||||
|
||||
static int sao_mode_bits_band(int band_position, int offsets[4],
|
||||
sao_info *sao_top, sao_info *sao_left)
|
||||
{
|
||||
int mode_bits = 0;
|
||||
|
||||
// FL coded merges.
|
||||
if (sao_left != NULL) {
|
||||
mode_bits += 1;
|
||||
if (sao_check_merge(sao_left, SAO_TYPE_BAND, offsets, band_position, 0)) {
|
||||
return mode_bits;
|
||||
}
|
||||
}
|
||||
if (sao_top != NULL) {
|
||||
mode_bits += 1;
|
||||
if (sao_check_merge(sao_top, SAO_TYPE_BAND, offsets, band_position, 0)) {
|
||||
return mode_bits;
|
||||
}
|
||||
}
|
||||
|
||||
// TR coded sao_type_idx_, band = 1
|
||||
mode_bits += 2;
|
||||
|
||||
// TR coded offsets and possible FL coded offset signs.
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
int abs_offset = abs(offsets[i]);
|
||||
if (abs_offset == 0) {
|
||||
mode_bits += abs_offset + 1;
|
||||
} else if (abs_offset == SAO_ABS_OFFSET_MAX) {
|
||||
mode_bits += abs_offset + 1 + 1;
|
||||
} else {
|
||||
mode_bits += abs_offset + 2 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FL coded band position.
|
||||
mode_bits += 5;
|
||||
|
||||
return mode_bits;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief calculate an array of intensity correlations for each intensity value
|
||||
*/
|
||||
|
@ -102,7 +282,7 @@ static void calc_sao_offset_array(const sao_info *sao, int *offset)
|
|||
* \param sao_bands an array of bands for original and reconstructed block
|
||||
*/
|
||||
static int calc_sao_band_offsets(int sao_bands[2][32], int offsets[4],
|
||||
int *band_position, int *rate)
|
||||
int *band_position)
|
||||
{
|
||||
int band;
|
||||
int offset;
|
||||
|
@ -129,12 +309,6 @@ static int calc_sao_band_offsets(int sao_bands[2][32], int offsets[4],
|
|||
while(offset != 0) {
|
||||
temp_dist = sao_bands[1][band]*offset*offset - 2*offset*sao_bands[0][band];
|
||||
|
||||
// Calculate used rate to code this
|
||||
temp_rate[band] = abs(offset+2);
|
||||
// When max offset, no need to signal final bit
|
||||
if (abs(offset)==SAO_ABS_OFFSET_MAX) {
|
||||
temp_rate[band] --;
|
||||
}
|
||||
// Store best distortion and offset
|
||||
if(temp_dist < best_dist) {
|
||||
dist[band] = temp_dist;
|
||||
|
@ -153,8 +327,6 @@ static int calc_sao_band_offsets(int sao_bands[2][32], int offsets[4],
|
|||
best_dist_pos = band;
|
||||
}
|
||||
}
|
||||
*rate = temp_rate[best_dist_pos]+temp_rate[best_dist_pos+1]+
|
||||
temp_rate[best_dist_pos+2]+temp_rate[best_dist_pos+3];
|
||||
// Copy best offsets to output
|
||||
memcpy(offsets, &temp_offsets[best_dist_pos], 4*sizeof(int));
|
||||
|
||||
|
@ -224,8 +396,6 @@ static void sao_reconstruct_color(const pixel *rec_data, pixel *new_rec_data,
|
|||
int block_width, int block_height)
|
||||
{
|
||||
int y, x;
|
||||
vector2d a_ofs = g_sao_edge_offsets[sao->eo_class][0];
|
||||
vector2d b_ofs = g_sao_edge_offsets[sao->eo_class][1];
|
||||
// Arrays orig_data and rec_data are quarter size for chroma.
|
||||
|
||||
|
||||
|
@ -242,6 +412,8 @@ static void sao_reconstruct_color(const pixel *rec_data, pixel *new_rec_data,
|
|||
// their neighbours.
|
||||
for (y = 0; y < block_height; ++y) {
|
||||
for (x = 0; x < block_width; ++x) {
|
||||
vector2d a_ofs = g_sao_edge_offsets[sao->eo_class][0];
|
||||
vector2d b_ofs = g_sao_edge_offsets[sao->eo_class][1];
|
||||
const pixel *c_data = &rec_data[y * stride + x];
|
||||
pixel *new_data = &new_rec_data[y * new_stride + x];
|
||||
pixel a = c_data[a_ofs.y * stride + a_ofs.x];
|
||||
|
@ -431,15 +603,8 @@ void sao_reconstruct(picture *pic, const pixel *old_rec,
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* \param data Array of pointers to reference pixels.
|
||||
* \param recdata Array of pointers to reconstructed pixels.
|
||||
* \param block_width Width of the area to be examined.
|
||||
* \param block_height Height of the area to be examined.
|
||||
* \param buf_cnt Number of pointers data and recdata have.
|
||||
* \param sao_out Output parameter for the best sao parameters.
|
||||
*/
|
||||
static void sao_search_best_mode(const pixel * data[], const pixel * recdata[],
|
||||
|
||||
static void sao_search_edge_sao(const pixel * data[], const pixel * recdata[],
|
||||
int block_width, int block_height,
|
||||
unsigned buf_cnt,
|
||||
sao_info *sao_out, sao_info *sao_top,
|
||||
|
@ -449,9 +614,9 @@ static void sao_search_best_mode(const pixel * data[], const pixel * recdata[],
|
|||
// This array is used to calculate the mean offset used to minimize distortion.
|
||||
int cat_sum_cnt[2][NUM_SAO_EDGE_CATEGORIES];
|
||||
unsigned i = 0;
|
||||
int temp_rate = 0;
|
||||
memset(cat_sum_cnt, 0, sizeof(int) * 2 * NUM_SAO_EDGE_CATEGORIES);
|
||||
|
||||
sao_out->type = SAO_TYPE_EDGE;
|
||||
sao_out->ddistortion = INT_MAX;
|
||||
|
||||
for (edge_class = SAO_EO0; edge_class <= SAO_EO3; ++edge_class) {
|
||||
|
@ -495,20 +660,12 @@ static void sao_search_best_mode(const pixel * data[], const pixel * recdata[],
|
|||
// increase increase SSE by h^2 and all pixels that are improved by
|
||||
// offset decrease SSE by h*E.
|
||||
sum_ddistortion += cat_cnt * offset * offset - 2 * offset * cat_sum;
|
||||
}
|
||||
|
||||
// Calculate used rate to code this
|
||||
temp_rate += abs(offset+2);
|
||||
// When max offset, no need to signal final bit
|
||||
if (abs(offset)==SAO_ABS_OFFSET_MAX) {
|
||||
temp_rate --;
|
||||
{
|
||||
int mode_bits = sao_mode_bits_edge(edge_class, edge_offset, sao_top, sao_left);
|
||||
sum_ddistortion += (int)((double)mode_bits*(g_cur_lambda_cost+0.5));
|
||||
}
|
||||
}
|
||||
// If can merge, use lower cost
|
||||
if((sao_top != NULL && sao_check_merge(sao_top, SAO_TYPE_EDGE, edge_offset,0, edge_class)) ||
|
||||
(sao_left != NULL && sao_check_merge(sao_left, SAO_TYPE_EDGE, edge_offset,0, edge_class))) {
|
||||
temp_rate = 1;
|
||||
}
|
||||
sum_ddistortion += (int)((double)temp_rate*(g_cur_lambda_cost+0.5));
|
||||
// SAO is not applied for category 0.
|
||||
edge_offset[SAO_EO_CAT0] = 0;
|
||||
|
||||
|
@ -519,6 +676,19 @@ static void sao_search_best_mode(const pixel * data[], const pixel * recdata[],
|
|||
memcpy(sao_out->offsets, edge_offset, sizeof(int) * NUM_SAO_EDGE_CATEGORIES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void sao_search_band_sao(const pixel * data[], const pixel * recdata[],
|
||||
int block_width, int block_height,
|
||||
unsigned buf_cnt,
|
||||
sao_info *sao_out, sao_info *sao_top,
|
||||
sao_info *sao_left)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
sao_out->type = SAO_TYPE_BAND;
|
||||
sao_out->ddistortion = MAX_INT;
|
||||
|
||||
// Band offset
|
||||
{
|
||||
|
@ -533,12 +703,9 @@ static void sao_search_best_mode(const pixel * data[], const pixel * recdata[],
|
|||
block_height,sao_bands);
|
||||
}
|
||||
|
||||
ddistortion = calc_sao_band_offsets(sao_bands, temp_offsets, &sao_out->band_position, &temp_rate);
|
||||
ddistortion = calc_sao_band_offsets(sao_bands, temp_offsets, &sao_out->band_position);
|
||||
|
||||
if((sao_top != NULL && sao_check_merge(sao_top, SAO_TYPE_BAND, temp_offsets,sao_out->band_position, 0)) ||
|
||||
(sao_left != NULL && sao_check_merge(sao_left, SAO_TYPE_BAND, temp_offsets,sao_out->band_position, 0))) {
|
||||
temp_rate = 1;
|
||||
}
|
||||
temp_rate = sao_mode_bits_band(sao_out->band_position, temp_offsets, sao_top, sao_left);
|
||||
ddistortion += (int)((double)temp_rate*(g_cur_lambda_cost+0.5));
|
||||
|
||||
// Select band sao over edge sao when distortion is lower
|
||||
|
@ -548,24 +715,78 @@ static void sao_search_best_mode(const pixel * data[], const pixel * recdata[],
|
|||
memcpy(&sao_out->offsets[1], temp_offsets, sizeof(int) * 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When BD-rate would increase because of SAO, disable it
|
||||
if(sao_out->ddistortion >= 0) {
|
||||
sao_out->type = SAO_TYPE_NONE;
|
||||
return;
|
||||
|
||||
/**
|
||||
* \param data Array of pointers to reference pixels.
|
||||
* \param recdata Array of pointers to reconstructed pixels.
|
||||
* \param block_width Width of the area to be examined.
|
||||
* \param block_height Height of the area to be examined.
|
||||
* \param buf_cnt Number of pointers data and recdata have.
|
||||
* \param sao_out Output parameter for the best sao parameters.
|
||||
*/
|
||||
static void sao_search_best_mode(const pixel * data[], const pixel * recdata[],
|
||||
int block_width, int block_height,
|
||||
unsigned buf_cnt,
|
||||
sao_info *sao_out, sao_info *sao_top,
|
||||
sao_info *sao_left)
|
||||
{
|
||||
sao_info edge_sao;
|
||||
sao_info band_sao;
|
||||
|
||||
sao_search_edge_sao(data, recdata, block_width, block_height, buf_cnt, &edge_sao, sao_top, sao_left);
|
||||
sao_search_band_sao(data, recdata, block_width, block_height, buf_cnt, &band_sao, sao_top, sao_left);
|
||||
|
||||
{
|
||||
int mode_bits = sao_mode_bits_edge(edge_sao.eo_class, edge_sao.offsets, sao_top, sao_left);
|
||||
int ddistortion = mode_bits * (int)(g_cur_lambda_cost + 0.5);
|
||||
unsigned buf_i;
|
||||
|
||||
for (buf_i = 0; buf_i < buf_cnt; ++buf_i) {
|
||||
ddistortion += sao_edge_ddistortion(data[buf_i], recdata[buf_i],
|
||||
block_width, block_height,
|
||||
edge_sao.eo_class, edge_sao.offsets);
|
||||
}
|
||||
|
||||
edge_sao.ddistortion = ddistortion;
|
||||
}
|
||||
|
||||
{
|
||||
int mode_bits = sao_mode_bits_band(band_sao.band_position, &band_sao.offsets[1], sao_top, sao_left);
|
||||
int ddistortion = mode_bits * (int)(g_cur_lambda_cost + 0.5);
|
||||
unsigned buf_i;
|
||||
|
||||
for (buf_i = 0; buf_i < buf_cnt; ++buf_i) {
|
||||
ddistortion += sao_band_ddistortion(data[buf_i], recdata[buf_i],
|
||||
block_width, block_height,
|
||||
band_sao.band_position, &band_sao.offsets[1]);
|
||||
}
|
||||
|
||||
band_sao.ddistortion = ddistortion;
|
||||
}
|
||||
|
||||
if (edge_sao.ddistortion <= band_sao.ddistortion) {
|
||||
*sao_out = edge_sao;
|
||||
} else {
|
||||
*sao_out = band_sao;
|
||||
}
|
||||
|
||||
// Choose between SAO and doing nothing, taking into account the
|
||||
// rate-distortion cost of coding do nothing.
|
||||
{
|
||||
int cost_of_nothing = sao_mode_bits_none(sao_top, sao_left) * (int)(g_cur_lambda_cost + 0.5);
|
||||
if (sao_out->ddistortion >= cost_of_nothing) {
|
||||
sao_out->type = SAO_TYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for merge modes
|
||||
if (sao_top != NULL) {
|
||||
sao_out->merge_up_flag = sao_check_merge(sao_top, sao_out->type, sao_out->offsets,
|
||||
sao_out->band_position, sao_out->eo_class);
|
||||
}
|
||||
|
||||
// Check for merge modes
|
||||
if (sao_left != NULL) {
|
||||
sao_out->merge_left_flag = sao_check_merge(sao_left, sao_out->type, sao_out->offsets,
|
||||
sao_out->band_position, sao_out->eo_class);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void sao_search_chroma(const picture *pic, unsigned x_ctb, unsigned y_ctb, sao_info *sao, sao_info *sao_top, sao_info *sao_left)
|
||||
|
|
Loading…
Reference in a new issue