mirror of
https://github.com/ultravideo/uvg266.git
synced 2024-12-12 08:54:06 +00:00
Rewrite SAO recon to handle arbitrary sized blocks
Adds width and height parameters to function kvz_sao_reconstruct and changes it to take coordinates in units of pixels. This will be useful for doing SAO for areas smaller than a whole CTU.
This commit is contained in:
parent
61fc028eef
commit
ec9ff42077
|
@ -606,10 +606,36 @@ static void encoder_state_worker_sao_reconstruct_lcu(void *opaque) {
|
||||||
// sao_do_rdo(encoder, lcu.x, lcu.y, sao_luma, sao_chroma);
|
// sao_do_rdo(encoder, lcu.x, lcu.y, sao_luma, sao_chroma);
|
||||||
sao_info_t *sao_luma = &frame->sao_luma[data->y * stride + x];
|
sao_info_t *sao_luma = &frame->sao_luma[data->y * stride + x];
|
||||||
sao_info_t *sao_chroma = &frame->sao_chroma[data->y * stride + x];
|
sao_info_t *sao_chroma = &frame->sao_chroma[data->y * stride + x];
|
||||||
kvz_sao_reconstruct(data->encoder_state->encoder_control, frame, new_y_data, x, data->y, sao_luma, COLOR_Y);
|
|
||||||
|
kvz_sao_reconstruct(data->encoder_state,
|
||||||
|
&new_y_data[x * LCU_WIDTH + frame->width * (data->y * LCU_WIDTH)],
|
||||||
|
frame->width,
|
||||||
|
x * LCU_WIDTH,
|
||||||
|
data->y * LCU_WIDTH,
|
||||||
|
MIN(LCU_WIDTH, frame->width - x * LCU_WIDTH),
|
||||||
|
MIN(LCU_WIDTH, frame->height - data->y * LCU_WIDTH),
|
||||||
|
sao_luma,
|
||||||
|
COLOR_Y);
|
||||||
|
|
||||||
if (frame->rec->chroma_format != KVZ_CSP_400) {
|
if (frame->rec->chroma_format != KVZ_CSP_400) {
|
||||||
kvz_sao_reconstruct(data->encoder_state->encoder_control, frame, new_u_data, x, data->y, sao_chroma, COLOR_U);
|
kvz_sao_reconstruct(data->encoder_state,
|
||||||
kvz_sao_reconstruct(data->encoder_state->encoder_control, frame, new_v_data, x, data->y, sao_chroma, COLOR_V);
|
&new_u_data[x * LCU_WIDTH_C + frame->width/2 * (data->y * LCU_WIDTH_C)],
|
||||||
|
frame->width/2,
|
||||||
|
x * LCU_WIDTH_C,
|
||||||
|
data->y * LCU_WIDTH_C,
|
||||||
|
MIN(LCU_WIDTH_C, frame->width/2 - x * LCU_WIDTH_C),
|
||||||
|
MIN(LCU_WIDTH_C, frame->height/2 - data->y * LCU_WIDTH_C),
|
||||||
|
sao_chroma,
|
||||||
|
COLOR_U);
|
||||||
|
kvz_sao_reconstruct(data->encoder_state,
|
||||||
|
&new_v_data[x * LCU_WIDTH_C + frame->width/2 * (data->y * LCU_WIDTH_C)],
|
||||||
|
frame->width/2,
|
||||||
|
x * LCU_WIDTH_C,
|
||||||
|
data->y * LCU_WIDTH_C,
|
||||||
|
MIN(LCU_WIDTH_C, frame->width/2 - x * LCU_WIDTH_C),
|
||||||
|
MIN(LCU_WIDTH_C, frame->height/2 - data->y * LCU_WIDTH_C),
|
||||||
|
sao_chroma,
|
||||||
|
COLOR_V);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
270
src/sao.c
270
src/sao.c
|
@ -261,181 +261,115 @@ static void calc_sao_bands(const encoder_state_t * const state, const kvz_pixel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
static int sao_calc_eo_cat(kvz_pixel a, kvz_pixel b, kvz_pixel c)
|
||||||
* \brief Calculate dimensions of the buffer used by sao reconstruction.
|
|
||||||
|
|
||||||
* \param pic Picture.
|
|
||||||
* \param sao Sao parameters.
|
|
||||||
* \param rec Top-left corner of the LCU
|
|
||||||
*/
|
|
||||||
static void sao_calc_band_block_dims(const videoframe_t *frame, color_t color_i,
|
|
||||||
vector2d_t *rec, vector2d_t *block)
|
|
||||||
{
|
{
|
||||||
const int is_chroma = (color_i != COLOR_Y ? 1 : 0);
|
// Mapping relationships between a, b and c to eo_idx.
|
||||||
int width = frame->width >> is_chroma;
|
static const int sao_eo_idx_to_eo_category[] = { 1, 2, 0, 3, 4 };
|
||||||
int height = frame->height >> is_chroma;
|
|
||||||
int block_width = LCU_WIDTH >> is_chroma;
|
|
||||||
|
|
||||||
|
int eo_idx = 2 + SIGN3((int)c - (int)a) + SIGN3((int)c - (int)b);
|
||||||
|
|
||||||
// Handle right and bottom, taking care of non-LCU sized CUs.
|
return sao_eo_idx_to_eo_category[eo_idx];
|
||||||
if (rec->y + block_width >= height) {
|
|
||||||
if (rec->y + block_width >= height) {
|
|
||||||
block->y = height - rec->y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rec->x + block_width >= width) {
|
|
||||||
if (rec->x + block_width > width) {
|
|
||||||
block->x = width - rec->x;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rec->x = 0; rec->y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Calculate dimensions of the buffer used by sao reconstruction.
|
* \brief Reconstruct SAO.
|
||||||
*
|
*
|
||||||
* This function calculates 4 vectors that can be used to make the temporary
|
* \param encoder encoder state
|
||||||
* buffers required by sao_reconstruct_color.
|
* \param buffer Buffer containing the deblocked input pixels. The
|
||||||
*
|
* area to filter starts at index 0.
|
||||||
* Vector block is the area affected by sao. Vectors tr and br are top-left
|
* \param stride stride of buffer
|
||||||
* margin and bottom-right margin, which contain pixels that are not modified
|
* \param x x-coordinate of the top-left corner in pixels
|
||||||
* by the reconstruction of this LCU but are needed by the reconstruction.
|
* \param y y-coordinate of the top-left corner in pixels
|
||||||
* Vector rec is the offset from the CU to the required pixel area.
|
* \param width width of the area to filter
|
||||||
*
|
* \param height height of the area to filter
|
||||||
* The margins are always either 0 or 1, depending on the direction of the
|
* \param sao SAO information
|
||||||
* edge offset class.
|
* \param color color plane index
|
||||||
*
|
* \param border_left true, if the left border of the area exists
|
||||||
* This also takes into account borders of the picture and non-LCU sized
|
* \param border_right true, if the right border of the area exists
|
||||||
* CU's at the bottom and right of the picture.
|
* \param border_above true, if the top border of the area exists
|
||||||
*
|
* \param border_below true, if the bottom border of the area exists
|
||||||
* \ CU + rec
|
|
||||||
* +------+
|
|
||||||
* |\ tl |
|
|
||||||
* | +--+ |
|
|
||||||
* | |\ block
|
|
||||||
* | | \| |
|
|
||||||
* | +--+ |
|
|
||||||
* | \ br
|
|
||||||
* +------+
|
|
||||||
*
|
|
||||||
* \param pic Picture.
|
|
||||||
* \param sao Sao parameters.
|
|
||||||
* \param rec Top-left corner of the LCU, modified to be top-left corner of
|
|
||||||
*/
|
*/
|
||||||
static void sao_calc_edge_block_dims(const videoframe_t * const frame, color_t color_i,
|
void kvz_sao_reconstruct(const encoder_state_t *state,
|
||||||
const sao_info_t *sao, vector2d_t *rec,
|
const kvz_pixel *buffer,
|
||||||
vector2d_t *tl, vector2d_t *br,
|
int stride,
|
||||||
vector2d_t *block)
|
int frame_x,
|
||||||
|
int frame_y,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
const sao_info_t *sao,
|
||||||
|
color_t color)
|
||||||
{
|
{
|
||||||
vector2d_t a_ofs = g_sao_edge_offsets[sao->eo_class][0];
|
const encoder_control_t *const ctrl = state->encoder_control;
|
||||||
vector2d_t b_ofs = g_sao_edge_offsets[sao->eo_class][1];
|
videoframe_t *const frame = state->tile->frame;
|
||||||
const int is_chroma = (color_i != COLOR_Y ? 1 : 0);
|
const int shift = color == COLOR_Y ? 0 : 1;
|
||||||
int width = frame->width >> is_chroma;
|
|
||||||
int height = frame->height >> is_chroma;
|
|
||||||
int block_width = LCU_WIDTH >> is_chroma;
|
|
||||||
|
|
||||||
// Handle top and left.
|
const int frame_width = frame->width >> shift;
|
||||||
if (rec->y == 0) {
|
const int frame_height = frame->height >> shift;
|
||||||
tl->y = 0;
|
kvz_pixel *output = &frame->rec->data[color][frame_x + frame_y * frame_width];
|
||||||
if (a_ofs.y == -1 || b_ofs.y == -1) {
|
|
||||||
block->y -= 1;
|
switch (sao->type) {
|
||||||
tl->y += 1;
|
|
||||||
|
case SAO_TYPE_NONE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SAO_TYPE_BAND: {
|
||||||
|
int offsets[1 << KVZ_BIT_DEPTH];
|
||||||
|
kvz_calc_sao_offset_array(ctrl, sao, offsets, color);
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
output[x + y * frame_width] = offsets[buffer[x + y * stride]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rec->x == 0) {
|
break;
|
||||||
tl->x = 0;
|
|
||||||
if (a_ofs.x == -1 || b_ofs.x == -1) {
|
|
||||||
block->x -= 1;
|
|
||||||
tl->x += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle right and bottom, taking care of non-LCU sized CUs.
|
case SAO_TYPE_EDGE: {
|
||||||
if (rec->y + block_width >= height) {
|
const int offset_v = color == COLOR_V ? 5 : 0;
|
||||||
br->y = 0;
|
const vector2d_t *offset = g_sao_edge_offsets[sao->eo_class];
|
||||||
block->y -= block_width + rec->y - height;
|
|
||||||
if (a_ofs.y == 1 || b_ofs.y == 1) {
|
|
||||||
block->y -= 1;
|
|
||||||
br->y += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rec->x + block_width >= width) {
|
|
||||||
br->x = 0;
|
|
||||||
block->x -= block_width + rec->x - width;
|
|
||||||
if (a_ofs.x == 1 || b_ofs.x == 1) {
|
|
||||||
block->x -= 1;
|
|
||||||
br->x += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rec->y = (rec->y == 0 ? 0 : -1);
|
int x_orig = 0;
|
||||||
rec->x = (rec->x == 0 ? 0 : -1);
|
int y_orig = 0;
|
||||||
}
|
|
||||||
|
|
||||||
void kvz_sao_reconstruct(const encoder_control_t * const encoder, videoframe_t * frame, const kvz_pixel *old_rec,
|
if (frame_x + offset[0].x < 0 || frame_x + offset[1].x < 0) {
|
||||||
unsigned x_ctb, unsigned y_ctb,
|
// Nothing to do for the leftmost column.
|
||||||
const sao_info_t *sao, color_t color_i)
|
x_orig += 1;
|
||||||
|
}
|
||||||
|
if (frame_x + width + offset[0].x > frame_width ||
|
||||||
|
frame_x + width + offset[1].x > frame_width)
|
||||||
{
|
{
|
||||||
const int is_chroma = (color_i != COLOR_Y ? 1 : 0);
|
// Nothing to do for the rightmost column.
|
||||||
const int pic_stride = frame->width >> is_chroma;
|
width -= 1;
|
||||||
const int lcu_stride = LCU_WIDTH >> is_chroma;
|
}
|
||||||
const int buf_stride = lcu_stride + 2;
|
if (frame_y + offset[0].y < 0 || frame_y + offset[1].y < 0) {
|
||||||
|
// Nothing to do for the topmost row.
|
||||||
kvz_pixel *recdata = frame->rec->data[color_i];
|
y_orig += 1;
|
||||||
kvz_pixel buf_rec[(LCU_WIDTH + 2) * (LCU_WIDTH + 2)];
|
}
|
||||||
kvz_pixel new_rec[LCU_WIDTH * LCU_WIDTH];
|
if (frame_y + height + offset[0].y > frame_height ||
|
||||||
// Calling CU_TO_PIXEL with depth 1 is the same as using block size of 32.
|
frame_y + height + offset[1].y > frame_height)
|
||||||
kvz_pixel *lcu_rec = &recdata[CU_TO_PIXEL(x_ctb, y_ctb, is_chroma, frame->rec->stride>>is_chroma)];
|
{
|
||||||
const kvz_pixel *old_lcu_rec = &old_rec[CU_TO_PIXEL(x_ctb, y_ctb, is_chroma, pic_stride)];
|
// Nothing to do for the bottommost row.
|
||||||
|
height -= 1;
|
||||||
vector2d_t ofs;
|
|
||||||
vector2d_t tl = { 1, 1 };
|
|
||||||
vector2d_t br = { 1, 1 };
|
|
||||||
vector2d_t block;
|
|
||||||
|
|
||||||
if (sao->type == SAO_TYPE_NONE) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ofs.x = x_ctb * lcu_stride;
|
for (int y = y_orig; y < height; y++) {
|
||||||
ofs.y = y_ctb * lcu_stride;
|
for (int x = x_orig; x < width; x++) {
|
||||||
block.x = lcu_stride;
|
const kvz_pixel *data = &buffer[x + y * stride];
|
||||||
block.y = lcu_stride;
|
|
||||||
if (sao->type == SAO_TYPE_BAND) {
|
kvz_pixel a = data[offset[0].x + offset[0].y * stride];
|
||||||
tl.x = 0; tl.y = 0;
|
kvz_pixel c = data[0];
|
||||||
br.x = 0; br.y = 0;
|
kvz_pixel b = data[offset[1].x + offset[1].y * stride];
|
||||||
sao_calc_band_block_dims(frame, color_i, &ofs, &block);
|
|
||||||
|
const int eo_cat = sao_calc_eo_cat(a, b, c);
|
||||||
|
|
||||||
|
output[x + y * frame_width] =
|
||||||
|
CLIP(0, (1 << KVZ_BIT_DEPTH) - 1, c + sao->offsets[eo_cat + offset_v]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
sao_calc_edge_block_dims(frame, color_i, sao, &ofs, &tl, &br, &block);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(ofs.x + tl.x + block.x + br.x <= frame->width);
|
|
||||||
assert(ofs.y + tl.y + block.y + br.y <= frame->height);
|
|
||||||
|
|
||||||
CHECKPOINT("ofs.x=%d ofs.y=%d tl.x=%d tl.y=%d block.x=%d block.y=%d br.x=%d br.y=%d",
|
|
||||||
ofs.x, ofs.y, tl.x, tl.y, block.x, block.y, br.x, br.y);
|
|
||||||
|
|
||||||
// Data to tmp buffer.
|
|
||||||
kvz_pixels_blit(&old_lcu_rec[ofs.y * pic_stride + ofs.x],
|
|
||||||
buf_rec,
|
|
||||||
tl.x + block.x + br.x,
|
|
||||||
tl.y + block.y + br.y,
|
|
||||||
pic_stride, buf_stride);
|
|
||||||
|
|
||||||
kvz_sao_reconstruct_color(encoder, &buf_rec[tl.y * buf_stride + tl.x],
|
|
||||||
&new_rec[(ofs.y + tl.y) * lcu_stride + ofs.x + tl.x],
|
|
||||||
sao,
|
|
||||||
buf_stride, lcu_stride,
|
|
||||||
block.x, block.y, color_i);
|
|
||||||
|
|
||||||
// Copy reconstructed block from tmp buffer to rec image.
|
|
||||||
kvz_pixels_blit(&new_rec[(tl.y + ofs.y) * lcu_stride + (tl.x + ofs.x)],
|
|
||||||
&lcu_rec[(tl.y + ofs.y) * (frame->rec->stride >> is_chroma) + (tl.x + ofs.x)],
|
|
||||||
block.x, block.y, lcu_stride, frame->rec->stride >> is_chroma);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void sao_search_edge_sao(const encoder_state_t * const state,
|
static void sao_search_edge_sao(const encoder_state_t * const state,
|
||||||
|
@ -820,7 +754,16 @@ void kvz_sao_reconstruct_frame(encoder_state_t * const state)
|
||||||
sao_info_t *sao_luma = &frame->sao_luma[lcu.y * stride + lcu.x];
|
sao_info_t *sao_luma = &frame->sao_luma[lcu.y * stride + lcu.x];
|
||||||
|
|
||||||
// sao_do_rdo(encoder, lcu.x, lcu.y, sao_luma, sao_chroma);
|
// sao_do_rdo(encoder, lcu.x, lcu.y, sao_luma, sao_chroma);
|
||||||
kvz_sao_reconstruct(state->encoder_control, frame, new_y_data, lcu.x, lcu.y, sao_luma, COLOR_Y);
|
vector2d_t pos = { lcu.x * LCU_WIDTH, lcu.y * LCU_WIDTH };
|
||||||
|
kvz_sao_reconstruct(state,
|
||||||
|
&new_y_data[pos.x + pos.y * frame->width],
|
||||||
|
frame->width,
|
||||||
|
pos.x,
|
||||||
|
pos.y,
|
||||||
|
MIN(LCU_WIDTH, frame->width - pos.x),
|
||||||
|
MIN(LCU_WIDTH, frame->height - pos.y),
|
||||||
|
sao_luma,
|
||||||
|
COLOR_Y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(new_y_data);
|
free(new_y_data);
|
||||||
|
@ -837,8 +780,25 @@ void kvz_sao_reconstruct_frame(encoder_state_t * const state)
|
||||||
unsigned stride = frame->width_in_lcu;
|
unsigned stride = frame->width_in_lcu;
|
||||||
sao_info_t *sao_chroma = &frame->sao_chroma[lcu.y * stride + lcu.x];
|
sao_info_t *sao_chroma = &frame->sao_chroma[lcu.y * stride + lcu.x];
|
||||||
|
|
||||||
kvz_sao_reconstruct(state->encoder_control, frame, new_u_data, lcu.x, lcu.y, sao_chroma, COLOR_U);
|
vector2d_t pos = { lcu.x * LCU_WIDTH_C, lcu.y * LCU_WIDTH_C };
|
||||||
kvz_sao_reconstruct(state->encoder_control, frame, new_v_data, lcu.x, lcu.y, sao_chroma, COLOR_V);
|
kvz_sao_reconstruct(state,
|
||||||
|
&new_u_data[pos.x + pos.y * frame->width/2],
|
||||||
|
frame->width / 2,
|
||||||
|
pos.x,
|
||||||
|
pos.y,
|
||||||
|
MIN(LCU_WIDTH_C, frame->width/2 - pos.x),
|
||||||
|
MIN(LCU_WIDTH_C, frame->height/2 - pos.y),
|
||||||
|
sao_chroma,
|
||||||
|
COLOR_U);
|
||||||
|
kvz_sao_reconstruct(state,
|
||||||
|
&new_v_data[pos.x + pos.y * frame->width/2],
|
||||||
|
frame->width / 2,
|
||||||
|
pos.x,
|
||||||
|
pos.y,
|
||||||
|
MIN(LCU_WIDTH_C, frame->width/2 - pos.x),
|
||||||
|
MIN(LCU_WIDTH_C, frame->height/2 - pos.y),
|
||||||
|
sao_chroma,
|
||||||
|
COLOR_V);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
src/sao.h
13
src/sao.h
|
@ -72,9 +72,16 @@ static const vector2d_t g_sao_edge_offsets[SAO_NUM_EO][2] = {
|
||||||
(sao).offsets[0], (sao).offsets[1], (sao).offsets[2], (sao).offsets[3], (sao).offsets[4])
|
(sao).offsets[0], (sao).offsets[1], (sao).offsets[2], (sao).offsets[3], (sao).offsets[4])
|
||||||
|
|
||||||
|
|
||||||
void kvz_sao_reconstruct(const encoder_control_t * encoder, videoframe_t *frame, const kvz_pixel *old_rec,
|
void kvz_sao_reconstruct(const encoder_state_t *state,
|
||||||
unsigned x_ctb, unsigned y_ctb,
|
const kvz_pixel *buffer,
|
||||||
const sao_info_t *sao, color_t color_i);
|
int stride,
|
||||||
|
int frame_x,
|
||||||
|
int frame_y,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
const sao_info_t *sao,
|
||||||
|
color_t color);
|
||||||
|
|
||||||
void kvz_sao_reconstruct_frame(encoder_state_t *state);
|
void kvz_sao_reconstruct_frame(encoder_state_t *state);
|
||||||
void kvz_sao_search_lcu(const encoder_state_t* const state, int lcu_x, int lcu_y);
|
void kvz_sao_search_lcu(const encoder_state_t* const state, int lcu_x, int lcu_y);
|
||||||
void kvz_calc_sao_offset_array(const encoder_control_t * const encoder, const sao_info_t *sao, int *offset, color_t color_i);
|
void kvz_calc_sao_offset_array(const encoder_control_t * const encoder, const sao_info_t *sao, int *offset, color_t color_i);
|
||||||
|
|
Loading…
Reference in a new issue