blob: c73a2f9a16f105ede0f7dba8863bccf2a4f643a1 [file] [log] [blame]
/*
* Copyright (c) 2016 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <math.h>
#include "./vpx_config.h"
#include "./vpx_dsp_rtcd.h"
#include "vp10/common/onyxc_int.h"
#include "vp10/common/restoration.h"
#include "vpx_dsp/vpx_dsp_common.h"
#include "vpx_mem/vpx_mem.h"
#include "vpx_ports/mem.h"
#define RESTORATION_RANGE 256
#define RESTORATION_RANGE_SYM (2 * RESTORATION_RANGE + 1)
static double restoration_filters_r_kf[RESTORATION_LEVELS_KF + 1]
[RESTORATION_RANGE_SYM];
static double restoration_filters_r[RESTORATION_LEVELS + 1]
[RESTORATION_RANGE_SYM];
static double restoration_filters_s_kf[RESTORATION_LEVELS_KF + 1]
[RESTORATION_WIN][RESTORATION_WIN];
static double restoration_filters_s[RESTORATION_LEVELS + 1]
[RESTORATION_WIN][RESTORATION_WIN];
void vp10_loop_restoration_precal() {
int i;
for (i = 1; i < RESTORATION_LEVELS_KF + 1; i ++) {
const restoration_params_t param = vp10_restoration_level_to_params(i, 1);
const int sigma_x = param.sigma_x;
const int sigma_y = param.sigma_y;
const int sigma_r = param.sigma_r;
const double sigma_r_d = (double)sigma_r / RESTORATION_PRECISION;
const double sigma_x_d = (double)sigma_x / RESTORATION_PRECISION;
const double sigma_y_d = (double)sigma_y / RESTORATION_PRECISION;
double *fr = restoration_filters_r_kf[i] + RESTORATION_RANGE;
int j, x, y;
for (j = 0; j <= RESTORATION_RANGE; j++) {
fr[j] = exp(-(j * j) / (2 * sigma_r_d * sigma_r_d));
fr[-j] = fr[j];
}
for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; y++) {
for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; x++) {
restoration_filters_s_kf[i][y + RESTORATION_HALFWIN]
[x + RESTORATION_HALFWIN] =
exp(-(x * x) / (2 * sigma_x_d * sigma_x_d)
-(y * y) / (2 * sigma_y_d * sigma_y_d));
}
}
}
for (i = 1; i < RESTORATION_LEVELS + 1; i ++) {
const restoration_params_t param = vp10_restoration_level_to_params(i, 0);
const int sigma_x = param.sigma_x;
const int sigma_y = param.sigma_y;
const int sigma_r = param.sigma_r;
const double sigma_r_d = (double)sigma_r / RESTORATION_PRECISION;
const double sigma_x_d = (double)sigma_x / RESTORATION_PRECISION;
const double sigma_y_d = (double)sigma_y / RESTORATION_PRECISION;
double *fr = restoration_filters_r[i] + RESTORATION_RANGE;
int j, x, y;
for (j = 0; j <= RESTORATION_RANGE; j++) {
fr[j] = exp(-(j * j) / (2 * sigma_r_d * sigma_r_d));
fr[-j] = fr[j];
}
for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; y++) {
for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; x++) {
restoration_filters_s
[i][y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] =
exp(-(x * x) / (2 * sigma_x_d * sigma_x_d)
-(y * y) / (2 * sigma_y_d * sigma_y_d));
}
}
}
}
int vp10_restoration_level_bits(const VP10_COMMON *const cm) {
return cm->frame_type == KEY_FRAME ?
RESTORATION_LEVEL_BITS_KF : RESTORATION_LEVEL_BITS;
}
int vp10_loop_restoration_used(int level, int kf) {
const restoration_params_t param =
vp10_restoration_level_to_params(level, kf);
return (param.sigma_x && param.sigma_y && param.sigma_r);
}
void vp10_loop_restoration_init(restoration_info_n *rst,
int level, int kf) {
rst->restoration_used = vp10_loop_restoration_used(level, kf);
if (rst->restoration_used) {
int i;
rst->wr_lut = kf ? restoration_filters_r_kf[level] :
restoration_filters_r[level];
for (i = 0; i < RESTORATION_WIN; i++)
rst->wx_lut[i] = kf ? restoration_filters_s_kf[level][i] :
restoration_filters_s[level][i];
}
}
static int is_in_image(int x, int y, int width, int height) {
return (x >= 0 && x < width && y >= 0 && y < height);
}
static void loop_restoration_filter(uint8_t *data, int width, int height,
int stride, restoration_info_n *rst,
uint8_t *tmpdata, int tmpstride) {
int i, j;
const double *wr_lut_ = rst->wr_lut + RESTORATION_RANGE;
uint8_t *data_p = data;
uint8_t *tmpdata_p = tmpdata;
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
int x, y;
double flsum = 0, wtsum = 0, wt;
uint8_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride;
for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) {
for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) {
if (!is_in_image(j + x, i + y, width, height))
continue;
wt = rst->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] *
wr_lut_[data_p2[x] - data_p[j]];
wtsum += wt;
flsum += wt * data_p2[x];
}
data_p2 += stride;
}
assert(wtsum > 0);
tmpdata_p[j] = clip_pixel((int)(flsum / wtsum + 0.5));
}
tmpdata_p += tmpstride;
data_p += stride;
}
for (i = 0; i < height; ++i) {
memcpy(data + i * stride, tmpdata + i * tmpstride,
width * sizeof(*data));
}
}
#if 0 // TODO(yaowu): remove when the experiment is finalized
// Normalized non-separable filter where weights all sum to 1
static void loop_restoration_filter_norm(uint8_t *data, int width, int height,
int stride, restoration_info_n *rst,
uint8_t *tmpdata, int tmpstride) {
int i, j;
uint8_t *data_p = data;
uint8_t *tmpdata_p = tmpdata;
for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; ++i) {
for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; ++j) {
int x, y;
double flsum = 0;
uint8_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride;
for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) {
for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) {
flsum += data_p2[x] *
rst->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN];
}
data_p2 += stride;
}
tmpdata_p[j] = clip_pixel((int)(flsum + 0.5));
}
tmpdata_p += tmpstride;
data_p += stride;
}
for (i = 0; i < height; ++i) {
memcpy(data + i * stride, tmpdata + i * tmpstride,
width * sizeof(*data));
}
}
#endif
#if CONFIG_VP9_HIGHBITDEPTH
static void loop_restoration_filter_highbd(
uint8_t *data8, int width, int height,
int stride, restoration_info_n *rst,
uint8_t *tmpdata8, int tmpstride, int bit_depth) {
int i, j;
const double *wr_lut_ = rst->wr_lut + RESTORATION_RANGE;
uint16_t *data = CONVERT_TO_SHORTPTR(data8);
uint16_t *tmpdata = CONVERT_TO_SHORTPTR(tmpdata8);
uint16_t *data_p = data;
uint16_t *tmpdata_p = tmpdata;
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
int x, y, diff_r;
double flsum = 0, wtsum = 0, wt;
uint16_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride;
for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) {
for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) {
if (!is_in_image(j + x, i + y, width, height))
continue;
diff_r = (data_p2[x] - data_p[j]) >> (bit_depth - 8);
assert(diff_r >= -RESTORATION_RANGE && diff_r <= RESTORATION_RANGE);
wt = rst->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] *
wr_lut_[diff_r];
wtsum += wt;
flsum += wt * data_p2[x];
}
data_p2 += stride;
}
assert(wtsum > 0);
tmpdata_p[j] = (int)(flsum / wtsum + 0.5);
}
tmpdata_p += tmpstride;
data_p += stride;
}
for (i = 0; i < height; ++i) {
memcpy(data + i * stride, tmpdata + i * tmpstride,
width * sizeof(*data));
}
}
#if 0 // TODO(yaowu): remove when the experiment is finalized
// Normalized non-separable filter where weights all sum to 1
static void loop_restoration_filter_norm_highbd(
uint8_t *data8, int width, int height,
int stride, restoration_info_n *rst,
uint8_t *tmpdata8, int tmpstride) {
int i, j;
uint16_t *data = CONVERT_TO_SHORTPTR(data8);
uint16_t *tmpdata = CONVERT_TO_SHORTPTR(tmpdata8);
uint16_t *data_p = data;
uint16_t *tmpdata_p = tmpdata;
for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; ++i) {
for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; ++j) {
int x, y;
double flsum = 0;
uint16_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride;
for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) {
for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) {
flsum += data_p2[x] *
rst->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN];
}
data_p2 += stride;
}
tmpdata_p[j] = (int)(flsum + 0.5);
}
tmpdata_p += tmpstride;
data_p += stride;
}
for (i = 0; i < height; ++i) {
memcpy(data + i * stride, tmpdata + i * tmpstride,
width * sizeof(*data));
}
}
#endif
#endif // CONFIG_VP9_HIGHBITDEPTH
void vp10_loop_restoration_rows(YV12_BUFFER_CONFIG *frame,
VP10_COMMON *cm,
int start_mi_row, int end_mi_row,
int y_only) {
const int ywidth = frame->y_crop_width;
const int ystride = frame->y_stride;
const int uvwidth = frame->uv_crop_width;
const int uvstride = frame->uv_stride;
const int ystart = start_mi_row << MI_SIZE_LOG2;
const int uvstart = ystart >> cm->subsampling_y;
int yend = end_mi_row << MI_SIZE_LOG2;
int uvend = yend >> cm->subsampling_y;
YV12_BUFFER_CONFIG *tmp_buf;
yend = VPXMIN(yend, cm->height);
uvend = VPXMIN(uvend, cm->subsampling_y ? (cm->height + 1) >> 1 : cm->height);
if (vpx_realloc_frame_buffer(&cm->tmp_loop_buf, cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
#if CONFIG_VP9_HIGHBITDEPTH
cm->use_highbitdepth,
#endif
VP9_DEC_BORDER_IN_PIXELS, cm->byte_alignment,
NULL, NULL, NULL) < 0)
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
"Failed to allocate tmp restoration buffer");
tmp_buf = &cm->tmp_loop_buf;
#if CONFIG_VP9_HIGHBITDEPTH
if (cm->use_highbitdepth)
loop_restoration_filter_highbd(
frame->y_buffer + ystart * ystride,
ywidth, yend - ystart, ystride, &cm->rst_info,
tmp_buf->y_buffer + ystart * tmp_buf->y_stride,
tmp_buf->y_stride, cm->bit_depth);
else
#endif // CONFIG_VP9_HIGHBITDEPTH
loop_restoration_filter(
frame->y_buffer + ystart * ystride,
ywidth, yend - ystart, ystride, &cm->rst_info,
tmp_buf->y_buffer + ystart * tmp_buf->y_stride,
tmp_buf->y_stride);
if (!y_only) {
#if CONFIG_VP9_HIGHBITDEPTH
if (cm->use_highbitdepth) {
loop_restoration_filter_highbd(
frame->u_buffer + uvstart * uvstride,
uvwidth, uvend - uvstart, uvstride, &cm->rst_info,
tmp_buf->u_buffer + uvstart * tmp_buf->uv_stride,
tmp_buf->uv_stride, cm->bit_depth);
loop_restoration_filter_highbd(
frame->v_buffer + uvstart * uvstride,
uvwidth, uvend - uvstart, uvstride, &cm->rst_info,
tmp_buf->v_buffer + uvstart * tmp_buf->uv_stride,
tmp_buf->uv_stride, cm->bit_depth);
} else {
#endif // CONFIG_VP9_HIGHBITDEPTH
loop_restoration_filter(
frame->u_buffer + uvstart * uvstride,
uvwidth, uvend - uvstart, uvstride, &cm->rst_info,
tmp_buf->u_buffer + uvstart * tmp_buf->uv_stride,
tmp_buf->uv_stride);
loop_restoration_filter(
frame->v_buffer + uvstart * uvstride,
uvwidth, uvend - uvstart, uvstride, &cm->rst_info,
tmp_buf->v_buffer + uvstart * tmp_buf->uv_stride,
tmp_buf->uv_stride);
#if CONFIG_VP9_HIGHBITDEPTH
}
#endif // CONFIG_VP9_HIGHBITDEPTH
}
}
void vp10_loop_restoration_frame(YV12_BUFFER_CONFIG *frame,
VP10_COMMON *cm,
int restoration_level,
int y_only, int partial_frame) {
int start_mi_row, end_mi_row, mi_rows_to_filter;
vp10_loop_restoration_init(&cm->rst_info, restoration_level,
cm->frame_type == KEY_FRAME);
if (!cm->rst_info.restoration_used)
return;
start_mi_row = 0;
mi_rows_to_filter = cm->mi_rows;
if (partial_frame && cm->mi_rows > 8) {
start_mi_row = cm->mi_rows >> 1;
start_mi_row &= 0xfffffff8;
mi_rows_to_filter = VPXMAX(cm->mi_rows / 8, 8);
}
end_mi_row = start_mi_row + mi_rows_to_filter;
vp10_loop_restoration_rows(frame, cm, start_mi_row, end_mi_row, y_only);
}