diff --git a/vp10/common/alloccommon.c b/vp10/common/alloccommon.c index 10b4bf1682985056300eb9a1ae60839479070fa3..2706dad357e3552d665996ef2b8b6dd053869f52 100644 --- a/vp10/common/alloccommon.c +++ b/vp10/common/alloccommon.c @@ -82,7 +82,14 @@ void vp10_free_ref_frame_buffers(BufferPool *pool) { #if CONFIG_LOOP_RESTORATION void vp10_free_restoration_buffers(VP10_COMMON *cm) { - vpx_free_frame_buffer(&cm->tmp_loop_buf); + vpx_free(cm->rst_info.bilateral_level); + cm->rst_info.bilateral_level = NULL; + vpx_free(cm->rst_info.vfilter); + cm->rst_info.vfilter = NULL; + vpx_free(cm->rst_info.hfilter); + cm->rst_info.hfilter = NULL; + vpx_free(cm->rst_info.wiener_level); + cm->rst_info.wiener_level = NULL; } #endif // CONFIG_LOOP_RESTORATION diff --git a/vp10/common/entropymode.c b/vp10/common/entropymode.c index 411f4fb08fb86287f5990172dd149a974e58a425..937912205fb064a9304fb23e3119301b38f7f935 100644 --- a/vp10/common/entropymode.c +++ b/vp10/common/entropymode.c @@ -1602,7 +1602,10 @@ void vp10_setup_past_independence(VP10_COMMON *cm) { // To force update of the sharpness lf->last_sharpness_level = -1; #if CONFIG_LOOP_RESTORATION - cm->rst_info.restoration_level = -1; + if (cm->rst_info.bilateral_level) { + for (i = 0; i < cm->rst_internal.ntiles; ++i) + cm->rst_info.bilateral_level[i] = -1; + } #endif // CONFIG_LOOP_RESTORATION vp10_default_coef_probs(cm); diff --git a/vp10/common/onyxc_int.h b/vp10/common/onyxc_int.h index fcd328f616acffc8b75b36a6bf6cae2fa7523477..cbbce936da8a4d6e3cb0d26aadcbba985c68c38e 100644 --- a/vp10/common/onyxc_int.h +++ b/vp10/common/onyxc_int.h @@ -168,10 +168,6 @@ typedef struct VP10Common { int new_fb_idx; -#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 // frame type of the frame before last frame diff --git a/vp10/common/restoration.c b/vp10/common/restoration.c index 1489297c1705ab5a36c36a6c2fb714586c85314e..98978f8ed7e177f27c1423ed7401be50e963344e 100644 --- a/vp10/common/restoration.c +++ b/vp10/common/restoration.c @@ -18,33 +18,33 @@ #include "vpx_mem/vpx_mem.h" #include "vpx_ports/mem.h" -#define RESTORATION_PARAM_PRECISION 16 -#define RESTORATION_RANGE 256 -#define RESTORATION_RANGE_SYM (2 * RESTORATION_RANGE + 1) +#define BILATERAL_PARAM_PRECISION 16 +#define BILATERAL_AMP_RANGE 256 +#define BILATERAL_AMP_RANGE_SYM (2 * BILATERAL_AMP_RANGE + 1) static uint8_t - restoration_filters_r_kf[RESTORATION_LEVELS_KF][RESTORATION_RANGE_SYM]; -static uint8_t restoration_filters_r[RESTORATION_LEVELS][RESTORATION_RANGE_SYM]; -static uint8_t restoration_filters_s_kf[RESTORATION_LEVELS_KF][RESTORATION_WIN] - [RESTORATION_WIN]; + bilateral_filter_coeffs_r_kf[BILATERAL_LEVELS_KF][BILATERAL_AMP_RANGE_SYM]; static uint8_t - restoration_filters_s[RESTORATION_LEVELS][RESTORATION_WIN][RESTORATION_WIN]; + bilateral_filter_coeffs_r[BILATERAL_LEVELS][BILATERAL_AMP_RANGE_SYM]; +static uint8_t bilateral_filter_coeffs_s_kf[BILATERAL_LEVELS_KF] + [RESTORATION_WIN][RESTORATION_WIN]; +static uint8_t bilateral_filter_coeffs_s[BILATERAL_LEVELS][RESTORATION_WIN] + [RESTORATION_WIN]; -typedef struct restoration_params { +typedef struct bilateral_params { int sigma_x; // spatial variance x int sigma_y; // spatial variance y int sigma_r; // range variance -} RestorationParamsType; +} BilateralParamsType; -static RestorationParamsType - restoration_level_to_params_arr[RESTORATION_LEVELS] = { - // Values are rounded to 1/16 th precision - { 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 BilateralParamsType bilateral_level_to_params_arr[BILATERAL_LEVELS] = { + // Values are rounded to 1/16 th precision + { 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 RestorationParamsType - restoration_level_to_params_arr_kf[RESTORATION_LEVELS_KF] = { +static BilateralParamsType + bilateral_level_to_params_arr_kf[BILATERAL_LEVELS_KF] = { // Values are rounded to 1/16 th precision { 8, 8, 30 }, { 9, 9, 32 }, { 10, 10, 32 }, { 12, 12, 32 }, { 14, 14, 32 }, { 18, 18, 36 }, { 24, 24, 40 }, { 30, 30, 44 }, @@ -55,7 +55,6 @@ static RestorationParamsType typedef void (*restore_func_type)(uint8_t *data8, int width, int height, int stride, RestorationInternal *rst, uint8_t *tmpdata8, int tmpstride); - #if CONFIG_VP9_HIGHBITDEPTH typedef void (*restore_func_highbd_type)(uint8_t *data8, int width, int height, int stride, RestorationInternal *rst, @@ -63,26 +62,56 @@ typedef void (*restore_func_highbd_type)(uint8_t *data8, int width, int height, int bit_depth); #endif // CONFIG_VP9_HIGHBITDEPTH -static INLINE RestorationParamsType vp10_restoration_level_to_params(int index, - int kf) { - return kf ? restoration_level_to_params_arr_kf[index] - : restoration_level_to_params_arr[index]; +static INLINE BilateralParamsType vp10_bilateral_level_to_params(int index, + int kf) { + return kf ? bilateral_level_to_params_arr_kf[index] + : bilateral_level_to_params_arr[index]; +} + +typedef struct TileParams { + int width; + int height; +} TileParams; + +static TileParams restoration_tile_sizes[RESTORATION_TILESIZES] = { + { 64, 64 }, { 128, 128 }, { 256, 256 } +}; + +void vp10_get_restoration_tile_size(int tilesize, int width, int height, + int *tile_width, int *tile_height, + int *nhtiles, int *nvtiles) { + *tile_width = (tilesize < 0) + ? width + : VPXMIN(restoration_tile_sizes[tilesize].width, width); + *tile_height = (tilesize < 0) + ? height + : VPXMIN(restoration_tile_sizes[tilesize].height, height); + *nhtiles = (width + (*tile_width >> 1)) / *tile_width; + *nvtiles = (height + (*tile_height >> 1)) / *tile_height; +} + +int vp10_get_restoration_ntiles(int tilesize, int width, int height) { + int nhtiles, nvtiles; + int tile_width, tile_height; + vp10_get_restoration_tile_size(tilesize, width, height, &tile_width, + &tile_height, &nhtiles, &nvtiles); + return (nhtiles * nvtiles); } void vp10_loop_restoration_precal() { int i; - for (i = 0; i < RESTORATION_LEVELS_KF; i++) { - const RestorationParamsType param = vp10_restoration_level_to_params(i, 1); + for (i = 0; i < BILATERAL_LEVELS_KF; i++) { + const BilateralParamsType 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 / RESTORATION_PARAM_PRECISION; - const double sigma_x_d = (double)sigma_x / RESTORATION_PARAM_PRECISION; - const double sigma_y_d = (double)sigma_y / RESTORATION_PARAM_PRECISION; + const double sigma_r_d = (double)sigma_r / BILATERAL_PARAM_PRECISION; + const double sigma_x_d = (double)sigma_x / BILATERAL_PARAM_PRECISION; + const double sigma_y_d = (double)sigma_y / BILATERAL_PARAM_PRECISION; - uint8_t *fr = restoration_filters_r_kf[i] + RESTORATION_RANGE; + uint8_t *fr = bilateral_filter_coeffs_r_kf[i] + BILATERAL_AMP_RANGE; int j, x, y; - for (j = 0; j <= RESTORATION_RANGE; j++) { + for (j = 0; j <= BILATERAL_AMP_RANGE; j++) { fr[j] = (uint8_t)(0.5 + RESTORATION_FILT_STEP * exp(-(j * j) / (2 * sigma_r_d * sigma_r_d))); @@ -90,28 +119,27 @@ void vp10_loop_restoration_precal() { } 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] = - (uint8_t)(0.5 + - RESTORATION_FILT_STEP * - exp(-(x * x) / (2 * sigma_x_d * sigma_x_d) - - (y * y) / (2 * sigma_y_d * sigma_y_d))); + bilateral_filter_coeffs_s_kf + [i][y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] = + (uint8_t)(0.5 + + RESTORATION_FILT_STEP * + exp(-(x * x) / (2 * sigma_x_d * sigma_x_d) - + (y * y) / (2 * sigma_y_d * sigma_y_d))); } } } - for (i = 0; i < RESTORATION_LEVELS; i++) { - const RestorationParamsType param = vp10_restoration_level_to_params(i, 0); + for (i = 0; i < BILATERAL_LEVELS; i++) { + const BilateralParamsType 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 / RESTORATION_PARAM_PRECISION; - const double sigma_x_d = (double)sigma_x / RESTORATION_PARAM_PRECISION; - const double sigma_y_d = (double)sigma_y / RESTORATION_PARAM_PRECISION; + const double sigma_r_d = (double)sigma_r / BILATERAL_PARAM_PRECISION; + const double sigma_x_d = (double)sigma_x / BILATERAL_PARAM_PRECISION; + const double sigma_y_d = (double)sigma_y / BILATERAL_PARAM_PRECISION; - uint8_t *fr = restoration_filters_r[i] + RESTORATION_RANGE; + uint8_t *fr = bilateral_filter_coeffs_r[i] + BILATERAL_AMP_RANGE; int j, x, y; - for (j = 0; j <= RESTORATION_RANGE; j++) { + for (j = 0; j <= BILATERAL_AMP_RANGE; j++) { fr[j] = (uint8_t)(0.5 + RESTORATION_FILT_STEP * exp(-(j * j) / (2 * sigma_r_d * sigma_r_d))); @@ -119,7 +147,8 @@ void vp10_loop_restoration_precal() { } for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; y++) { for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; x++) { - restoration_filters_s[i][y + RESTORATION_HALFWIN][x + + bilateral_filter_coeffs_s[i][y + + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] = (uint8_t)(0.5 + RESTORATION_FILT_STEP * @@ -130,31 +159,68 @@ void vp10_loop_restoration_precal() { } } -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_bilateral_level_bits(const VP10_COMMON *const cm) { + return cm->frame_type == KEY_FRAME ? BILATERAL_LEVEL_BITS_KF + : BILATERAL_LEVEL_BITS; } void vp10_loop_restoration_init(RestorationInternal *rst, RestorationInfo *rsi, - int kf) { - int i; + int kf, int width, int height) { + int i, tile_idx; rst->restoration_type = rsi->restoration_type; + rst->subsampling_x = 0; + rst->subsampling_y = 0; if (rsi->restoration_type == RESTORE_BILATERAL) { - const int level = rsi->restoration_level; - assert(level >= 0); - 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]; + rst->tilesize_index = BILATERAL_TILESIZE; + rst->ntiles = + vp10_get_restoration_ntiles(rst->tilesize_index, width, height); + vp10_get_restoration_tile_size(rst->tilesize_index, width, height, + &rst->tile_width, &rst->tile_height, + &rst->nhtiles, &rst->nvtiles); + rst->bilateral_level = rsi->bilateral_level; + rst->wr_lut = (uint8_t **)malloc(sizeof(*rst->wr_lut) * rst->ntiles); + assert(rst->wr_lut != NULL); + rst->wx_lut = (uint8_t(**)[RESTORATION_WIN])malloc(sizeof(*rst->wx_lut) * + rst->ntiles); + assert(rst->wx_lut != NULL); + for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) { + const int level = rsi->bilateral_level[tile_idx]; + if (level >= 0) { + rst->wr_lut[tile_idx] = kf ? bilateral_filter_coeffs_r_kf[level] + : bilateral_filter_coeffs_r[level]; + rst->wx_lut[tile_idx] = kf ? bilateral_filter_coeffs_s_kf[level] + : bilateral_filter_coeffs_s[level]; + } + } } else if (rsi->restoration_type == RESTORE_WIENER) { - rst->vfilter[RESTORATION_HALFWIN] = rst->hfilter[RESTORATION_HALFWIN] = - RESTORATION_FILT_STEP; - for (i = 0; i < RESTORATION_HALFWIN; ++i) { - rst->vfilter[i] = rst->vfilter[RESTORATION_WIN - 1 - i] = rsi->vfilter[i]; - rst->hfilter[i] = rst->hfilter[RESTORATION_WIN - 1 - i] = rsi->hfilter[i]; - rst->vfilter[RESTORATION_HALFWIN] -= 2 * rsi->vfilter[i]; - rst->hfilter[RESTORATION_HALFWIN] -= 2 * rsi->hfilter[i]; + rst->tilesize_index = WIENER_TILESIZE; + rst->ntiles = + vp10_get_restoration_ntiles(rst->tilesize_index, width, height); + vp10_get_restoration_tile_size(rst->tilesize_index, width, height, + &rst->tile_width, &rst->tile_height, + &rst->nhtiles, &rst->nvtiles); + rst->wiener_level = rsi->wiener_level; + rst->vfilter = + (int(*)[RESTORATION_WIN])malloc(sizeof(*rst->vfilter) * rst->ntiles); + assert(rst->vfilter != NULL); + rst->hfilter = + (int(*)[RESTORATION_WIN])malloc(sizeof(*rst->hfilter) * rst->ntiles); + assert(rst->hfilter != NULL); + for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) { + rst->vfilter[tile_idx][RESTORATION_HALFWIN] = + rst->hfilter[tile_idx][RESTORATION_HALFWIN] = RESTORATION_FILT_STEP; + for (i = 0; i < RESTORATION_HALFWIN; ++i) { + rst->vfilter[tile_idx][i] = + rst->vfilter[tile_idx][RESTORATION_WIN - 1 - i] = + rsi->vfilter[tile_idx][i]; + rst->hfilter[tile_idx][i] = + rst->hfilter[tile_idx][RESTORATION_WIN - 1 - i] = + rsi->hfilter[tile_idx][i]; + rst->vfilter[tile_idx][RESTORATION_HALFWIN] -= + 2 * rsi->vfilter[tile_idx][i]; + rst->hfilter[tile_idx][RESTORATION_HALFWIN] -= + 2 * rsi->hfilter[tile_idx][i]; + } } } } @@ -162,39 +228,60 @@ void vp10_loop_restoration_init(RestorationInternal *rst, RestorationInfo *rsi, static void loop_bilateral_filter(uint8_t *data, int width, int height, int stride, RestorationInternal *rst, uint8_t *tmpdata, int tmpstride) { - int i, j; - const uint8_t *wr_lut_ = rst->wr_lut + RESTORATION_RANGE; - - uint8_t *data_p = data + RESTORATION_HALFWIN * stride; - uint8_t *tmpdata_p = tmpdata + RESTORATION_HALFWIN * tmpstride; - for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; ++i) { - for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; ++j) { - int x, y; - int 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) { - wt = (int)rst - ->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] * - (int)wr_lut_[data_p2[x] - data_p[j]]; - wtsum += wt; - flsum += wt * data_p2[x]; + int i, j, tile_idx, htile_idx, vtile_idx; + int h_start, h_end, v_start, v_end; + int tile_width, tile_height; + + tile_width = rst->tile_width >> rst->subsampling_x; + tile_height = rst->tile_height >> rst->subsampling_y; + + for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) { + uint8_t *data_p, *tmpdata_p; + const uint8_t *wr_lut_ = rst->wr_lut[tile_idx] + BILATERAL_AMP_RANGE; + + if (rst->bilateral_level[tile_idx] < 0) continue; + + htile_idx = tile_idx % rst->nhtiles; + vtile_idx = tile_idx / rst->nhtiles; + h_start = + htile_idx * tile_width + ((htile_idx > 0) ? 0 : RESTORATION_HALFWIN); + h_end = (htile_idx < rst->nhtiles - 1) ? ((htile_idx + 1) * tile_width) + : (width - RESTORATION_HALFWIN); + v_start = + vtile_idx * tile_height + ((vtile_idx > 0) ? 0 : RESTORATION_HALFWIN); + v_end = (vtile_idx < rst->nvtiles - 1) ? ((vtile_idx + 1) * tile_height) + : (height - RESTORATION_HALFWIN); + + data_p = data + h_start + v_start * stride; + tmpdata_p = tmpdata + h_start + v_start * tmpstride; + + for (i = 0; i < (v_end - v_start); ++i) { + for (j = 0; j < (h_end - h_start); ++j) { + int x, y; + int 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) { + wt = (int)rst->wx_lut[tile_idx][y + RESTORATION_HALFWIN] + [x + RESTORATION_HALFWIN] * + (int)wr_lut_[data_p2[x] - data_p[j]]; + wtsum += wt; + flsum += wt * data_p2[x]; + } + data_p2 += stride; } - data_p2 += stride; + if (wtsum > 0) + tmpdata_p[j] = clip_pixel((int)((flsum + wtsum / 2) / wtsum)); + else + tmpdata_p[j] = data_p[j]; } - if (wtsum > 0) - tmpdata_p[j] = clip_pixel((int)((flsum + wtsum / 2) / wtsum)); - else - tmpdata_p[j] = data_p[j]; + tmpdata_p += tmpstride; + data_p += stride; + } + for (i = v_start; i < v_end; ++i) { + memcpy(data + i * stride + h_start, tmpdata + i * tmpstride + h_start, + (h_end - h_start) * sizeof(*data)); } - tmpdata_p += tmpstride; - data_p += stride; - } - - for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; ++i) { - memcpy(data + i * stride + RESTORATION_HALFWIN, - tmpdata + i * tmpstride + RESTORATION_HALFWIN, - (width - RESTORATION_HALFWIN * 2) * sizeof(*data)); } } @@ -219,38 +306,70 @@ uint8_t ver_sym_filter(uint8_t *d, int stride, int *vfilter) { static void loop_wiener_filter(uint8_t *data, int width, int height, int stride, RestorationInternal *rst, uint8_t *tmpdata, int tmpstride) { - uint8_t *data_p = data; - uint8_t *tmpdata_p = tmpdata; - int i, j; + int i, j, tile_idx, htile_idx, vtile_idx; + int h_start, h_end, v_start, v_end; + int tile_width, tile_height; + uint8_t *data_p, *tmpdata_p; - for (i = 0; i < height; ++i) { - memcpy(tmpdata_p, data_p, sizeof(*data_p) * RESTORATION_HALFWIN); - data_p += RESTORATION_HALFWIN; - tmpdata_p += RESTORATION_HALFWIN; - for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; ++j) { - *tmpdata_p++ = hor_sym_filter(data_p++, rst->hfilter); - } - memcpy(tmpdata_p, data_p, sizeof(*data_p) * RESTORATION_HALFWIN); - data_p += RESTORATION_HALFWIN - width + stride; - tmpdata_p += RESTORATION_HALFWIN - width + tmpstride; - } + tile_width = rst->tile_width >> rst->subsampling_x; + tile_height = rst->tile_height >> rst->subsampling_y; + + // Initialize tmp buffer data_p = data; tmpdata_p = tmpdata; - for (i = 0; i < RESTORATION_HALFWIN; ++i) { - memcpy(data_p, tmpdata_p, sizeof(*data_p) * width); + for (i = 0; i < height; ++i) { + memcpy(tmpdata_p, data_p, sizeof(*data_p) * width); data_p += stride; tmpdata_p += tmpstride; } - for (; i < height - RESTORATION_HALFWIN; ++i) { - for (j = 0; j < width; ++j) - *data_p++ = ver_sym_filter(tmpdata_p++, tmpstride, rst->vfilter); - data_p += stride - width; - tmpdata_p += tmpstride - width; + + // Filter row-wise tile-by-tile + for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) { + if (rst->wiener_level[tile_idx] == 0) continue; + htile_idx = tile_idx % rst->nhtiles; + vtile_idx = tile_idx / rst->nhtiles; + h_start = + htile_idx * tile_width + ((htile_idx > 0) ? 0 : RESTORATION_HALFWIN); + h_end = (htile_idx < rst->nhtiles - 1) ? ((htile_idx + 1) * tile_width) + : (width - RESTORATION_HALFWIN); + v_start = vtile_idx * tile_height; + v_end = (vtile_idx < rst->nvtiles - 1) ? ((vtile_idx + 1) * tile_height) + : height; + data_p = data + h_start + v_start * stride; + tmpdata_p = tmpdata + h_start + v_start * tmpstride; + for (i = 0; i < (v_end - v_start); ++i) { + for (j = 0; j < (h_end - h_start); ++j) { + *tmpdata_p++ = hor_sym_filter(data_p++, rst->hfilter[tile_idx]); + } + data_p += stride - (h_end - h_start); + tmpdata_p += tmpstride - (h_end - h_start); + } } - for (; i < height; ++i) { - memcpy(data_p, tmpdata_p, sizeof(*data_p) * width); - data_p += stride; - tmpdata_p += tmpstride; + + // Filter column-wise tile-by-tile (bands of thickness RESTORATION_HALFWIN + // at top and bottom of tiles allow filtering overlap, and are not optimally + // filtered) + for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) { + if (rst->wiener_level[tile_idx] == 0) continue; + htile_idx = tile_idx % rst->nhtiles; + vtile_idx = tile_idx / rst->nhtiles; + h_start = htile_idx * tile_width; + h_end = + (htile_idx < rst->nhtiles - 1) ? ((htile_idx + 1) * tile_width) : width; + v_start = + vtile_idx * tile_height + ((vtile_idx > 0) ? 0 : RESTORATION_HALFWIN); + v_end = (vtile_idx < rst->nvtiles - 1) ? ((vtile_idx + 1) * tile_height) + : (height - RESTORATION_HALFWIN); + data_p = data + h_start + v_start * stride; + tmpdata_p = tmpdata + h_start + v_start * tmpstride; + for (i = 0; i < (v_end - v_start); ++i) { + for (j = 0; j < (h_end - h_start); ++j) { + *data_p++ = + ver_sym_filter(tmpdata_p++, tmpstride, rst->vfilter[tile_idx]); + } + data_p += stride - (h_end - h_start); + tmpdata_p += tmpstride - (h_end - h_start); + } } } @@ -259,44 +378,64 @@ static void loop_bilateral_filter_highbd(uint8_t *data8, int width, int height, int stride, RestorationInternal *rst, uint8_t *tmpdata8, int tmpstride, int bit_depth) { - int i, j; - const uint8_t *wr_lut_ = rst->wr_lut + RESTORATION_RANGE; + int i, j, tile_idx, htile_idx, vtile_idx; + int h_start, h_end, v_start, v_end; + int tile_width, tile_height; uint16_t *data = CONVERT_TO_SHORTPTR(data8); uint16_t *tmpdata = CONVERT_TO_SHORTPTR(tmpdata8); - uint16_t *data_p = data + RESTORATION_HALFWIN * stride; - uint16_t *tmpdata_p = tmpdata + RESTORATION_HALFWIN * tmpstride; - for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; ++i) { - for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; ++j) { - int x, y, diff_r; - int 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) { - diff_r = (data_p2[x] - data_p[j]) >> (bit_depth - 8); - assert(diff_r >= -RESTORATION_RANGE && diff_r <= RESTORATION_RANGE); - wt = (int)rst - ->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] * - (int)wr_lut_[diff_r]; - wtsum += wt; - flsum += wt * data_p2[x]; + + tile_width = rst->tile_width >> rst->subsampling_x; + tile_height = rst->tile_height >> rst->subsampling_y; + + for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) { + uint16_t *data_p, *tmpdata_p; + const uint8_t *wr_lut_ = rst->wr_lut[tile_idx] + BILATERAL_AMP_RANGE; + + if (rst->bilateral_level[tile_idx] < 0) continue; + + htile_idx = tile_idx % rst->nhtiles; + vtile_idx = tile_idx / rst->nhtiles; + h_start = + htile_idx * tile_width + ((htile_idx > 0) ? 0 : RESTORATION_HALFWIN); + h_end = (htile_idx < rst->nhtiles - 1) ? ((htile_idx + 1) * tile_width) + : (width - RESTORATION_HALFWIN); + v_start = + vtile_idx * tile_height + ((vtile_idx > 0) ? 0 : RESTORATION_HALFWIN); + v_end = (vtile_idx < rst->nvtiles - 1) ? ((vtile_idx + 1) * tile_height) + : (height - RESTORATION_HALFWIN); + + data_p = data + h_start + v_start * stride; + tmpdata_p = tmpdata + h_start + v_start * tmpstride; + + for (i = 0; i < (v_end - v_start); ++i) { + for (j = 0; j < (h_end - h_start); ++j) { + int x, y; + int 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) { + wt = (int)rst->wx_lut[tile_idx][y + RESTORATION_HALFWIN] + [x + RESTORATION_HALFWIN] * + (int)wr_lut_[data_p2[x] - data_p[j]]; + wtsum += wt; + flsum += wt * data_p2[x]; + } + data_p2 += stride; } - data_p2 += stride; + if (wtsum > 0) + tmpdata_p[j] = + clip_pixel_highbd((int)((flsum + wtsum / 2) / wtsum), bit_depth); + else + tmpdata_p[j] = data_p[j]; } - if (wtsum > 0) - tmpdata_p[j] = - clip_pixel_highbd((int)((flsum + wtsum / 2) / wtsum), bit_depth); - else - tmpdata_p[j] = data_p[j]; + tmpdata_p += tmpstride; + data_p += stride; + } + for (i = v_start; i < v_end; ++i) { + memcpy(data + i * stride + h_start, tmpdata + i * tmpstride + h_start, + (h_end - h_start) * sizeof(*data)); } - tmpdata_p += tmpstride; - data_p += stride; - } - - for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; ++i) { - memcpy(data + i * stride + RESTORATION_HALFWIN, - tmpdata + i * tmpstride + RESTORATION_HALFWIN, - (width - RESTORATION_HALFWIN * 2) * sizeof(*data)); } } @@ -324,38 +463,71 @@ static void loop_wiener_filter_highbd(uint8_t *data8, int width, int height, int bit_depth) { uint16_t *data = CONVERT_TO_SHORTPTR(data8); uint16_t *tmpdata = CONVERT_TO_SHORTPTR(tmpdata8); - uint16_t *data_p = data; - uint16_t *tmpdata_p = tmpdata; - int i, j; - for (i = 0; i < height; ++i) { - memcpy(tmpdata_p, data_p, sizeof(*data_p) * RESTORATION_HALFWIN); - data_p += RESTORATION_HALFWIN; - tmpdata_p += RESTORATION_HALFWIN; - for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; ++j) { - *tmpdata_p++ = hor_sym_filter_highbd(data_p++, rst->hfilter, bit_depth); - } - memcpy(tmpdata_p, data_p, sizeof(*data_p) * RESTORATION_HALFWIN); - data_p += RESTORATION_HALFWIN - width + stride; - tmpdata_p += RESTORATION_HALFWIN - width + tmpstride; - } + int i, j, tile_idx, htile_idx, vtile_idx; + int h_start, h_end, v_start, v_end; + int tile_width, tile_height; + uint16_t *data_p, *tmpdata_p; + + tile_width = rst->tile_width >> rst->subsampling_x; + tile_height = rst->tile_height >> rst->subsampling_y; + + // Initialize tmp buffer data_p = data; tmpdata_p = tmpdata; - for (i = 0; i < RESTORATION_HALFWIN; ++i) { - memcpy(data_p, tmpdata_p, sizeof(*data_p) * width); + for (i = 0; i < height; ++i) { + memcpy(tmpdata_p, data_p, sizeof(*data_p) * width); data_p += stride; tmpdata_p += tmpstride; } - for (; i < height - RESTORATION_HALFWIN; ++i) { - for (j = 0; j < width; ++j) - *data_p++ = ver_sym_filter_highbd(tmpdata_p++, tmpstride, rst->vfilter, - bit_depth); - data_p += stride - width; - tmpdata_p += tmpstride - width; + + // Filter row-wise tile-by-tile + for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) { + if (rst->wiener_level[tile_idx] == 0) continue; + htile_idx = tile_idx % rst->nhtiles; + vtile_idx = tile_idx / rst->nhtiles; + h_start = + htile_idx * tile_width + ((htile_idx > 0) ? 0 : RESTORATION_HALFWIN); + h_end = (htile_idx < rst->nhtiles - 1) ? ((htile_idx + 1) * tile_width) + : (width - RESTORATION_HALFWIN); + v_start = vtile_idx * tile_height; + v_end = (vtile_idx < rst->nvtiles - 1) ? ((vtile_idx + 1) * tile_height) + : height; + data_p = data + h_start + v_start * stride; + tmpdata_p = tmpdata + h_start + v_start * tmpstride; + for (i = 0; i < (v_end - v_start); ++i) { + for (j = 0; j < (h_end - h_start); ++j) { + *tmpdata_p++ = + hor_sym_filter_highbd(data_p++, rst->hfilter[tile_idx], bit_depth); + } + data_p += stride - (h_end - h_start); + tmpdata_p += tmpstride - (h_end - h_start); + } } - for (; i < height; ++i) { - memcpy(data_p, tmpdata_p, sizeof(*data_p) * width); - data_p += stride; - tmpdata_p += tmpstride; + + // Filter column-wise tile-by-tile (bands of thickness RESTORATION_HALFWIN + // at top and bottom of tiles allow filtering overlap, and are not optimally + // filtered) + for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) { + if (rst->wiener_level[tile_idx] == 0) continue; + htile_idx = tile_idx % rst->nhtiles; + vtile_idx = tile_idx / rst->nhtiles; + h_start = htile_idx * tile_width; + h_end = + (htile_idx < rst->nhtiles - 1) ? ((htile_idx + 1) * tile_width) : width; + v_start = + vtile_idx * tile_height + ((vtile_idx > 0) ? 0 : RESTORATION_HALFWIN); + v_end = (vtile_idx < rst->nvtiles - 1) ? ((vtile_idx + 1) * tile_height) + : (height - RESTORATION_HALFWIN); + data_p = data + h_start + v_start * stride; + tmpdata_p = tmpdata + h_start + v_start * tmpstride; + for (i = 0; i < (v_end - v_start); ++i) { + for (j = 0; j < (h_end - h_start); ++j) { + *data_p++ = ver_sym_filter_highbd(tmpdata_p++, tmpstride, + rst->vfilter[tile_idx], bit_depth); + } + data_p += stride - (h_end - h_start); + tmpdata_p += tmpstride - (h_end - h_start); + } } } #endif // CONFIG_VP9_HIGHBITDEPTH @@ -380,60 +552,73 @@ void vp10_loop_restoration_rows(YV12_BUFFER_CONFIG *frame, VP10_COMMON *cm, ? loop_bilateral_filter_highbd : loop_wiener_filter_highbd; #endif // CONFIG_VP9_HIGHBITDEPTH - YV12_BUFFER_CONFIG *tmp_buf; + YV12_BUFFER_CONFIG tmp_buf; + memset(&tmp_buf, 0, sizeof(YV12_BUFFER_CONFIG)); 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 (vpx_realloc_frame_buffer( + &tmp_buf, cm->width, cm->height, cm->subsampling_x, cm->subsampling_y, #if CONFIG_VP9_HIGHBITDEPTH - cm->use_highbitdepth, + cm->use_highbitdepth, #endif - VPX_DEC_BORDER_IN_PIXELS, cm->byte_alignment, - NULL, NULL, NULL) < 0) + VPX_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) restore_func_highbd(frame->y_buffer + ystart * ystride, ywidth, yend - ystart, ystride, &cm->rst_internal, - tmp_buf->y_buffer + ystart * tmp_buf->y_stride, - tmp_buf->y_stride, cm->bit_depth); + tmp_buf.y_buffer + ystart * tmp_buf.y_stride, + tmp_buf.y_stride, cm->bit_depth); else #endif // CONFIG_VP9_HIGHBITDEPTH restore_func(frame->y_buffer + ystart * ystride, ywidth, yend - ystart, ystride, &cm->rst_internal, - tmp_buf->y_buffer + ystart * tmp_buf->y_stride, - tmp_buf->y_stride); + tmp_buf.y_buffer + ystart * tmp_buf.y_stride, + tmp_buf.y_stride); if (!y_only) { + cm->rst_internal.subsampling_x = cm->subsampling_x; + cm->rst_internal.subsampling_y = cm->subsampling_y; #if CONFIG_VP9_HIGHBITDEPTH if (cm->use_highbitdepth) { restore_func_highbd(frame->u_buffer + uvstart * uvstride, uvwidth, uvend - uvstart, uvstride, &cm->rst_internal, - tmp_buf->u_buffer + uvstart * tmp_buf->uv_stride, - tmp_buf->uv_stride, cm->bit_depth); + tmp_buf.u_buffer + uvstart * tmp_buf.uv_stride, + tmp_buf.uv_stride, cm->bit_depth); restore_func_highbd(frame->v_buffer + uvstart * uvstride, uvwidth, uvend - uvstart, uvstride, &cm->rst_internal, - tmp_buf->v_buffer + uvstart * tmp_buf->uv_stride, - tmp_buf->uv_stride, cm->bit_depth); + tmp_buf.v_buffer + uvstart * tmp_buf.uv_stride, + tmp_buf.uv_stride, cm->bit_depth); } else { #endif // CONFIG_VP9_HIGHBITDEPTH restore_func(frame->u_buffer + uvstart * uvstride, uvwidth, uvend - uvstart, uvstride, &cm->rst_internal, - tmp_buf->u_buffer + uvstart * tmp_buf->uv_stride, - tmp_buf->uv_stride); + tmp_buf.u_buffer + uvstart * tmp_buf.uv_stride, + tmp_buf.uv_stride); restore_func(frame->v_buffer + uvstart * uvstride, uvwidth, uvend - uvstart, uvstride, &cm->rst_internal, - tmp_buf->v_buffer + uvstart * tmp_buf->uv_stride, - tmp_buf->uv_stride); + tmp_buf.v_buffer + uvstart * tmp_buf.uv_stride, + tmp_buf.uv_stride); #if CONFIG_VP9_HIGHBITDEPTH } #endif // CONFIG_VP9_HIGHBITDEPTH } + vpx_free_frame_buffer(&tmp_buf); + if (cm->rst_internal.restoration_type == RESTORE_BILATERAL) { + free(cm->rst_internal.wr_lut); + cm->rst_internal.wr_lut = NULL; + free(cm->rst_internal.wx_lut); + cm->rst_internal.wx_lut = NULL; + } + if (cm->rst_internal.restoration_type == RESTORE_WIENER) { + free(cm->rst_internal.vfilter); + cm->rst_internal.vfilter = NULL; + free(cm->rst_internal.hfilter); + cm->rst_internal.hfilter = NULL; + } } void vp10_loop_restoration_frame(YV12_BUFFER_CONFIG *frame, VP10_COMMON *cm, @@ -450,7 +635,8 @@ void vp10_loop_restoration_frame(YV12_BUFFER_CONFIG *frame, VP10_COMMON *cm, } end_mi_row = start_mi_row + mi_rows_to_filter; vp10_loop_restoration_init(&cm->rst_internal, rsi, - cm->frame_type == KEY_FRAME); + cm->frame_type == KEY_FRAME, cm->width, + cm->height); vp10_loop_restoration_rows(frame, cm, start_mi_row, end_mi_row, y_only); } } diff --git a/vp10/common/restoration.h b/vp10/common/restoration.h index 8d1cbd1a676143eec3b60b4e91001d45a37f3599..5ad04f93be76b5427c6d6eb4bacf8ec984e8a6e2 100644 --- a/vp10/common/restoration.h +++ b/vp10/common/restoration.h @@ -11,8 +11,8 @@ #ifndef VP10_COMMON_RESTORATION_H_ #define VP10_COMMON_RESTORATION_H_ -#include "vpx_ports/mem.h" #include "./vpx_config.h" +#include "vpx_ports/mem.h" #include "vp10/common/blockd.h" @@ -20,11 +20,15 @@ extern "C" { #endif -#define RESTORATION_LEVEL_BITS_KF 4 -#define RESTORATION_LEVELS_KF (1 << RESTORATION_LEVEL_BITS_KF) -#define RESTORATION_LEVEL_BITS 3 -#define RESTORATION_LEVELS (1 << RESTORATION_LEVEL_BITS) -#define DEF_RESTORATION_LEVEL 2 +#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 RESTORATION_TILESIZES 3 +#define BILATERAL_TILESIZE 0 +#define WIENER_TILESIZE 2 #define RESTORATION_HALFWIN 3 #define RESTORATION_HALFWIN1 (RESTORATION_HALFWIN + 1) @@ -34,9 +38,9 @@ extern "C" { #define RESTORATION_FILT_BITS 7 #define RESTORATION_FILT_STEP (1 << RESTORATION_FILT_BITS) -#define WIENER_FILT_TAP0_MINV -5 +#define WIENER_FILT_TAP0_MINV (-5) #define WIENER_FILT_TAP1_MINV (-23) -#define WIENER_FILT_TAP2_MINV -20 +#define WIENER_FILT_TAP2_MINV (-16) #define WIENER_FILT_TAP0_BITS 4 #define WIENER_FILT_TAP1_BITS 5 @@ -60,20 +64,37 @@ typedef enum { typedef struct { RestorationType restoration_type; - int restoration_level; - int vfilter[RESTORATION_HALFWIN], hfilter[RESTORATION_HALFWIN]; + // Bilateral filter + int *bilateral_level; + // Wiener filter + int *wiener_level; + int (*vfilter)[RESTORATION_HALFWIN], (*hfilter)[RESTORATION_HALFWIN]; } RestorationInfo; typedef struct { RestorationType restoration_type; - uint8_t *wx_lut[RESTORATION_WIN]; - uint8_t *wr_lut; - int vfilter[RESTORATION_WIN], hfilter[RESTORATION_WIN]; + int subsampling_x; + int subsampling_y; + int tilesize_index; + int ntiles; + int tile_width, tile_height; + int nhtiles, nvtiles; + // Bilateral filter + int *bilateral_level; + uint8_t (**wx_lut)[RESTORATION_WIN]; + uint8_t **wr_lut; + // Wiener filter + int *wiener_level; + int (*vfilter)[RESTORATION_WIN], (*hfilter)[RESTORATION_WIN]; } RestorationInternal; -int vp10_restoration_level_bits(const struct VP10Common *const cm); +int vp10_bilateral_level_bits(const struct VP10Common *const cm); +int vp10_get_restoration_ntiles(int tilesize, int width, int height); +void vp10_get_restoration_tile_size(int tilesize, int width, int height, + int *tile_width, int *tile_height, + int *nhtiles, int *nvtiles); void vp10_loop_restoration_init(RestorationInternal *rst, RestorationInfo *rsi, - int kf); + int kf, int width, int height); void vp10_loop_restoration_frame(YV12_BUFFER_CONFIG *frame, struct VP10Common *cm, RestorationInfo *rsi, int y_only, int partial_frame); diff --git a/vp10/decoder/decodeframe.c b/vp10/decoder/decodeframe.c index 706954beeb3d55cc167681945aa09886a52df6b8..bddfe57ff2ff2115957f99b03a928ebff258515d 100644 --- a/vp10/decoder/decodeframe.c +++ b/vp10/decoder/decodeframe.c @@ -12,12 +12,12 @@ #include // qsort() #include "./vp10_rtcd.h" +#include "./vpx_config.h" #include "./vpx_dsp_rtcd.h" #include "./vpx_scale_rtcd.h" -#include "./vpx_config.h" -#include "vpx_dsp/bitreader_buffer.h" #include "vp10/decoder/bitreader.h" +#include "vpx_dsp/bitreader_buffer.h" #include "vpx_dsp/vpx_dsp_common.h" #include "vpx_mem/vpx_mem.h" #include "vpx_ports/mem.h" @@ -36,18 +36,18 @@ #include "vp10/common/entropy.h" #include "vp10/common/entropymode.h" #include "vp10/common/idct.h" -#include "vp10/common/thread_common.h" #include "vp10/common/pred_common.h" #include "vp10/common/quant_common.h" -#include "vp10/common/reconintra.h" #include "vp10/common/reconinter.h" +#include "vp10/common/reconintra.h" #include "vp10/common/seg_common.h" +#include "vp10/common/thread_common.h" #include "vp10/common/tile_common.h" #include "vp10/decoder/decodeframe.h" -#include "vp10/decoder/detokenize.h" #include "vp10/decoder/decodemv.h" #include "vp10/decoder/decoder.h" +#include "vp10/decoder/detokenize.h" #include "vp10/decoder/dsubexp.h" #define MAX_VPX_HEADER_SIZE 80 @@ -1922,29 +1922,61 @@ static void setup_segmentation(VP10_COMMON *const cm, #if CONFIG_LOOP_RESTORATION static void setup_restoration(VP10_COMMON *cm, struct vpx_read_bit_buffer *rb) { - RestorationInfo *rst = &cm->rst_info; + int i; + RestorationInfo *rsi = &cm->rst_info; + int ntiles; if (vpx_rb_read_bit(rb)) { if (vpx_rb_read_bit(rb)) { - rst->restoration_type = RESTORE_BILATERAL; - rst->restoration_level = - vpx_rb_read_literal(rb, vp10_restoration_level_bits(cm)); + rsi->restoration_type = RESTORE_BILATERAL; + ntiles = vp10_get_restoration_ntiles(BILATERAL_TILESIZE, cm->width, + cm->height); + rsi->bilateral_level = (int *)vpx_realloc( + rsi->bilateral_level, sizeof(*rsi->bilateral_level) * ntiles); + assert(rsi->bilateral_level != NULL); + for (i = 0; i < ntiles; ++i) { + if (vpx_rb_read_bit(rb)) { + rsi->bilateral_level[i] = + vpx_rb_read_literal(rb, vp10_bilateral_level_bits(cm)); + } else { + rsi->bilateral_level[i] = -1; + } + } } else { - rst->restoration_type = RESTORE_WIENER; - rst->vfilter[0] = vpx_rb_read_literal(rb, WIENER_FILT_TAP0_BITS) + - WIENER_FILT_TAP0_MINV; - rst->vfilter[1] = vpx_rb_read_literal(rb, WIENER_FILT_TAP1_BITS) + - WIENER_FILT_TAP1_MINV; - rst->vfilter[2] = vpx_rb_read_literal(rb, WIENER_FILT_TAP2_BITS) + - WIENER_FILT_TAP2_MINV; - rst->hfilter[0] = vpx_rb_read_literal(rb, WIENER_FILT_TAP0_BITS) + - WIENER_FILT_TAP0_MINV; - rst->hfilter[1] = vpx_rb_read_literal(rb, WIENER_FILT_TAP1_BITS) + - WIENER_FILT_TAP1_MINV; - rst->hfilter[2] = vpx_rb_read_literal(rb, WIENER_FILT_TAP2_BITS) + - WIENER_FILT_TAP2_MINV; + rsi->restoration_type = RESTORE_WIENER; + ntiles = + vp10_get_restoration_ntiles(WIENER_TILESIZE, cm->width, cm->height); + rsi->wiener_level = (int *)vpx_realloc( + rsi->wiener_level, sizeof(*rsi->wiener_level) * ntiles); + assert(rsi->wiener_level != NULL); + rsi->vfilter = (int(*)[RESTORATION_HALFWIN])vpx_realloc( + rsi->vfilter, sizeof(*rsi->vfilter) * ntiles); + assert(rsi->vfilter != NULL); + rsi->hfilter = (int(*)[RESTORATION_HALFWIN])vpx_realloc( + rsi->hfilter, sizeof(*rsi->hfilter) * ntiles); + assert(rsi->hfilter != NULL); + for (i = 0; i < ntiles; ++i) { + rsi->wiener_level[i] = vpx_rb_read_bit(rb); + if (rsi->wiener_level[i]) { + rsi->vfilter[i][0] = vpx_rb_read_literal(rb, WIENER_FILT_TAP0_BITS) + + WIENER_FILT_TAP0_MINV; + rsi->vfilter[i][1] = vpx_rb_read_literal(rb, WIENER_FILT_TAP1_BITS) + + WIENER_FILT_TAP1_MINV; + rsi->vfilter[i][2] = vpx_rb_read_literal(rb, WIENER_FILT_TAP2_BITS) + + WIENER_FILT_TAP2_MINV; + rsi->hfilter[i][0] = vpx_rb_read_literal(rb, WIENER_FILT_TAP0_BITS) + + WIENER_FILT_TAP0_MINV; + rsi->hfilter[i][1] = vpx_rb_read_literal(rb, WIENER_FILT_TAP1_BITS) + + WIENER_FILT_TAP1_MINV; + rsi->hfilter[i][2] = vpx_rb_read_literal(rb, WIENER_FILT_TAP2_BITS) + + WIENER_FILT_TAP2_MINV; + } else { + rsi->vfilter[i][0] = rsi->vfilter[i][1] = rsi->vfilter[i][2] = 0; + rsi->hfilter[i][0] = rsi->hfilter[i][1] = rsi->hfilter[i][2] = 0; + } + } } } else { - rst->restoration_type = RESTORE_NONE; + rsi->restoration_type = RESTORE_NONE; } } #endif // CONFIG_LOOP_RESTORATION @@ -3817,7 +3849,8 @@ void vp10_decode_frame(VP10Decoder *pbi, const uint8_t *data, #if CONFIG_LOOP_RESTORATION if (cm->rst_info.restoration_type != RESTORE_NONE) { vp10_loop_restoration_init(&cm->rst_internal, &cm->rst_info, - cm->frame_type == KEY_FRAME); + cm->frame_type == KEY_FRAME, cm->width, + cm->height); vp10_loop_restoration_rows(new_fb, cm, 0, cm->mi_rows, 0); } #endif // CONFIG_LOOP_RESTORATION diff --git a/vp10/encoder/bitstream.c b/vp10/encoder/bitstream.c index d33f84d320ee86b22fb9a4b8e1cfdf26483a8878..5fb11fa81c57aeb8389249c9cbf10e65f0125dba 100644 --- a/vp10/encoder/bitstream.c +++ b/vp10/encoder/bitstream.c @@ -9,8 +9,8 @@ */ #include -#include #include +#include #include "vpx/vpx_encoder.h" #include "vpx_dsp/bitwriter_buffer.h" @@ -38,8 +38,8 @@ #if CONFIG_ANS #include "vp10/encoder/buf_ans.h" #endif // CONFIG_ANS -#include "vp10/encoder/cost.h" #include "vp10/encoder/bitstream.h" +#include "vp10/encoder/cost.h" #include "vp10/encoder/encodemv.h" #include "vp10/encoder/mcomp.h" #include "vp10/encoder/segmentation.h" @@ -2410,27 +2410,42 @@ static void update_coef_probs(VP10_COMP *cpi, vp10_writer *w) { #if CONFIG_LOOP_RESTORATION static void encode_restoration(VP10_COMMON *cm, struct vpx_write_bit_buffer *wb) { + int i; RestorationInfo *rst = &cm->rst_info; vpx_wb_write_bit(wb, rst->restoration_type != RESTORE_NONE); if (rst->restoration_type != RESTORE_NONE) { if (rst->restoration_type == RESTORE_BILATERAL) { vpx_wb_write_bit(wb, 1); - vpx_wb_write_literal(wb, rst->restoration_level, - vp10_restoration_level_bits(cm)); + for (i = 0; i < cm->rst_internal.ntiles; ++i) { + if (rst->bilateral_level[i] >= 0) { + vpx_wb_write_bit(wb, 1); + vpx_wb_write_literal(wb, rst->bilateral_level[i], + vp10_bilateral_level_bits(cm)); + } else { + vpx_wb_write_bit(wb, 0); + } + } } else { vpx_wb_write_bit(wb, 0); - vpx_wb_write_literal(wb, rst->vfilter[0] - WIENER_FILT_TAP0_MINV, - WIENER_FILT_TAP0_BITS); - vpx_wb_write_literal(wb, rst->vfilter[1] - WIENER_FILT_TAP1_MINV, - WIENER_FILT_TAP1_BITS); - vpx_wb_write_literal(wb, rst->vfilter[2] - WIENER_FILT_TAP2_MINV, - WIENER_FILT_TAP2_BITS); - vpx_wb_write_literal(wb, rst->hfilter[0] - WIENER_FILT_TAP0_MINV, - WIENER_FILT_TAP0_BITS); - vpx_wb_write_literal(wb, rst->hfilter[1] - WIENER_FILT_TAP1_MINV, - WIENER_FILT_TAP1_BITS); - vpx_wb_write_literal(wb, rst->hfilter[2] - WIENER_FILT_TAP2_MINV, - WIENER_FILT_TAP2_BITS); + for (i = 0; i < cm->rst_internal.ntiles; ++i) { + if (rst->wiener_level[i]) { + vpx_wb_write_bit(wb, 1); + vpx_wb_write_literal(wb, rst->vfilter[i][0] - WIENER_FILT_TAP0_MINV, + WIENER_FILT_TAP0_BITS); + vpx_wb_write_literal(wb, rst->vfilter[i][1] - WIENER_FILT_TAP1_MINV, + WIENER_FILT_TAP1_BITS); + vpx_wb_write_literal(wb, rst->vfilter[i][2] - WIENER_FILT_TAP2_MINV, + WIENER_FILT_TAP2_BITS); + vpx_wb_write_literal(wb, rst->hfilter[i][0] - WIENER_FILT_TAP0_MINV, + WIENER_FILT_TAP0_BITS); + vpx_wb_write_literal(wb, rst->hfilter[i][1] - WIENER_FILT_TAP1_MINV, + WIENER_FILT_TAP1_BITS); + vpx_wb_write_literal(wb, rst->hfilter[i][2] - WIENER_FILT_TAP2_MINV, + WIENER_FILT_TAP2_BITS); + } else { + vpx_wb_write_bit(wb, 0); + } + } } } } diff --git a/vp10/encoder/encoder.c b/vp10/encoder/encoder.c index 639fb3f42279fbbc1559a0bfbb9292c1f654d2ce..eb8276394ed3825304fb42c5e159e215271d2700 100644 --- a/vp10/encoder/encoder.c +++ b/vp10/encoder/encoder.c @@ -8,9 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include -#include #include "./vpx_config.h" @@ -459,14 +459,12 @@ static void dealloc_compressor_data(VP10_COMP *cpi) { vpx_free_frame_buffer(&cpi->upsampled_ref_bufs[i].buf); vp10_free_ref_frame_buffers(cm->buffer_pool); -#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); + vp10_free_restoration_buffers(cm); #endif // CONFIG_LOOP_RESTORATION vpx_free_frame_buffer(&cpi->scaled_source); vpx_free_frame_buffer(&cpi->scaled_last_source); @@ -3492,7 +3490,8 @@ static void loopfilter_frame(VP10_COMP *cpi, VP10_COMMON *cm) { #if CONFIG_LOOP_RESTORATION if (cm->rst_info.restoration_type != RESTORE_NONE) { vp10_loop_restoration_init(&cm->rst_internal, &cm->rst_info, - cm->frame_type == KEY_FRAME); + cm->frame_type == KEY_FRAME, cm->width, + cm->height); vp10_loop_restoration_rows(cm->frame_to_show, cm, 0, cm->mi_rows, 0); } #endif // CONFIG_LOOP_RESTORATION diff --git a/vp10/encoder/pickrst.c b/vp10/encoder/pickrst.c index 342a8ef4f6134dc6a3a902fb72cd53b111c897e2..242dbd3b557bc7fb5204fd71bab8be1ebd529df1 100644 --- a/vp10/encoder/pickrst.c +++ b/vp10/encoder/pickrst.c @@ -24,9 +24,9 @@ #include "vp10/common/quant_common.h" #include "vp10/encoder/encoder.h" -#include "vp10/encoder/quantize.h" #include "vp10/encoder/picklpf.h" #include "vp10/encoder/pickrst.h" +#include "vp10/encoder/quantize.h" static int64_t try_restoration_frame(const YV12_BUFFER_CONFIG *sd, VP10_COMP *const cpi, RestorationInfo *rsi, @@ -51,17 +51,18 @@ static int64_t try_restoration_frame(const YV12_BUFFER_CONFIG *sd, static int search_bilateral_level(const YV12_BUFFER_CONFIG *sd, VP10_COMP *cpi, int filter_level, int partial_frame, - double *best_cost_ret) { + int *bilateral_level, double *best_cost_ret) { VP10_COMMON *const cm = &cpi->common; - int i, restoration_best; + int i, j, tile_idx; int64_t err; - double best_cost; - double cost; - const int restoration_level_bits = vp10_restoration_level_bits(&cpi->common); - const int restoration_levels = 1 << restoration_level_bits; - MACROBLOCK *x = &cpi->td.mb; int bits; + double cost, best_cost, cost_norestore, cost_bilateral; + const int bilateral_level_bits = vp10_bilateral_level_bits(&cpi->common); + const int bilateral_levels = 1 << bilateral_level_bits; + MACROBLOCK *x = &cpi->td.mb; RestorationInfo rsi; + const int ntiles = + vp10_get_restoration_ntiles(BILATERAL_TILESIZE, cm->width, cm->height); // Make a copy of the unfiltered / processed recon buffer vpx_yv12_copy_y(cm->frame_to_show, &cpi->last_frame_uf); @@ -69,60 +70,102 @@ static int search_bilateral_level(const YV12_BUFFER_CONFIG *sd, VP10_COMP *cpi, 1, partial_frame); vpx_yv12_copy_y(cm->frame_to_show, &cpi->last_frame_db); - restoration_best = -1; + // RD cost associated with no restoration rsi.restoration_type = RESTORE_NONE; err = try_restoration_frame(sd, cpi, &rsi, partial_frame); bits = 0; - best_cost = RDCOST_DBL(x->rdmult, x->rddiv, - (bits << (VP10_PROB_COST_SHIFT - 4)), err); - for (i = 0; i < restoration_levels; ++i) { - rsi.restoration_type = RESTORE_BILATERAL; - rsi.restoration_level = i; - err = try_restoration_frame(sd, cpi, &rsi, partial_frame); - // 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 = restoration_level_bits; - cost = RDCOST_DBL(x->rdmult, x->rddiv, (bits << (VP10_PROB_COST_SHIFT - 4)), - err); - if (cost < best_cost) { - restoration_best = i; - best_cost = cost; + cost_norestore = RDCOST_DBL(x->rdmult, x->rddiv, + (bits << (VP10_PROB_COST_SHIFT - 4)), err); + best_cost = cost_norestore; + + // RD cost associated with bilateral filtering + rsi.restoration_type = RESTORE_BILATERAL; + rsi.bilateral_level = + (int *)vpx_malloc(sizeof(*rsi.bilateral_level) * ntiles); + assert(rsi.bilateral_level != NULL); + + for (j = 0; j < ntiles; ++j) bilateral_level[j] = -1; + + // Find best filter for each tile + for (tile_idx = 0; tile_idx < ntiles; ++tile_idx) { + for (j = 0; j < ntiles; ++j) rsi.bilateral_level[j] = -1; + best_cost = cost_norestore; + for (i = 0; i < bilateral_levels; ++i) { + rsi.bilateral_level[tile_idx] = i; + err = try_restoration_frame(sd, cpi, &rsi, partial_frame); + bits = bilateral_level_bits + 1; + // 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. + cost = RDCOST_DBL(x->rdmult, x->rddiv, + (bits << (VP10_PROB_COST_SHIFT - 4)), err); + if (cost < best_cost) { + bilateral_level[tile_idx] = i; + best_cost = cost; + } } } - if (best_cost_ret) *best_cost_ret = best_cost; + // Find cost for combined configuration + bits = 0; + for (j = 0; j < ntiles; ++j) { + rsi.bilateral_level[j] = bilateral_level[j]; + if (rsi.bilateral_level[j] >= 0) { + bits += (bilateral_level_bits + 1); + } else { + bits += 1; + } + } + err = try_restoration_frame(sd, cpi, &rsi, partial_frame); + cost_bilateral = RDCOST_DBL(x->rdmult, x->rddiv, + (bits << (VP10_PROB_COST_SHIFT - 4)), err); + + vpx_free(rsi.bilateral_level); + vpx_yv12_copy_y(&cpi->last_frame_uf, cm->frame_to_show); - return restoration_best; + if (cost_bilateral < cost_norestore) { + if (best_cost_ret) *best_cost_ret = cost_bilateral; + return 1; + } else { + if (best_cost_ret) *best_cost_ret = cost_norestore; + return 0; + } } static int search_filter_bilateral_level(const YV12_BUFFER_CONFIG *sd, VP10_COMP *cpi, int partial_frame, - int *restoration_level, + int *filter_best, int *bilateral_level, double *best_cost_ret) { 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 = vp10_get_max_filter_level(cpi); int filt_direction = 0; - int filt_best, restoration_best; + int filt_best; double best_err; - int i; - int bilateral_lev; + int i, j; + int *tmp_level; + int bilateral_success[MAX_LOOP_FILTER + 1]; + + const int ntiles = + vp10_get_restoration_ntiles(BILATERAL_TILESIZE, cm->width, cm->height); // 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]; - // Set each entry to -1 for (i = 0; i <= MAX_LOOP_FILTER; ++i) ss_err[i] = -1.0; - bilateral_lev = - search_bilateral_level(sd, cpi, filt_mid, partial_frame, &best_err); + tmp_level = (int *)vpx_malloc(sizeof(*tmp_level) * ntiles); + + bilateral_success[filt_mid] = search_bilateral_level( + sd, cpi, filt_mid, partial_frame, tmp_level, &best_err); filt_best = filt_mid; - restoration_best = bilateral_lev; ss_err[filt_mid] = best_err; + for (j = 0; j < ntiles; ++j) { + bilateral_level[j] = tmp_level[j]; + } while (filter_step > 0) { const int filt_high = VPXMIN(filt_mid + filter_step, max_filter_level); @@ -140,8 +183,8 @@ static int search_filter_bilateral_level(const YV12_BUFFER_CONFIG *sd, if (filt_direction <= 0 && filt_low != filt_mid) { // Get Low filter error score if (ss_err[filt_low] < 0) { - bilateral_lev = search_bilateral_level(sd, cpi, filt_low, partial_frame, - &ss_err[filt_low]); + bilateral_success[filt_low] = search_bilateral_level( + sd, cpi, filt_low, partial_frame, tmp_level, &ss_err[filt_low]); } // If value is close to the best so far then bias towards a lower loop // filter value. @@ -151,22 +194,26 @@ static int search_filter_bilateral_level(const YV12_BUFFER_CONFIG *sd, best_err = ss_err[filt_low]; } filt_best = filt_low; - restoration_best = bilateral_lev; + for (j = 0; j < ntiles; ++j) { + bilateral_level[j] = tmp_level[j]; + } } } // Now look at filt_high if (filt_direction >= 0 && filt_high != filt_mid) { if (ss_err[filt_high] < 0) { - bilateral_lev = search_bilateral_level( - sd, cpi, filt_high, partial_frame, &ss_err[filt_high]); + bilateral_success[filt_high] = search_bilateral_level( + sd, cpi, filt_high, partial_frame, tmp_level, &ss_err[filt_high]); } // If value is significantly better than previous best, bias added against // raising filter value if (ss_err[filt_high] < (best_err - bias)) { best_err = ss_err[filt_high]; filt_best = filt_high; - restoration_best = bilateral_lev; + for (j = 0; j < ntiles; ++j) { + bilateral_level[j] = tmp_level[j]; + } } } @@ -180,35 +227,40 @@ static int search_filter_bilateral_level(const YV12_BUFFER_CONFIG *sd, } } + vpx_free(tmp_level); + // Update best error best_err = ss_err[filt_best]; - *restoration_level = restoration_best; if (best_cost_ret) *best_cost_ret = best_err; - return filt_best; + if (filter_best) *filter_best = filt_best; + + return bilateral_success[filt_best]; } -static double find_average(uint8_t *src, int width, int height, int stride) { +static double find_average(uint8_t *src, int h_start, int h_end, int v_start, + int v_end, int stride) { uint64_t sum = 0; double avg = 0; int i, j; - for (i = 0; i < height; i++) - for (j = 0; j < width; j++) sum += src[i * stride + j]; - avg = (double)sum / (height * width); + for (i = v_start; i < v_end; i++) + for (j = h_start; j < h_end; j++) sum += src[i * stride + j]; + avg = (double)sum / ((v_end - v_start) * (h_end - h_start)); return avg; } -static void compute_stats(uint8_t *dgd, uint8_t *src, int width, int height, - int dgd_stride, int src_stride, double *M, - double *H) { +static void compute_stats(uint8_t *dgd, uint8_t *src, int h_start, int h_end, + int v_start, int v_end, int dgd_stride, + int src_stride, double *M, double *H) { int i, j, k, l; double Y[RESTORATION_WIN2]; - const double avg = find_average(dgd, width, height, dgd_stride); + const double avg = + find_average(dgd, h_start, h_end, v_start, v_end, dgd_stride); memset(M, 0, sizeof(*M) * RESTORATION_WIN2); memset(H, 0, sizeof(*H) * RESTORATION_WIN2 * RESTORATION_WIN2); - for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; i++) { - for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; j++) { + for (i = v_start; i < v_end; i++) { + for (j = h_start; j < h_end; j++) { const double X = (double)src[i * src_stride + j] - avg; int idx = 0; for (k = -RESTORATION_HALFWIN; k <= RESTORATION_HALFWIN; k++) { @@ -231,30 +283,32 @@ static void compute_stats(uint8_t *dgd, uint8_t *src, int width, int height, } #if CONFIG_VP9_HIGHBITDEPTH -static double find_average_highbd(uint16_t *src, int width, int height, - int stride) { +static double find_average_highbd(uint16_t *src, int h_start, int h_end, + int v_start, int v_end, int stride) { uint64_t sum = 0; double avg = 0; int i, j; - for (i = 0; i < height; i++) - for (j = 0; j < width; j++) sum += src[i * stride + j]; - avg = (double)sum / (height * width); + for (i = v_start; i < v_end; i++) + for (j = h_start; j < h_end; j++) sum += src[i * stride + j]; + avg = (double)sum / ((v_end - v_start) * (h_end - h_start)); return avg; } -static void compute_stats_highbd(uint8_t *dgd8, uint8_t *src8, int width, - int height, int dgd_stride, int src_stride, - double *M, double *H) { +static void compute_stats_highbd(uint8_t *dgd8, uint8_t *src8, int h_start, + int h_end, int v_start, int v_end, + int dgd_stride, int src_stride, double *M, + double *H) { int i, j, k, l; double Y[RESTORATION_WIN2]; uint16_t *src = CONVERT_TO_SHORTPTR(src8); uint16_t *dgd = CONVERT_TO_SHORTPTR(dgd8); - const double avg = find_average_highbd(dgd, width, height, dgd_stride); + const double avg = + find_average_highbd(dgd, h_start, h_end, v_start, v_end, dgd_stride); memset(M, 0, sizeof(*M) * RESTORATION_WIN2); memset(H, 0, sizeof(*H) * RESTORATION_WIN2 * RESTORATION_WIN2); - for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; i++) { - for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; j++) { + for (i = v_start; i < v_end; i++) { + for (j = h_start; j < h_end; j++) { const double X = (double)src[i * src_stride + j] - avg; int idx = 0; for (k = -RESTORATION_HALFWIN; k <= RESTORATION_HALFWIN; k++) { @@ -492,8 +546,9 @@ static void quantize_sym_filter(double *f, int *fi) { static int search_wiener_filter(const YV12_BUFFER_CONFIG *src, VP10_COMP *cpi, int filter_level, int partial_frame, - int *vfilter, int *hfilter, - double *best_cost_ret) { + int (*vfilter)[RESTORATION_HALFWIN], + int (*hfilter)[RESTORATION_HALFWIN], + int *process_tile, double *best_cost_ret) { VP10_COMMON *const cm = &cpi->common; RestorationInfo rsi; int64_t err; @@ -509,12 +564,21 @@ static int search_wiener_filter(const YV12_BUFFER_CONFIG *src, VP10_COMP *cpi, const int src_stride = src->y_stride; const int dgd_stride = dgd->y_stride; double score; + int tile_idx, htile_idx, vtile_idx, tile_width, tile_height, nhtiles, nvtiles; + int h_start, h_end, v_start, v_end; + int i, j; + + const int tilesize = WIENER_TILESIZE; + const int ntiles = vp10_get_restoration_ntiles(tilesize, width, height); assert(width == dgd->y_crop_width); assert(height == dgd->y_crop_height); assert(width == src->y_crop_width); assert(height == src->y_crop_height); + vp10_get_restoration_tile_size(tilesize, width, height, &tile_width, + &tile_height, &nhtiles, &nvtiles); + // 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, @@ -527,45 +591,91 @@ static int search_wiener_filter(const YV12_BUFFER_CONFIG *src, VP10_COMP *cpi, cost_norestore = RDCOST_DBL(x->rdmult, x->rddiv, (bits << (VP10_PROB_COST_SHIFT - 4)), err); + rsi.restoration_type = RESTORE_WIENER; + rsi.vfilter = + (int(*)[RESTORATION_HALFWIN])vpx_malloc(sizeof(*rsi.vfilter) * ntiles); + assert(rsi.vfilter != NULL); + rsi.hfilter = + (int(*)[RESTORATION_HALFWIN])vpx_malloc(sizeof(*rsi.hfilter) * ntiles); + assert(rsi.hfilter != NULL); + rsi.wiener_level = (int *)vpx_malloc(sizeof(*rsi.wiener_level) * ntiles); + assert(rsi.wiener_level != NULL); + + // Compute best Wiener filters for each tile + for (tile_idx = 0; tile_idx < ntiles; ++tile_idx) { + htile_idx = tile_idx % nhtiles; + vtile_idx = tile_idx / nhtiles; + h_start = + htile_idx * tile_width + ((htile_idx > 0) ? 0 : RESTORATION_HALFWIN); + h_end = (htile_idx < nhtiles - 1) ? ((htile_idx + 1) * tile_width) + : (width - RESTORATION_HALFWIN); + v_start = + vtile_idx * tile_height + ((vtile_idx > 0) ? 0 : RESTORATION_HALFWIN); + v_end = (vtile_idx < nvtiles - 1) ? ((vtile_idx + 1) * tile_height) + : (height - RESTORATION_HALFWIN); + #if CONFIG_VP9_HIGHBITDEPTH - if (cm->use_highbitdepth) - compute_stats_highbd(dgd->y_buffer, src->y_buffer, width, height, - dgd_stride, src_stride, M, H); - else + if (cm->use_highbitdepth) + compute_stats_highbd(dgd->y_buffer, src->y_buffer, h_start, h_end, + v_start, v_end, dgd_stride, src_stride, M, H); + else #endif // CONFIG_VP9_HIGHBITDEPTH - compute_stats(dgd->y_buffer, src->y_buffer, width, height, dgd_stride, - src_stride, M, H); + compute_stats(dgd->y_buffer, src->y_buffer, h_start, h_end, v_start, + v_end, dgd_stride, src_stride, M, H); + + if (!wiener_decompose_sep_sym(M, H, vfilterd, hfilterd)) { + for (i = 0; i < RESTORATION_HALFWIN; ++i) + rsi.vfilter[tile_idx][i] = rsi.hfilter[tile_idx][i] = 0; + process_tile[tile_idx] = 0; + continue; + } + quantize_sym_filter(vfilterd, rsi.vfilter[tile_idx]); + quantize_sym_filter(hfilterd, rsi.hfilter[tile_idx]); + process_tile[tile_idx] = 1; + + // Filter score computes the value of the function x'*A*x - x'*b for the + // learned filter and compares it against identity filer. If there is no + // reduction in the function, the filter is reverted back to identity + score = compute_score(M, H, rsi.vfilter[tile_idx], rsi.hfilter[tile_idx]); + if (score > 0.0) { + for (i = 0; i < RESTORATION_HALFWIN; ++i) + rsi.vfilter[tile_idx][i] = rsi.hfilter[tile_idx][i] = 0; + process_tile[tile_idx] = 0; + continue; + } - if (!wiener_decompose_sep_sym(M, H, vfilterd, hfilterd)) { - *best_cost_ret = DBL_MAX; - return 0; + for (j = 0; j < ntiles; ++j) rsi.wiener_level[j] = 0; + rsi.wiener_level[tile_idx] = 1; + + err = try_restoration_frame(src, cpi, &rsi, partial_frame); + bits = 1 + WIENER_FILT_BITS; + cost_wiener = RDCOST_DBL(x->rdmult, x->rddiv, + (bits << (VP10_PROB_COST_SHIFT - 4)), err); + if (cost_wiener >= cost_norestore) process_tile[tile_idx] = 0; } - quantize_sym_filter(vfilterd, vfilter); - quantize_sym_filter(hfilterd, hfilter); - - // Filter score computes the value of the function x'*A*x - x'*b for the - // learned filter and compares it against identity filer. If there is no - // reduction in the function, the filter is reverted back to identity - score = compute_score(M, H, vfilter, hfilter); - if (score > 0.0) { - int i; - for (i = 0; i < RESTORATION_HALFWIN; ++i) vfilter[i] = hfilter[i] = 0; - rsi.restoration_type = RESTORE_NONE; - if (best_cost_ret) *best_cost_ret = cost_norestore; - vpx_yv12_copy_y(&cpi->last_frame_uf, cm->frame_to_show); - return 0; + // Cost for Wiener filtering + bits = 0; + for (tile_idx = 0; tile_idx < ntiles; ++tile_idx) { + bits += (process_tile[tile_idx] ? (WIENER_FILT_BITS + 1) : 1); + rsi.wiener_level[tile_idx] = process_tile[tile_idx]; } - - rsi.restoration_type = RESTORE_WIENER; - memcpy(rsi.vfilter, vfilter, sizeof(rsi.vfilter)); - memcpy(rsi.hfilter, hfilter, sizeof(rsi.hfilter)); err = try_restoration_frame(src, cpi, &rsi, partial_frame); - bits = WIENER_FILT_BITS; cost_wiener = RDCOST_DBL(x->rdmult, x->rddiv, (bits << (VP10_PROB_COST_SHIFT - 4)), err); - vpx_yv12_copy_y(&cpi->last_frame_uf, cm->frame_to_show); + for (tile_idx = 0; tile_idx < ntiles; ++tile_idx) { + if (process_tile[tile_idx] == 0) continue; + for (i = 0; i < RESTORATION_HALFWIN; ++i) { + vfilter[tile_idx][i] = rsi.vfilter[tile_idx][i]; + hfilter[tile_idx][i] = rsi.hfilter[tile_idx][i]; + } + } + + vpx_free(rsi.vfilter); + vpx_free(rsi.hfilter); + vpx_free(rsi.wiener_level); + vpx_yv12_copy_y(&cpi->last_frame_uf, cm->frame_to_show); if (cost_wiener < cost_norestore) { if (best_cost_ret) *best_cost_ret = cost_wiener; return 1; @@ -580,9 +690,29 @@ void vp10_pick_filter_restoration(const YV12_BUFFER_CONFIG *sd, VP10_COMP *cpi, VP10_COMMON *const cm = &cpi->common; struct loopfilter *const lf = &cm->lf; int wiener_success = 0; + int bilateral_success = 0; double cost_bilateral = DBL_MAX; double cost_wiener = DBL_MAX; double cost_norestore = DBL_MAX; + int ntiles; + + ntiles = + vp10_get_restoration_ntiles(BILATERAL_TILESIZE, cm->width, cm->height); + cm->rst_info.bilateral_level = + (int *)vpx_realloc(cm->rst_info.bilateral_level, + sizeof(*cm->rst_info.bilateral_level) * ntiles); + assert(cm->rst_info.bilateral_level != NULL); + + ntiles = vp10_get_restoration_ntiles(WIENER_TILESIZE, cm->width, cm->height); + cm->rst_info.wiener_level = (int *)vpx_realloc( + cm->rst_info.wiener_level, sizeof(*cm->rst_info.wiener_level) * ntiles); + assert(cm->rst_info.wiener_level != NULL); + cm->rst_info.vfilter = (int(*)[RESTORATION_HALFWIN])vpx_realloc( + cm->rst_info.vfilter, sizeof(*cm->rst_info.vfilter) * ntiles); + assert(cm->rst_info.vfilter != NULL); + cm->rst_info.hfilter = (int(*)[RESTORATION_HALFWIN])vpx_realloc( + cm->rst_info.hfilter, sizeof(*cm->rst_info.hfilter) * ntiles); + assert(cm->rst_info.hfilter != NULL); lf->sharpness_level = cm->frame_type == KEY_FRAME ? 0 : cpi->oxcf.sharpness; @@ -618,14 +748,15 @@ void vp10_pick_filter_restoration(const YV12_BUFFER_CONFIG *sd, VP10_COMP *cpi, #endif // CONFIG_VP9_HIGHBITDEPTH if (cm->frame_type == KEY_FRAME) filt_guess -= 4; lf->filter_level = clamp(filt_guess, min_filter_level, max_filter_level); - cm->rst_info.restoration_level = search_bilateral_level( + bilateral_success = search_bilateral_level( sd, cpi, lf->filter_level, method == LPF_PICK_FROM_SUBIMAGE, - &cost_bilateral); + cm->rst_info.bilateral_level, &cost_bilateral); wiener_success = search_wiener_filter( sd, cpi, lf->filter_level, method == LPF_PICK_FROM_SUBIMAGE, - cm->rst_info.vfilter, cm->rst_info.hfilter, &cost_wiener); + cm->rst_info.vfilter, cm->rst_info.hfilter, cm->rst_info.wiener_level, + &cost_wiener); if (cost_bilateral < cost_wiener) { - if (cm->rst_info.restoration_level != -1) + if (bilateral_success) cm->rst_info.restoration_type = RESTORE_BILATERAL; else cm->rst_info.restoration_type = RESTORE_NONE; @@ -637,17 +768,18 @@ void vp10_pick_filter_restoration(const YV12_BUFFER_CONFIG *sd, VP10_COMP *cpi, } } else { int blf_filter_level = -1; - blf_filter_level = search_filter_bilateral_level( - sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, - &cm->rst_info.restoration_level, &cost_bilateral); + bilateral_success = search_filter_bilateral_level( + sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, &blf_filter_level, + cm->rst_info.bilateral_level, &cost_bilateral); lf->filter_level = vp10_search_filter_level( sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, &cost_norestore); wiener_success = search_wiener_filter( sd, cpi, lf->filter_level, method == LPF_PICK_FROM_SUBIMAGE, - cm->rst_info.vfilter, cm->rst_info.hfilter, &cost_wiener); + cm->rst_info.vfilter, cm->rst_info.hfilter, cm->rst_info.wiener_level, + &cost_wiener); if (cost_bilateral < cost_wiener) { lf->filter_level = blf_filter_level; - if (cm->rst_info.restoration_level != -1) + if (bilateral_success) cm->rst_info.restoration_type = RESTORE_BILATERAL; else cm->rst_info.restoration_type = RESTORE_NONE; @@ -658,7 +790,19 @@ void vp10_pick_filter_restoration(const YV12_BUFFER_CONFIG *sd, VP10_COMP *cpi, cm->rst_info.restoration_type = RESTORE_NONE; } // printf("[%d] Costs %g %g (%d) %g (%d)\n", cm->rst_info.restoration_type, - // cost_norestore, cost_bilateral, lf->filter_level, cost_wiener, - // wiener_success); + // cost_norestore, cost_bilateral, lf->filter_level, cost_wiener, + // wiener_success); + } + if (cm->rst_info.restoration_type != RESTORE_BILATERAL) { + vpx_free(cm->rst_info.bilateral_level); + cm->rst_info.bilateral_level = NULL; + } + if (cm->rst_info.restoration_type != RESTORE_WIENER) { + vpx_free(cm->rst_info.vfilter); + cm->rst_info.vfilter = NULL; + vpx_free(cm->rst_info.hfilter); + cm->rst_info.hfilter = NULL; + vpx_free(cm->rst_info.wiener_level); + cm->rst_info.wiener_level = NULL; } }