}
}
+#if CONFIG_LOOP_RESTORATION
+void vp10_free_restoration_buffers(VP10_COMMON *cm) {
+ vpx_free_frame_buffer(&cm->tmp_loop_buf);
+}
+#endif // CONFIG_LOOP_RESTORATION
+
void vp10_free_postproc_buffers(VP10_COMMON *cm) {
#if CONFIG_VP9_POSTPROC
vpx_free_frame_buffer(&cm->post_proc_buffer);
void vp10_free_ref_frame_buffers(struct BufferPool *pool);
void vp10_free_postproc_buffers(struct VP10Common *cm);
+#if CONFIG_LOOP_RESTORATION
+void vp10_free_restoration_buffers(struct VP10Common *cm);
+#endif // CONFIG_LOOP_RESTORATION
int vp10_alloc_state_buffers(struct VP10Common *cm, int width, int height);
void vp10_free_state_buffers(struct VP10Common *cm);
// To force update of the sharpness
lf->last_sharpness_level = -1;
+#if CONFIG_LOOP_RESTORATION
+ lf->bilateral_level = 0;
+ lf->last_bilateral_level = 0;
+#endif // CONFIG_LOOP_RESTORATION
vp10_default_coef_probs(cm);
init_mode_probs(cm->fc);
* 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/loopfilter.h"
#endif // CONFIG_EXT_INTER
};
+#if CONFIG_LOOP_RESTORATION
+#define BILATERAL_RANGE 256
+#define BILATERAL_RANGE_SYM (2 * BILATERAL_RANGE + 1)
+static double bilateral_filters_r_kf[BILATERAL_LEVELS_KF + 1]
+ [BILATERAL_RANGE_SYM];
+static double bilateral_filters_r[BILATERAL_LEVELS + 1][BILATERAL_RANGE_SYM];
+static double bilateral_filters_s_kf[BILATERAL_LEVELS_KF + 1]
+ [BILATERAL_WIN][BILATERAL_WIN];
+static double bilateral_filters_s[BILATERAL_LEVELS + 1]
+ [BILATERAL_WIN][BILATERAL_WIN];
+
+void vp10_loop_bilateral_precal() {
+ int i;
+ for (i = 1; i < BILATERAL_LEVELS_KF + 1; i ++) {
+ const bilateral_params_t param = vp10_bilateral_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 / BILATERAL_PRECISION;
+ const double sigma_x_d = (double)sigma_x / BILATERAL_PRECISION;
+ const double sigma_y_d = (double)sigma_y / BILATERAL_PRECISION;
+
+ double *fr = bilateral_filters_r_kf[i] + BILATERAL_RANGE;
+ int j, x, y;
+ for (j = 0; j <= BILATERAL_RANGE; j++) {
+ fr[j] = exp(-(j * j) / (2 * sigma_r_d * sigma_r_d));
+ fr[-j] = fr[j];
+ }
+ for (y = -BILATERAL_HALFWIN; y <= BILATERAL_HALFWIN; y++) {
+ for (x = -BILATERAL_HALFWIN; x <= BILATERAL_HALFWIN; x++) {
+ bilateral_filters_s_kf[i][y + BILATERAL_HALFWIN]
+ [x + BILATERAL_HALFWIN] =
+ exp(-(x * x) / (2 * sigma_x_d * sigma_x_d)
+ -(y * y) / (2 * sigma_y_d * sigma_y_d));
+ }
+ }
+ }
+ for (i = 1; i < BILATERAL_LEVELS + 1; i ++) {
+ const bilateral_params_t param = vp10_bilateral_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 / BILATERAL_PRECISION;
+ const double sigma_x_d = (double)sigma_x / BILATERAL_PRECISION;
+ const double sigma_y_d = (double)sigma_y / BILATERAL_PRECISION;
+
+ double *fr = bilateral_filters_r[i] + BILATERAL_RANGE;
+ int j, x, y;
+ for (j = 0; j <= BILATERAL_RANGE; j++) {
+ fr[j] = exp(-(j * j) / (2 * sigma_r_d * sigma_r_d));
+ fr[-j] = fr[j];
+ }
+ for (y = -BILATERAL_HALFWIN; y <= BILATERAL_HALFWIN; y++) {
+ for (x = -BILATERAL_HALFWIN; x <= BILATERAL_HALFWIN; x++) {
+ bilateral_filters_s[i][y + BILATERAL_HALFWIN][x + BILATERAL_HALFWIN] =
+ exp(-(x * x) / (2 * sigma_x_d * sigma_x_d)
+ -(y * y) / (2 * sigma_y_d * sigma_y_d));
+ }
+ }
+ }
+}
+
+int vp10_bilateral_level_bits(const VP10_COMMON *const cm) {
+ return cm->frame_type == KEY_FRAME ?
+ BILATERAL_LEVEL_BITS_KF : BILATERAL_LEVEL_BITS;
+}
+
+int vp10_loop_bilateral_used(int level, int kf) {
+ const bilateral_params_t param = vp10_bilateral_level_to_params(level, kf);
+ return (param.sigma_x && param.sigma_y && param.sigma_r);
+}
+
+void vp10_loop_bilateral_init(loop_filter_info_n *lfi, int level, int kf) {
+ lfi->bilateral_used = vp10_loop_bilateral_used(level, kf);
+
+ if (lfi->bilateral_used) {
+ int i;
+ lfi->wr_lut = kf ? bilateral_filters_r_kf[level] :
+ bilateral_filters_r[level];
+ for (i = 0; i < BILATERAL_WIN; i++)
+ lfi->wx_lut[i] = kf ? bilateral_filters_s_kf[level][i] :
+ bilateral_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);
+}
+
+void loop_bilateral_filter(uint8_t *data, int width, int height,
+ int stride, loop_filter_info_n *lfi,
+ uint8_t *tmpdata, int tmpstride) {
+ int i, j;
+ const double *wr_lut_ = lfi->wr_lut + 256;
+
+ 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 - BILATERAL_HALFWIN * stride;
+ for (y = -BILATERAL_HALFWIN; y <= BILATERAL_HALFWIN; ++y) {
+ for (x = -BILATERAL_HALFWIN; x <= BILATERAL_HALFWIN; ++x) {
+ if (!is_in_image(j + x, i + y, width, height))
+ continue;
+ wt = lfi->wx_lut[y + BILATERAL_HALFWIN][x + BILATERAL_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 CONFIG_VP9_HIGHBITDEPTH
+void loop_bilateral_filter_highbd(uint8_t *data8, int width, int height,
+ int stride, loop_filter_info_n *lfi,
+ uint8_t *tmpdata8, int tmpstride,
+ int bit_depth) {
+ int i, j;
+ const double *wr_lut_ = lfi->wr_lut + 256;
+
+ 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 - BILATERAL_HALFWIN * stride;
+
+ for (y = -BILATERAL_HALFWIN; y <= BILATERAL_HALFWIN; ++y) {
+ for (x = -BILATERAL_HALFWIN; x <= BILATERAL_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 >= -256 && diff_r <= 256);
+
+ wt = lfi->wx_lut[y + BILATERAL_HALFWIN][x + BILATERAL_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));
+ }
+}
+#endif // CONFIG_VP9_HIGHBITDEPTH
+
+void vp10_loop_bilateral_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_bilateral_filter_highbd(frame->y_buffer + ystart * ystride,
+ ywidth, yend - ystart, ystride, &cm->lf_info,
+ tmp_buf->y_buffer + ystart * tmp_buf->y_stride,
+ tmp_buf->y_stride, cm->bit_depth);
+ else
+#endif // CONFIG_VP9_HIGHBITDEPTH
+ loop_bilateral_filter(frame->y_buffer + ystart * ystride,
+ ywidth, yend - ystart, ystride, &cm->lf_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_bilateral_filter_highbd(
+ frame->u_buffer + uvstart * uvstride,
+ uvwidth, uvend - uvstart, uvstride, &cm->lf_info,
+ tmp_buf->u_buffer + uvstart * tmp_buf->uv_stride,
+ tmp_buf->uv_stride, cm->bit_depth);
+ loop_bilateral_filter_highbd(
+ frame->v_buffer + uvstart * uvstride,
+ uvwidth, uvend - uvstart, uvstride, &cm->lf_info,
+ tmp_buf->v_buffer + uvstart * tmp_buf->uv_stride,
+ tmp_buf->uv_stride, cm->bit_depth);
+ } else {
+#endif // CONFIG_VP9_HIGHBITDEPTH
+ loop_bilateral_filter(frame->u_buffer + uvstart * uvstride,
+ uvwidth, uvend - uvstart, uvstride, &cm->lf_info,
+ tmp_buf->u_buffer + uvstart * tmp_buf->uv_stride,
+ tmp_buf->uv_stride);
+ loop_bilateral_filter(frame->v_buffer + uvstart * uvstride,
+ uvwidth, uvend - uvstart, uvstride, &cm->lf_info,
+ tmp_buf->v_buffer + uvstart * tmp_buf->uv_stride,
+ tmp_buf->uv_stride);
+#if CONFIG_VP9_HIGHBITDEPTH
+ }
+#endif // CONFIG_VP9_HIGHBITDEPTH
+ }
+}
+#endif // CONFIG_LOOP_RESTORATION
+
static void update_sharpness(loop_filter_info_n *lfi, int sharpness_lvl) {
int lvl;
// init hev threshold const vectors
for (lvl = 0; lvl <= MAX_LOOP_FILTER; lvl++)
memset(lfi->lfthr[lvl].hev_thr, (lvl >> 4), SIMD_WIDTH);
+
+#if CONFIG_LOOP_RESTORATION
+ vp10_loop_bilateral_precal();
+#endif // CONFIG_LOOP_RESTORATION
}
void vp10_loop_filter_frame_init(VP10_COMMON *cm, int default_filt_lvl) {
memcpy(lf_data->planes, planes, sizeof(lf_data->planes));
}
+#if CONFIG_LOOP_RESTORATION
+void vp10_loop_bilateral_frame(YV12_BUFFER_CONFIG *frame,
+ VP10_COMMON *cm,
+ int bilateral_level,
+ int y_only, int partial_frame) {
+ int start_mi_row, end_mi_row, mi_rows_to_filter;
+ // const int loop_bilateral_used = vp10_loop_bilateral_used(
+ // bilateral_level, cm->frame_type == KEY_FRAME);
+ vp10_loop_bilateral_init(&cm->lf_info, bilateral_level,
+ cm->frame_type == KEY_FRAME);
+ if (!cm->lf_info.bilateral_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_bilateral_rows(frame, cm, start_mi_row, end_mi_row, y_only);
+}
+#endif // CONFIG_LOOP_RESTORATION
+
int vp10_loop_filter_worker(LFWorkerData *const lf_data, void *unused) {
(void)unused;
vp10_loop_filter_rows(lf_data->frame_buffer, lf_data->cm, lf_data->planes,
#define MAX_MODE_LF_DELTAS 2
+#if CONFIG_LOOP_RESTORATION
+#define BILATERAL_LEVEL_BITS_KF 4
+#define BILATERAL_LEVELS_KF (1 << BILATERAL_LEVEL_BITS_KF)
+#define BILATERAL_LEVEL_BITS 3
+#define BILATERAL_LEVELS (1 << BILATERAL_LEVEL_BITS)
+#define DEF_BILATERAL_LEVEL 2
+
+#define BILATERAL_PRECISION 16
+#define BILATERAL_HALFWIN 3
+#define BILATERAL_WIN (2 * BILATERAL_HALFWIN + 1)
+
+typedef struct bilateral_params {
+ int sigma_x; // spatial variance x
+ int sigma_y; // spatial variance y
+ int sigma_r; // range variance
+} bilateral_params_t;
+
+static bilateral_params_t
+ bilateral_level_to_params_arr[BILATERAL_LEVELS + 1] = {
+ // Values are rounded to 1/16 th precision
+ {0, 0, 0}, // 0 - default
+ {8, 9, 30},
+ {9, 8, 30},
+ {9, 11, 32},
+ {11, 9, 32},
+ {14, 14, 32},
+ {18, 18, 36},
+ {24, 24, 40},
+ {32, 32, 40},
+};
+
+static bilateral_params_t
+ bilateral_level_to_params_arr_kf[BILATERAL_LEVELS_KF + 1] = {
+ // Values are rounded to 1/16 th precision
+ {0, 0, 0}, // 0 - default
+ {8, 8, 30},
+ {9, 9, 32},
+ {10, 10, 32},
+ {12, 12, 32},
+ {14, 14, 32},
+ {18, 18, 36},
+ {24, 24, 40},
+ {30, 30, 44},
+ {36, 36, 48},
+ {42, 42, 48},
+ {48, 48, 48},
+ {48, 48, 56},
+ {56, 56, 48},
+ {56, 56, 56},
+ {56, 56, 64},
+ {64, 64, 48},
+};
+
+int vp10_bilateral_level_bits(const struct VP10Common *const cm);
+int vp10_loop_bilateral_used(int level, int kf);
+
+static INLINE bilateral_params_t vp10_bilateral_level_to_params(
+ int index, int kf) {
+ return kf ? bilateral_level_to_params_arr_kf[index] :
+ bilateral_level_to_params_arr[index];
+}
+#endif // CONFIG_LOOP_RESTORATION
+
enum lf_path {
LF_PATH_420,
LF_PATH_444,
// 0 = ZERO_MV, MV
signed char mode_deltas[MAX_MODE_LF_DELTAS];
signed char last_mode_deltas[MAX_MODE_LF_DELTAS];
+
+#if CONFIG_LOOP_RESTORATION
+ int bilateral_level;
+ int last_bilateral_level;
+#endif // CONFIG_LOOP_RESTORATION
};
// Need to align this structure so when it is declared and
typedef struct {
loop_filter_thresh lfthr[MAX_LOOP_FILTER + 1];
uint8_t lvl[MAX_SEGMENTS][MAX_REF_FRAMES][MAX_MODE_LF_DELTAS];
+#if CONFIG_LOOP_RESTORATION
+ double * wx_lut[BILATERAL_WIN];
+ double * wr_lut;
+ int bilateral_sigma_x_set;
+ int bilateral_sigma_y_set;
+ int bilateral_sigma_r_set;
+ int bilateral_used;
+#endif // CONFIG_LOOP_RESTORATION
} loop_filter_info_n;
// This structure holds bit masks for all 8x8 blocks in a 64x64 region.
struct macroblockd_plane planes[MAX_MB_PLANE],
int start, int stop, int y_only);
+#if CONFIG_LOOP_RESTORATION
+void vp10_loop_bilateral_frame(YV12_BUFFER_CONFIG *frame,
+ struct VP10Common *cm,
+ int bilateral_level,
+ int y_only, int partial_frame);
+void vp10_loop_filter_bilateral_frame(YV12_BUFFER_CONFIG *frame,
+ struct VP10Common *cm,
+ struct macroblockd *mbd,
+ int frame_filter_level,
+ int bilateral_level,
+ int y_only, int partial_frame);
+void vp10_loop_bilateral_init(loop_filter_info_n *lfi, int T, int kf);
+void vp10_loop_bilateral_rows(YV12_BUFFER_CONFIG *frame,
+ struct VP10Common *cm,
+ int start_mi_row, int end_mi_row,
+ int y_only);
+#endif // CONFIG_LOOP_RESTORATION
+
typedef struct LoopFilterWorkerData {
YV12_BUFFER_CONFIG *frame_buffer;
struct VP10Common *cm;
YV12_BUFFER_CONFIG post_proc_buffer;
YV12_BUFFER_CONFIG post_proc_buffer_int;
#endif
+#if CONFIG_LOOP_RESTORATION
+ YV12_BUFFER_CONFIG tmp_loop_buf;
+#endif // CONFIG_LOOP_RESTORATION
FRAME_TYPE last_frame_type; /* last frame's frame type for motion search.*/
#if CONFIG_EXT_REFS
}
}
-static void setup_loopfilter(struct loopfilter *lf,
+static void setup_loopfilter(VP10_COMMON *cm,
struct vpx_read_bit_buffer *rb) {
+ struct loopfilter *lf = &cm->lf;
lf->filter_level = vpx_rb_read_literal(rb, 6);
lf->sharpness_level = vpx_rb_read_literal(rb, 3);
lf->mode_deltas[i] = vpx_rb_read_inv_signed_literal(rb, 6);
}
}
+#if CONFIG_LOOP_RESTORATION
+ lf->bilateral_level = vpx_rb_read_bit(rb);
+ if (lf->bilateral_level) {
+ int level = vpx_rb_read_literal(rb, vp10_bilateral_level_bits(cm));
+ lf->bilateral_level = level + (level >= lf->last_bilateral_level);
+ } else {
+ lf->bilateral_level = lf->last_bilateral_level;
+ }
+ if (cm->frame_type != KEY_FRAME)
+ cm->lf.last_bilateral_level = cm->lf.bilateral_level;
+ else
+ cm->lf.last_bilateral_level = 0;
+#endif // CONFIG_LOOP_RESTORATION
}
static INLINE int read_delta_q(struct vpx_read_bit_buffer *rb) {
if (frame_is_intra_only(cm) || cm->error_resilient_mode)
vp10_setup_past_independence(cm);
- setup_loopfilter(&cm->lf, rb);
+ setup_loopfilter(cm, rb);
setup_quantization(cm, rb);
#if CONFIG_VP9_HIGHBITDEPTH
xd->bd = (int)cm->bit_depth;
} else {
*p_data_end = decode_tiles(pbi, data + first_partition_size, data_end);
}
+#if CONFIG_LOOP_RESTORATION
+ vp10_loop_bilateral_init(&cm->lf_info, cm->lf.bilateral_level,
+ cm->frame_type == KEY_FRAME);
+ if (cm->lf_info.bilateral_used) {
+ vp10_loop_bilateral_rows(new_fb, cm, 0, cm->mi_rows, 0);
+ }
+#endif // CONFIG_LOOP_RESTORATION
if (!xd->corrupted) {
if (cm->refresh_frame_context == REFRESH_FRAME_CONTEXT_BACKWARD) {
}
}
-static void encode_loopfilter(struct loopfilter *lf,
+static void encode_loopfilter(VP10_COMMON *cm,
struct vpx_write_bit_buffer *wb) {
int i;
+ struct loopfilter *lf = &cm->lf;
// Encode the loop filter level and type
vpx_wb_write_literal(wb, lf->filter_level, 6);
}
}
}
+#if CONFIG_LOOP_RESTORATION
+ vpx_wb_write_bit(wb, lf->bilateral_level != lf->last_bilateral_level);
+ if (lf->bilateral_level != lf->last_bilateral_level) {
+ int level = lf->bilateral_level -
+ (lf->bilateral_level > lf->last_bilateral_level);
+ vpx_wb_write_literal(wb, level,
+ vp10_bilateral_level_bits(cm));
+ }
+#endif // CONFIG_LOOP_RESTORATION
}
static void write_delta_q(struct vpx_write_bit_buffer *wb, int delta_q) {
vpx_wb_write_literal(wb, cm->frame_context_idx, FRAME_CONTEXTS_LOG2);
- encode_loopfilter(&cm->lf, wb);
+ encode_loopfilter(cm, wb);
encode_quantization(cm, wb);
encode_segmentation(cm, xd, wb);
if (!cm->seg.enabled && xd->lossless[0])
vp10_free_ref_frame_buffers(cm->buffer_pool);
#if CONFIG_VP9_POSTPROC
vp10_free_postproc_buffers(cm);
-#endif
+#endif // CONFIG_VP9_POSTPROC
+#if CONFIG_LOOP_RESTORATION
+ vp10_free_restoration_buffers(cm);
+#endif // CONFIG_LOOP_RESTORATION
vp10_free_context_buffers(cm);
vpx_free_frame_buffer(&cpi->last_frame_uf);
+#if CONFIG_LOOP_RESTORATION
+ vpx_free_frame_buffer(&cpi->last_frame_db);
+#endif // CONFIG_LOOP_RESTORATION
vpx_free_frame_buffer(&cpi->scaled_source);
vpx_free_frame_buffer(&cpi->scaled_last_source);
vpx_free_frame_buffer(&cpi->alt_ref_buffer);
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
"Failed to allocate last frame buffer");
+#if CONFIG_LOOP_RESTORATION
+ if (vpx_realloc_frame_buffer(&cpi->last_frame_db,
+ cm->width, cm->height,
+ cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
+ VP9_ENC_BORDER_IN_PIXELS, cm->byte_alignment,
+ NULL, NULL, NULL))
+ vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
+ "Failed to allocate last frame deblocked buffer");
+#endif // CONFIG_LOOP_RESTORATION
+
if (vpx_realloc_frame_buffer(&cpi->scaled_source,
cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
vp10_loop_filter_frame(cm->frame_to_show, cm, xd, lf->filter_level, 0, 0);
#endif
}
+#if CONFIG_LOOP_RESTORATION
+ vp10_loop_bilateral_init(&cm->lf_info, cm->lf.bilateral_level,
+ cm->frame_type == KEY_FRAME);
+ if (cm->lf_info.bilateral_used)
+ vp10_loop_bilateral_rows(cm->frame_to_show, cm, 0, cm->mi_rows, 0);
+#endif // CONFIG_LOOP_RESTORATION
vpx_extend_frame_inner_borders(cm->frame_to_show);
}
cm->last2_frame_type = cm->last_frame_type;
#endif // CONFIG_EXT_REFS
cm->last_frame_type = cm->frame_type;
+#if CONFIG_LOOP_RESTORATION
+ if (cm->frame_type != KEY_FRAME)
+ cm->lf.last_bilateral_level = cm->lf.bilateral_level;
+ else
+ cm->lf.last_bilateral_level = 0;
+#endif // CONFIG_LOOP_RESTORATION
vp10_rc_postencode_update(cpi, *size);
int ext_refresh_frame_context;
YV12_BUFFER_CONFIG last_frame_uf;
+#if CONFIG_LOOP_RESTORATION
+ YV12_BUFFER_CONFIG last_frame_db;
+#endif // CONFIG_LOOP_RESTORATION
TOKENEXTRA *tile_tok[4][1 << 6];
unsigned int tok_count[4][1 << 6];
#if CONFIG_VAR_TX
vp10_loop_filter_frame(cm->frame_to_show, cm, &cpi->td.mb.e_mbd, filt_level,
- 1, partial_frame);
+ 1, partial_frame);
#else
if (cpi->num_workers > 1)
vp10_loop_filter_frame_mt(cm->frame_to_show, cm, cpi->td.mb.e_mbd.plane,
- filt_level, 1, partial_frame,
- cpi->workers, cpi->num_workers, &cpi->lf_row_sync);
+ filt_level, 1, partial_frame,
+ cpi->workers, cpi->num_workers,
+ &cpi->lf_row_sync);
else
vp10_loop_filter_frame(cm->frame_to_show, cm, &cpi->td.mb.e_mbd, filt_level,
- 1, partial_frame);
+ 1, partial_frame);
#endif
#if CONFIG_VP9_HIGHBITDEPTH
return filt_err;
}
+#if CONFIG_LOOP_RESTORATION
+#define JOINT_FILTER_BILATERAL_SEARCH
+#define USE_RD_LOOP_POSTFILTER_SEARCH
+static int try_bilateral_frame(const YV12_BUFFER_CONFIG *sd,
+ VP10_COMP *const cpi,
+ int bilateral_level,
+ int partial_frame) {
+ VP10_COMMON *const cm = &cpi->common;
+ int filt_err;
+ vp10_loop_bilateral_frame(cm->frame_to_show, cm,
+ bilateral_level, 1, partial_frame);
+#if CONFIG_VP9_HIGHBITDEPTH
+ if (cm->use_highbitdepth) {
+ filt_err = vp10_highbd_get_y_sse(sd, cm->frame_to_show);
+ } else {
+ filt_err = vp10_get_y_sse(sd, cm->frame_to_show);
+ }
+#else
+ filt_err = vp10_get_y_sse(sd, cm->frame_to_show);
+#endif // CONFIG_VP9_HIGHBITDEPTH
+
+ // Re-instate the unfiltered frame
+ vpx_yv12_copy_y(&cpi->last_frame_db, cm->frame_to_show);
+ return filt_err;
+}
+
+static int search_bilateral_level(const YV12_BUFFER_CONFIG *sd,
+ VP10_COMP *cpi,
+ int filter_level, int partial_frame,
+ double *best_cost_ret) {
+ VP10_COMMON *const cm = &cpi->common;
+ int i, bilateral_best, err;
+ double best_cost;
+ double cost;
+ const int bilateral_level_bits = vp10_bilateral_level_bits(&cpi->common);
+ const int bilateral_levels = 1 << bilateral_level_bits;
+#ifdef USE_RD_LOOP_POSTFILTER_SEARCH
+ MACROBLOCK *x = &cpi->td.mb;
+ int bits;
+#endif
+
+ // Make a copy of the unfiltered / processed recon buffer
+ vpx_yv12_copy_y(cm->frame_to_show, &cpi->last_frame_uf);
+ vp10_loop_filter_frame(cm->frame_to_show, cm, &cpi->td.mb.e_mbd, filter_level,
+ 1, partial_frame);
+ vpx_yv12_copy_y(cm->frame_to_show, &cpi->last_frame_db);
+
+ bilateral_best = 0;
+ err = try_bilateral_frame(sd, cpi, 0, partial_frame);
+#ifdef USE_RD_LOOP_POSTFILTER_SEARCH
+ bits = cm->lf.last_bilateral_level == 0 ? 0 : bilateral_level_bits;
+ cost = RDCOST_DBL(x->rdmult, x->rddiv, (bits << 2), err);
+#else
+ cost = (double)err;
+#endif // USE_RD_LOOP_POSTFILTER_SEARCH
+ best_cost = cost;
+ for (i = 1; i <= bilateral_levels; ++i) {
+ err = try_bilateral_frame(sd, cpi, i, partial_frame);
+#ifdef USE_RD_LOOP_POSTFILTER_SEARCH
+ // Normally the rate is rate in bits * 256 and dist is sum sq err * 64
+ // when RDCOST is used. However below we just scale both in the correct
+ // ratios appropriately but not exactly by these values.
+ bits = cm->lf.last_bilateral_level == i ? 0 : bilateral_level_bits;
+ cost = RDCOST_DBL(x->rdmult, x->rddiv, (bits << 2), err);
+#else
+ cost = (double)err;
+#endif // USE_RD_LOOP_POSTFILTER_SEARCH
+ if (cost < best_cost) {
+ bilateral_best = i;
+ best_cost = cost;
+ }
+ }
+ if (best_cost_ret) *best_cost_ret = best_cost;
+ vpx_yv12_copy_y(&cpi->last_frame_uf, cm->frame_to_show);
+ return bilateral_best;
+}
+
+#ifdef JOINT_FILTER_BILATERAL_SEARCH
+static int search_filter_bilateral_level(const YV12_BUFFER_CONFIG *sd,
+ VP10_COMP *cpi,
+ int partial_frame,
+ int *bilateral_level) {
+ const VP10_COMMON *const cm = &cpi->common;
+ const struct loopfilter *const lf = &cm->lf;
+ const int min_filter_level = 0;
+ const int max_filter_level = get_max_filter_level(cpi);
+ int filt_direction = 0;
+ int filt_best, bilateral_best;
+ double best_err;
+ int i;
+
+ // Start the search at the previous frame filter level unless it is now out of
+ // range.
+ int filt_mid = clamp(lf->filter_level, min_filter_level, max_filter_level);
+ int filter_step = filt_mid < 16 ? 4 : filt_mid / 4;
+ double ss_err[MAX_LOOP_FILTER + 1];
+ int bilateral;
+
+ // Set each entry to -1
+ for (i = 0; i <= MAX_LOOP_FILTER; ++i)
+ ss_err[i] = -1.0;
+
+ bilateral = search_bilateral_level(sd, cpi, filt_mid,
+ partial_frame, &best_err);
+ filt_best = filt_mid;
+ bilateral_best = bilateral;
+ ss_err[filt_mid] = best_err;
+
+ while (filter_step > 0) {
+ const int filt_high = VPXMIN(filt_mid + filter_step, max_filter_level);
+ const int filt_low = VPXMAX(filt_mid - filter_step, min_filter_level);
+
+ // Bias against raising loop filter in favor of lowering it.
+ double bias = (best_err / (1 << (15 - (filt_mid / 8)))) * filter_step;
+
+ if ((cpi->oxcf.pass == 2) && (cpi->twopass.section_intra_rating < 20))
+ bias = (bias * cpi->twopass.section_intra_rating) / 20;
+
+ // yx, bias less for large block size
+ if (cm->tx_mode != ONLY_4X4)
+ bias /= 2;
+
+ if (filt_direction <= 0 && filt_low != filt_mid) {
+ // Get Low filter error score
+ if (ss_err[filt_low] < 0) {
+ bilateral = search_bilateral_level(sd, cpi, filt_low,
+ partial_frame,
+ &ss_err[filt_low]);
+ }
+ // If value is close to the best so far then bias towards a lower loop
+ // filter value.
+ if ((ss_err[filt_low] - bias) < best_err) {
+ // Was it actually better than the previous best?
+ if (ss_err[filt_low] < best_err) {
+ best_err = ss_err[filt_low];
+ }
+
+ filt_best = filt_low;
+ bilateral_best = bilateral;
+ }
+ }
+
+ // Now look at filt_high
+ if (filt_direction >= 0 && filt_high != filt_mid) {
+ if (ss_err[filt_high] < 0) {
+ bilateral = search_bilateral_level(sd, cpi, filt_high, partial_frame,
+ &ss_err[filt_high]);
+ }
+ // Was it better than the previous best?
+ if (ss_err[filt_high] < (best_err - bias)) {
+ best_err = ss_err[filt_high];
+ filt_best = filt_high;
+ bilateral_best = bilateral;
+ }
+ }
+
+ // Half the step distance if the best filter value was the same as last time
+ if (filt_best == filt_mid) {
+ filter_step /= 2;
+ filt_direction = 0;
+ } else {
+ filt_direction = (filt_best < filt_mid) ? -1 : 1;
+ filt_mid = filt_best;
+ }
+ }
+ *bilateral_level = bilateral_best;
+ return filt_best;
+}
+#endif // JOINT_FILTER_BILATERAL_SEARCH
+#endif // CONFIG_LOOP_RESTORATION
+
static int search_filter_level(const YV12_BUFFER_CONFIG *sd, VP10_COMP *cpi,
int partial_frame) {
const VP10_COMMON *const cm = &cpi->common;
if (cm->frame_type == KEY_FRAME)
filt_guess -= 4;
lf->filter_level = clamp(filt_guess, min_filter_level, max_filter_level);
+#if CONFIG_LOOP_RESTORATION
+ lf->bilateral_level = search_bilateral_level(
+ sd, cpi, lf->filter_level, method == LPF_PICK_FROM_SUBIMAGE, NULL);
+#endif // CONFIG_LOOP_RESTORATION
} else {
- lf->filter_level = search_filter_level(sd, cpi,
- method == LPF_PICK_FROM_SUBIMAGE);
+#if CONFIG_LOOP_RESTORATION
+#ifdef JOINT_FILTER_BILATERAL_SEARCH
+ lf->filter_level = search_filter_bilateral_level(
+ sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, &lf->bilateral_level);
+#else
+ lf->filter_level = search_filter_level(
+ sd, cpi, method == LPF_PICK_FROM_SUBIMAGE);
+ lf->bilateral_level = search_bilateral_level(
+ sd, cpi, lf->filter_level, method == LPF_PICK_FROM_SUBIMAGE, NULL);
+#endif // JOINT_FILTER_BILATERAL_SEARCH
+#else
+ lf->filter_level = search_filter_level(
+ sd, cpi, method == LPF_PICK_FROM_SUBIMAGE);
+#endif // CONFIG_LOOP_RESTORATION
}
}
#define RDCOST(RM, DM, R, D) \
(((128 + ((int64_t)R) * (RM)) >> 8) + (D << DM))
+
+#define RDCOST_DBL(RM, DM, R, D) \
+ (((((double)(R)) * (RM)) / 256.0) + ((double)(D) * (1 << (DM))))
+
#define QIDX_SKIP_THRESH 115
#define MV_COST_WEIGHT 108
#if CONFIG_VP9_POSTPROC
vp10_free_postproc_buffers(&frame_worker_data->pbi->common);
#endif
+#if CONFIG_LOOP_RESTORATION
+ vp10_free_restoration_buffers(&frame_worker_data->pbi->common);
+#endif // CONFIG_LOOP_RESTORATION
vp10_decoder_remove(frame_worker_data->pbi);
vpx_free(frame_worker_data->scratch_buffer);
#if CONFIG_MULTITHREAD