Add domain transform recursive filter
This filter is meant to replace the bilateral filter,
but is currently added in addition to the bilateral filter
for testing.
Change-Id: Ia529701e69833d47c11b3367d5bf804eb8498079
diff --git a/av1/common/alloccommon.c b/av1/common/alloccommon.c
index 90f0ba5..fd5ce5a 100644
--- a/av1/common/alloccommon.c
+++ b/av1/common/alloccommon.c
@@ -91,6 +91,8 @@
cm->rst_info.wiener_info = NULL;
aom_free(cm->rst_info.sgrproj_info);
cm->rst_info.sgrproj_info = NULL;
+ aom_free(cm->rst_info.domaintxfmrf_info);
+ cm->rst_info.domaintxfmrf_info = NULL;
}
#endif // CONFIG_LOOP_RESTORATION
diff --git a/av1/common/entropymode.c b/av1/common/entropymode.c
index d91d6b3..cbad10a 100644
--- a/av1/common/entropymode.c
+++ b/av1/common/entropymode.c
@@ -1186,11 +1186,20 @@
#if CONFIG_LOOP_RESTORATION
const aom_tree_index av1_switchable_restore_tree[TREE_SIZE(
RESTORE_SWITCHABLE_TYPES)] = {
- -RESTORE_NONE, 2, -RESTORE_SGRPROJ, 4, -RESTORE_BILATERAL, -RESTORE_WIENER,
+ // -RESTORE_NONE, 2, -RESTORE_SGRPROJ, 4, -RESTORE_BILATERAL, -RESTORE_WIENER,
+ -RESTORE_NONE,
+ 2,
+ 4,
+ 6,
+ -RESTORE_SGRPROJ,
+ -RESTORE_DOMAINTXFMRF,
+ -RESTORE_BILATERAL,
+ -RESTORE_WIENER,
};
static const aom_prob default_switchable_restore_prob[RESTORE_SWITCHABLE_TYPES -
- 1] = { 32, 85, 128 };
+ 1] = { 32, 128, 128,
+ 128 };
#endif // CONFIG_LOOP_RESTORATION
#if CONFIG_PALETTE
diff --git a/av1/common/entropymode.h b/av1/common/entropymode.h
index 4059cad..dd753a1 100644
--- a/av1/common/entropymode.h
+++ b/av1/common/entropymode.h
@@ -346,6 +346,7 @@
#define RESTORE_NONE_SGRPROJ_PROB 64
#define RESTORE_NONE_BILATERAL_PROB 16
#define RESTORE_NONE_WIENER_PROB 64
+#define RESTORE_NONE_DOMAINTXFMRF_PROB 64
extern const aom_tree_index
av1_switchable_restore_tree[TREE_SIZE(RESTORE_SWITCHABLE_TYPES)];
#endif // CONFIG_LOOP_RESTORATION
diff --git a/av1/common/enums.h b/av1/common/enums.h
index 1ab4749..ac39c66 100644
--- a/av1/common/enums.h
+++ b/av1/common/enums.h
@@ -485,7 +485,8 @@
RESTORE_SGRPROJ = 1,
RESTORE_BILATERAL = 2,
RESTORE_WIENER = 3,
- RESTORE_SWITCHABLE = 4,
+ RESTORE_DOMAINTXFMRF = 4,
+ RESTORE_SWITCHABLE = 5,
RESTORE_SWITCHABLE_TYPES = RESTORE_SWITCHABLE,
RESTORE_TYPES,
} RestorationType;
diff --git a/av1/common/restoration.c b/av1/common/restoration.c
index 8eef650..08ab948 100644
--- a/av1/common/restoration.c
+++ b/av1/common/restoration.c
@@ -20,6 +20,16 @@
#include "aom_mem/aom_mem.h"
#include "aom_ports/mem.h"
+static int domaintxfmrf_vtable[DOMAINTXFMRF_ITERS][DOMAINTXFMRF_PARAMS][256];
+
+static const int domaintxfmrf_params[DOMAINTXFMRF_PARAMS] = {
+ 48, 52, 56, 60, 64, 68, 72, 76, 80, 82, 84, 86, 88,
+ 90, 92, 94, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 130, 132, 134,
+ 136, 138, 140, 142, 146, 150, 154, 158, 162, 166, 170, 174
+};
+
#define BILATERAL_PARAM_PRECISION 16
#define BILATERAL_AMP_RANGE 256
#define BILATERAL_AMP_RANGE_SYM (2 * BILATERAL_AMP_RANGE + 1)
@@ -76,7 +86,26 @@
: bilateral_level_to_params_arr[index];
}
-void av1_loop_restoration_precal() {
+static void GenDomainTxfmRFVtable() {
+ int i, j;
+ const double sigma_s = sqrt(2.0);
+ for (i = 0; i < DOMAINTXFMRF_ITERS; ++i) {
+ const int nm = (1 << (DOMAINTXFMRF_ITERS - i - 1));
+ const double A = exp(-DOMAINTXFMRF_MULT / (sigma_s * nm));
+ for (j = 0; j < DOMAINTXFMRF_PARAMS; ++j) {
+ const double sigma_r =
+ (double)domaintxfmrf_params[j] / DOMAINTXFMRF_SIGMA_SCALE;
+ const double scale = sigma_s / sigma_r;
+ int k;
+ for (k = 0; k < 256; ++k) {
+ domaintxfmrf_vtable[i][j][k] =
+ RINT(DOMAINTXFMRF_VTABLE_PREC * pow(A, 1.0 + k * scale));
+ }
+ }
+ }
+}
+
+static void GenBilateralTables() {
int i;
for (i = 0; i < BILATERAL_LEVELS_KF; i++) {
const BilateralParamsType param = av1_bilateral_level_to_params(i, 1);
@@ -140,6 +169,11 @@
}
}
+void av1_loop_restoration_precal() {
+ GenBilateralTables();
+ GenDomainTxfmRFVtable();
+}
+
int av1_bilateral_level_bits(const AV1_COMMON *const cm) {
return cm->frame_type == KEY_FRAME ? BILATERAL_LEVEL_BITS_KF
: BILATERAL_LEVEL_BITS;
@@ -157,18 +191,20 @@
&rst->nhtiles, &rst->nvtiles);
if (rsi->frame_restoration_type == RESTORE_WIENER) {
for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) {
- rsi->wiener_info[tile_idx].vfilter[RESTORATION_HALFWIN] =
- rsi->wiener_info[tile_idx].hfilter[RESTORATION_HALFWIN] =
- RESTORATION_FILT_STEP;
- for (i = 0; i < RESTORATION_HALFWIN; ++i) {
- rsi->wiener_info[tile_idx].vfilter[RESTORATION_WIN - 1 - i] =
- rsi->wiener_info[tile_idx].vfilter[i];
- rsi->wiener_info[tile_idx].hfilter[RESTORATION_WIN - 1 - i] =
- rsi->wiener_info[tile_idx].hfilter[i];
- rsi->wiener_info[tile_idx].vfilter[RESTORATION_HALFWIN] -=
- 2 * rsi->wiener_info[tile_idx].vfilter[i];
- rsi->wiener_info[tile_idx].hfilter[RESTORATION_HALFWIN] -=
- 2 * rsi->wiener_info[tile_idx].hfilter[i];
+ if (rsi->wiener_info[tile_idx].level) {
+ rsi->wiener_info[tile_idx].vfilter[RESTORATION_HALFWIN] =
+ rsi->wiener_info[tile_idx].hfilter[RESTORATION_HALFWIN] =
+ RESTORATION_FILT_STEP;
+ for (i = 0; i < RESTORATION_HALFWIN; ++i) {
+ rsi->wiener_info[tile_idx].vfilter[RESTORATION_WIN - 1 - i] =
+ rsi->wiener_info[tile_idx].vfilter[i];
+ rsi->wiener_info[tile_idx].hfilter[RESTORATION_WIN - 1 - i] =
+ rsi->wiener_info[tile_idx].hfilter[i];
+ rsi->wiener_info[tile_idx].vfilter[RESTORATION_HALFWIN] -=
+ 2 * rsi->wiener_info[tile_idx].vfilter[i];
+ rsi->wiener_info[tile_idx].hfilter[RESTORATION_HALFWIN] -=
+ 2 * rsi->wiener_info[tile_idx].hfilter[i];
+ }
}
}
} else if (rsi->frame_restoration_type == RESTORE_SWITCHABLE) {
@@ -678,6 +714,127 @@
aom_free(tmpbuf);
}
+static void apply_domaintxfmrf_hor(int iter, int param, uint8_t *img, int width,
+ int height, int img_stride, int32_t *dat,
+ int dat_stride) {
+ int i, j;
+ for (i = 0; i < height; ++i) {
+ uint8_t *ip = &img[i * img_stride];
+ int32_t *dp = &dat[i * dat_stride];
+ *dp *= DOMAINTXFMRF_VTABLE_PREC;
+ dp++;
+ ip++;
+ // left to right
+ for (j = 1; j < width; ++j, dp++, ip++) {
+ const int v = domaintxfmrf_vtable[iter][param][abs(ip[0] - ip[-1])];
+ dp[0] = dp[0] * (DOMAINTXFMRF_VTABLE_PREC - v) +
+ ((v * dp[-1] + DOMAINTXFMRF_VTABLE_PREC / 2) >>
+ DOMAINTXFMRF_VTABLE_PRECBITS);
+ }
+ // right to left
+ dp -= 2;
+ ip -= 2;
+ for (j = width - 2; j >= 0; --j, dp--, ip--) {
+ const int v = domaintxfmrf_vtable[iter][param][abs(ip[1] - ip[0])];
+ dp[0] = (dp[0] * (DOMAINTXFMRF_VTABLE_PREC - v) + v * dp[1] +
+ DOMAINTXFMRF_VTABLE_PREC / 2) >>
+ DOMAINTXFMRF_VTABLE_PRECBITS;
+ }
+ }
+}
+
+static void apply_domaintxfmrf_ver(int iter, int param, uint8_t *img, int width,
+ int height, int img_stride, int32_t *dat,
+ int dat_stride) {
+ int i, j;
+ for (j = 0; j < width; ++j) {
+ uint8_t *ip = &img[j];
+ int32_t *dp = &dat[j];
+ dp += dat_stride;
+ ip += img_stride;
+ // top to bottom
+ for (i = 1; i < height; ++i, dp += dat_stride, ip += img_stride) {
+ const int v =
+ domaintxfmrf_vtable[iter][param][abs(ip[0] - ip[-img_stride])];
+ dp[0] = (dp[0] * (DOMAINTXFMRF_VTABLE_PREC - v) +
+ (dp[-dat_stride] * v + DOMAINTXFMRF_VTABLE_PREC / 2)) >>
+ DOMAINTXFMRF_VTABLE_PRECBITS;
+ }
+ // bottom to top
+ dp -= 2 * dat_stride;
+ ip -= 2 * img_stride;
+ for (i = height - 2; i >= 0; --i, dp -= dat_stride, ip -= img_stride) {
+ const int v =
+ domaintxfmrf_vtable[iter][param][abs(ip[img_stride] - ip[0])];
+ dp[0] = (dp[0] * (DOMAINTXFMRF_VTABLE_PREC - v) + dp[dat_stride] * v +
+ DOMAINTXFMRF_VTABLE_PREC / 2) >>
+ DOMAINTXFMRF_VTABLE_PRECBITS;
+ }
+ }
+}
+
+static void apply_domaintxfmrf_reduce_prec(int32_t *dat, int width, int height,
+ int dat_stride) {
+ int i, j;
+ for (i = 0; i < height; ++i) {
+ for (j = 0; j < width; ++j) {
+ dat[i * dat_stride + j] = ROUND_POWER_OF_TWO_SIGNED(
+ dat[i * dat_stride + j], DOMAINTXFMRF_VTABLE_PRECBITS);
+ }
+ }
+}
+
+void av1_domaintxfmrf_restoration(uint8_t *dgd, int width, int height,
+ int stride, int param) {
+ int32_t dat[RESTORATION_TILEPELS_MAX];
+ int i, j, t;
+ for (i = 0; i < height; ++i) {
+ for (j = 0; j < width; ++j) {
+ dat[i * width + j] = dgd[i * stride + j];
+ }
+ }
+ for (t = 0; t < DOMAINTXFMRF_ITERS; ++t) {
+ apply_domaintxfmrf_hor(t, param, dgd, width, height, stride, dat, width);
+ apply_domaintxfmrf_ver(t, param, dgd, width, height, stride, dat, width);
+ apply_domaintxfmrf_reduce_prec(dat, width, height, width);
+ }
+ for (i = 0; i < height; ++i) {
+ for (j = 0; j < width; ++j) {
+ dgd[i * stride + j] = clip_pixel(dat[i * width + j]);
+ }
+ }
+}
+
+static void loop_domaintxfmrf_filter_tile(uint8_t *data, int tile_idx,
+ int width, int height, int stride,
+ RestorationInternal *rst,
+ void *tmpbuf) {
+ const int tile_width = rst->tile_width >> rst->subsampling_x;
+ const int tile_height = rst->tile_height >> rst->subsampling_y;
+ int h_start, h_end, v_start, v_end;
+ (void)tmpbuf;
+
+ if (rst->rsi->domaintxfmrf_info[tile_idx].level == 0) return;
+ av1_get_rest_tile_limits(tile_idx, 0, 0, rst->nhtiles, rst->nvtiles,
+ tile_width, tile_height, width, height, 0, 0,
+ &h_start, &h_end, &v_start, &v_end);
+ av1_domaintxfmrf_restoration(data + h_start + v_start * stride,
+ h_end - h_start, v_end - v_start, stride,
+ rst->rsi->domaintxfmrf_info[tile_idx].sigma_r);
+}
+
+static void loop_domaintxfmrf_filter(uint8_t *data, int width, int height,
+ int stride, RestorationInternal *rst,
+ uint8_t *tmpdata, int tmpstride) {
+ int tile_idx;
+ (void)tmpdata;
+ (void)tmpstride;
+ for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) {
+ loop_domaintxfmrf_filter_tile(data, tile_idx, width, height, stride, rst,
+ NULL);
+ }
+}
+
static void loop_switchable_filter(uint8_t *data, int width, int height,
int stride, RestorationInternal *rst,
uint8_t *tmpdata, int tmpstride) {
@@ -703,6 +860,9 @@
} else if (rst->rsi->restoration_type[tile_idx] == RESTORE_SGRPROJ) {
loop_sgrproj_filter_tile(data, tile_idx, width, height, stride, rst,
tmpbuf);
+ } else if (rst->rsi->restoration_type[tile_idx] == RESTORE_DOMAINTXFMRF) {
+ loop_domaintxfmrf_filter_tile(data, tile_idx, width, height, stride, rst,
+ tmpbuf);
}
}
aom_free(tmpbuf);
@@ -918,6 +1078,130 @@
aom_free(tmpbuf);
}
+static void apply_domaintxfmrf_hor_highbd(int iter, int param, uint16_t *img,
+ int width, int height, int img_stride,
+ int32_t *dat, int dat_stride,
+ int bd) {
+ const int shift = (bd - 8);
+ int i, j;
+ for (i = 0; i < height; ++i) {
+ uint16_t *ip = &img[i * img_stride];
+ int32_t *dp = &dat[i * dat_stride];
+ *dp *= DOMAINTXFMRF_VTABLE_PREC;
+ dp++;
+ ip++;
+ // left to right
+ for (j = 1; j < width; ++j, dp++, ip++) {
+ const int v =
+ domaintxfmrf_vtable[iter][param]
+ [abs((ip[0] >> shift) - (ip[-1] >> shift))];
+ dp[0] = dp[0] * (DOMAINTXFMRF_VTABLE_PREC - v) +
+ ((v * dp[-1] + DOMAINTXFMRF_VTABLE_PREC / 2) >>
+ DOMAINTXFMRF_VTABLE_PRECBITS);
+ }
+ // right to left
+ dp -= 2;
+ ip -= 2;
+ for (j = width - 2; j >= 0; --j, dp--, ip--) {
+ const int v =
+ domaintxfmrf_vtable[iter][param]
+ [abs((ip[1] >> shift) - (ip[0] >> shift))];
+ dp[0] = (dp[0] * (DOMAINTXFMRF_VTABLE_PREC - v) + v * dp[1] +
+ DOMAINTXFMRF_VTABLE_PREC / 2) >>
+ DOMAINTXFMRF_VTABLE_PRECBITS;
+ }
+ }
+}
+
+static void apply_domaintxfmrf_ver_highbd(int iter, int param, uint16_t *img,
+ int width, int height, int img_stride,
+ int32_t *dat, int dat_stride,
+ int bd) {
+ int i, j;
+ const int shift = (bd - 8);
+ for (j = 0; j < width; ++j) {
+ uint16_t *ip = &img[j];
+ int32_t *dp = &dat[j];
+ dp += dat_stride;
+ ip += img_stride;
+ // top to bottom
+ for (i = 1; i < height; ++i, dp += dat_stride, ip += img_stride) {
+ const int v = domaintxfmrf_vtable[iter][param][abs(
+ (ip[0] >> shift) - (ip[-img_stride] >> shift))];
+ dp[0] = (dp[0] * (DOMAINTXFMRF_VTABLE_PREC - v) +
+ (dp[-dat_stride] * v + DOMAINTXFMRF_VTABLE_PREC / 2)) >>
+ DOMAINTXFMRF_VTABLE_PRECBITS;
+ }
+ // bottom to top
+ dp -= 2 * dat_stride;
+ ip -= 2 * img_stride;
+ for (i = height - 2; i >= 0; --i, dp -= dat_stride, ip -= img_stride) {
+ const int v = domaintxfmrf_vtable[iter][param][abs(
+ (ip[img_stride] >> shift) - (ip[0] >> shift))];
+ dp[0] = (dp[0] * (DOMAINTXFMRF_VTABLE_PREC - v) + dp[dat_stride] * v +
+ DOMAINTXFMRF_VTABLE_PREC / 2) >>
+ DOMAINTXFMRF_VTABLE_PRECBITS;
+ }
+ }
+}
+
+void av1_domaintxfmrf_restoration_highbd(uint16_t *dgd, int width, int height,
+ int stride, int param, int bit_depth) {
+ int32_t dat[RESTORATION_TILEPELS_MAX];
+ int i, j, t;
+ for (i = 0; i < height; ++i) {
+ for (j = 0; j < width; ++j) {
+ dat[i * width + j] = dgd[i * stride + j];
+ }
+ }
+ for (t = 0; t < DOMAINTXFMRF_ITERS; ++t) {
+ apply_domaintxfmrf_hor_highbd(t, param, dgd, width, height, stride, dat,
+ width, bit_depth);
+ apply_domaintxfmrf_ver_highbd(t, param, dgd, width, height, stride, dat,
+ width, bit_depth);
+ apply_domaintxfmrf_reduce_prec(dat, width, height, width);
+ }
+ for (i = 0; i < height; ++i) {
+ for (j = 0; j < width; ++j) {
+ dgd[i * stride + j] = clip_pixel_highbd(dat[i * width + j], bit_depth);
+ }
+ }
+}
+
+static void loop_domaintxfmrf_filter_tile_highbd(uint16_t *data, int tile_idx,
+ int width, int height,
+ int stride,
+ RestorationInternal *rst,
+ int bit_depth, void *tmpbuf) {
+ const int tile_width = rst->tile_width >> rst->subsampling_x;
+ const int tile_height = rst->tile_height >> rst->subsampling_y;
+ int h_start, h_end, v_start, v_end;
+ (void)tmpbuf;
+
+ if (rst->rsi->domaintxfmrf_info[tile_idx].level == 0) return;
+ av1_get_rest_tile_limits(tile_idx, 0, 0, rst->nhtiles, rst->nvtiles,
+ tile_width, tile_height, width, height, 0, 0,
+ &h_start, &h_end, &v_start, &v_end);
+ av1_domaintxfmrf_restoration_highbd(
+ data + h_start + v_start * stride, h_end - h_start, v_end - v_start,
+ stride, rst->rsi->domaintxfmrf_info[tile_idx].sigma_r, bit_depth);
+}
+
+static void loop_domaintxfmrf_filter_highbd(uint8_t *data8, int width,
+ int height, int stride,
+ RestorationInternal *rst,
+ uint8_t *tmpdata, int tmpstride,
+ int bit_depth) {
+ int tile_idx;
+ uint16_t *data = CONVERT_TO_SHORTPTR(data8);
+ (void)tmpdata;
+ (void)tmpstride;
+ for (tile_idx = 0; tile_idx < rst->ntiles; ++tile_idx) {
+ loop_domaintxfmrf_filter_tile_highbd(data, tile_idx, width, height, stride,
+ rst, bit_depth, NULL);
+ }
+}
+
static void loop_switchable_filter_highbd(uint8_t *data8, int width, int height,
int stride, RestorationInternal *rst,
uint8_t *tmpdata8, int tmpstride,
@@ -946,6 +1230,9 @@
} else if (rst->rsi->restoration_type[tile_idx] == RESTORE_SGRPROJ) {
loop_sgrproj_filter_tile_highbd(data, tile_idx, width, height, stride,
rst, bit_depth, tmpbuf);
+ } else if (rst->rsi->restoration_type[tile_idx] == RESTORE_DOMAINTXFMRF) {
+ loop_domaintxfmrf_filter_tile_highbd(data, tile_idx, width, height,
+ stride, rst, bit_depth, tmpbuf);
}
}
aom_free(tmpbuf);
@@ -962,14 +1249,20 @@
const int uvstart = ystart >> cm->subsampling_y;
int yend = end_mi_row << MI_SIZE_LOG2;
int uvend = yend >> cm->subsampling_y;
- restore_func_type restore_funcs[RESTORE_TYPES] = { NULL, loop_sgrproj_filter,
+ restore_func_type restore_funcs[RESTORE_TYPES] = { NULL,
+ loop_sgrproj_filter,
loop_bilateral_filter,
loop_wiener_filter,
+ loop_domaintxfmrf_filter,
loop_switchable_filter };
#if CONFIG_AOM_HIGHBITDEPTH
restore_func_highbd_type restore_funcs_highbd[RESTORE_TYPES] = {
- NULL, loop_sgrproj_filter_highbd, loop_bilateral_filter_highbd,
- loop_wiener_filter_highbd, loop_switchable_filter_highbd
+ NULL,
+ loop_sgrproj_filter_highbd,
+ loop_bilateral_filter_highbd,
+ loop_wiener_filter_highbd,
+ loop_domaintxfmrf_filter_highbd,
+ loop_switchable_filter_highbd
};
#endif // CONFIG_AOM_HIGHBITDEPTH
restore_func_type restore_func =
diff --git a/av1/common/restoration.h b/av1/common/restoration.h
index 0e54a36..167120b 100644
--- a/av1/common/restoration.h
+++ b/av1/common/restoration.h
@@ -21,12 +21,27 @@
extern "C" {
#endif
+#define CLIP(x, lo, hi) ((x) < (lo) ? (lo) : (x) > (hi) ? (hi) : (x))
+#define RINT(x) ((x) < 0 ? (int)((x)-0.5) : (int)((x) + 0.5))
+
#define RESTORATION_TILESIZE_SML 128
#define RESTORATION_TILESIZE_BIG 256
#define RESTORATION_TILEPELS_MAX \
(RESTORATION_TILESIZE_BIG * RESTORATION_TILESIZE_BIG * 9 / 4)
-#define SGRPROJ_TMPBUF_SIZE (RESTORATION_TILEPELS_MAX * 6 * 8)
+#define DOMAINTXFMRF_PARAMS_BITS 6
+#define DOMAINTXFMRF_PARAMS (1 << DOMAINTXFMRF_PARAMS_BITS)
+#define DOMAINTXFMRF_SIGMA_SCALEBITS 4
+#define DOMAINTXFMRF_SIGMA_SCALE (1 << DOMAINTXFMRF_SIGMA_SCALEBITS)
+#define DOMAINTXFMRF_ITERS 3
+#define DOMAINTXFMRF_VTABLE_PRECBITS 8
+#define DOMAINTXFMRF_VTABLE_PREC (1 << DOMAINTXFMRF_VTABLE_PRECBITS)
+#define DOMAINTXFMRF_MULT \
+ sqrt(((1 << (DOMAINTXFMRF_ITERS * 2)) - 1) * 2.0 / 3.0)
+#define DOMAINTXFMRF_TMPBUF_SIZE (RESTORATION_TILEPELS_MAX)
+#define DOMAINTXFMRF_BITS (DOMAINTXFMRF_PARAMS_BITS)
+
+#define SGRPROJ_TMPBUF_SIZE (RESTORATION_TILEPELS_MAX * 6 * 8)
#define SGRPROJ_PARAMS_BITS 3
#define SGRPROJ_PARAMS (1 << SGRPROJ_PARAMS_BITS)
@@ -100,6 +115,11 @@
} SgrprojInfo;
typedef struct {
+ int level;
+ int sigma_r;
+} DomaintxfmrfInfo;
+
+typedef struct {
RestorationType frame_restoration_type;
RestorationType *restoration_type;
// Bilateral filter
@@ -108,6 +128,8 @@
WienerInfo *wiener_info;
// Selfguided proj filter
SgrprojInfo *sgrproj_info;
+ // Domain transform filter
+ DomaintxfmrfInfo *domaintxfmrf_info;
} RestorationInfo;
typedef struct {
@@ -181,6 +203,12 @@
void av1_selfguided_restoration(int64_t *dgd, int width, int height, int stride,
int bit_depth, int r, int eps, void *tmpbuf);
+void av1_domaintxfmrf_restoration(uint8_t *dgd, int width, int height,
+ int stride, int param);
+#if CONFIG_AOM_HIGHBITDEPTH
+void av1_domaintxfmrf_restoration_highbd(uint16_t *dgd, int width, int height,
+ int stride, int param, int bit_depth);
+#endif // CONFIG_AOM_HIGHBITDEPTH
void decode_xq(int *xqd, int *xq);
int av1_bilateral_level_bits(const struct AV1Common *const cm);
void av1_loop_restoration_init(RestorationInternal *rst, RestorationInfo *rsi,
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index aa5abcb..b44d5be 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -2317,7 +2317,9 @@
rsi->frame_restoration_type =
(aom_rb_read_bit(rb) ? RESTORE_WIENER : RESTORE_BILATERAL);
else
- rsi->frame_restoration_type = RESTORE_SGRPROJ;
+ rsi->frame_restoration_type =
+ (aom_rb_read_bit(rb) ? RESTORE_DOMAINTXFMRF : RESTORE_SGRPROJ);
+ // rsi->frame_restoration_type = RESTORE_SGRPROJ;
} else {
rsi->frame_restoration_type =
aom_rb_read_bit(rb) ? RESTORE_SWITCHABLE : RESTORE_NONE;
@@ -2353,6 +2355,12 @@
aom_read_literal(rb, SGRPROJ_PRJ_BITS, ACCT_STR) + SGRPROJ_PRJ_MIN1;
}
+static void read_domaintxfmrf_filter(DomaintxfmrfInfo *domaintxfmrf_info,
+ aom_reader *rb) {
+ domaintxfmrf_info->sigma_r =
+ aom_read_literal(rb, DOMAINTXFMRF_PARAMS_BITS, ACCT_STR);
+}
+
static void read_bilateral_filter(const AV1_COMMON *cm,
BilateralInfo *bilateral_info,
aom_reader *rb) {
@@ -2385,6 +2393,9 @@
rsi->sgrproj_info = (SgrprojInfo *)aom_realloc(
rsi->sgrproj_info, sizeof(*rsi->sgrproj_info) * ntiles);
assert(rsi->sgrproj_info != NULL);
+ rsi->domaintxfmrf_info = (DomaintxfmrfInfo *)aom_realloc(
+ rsi->domaintxfmrf_info, sizeof(*rsi->domaintxfmrf_info) * ntiles);
+ assert(rsi->domaintxfmrf_info != NULL);
for (i = 0; i < ntiles; ++i) {
rsi->restoration_type[i] =
aom_read_tree(rb, av1_switchable_restore_tree,
@@ -2402,6 +2413,9 @@
} else if (rsi->restoration_type[i] == RESTORE_SGRPROJ) {
rsi->sgrproj_info[i].level = 1;
read_sgrproj_filter(&rsi->sgrproj_info[i], rb);
+ } else if (rsi->restoration_type[i] == RESTORE_DOMAINTXFMRF) {
+ rsi->domaintxfmrf_info[i].level = 1;
+ read_domaintxfmrf_filter(&rsi->domaintxfmrf_info[i], rb);
}
}
} else if (rsi->frame_restoration_type == RESTORE_WIENER) {
@@ -2440,6 +2454,20 @@
rsi->restoration_type[i] = RESTORE_NONE;
}
}
+ } else if (rsi->frame_restoration_type == RESTORE_DOMAINTXFMRF) {
+ rsi->domaintxfmrf_info = (DomaintxfmrfInfo *)aom_realloc(
+ rsi->domaintxfmrf_info, sizeof(*rsi->domaintxfmrf_info) * ntiles);
+ assert(rsi->domaintxfmrf_info != NULL);
+ for (i = 0; i < ntiles; ++i) {
+ if (aom_read(rb, RESTORE_NONE_DOMAINTXFMRF_PROB, ACCT_STR)) {
+ rsi->restoration_type[i] = RESTORE_DOMAINTXFMRF;
+ rsi->domaintxfmrf_info[i].level = 1;
+ read_domaintxfmrf_filter(&rsi->domaintxfmrf_info[i], rb);
+ } else {
+ rsi->domaintxfmrf_info[i].level = 0;
+ rsi->restoration_type[i] = RESTORE_NONE;
+ }
+ }
}
}
}
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 7817204..e64414d 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -2986,9 +2986,21 @@
aom_wb_write_bit(wb, 0);
aom_wb_write_bit(wb, 1);
break;
+ /*
+ case RESTORE_SGRPROJ:
+ aom_wb_write_bit(wb, 1);
+ aom_wb_write_bit(wb, 0);
+ break;
+ */
case RESTORE_SGRPROJ:
aom_wb_write_bit(wb, 1);
aom_wb_write_bit(wb, 0);
+ aom_wb_write_bit(wb, 0);
+ break;
+ case RESTORE_DOMAINTXFMRF:
+ aom_wb_write_bit(wb, 1);
+ aom_wb_write_bit(wb, 0);
+ aom_wb_write_bit(wb, 1);
break;
case RESTORE_BILATERAL:
aom_wb_write_bit(wb, 1);
@@ -3027,6 +3039,11 @@
SGRPROJ_PRJ_BITS);
}
+static void write_domaintxfmrf_filter(DomaintxfmrfInfo *domaintxfmrf_info,
+ aom_writer *wb) {
+ aom_write_literal(wb, domaintxfmrf_info->sigma_r, DOMAINTXFMRF_PARAMS_BITS);
+}
+
static void write_bilateral_filter(const AV1_COMMON *cm,
BilateralInfo *bilateral_info,
aom_writer *wb) {
@@ -3061,6 +3078,8 @@
write_wiener_filter(&rsi->wiener_info[i], wb);
} else if (rsi->restoration_type[i] == RESTORE_SGRPROJ) {
write_sgrproj_filter(&rsi->sgrproj_info[i], wb);
+ } else if (rsi->restoration_type[i] == RESTORE_DOMAINTXFMRF) {
+ write_domaintxfmrf_filter(&rsi->domaintxfmrf_info[i], wb);
}
}
} else if (rsi->frame_restoration_type == RESTORE_BILATERAL) {
@@ -3082,6 +3101,14 @@
write_sgrproj_filter(&rsi->sgrproj_info[i], wb);
}
}
+ } else if (rsi->frame_restoration_type == RESTORE_DOMAINTXFMRF) {
+ for (i = 0; i < cm->rst_internal.ntiles; ++i) {
+ aom_write(wb, rsi->domaintxfmrf_info[i].level != 0,
+ RESTORE_NONE_DOMAINTXFMRF_PROB);
+ if (rsi->domaintxfmrf_info[i].level) {
+ write_domaintxfmrf_filter(&rsi->domaintxfmrf_info[i], wb);
+ }
+ }
}
}
}
diff --git a/av1/encoder/pickrst.c b/av1/encoder/pickrst.c
index 84d66af..07e738d 100644
--- a/av1/encoder/pickrst.c
+++ b/av1/encoder/pickrst.c
@@ -34,7 +34,8 @@
int partial_frame, RestorationInfo *info,
double *best_tile_cost);
-const int frame_level_restore_bits[RESTORE_TYPES] = { 2, 2, 3, 3, 2 };
+// const int frame_level_restore_bits[RESTORE_TYPES] = { 2, 2, 3, 3, 2 };
+const int frame_level_restore_bits[RESTORE_TYPES] = { 2, 3, 3, 3, 3, 2 };
static int64_t sse_restoration_tile(const YV12_BUFFER_CONFIG *src,
AV1_COMMON *const cm, int h_start,
@@ -323,6 +324,243 @@
return cost_sgrproj;
}
+static int64_t compute_sse(uint8_t *dgd, int width, int height, int dgd_stride,
+ uint8_t *src, int src_stride) {
+ int64_t sse = 0;
+ int i, j;
+ for (i = 0; i < height; ++i) {
+ for (j = 0; j < width; ++j) {
+ const int diff =
+ (int)dgd[i * dgd_stride + j] - (int)src[i * src_stride + j];
+ sse += diff * diff;
+ }
+ }
+ return sse;
+}
+
+#if CONFIG_AOM_HIGHBITDEPTH
+static int64_t compute_sse_highbd(uint16_t *dgd, int width, int height,
+ int dgd_stride, uint16_t *src,
+ int src_stride) {
+ int64_t sse = 0;
+ int i, j;
+ for (i = 0; i < height; ++i) {
+ for (j = 0; j < width; ++j) {
+ const int diff =
+ (int)dgd[i * dgd_stride + j] - (int)src[i * src_stride + j];
+ sse += diff * diff;
+ }
+ }
+ return sse;
+}
+#endif // CONFIG_AOM_HIGHBITDEPTH
+
+static void search_domaintxfmrf_restoration(uint8_t *dgd8, int width,
+ int height, int dgd_stride,
+ uint8_t *src8, int src_stride,
+ int bit_depth, int *sigma_r) {
+ const int first_p_step = 8;
+ const int second_p_range = first_p_step >> 1;
+ const int second_p_step = 2;
+ const int third_p_range = second_p_step >> 1;
+ const int third_p_step = 1;
+ int i, p, best_p0, best_p = -1;
+ int64_t best_sse = INT64_MAX, sse;
+ if (bit_depth == 8) {
+ uint8_t *tmp = (uint8_t *)aom_malloc(width * height * sizeof(*tmp));
+ uint8_t *dgd = dgd8;
+ uint8_t *src = src8;
+ // First phase
+ for (p = first_p_step / 2; p < DOMAINTXFMRF_PARAMS; p += first_p_step) {
+ for (i = 0; i < height; ++i) {
+ memcpy(&tmp[i * width], &dgd[i * dgd_stride], width * sizeof(dgd[0]));
+ }
+ av1_domaintxfmrf_restoration(tmp, width, height, width, p);
+ sse = compute_sse(tmp, width, height, width, src, src_stride);
+ if (sse < best_sse || best_p == -1) {
+ best_p = p;
+ best_sse = sse;
+ }
+ }
+ // Second Phase
+ best_p0 = best_p;
+ for (p = best_p0 - second_p_range; p <= best_p0 + second_p_range;
+ p += second_p_step) {
+ if (p < 0 || p == best_p || p >= DOMAINTXFMRF_PARAMS) continue;
+ for (i = 0; i < height; ++i) {
+ memcpy(&tmp[i * width], &dgd[i * dgd_stride], width * sizeof(dgd[0]));
+ }
+ av1_domaintxfmrf_restoration(tmp, width, height, width, p);
+ sse = compute_sse(tmp, width, height, width, src, src_stride);
+ if (sse < best_sse) {
+ best_p = p;
+ best_sse = sse;
+ }
+ }
+ // Third Phase
+ best_p0 = best_p;
+ for (p = best_p0 - third_p_range; p <= best_p0 + third_p_range;
+ p += third_p_step) {
+ if (p < 0 || p == best_p || p >= DOMAINTXFMRF_PARAMS) continue;
+ for (i = 0; i < height; ++i) {
+ memcpy(&tmp[i * width], &dgd[i * dgd_stride], width * sizeof(dgd[0]));
+ }
+ av1_domaintxfmrf_restoration(tmp, width, height, width, p);
+ sse = compute_sse(tmp, width, height, width, src, src_stride);
+ if (sse < best_sse) {
+ best_p = p;
+ best_sse = sse;
+ }
+ }
+ aom_free(tmp);
+ } else {
+#if CONFIG_AOM_HIGHBITDEPTH
+ uint16_t *tmp = (uint16_t *)aom_malloc(width * height * sizeof(*tmp));
+ uint16_t *dgd = CONVERT_TO_SHORTPTR(dgd8);
+ uint16_t *src = CONVERT_TO_SHORTPTR(src8);
+ // First phase
+ for (p = first_p_step / 2; p < DOMAINTXFMRF_PARAMS; p += first_p_step) {
+ for (i = 0; i < height; ++i) {
+ memcpy(&tmp[i * width], &dgd[i * dgd_stride], width * sizeof(dgd[0]));
+ }
+ av1_domaintxfmrf_restoration_highbd(tmp, width, height, width, p,
+ bit_depth);
+ sse = compute_sse_highbd(tmp, width, height, width, src, src_stride);
+ if (sse < best_sse || best_p == -1) {
+ best_p = p;
+ best_sse = sse;
+ }
+ }
+ // Second Phase
+ best_p0 = best_p;
+ for (p = best_p0 - second_p_range; p <= best_p0 + second_p_range;
+ p += second_p_step) {
+ if (p < 0 || p == best_p || p >= DOMAINTXFMRF_PARAMS) continue;
+ for (i = 0; i < height; ++i) {
+ memcpy(&tmp[i * width], &dgd[i * dgd_stride], width * sizeof(dgd[0]));
+ }
+ av1_domaintxfmrf_restoration_highbd(tmp, width, height, width, p,
+ bit_depth);
+ sse = compute_sse_highbd(tmp, width, height, width, src, src_stride);
+ if (sse < best_sse) {
+ best_p = p;
+ best_sse = sse;
+ }
+ }
+ // Third Phase
+ best_p0 = best_p;
+ for (p = best_p0 - third_p_range; p <= best_p0 + third_p_range;
+ p += third_p_step) {
+ if (p < 0 || p == best_p || p >= DOMAINTXFMRF_PARAMS) continue;
+ for (i = 0; i < height; ++i) {
+ memcpy(&tmp[i * width], &dgd[i * dgd_stride], width * sizeof(dgd[0]));
+ }
+ av1_domaintxfmrf_restoration_highbd(tmp, width, height, width, p,
+ bit_depth);
+ sse = compute_sse_highbd(tmp, width, height, width, src, src_stride);
+ if (sse < best_sse) {
+ best_p = p;
+ best_sse = sse;
+ }
+ }
+ aom_free(tmp);
+#else
+ assert(0);
+#endif // CONFIG_AOM_HIGHBITDEPTH
+ }
+ *sigma_r = best_p;
+}
+
+static double search_domaintxfmrf(const YV12_BUFFER_CONFIG *src, AV1_COMP *cpi,
+ int filter_level, int partial_frame,
+ RestorationInfo *info,
+ double *best_tile_cost) {
+ DomaintxfmrfInfo *domaintxfmrf_info = info->domaintxfmrf_info;
+ double err, cost_norestore, cost_domaintxfmrf;
+ int bits;
+ MACROBLOCK *x = &cpi->td.mb;
+ AV1_COMMON *const cm = &cpi->common;
+ const YV12_BUFFER_CONFIG *dgd = cm->frame_to_show;
+ RestorationInfo rsi;
+ int tile_idx, tile_width, tile_height, nhtiles, nvtiles;
+ int h_start, h_end, v_start, v_end;
+ const int ntiles = av1_get_rest_ntiles(cm->width, cm->height, &tile_width,
+ &tile_height, &nhtiles, &nvtiles);
+ // Make a copy of the unfiltered / processed recon buffer
+ aom_yv12_copy_y(cm->frame_to_show, &cpi->last_frame_uf);
+ av1_loop_filter_frame(cm->frame_to_show, cm, &cpi->td.mb.e_mbd, filter_level,
+ 1, partial_frame);
+ aom_yv12_copy_y(cm->frame_to_show, &cpi->last_frame_db);
+
+ rsi.frame_restoration_type = RESTORE_DOMAINTXFMRF;
+ rsi.domaintxfmrf_info =
+ (DomaintxfmrfInfo *)aom_malloc(sizeof(*rsi.domaintxfmrf_info) * ntiles);
+ assert(rsi.domaintxfmrf_info != NULL);
+
+ for (tile_idx = 0; tile_idx < ntiles; ++tile_idx)
+ rsi.domaintxfmrf_info[tile_idx].level = 0;
+ // Compute best Domaintxfm filters for each tile
+ for (tile_idx = 0; tile_idx < ntiles; ++tile_idx) {
+ av1_get_rest_tile_limits(tile_idx, 0, 0, nhtiles, nvtiles, tile_width,
+ tile_height, cm->width, cm->height, 0, 0, &h_start,
+ &h_end, &v_start, &v_end);
+ err = sse_restoration_tile(src, cm, h_start, h_end - h_start, v_start,
+ v_end - v_start);
+ // #bits when a tile is not restored
+ bits = av1_cost_bit(RESTORE_NONE_DOMAINTXFMRF_PROB, 0);
+ cost_norestore = RDCOST_DBL(x->rdmult, x->rddiv, (bits >> 4), err);
+ best_tile_cost[tile_idx] = DBL_MAX;
+
+ search_domaintxfmrf_restoration(
+ dgd->y_buffer + v_start * dgd->y_stride + h_start, h_end - h_start,
+ v_end - v_start, dgd->y_stride,
+ src->y_buffer + v_start * src->y_stride + h_start, src->y_stride,
+#if CONFIG_AOM_HIGHBITDEPTH
+ cm->bit_depth,
+#else
+ 8,
+#endif // CONFIG_AOM_HIGHBITDEPTH
+ &rsi.domaintxfmrf_info[tile_idx].sigma_r);
+
+ rsi.domaintxfmrf_info[tile_idx].level = 1;
+ err = try_restoration_tile(src, cpi, &rsi, partial_frame, tile_idx, 0, 0);
+ bits = DOMAINTXFMRF_PARAMS_BITS << AV1_PROB_COST_SHIFT;
+ bits += av1_cost_bit(RESTORE_NONE_DOMAINTXFMRF_PROB, 1);
+ cost_domaintxfmrf = RDCOST_DBL(x->rdmult, x->rddiv, (bits >> 4), err);
+ if (cost_domaintxfmrf >= cost_norestore) {
+ domaintxfmrf_info[tile_idx].level = 0;
+ } else {
+ memcpy(&domaintxfmrf_info[tile_idx], &rsi.domaintxfmrf_info[tile_idx],
+ sizeof(domaintxfmrf_info[tile_idx]));
+ bits = DOMAINTXFMRF_PARAMS_BITS << AV1_PROB_COST_SHIFT;
+ best_tile_cost[tile_idx] = RDCOST_DBL(
+ x->rdmult, x->rddiv,
+ (bits + cpi->switchable_restore_cost[RESTORE_DOMAINTXFMRF]) >> 4,
+ err);
+ }
+ rsi.domaintxfmrf_info[tile_idx].level = 0;
+ }
+ // Cost for Domaintxfmrf filtering
+ bits = frame_level_restore_bits[rsi.frame_restoration_type]
+ << AV1_PROB_COST_SHIFT;
+ for (tile_idx = 0; tile_idx < ntiles; ++tile_idx) {
+ bits += av1_cost_bit(RESTORE_NONE_DOMAINTXFMRF_PROB,
+ domaintxfmrf_info[tile_idx].level);
+ memcpy(&rsi.domaintxfmrf_info[tile_idx], &domaintxfmrf_info[tile_idx],
+ sizeof(domaintxfmrf_info[tile_idx]));
+ if (domaintxfmrf_info[tile_idx].level) {
+ bits += (DOMAINTXFMRF_PARAMS_BITS << AV1_PROB_COST_SHIFT);
+ }
+ }
+ err = try_restoration_frame(src, cpi, &rsi, partial_frame);
+ cost_domaintxfmrf = RDCOST_DBL(x->rdmult, x->rddiv, (bits >> 4), err);
+
+ aom_free(rsi.domaintxfmrf_info);
+
+ aom_yv12_copy_y(&cpi->last_frame_uf, cm->frame_to_show);
+ return cost_domaintxfmrf;
+}
+
static double search_bilateral(const YV12_BUFFER_CONFIG *src, AV1_COMP *cpi,
int filter_level, int partial_frame,
RestorationInfo *info, double *best_tile_cost) {
@@ -721,9 +959,6 @@
return Score - iScore;
}
-#define CLIP(x, lo, hi) ((x) < (lo) ? (lo) : (x) > (hi) ? (hi) : (x))
-#define RINT(x) ((x) < 0 ? (int)((x)-0.5) : (int)((x) + 0.5))
-
static void quantize_sym_filter(double *f, int *fi) {
int i;
for (i = 0; i < RESTORATION_HALFWIN; ++i) {
@@ -938,7 +1173,8 @@
void av1_pick_filter_restoration(const YV12_BUFFER_CONFIG *src, AV1_COMP *cpi,
LPF_PICK_METHOD method) {
static search_restore_type search_restore_fun[RESTORE_SWITCHABLE_TYPES] = {
- search_norestore, search_sgrproj, search_bilateral, search_wiener,
+ search_norestore, search_sgrproj, search_bilateral,
+ search_wiener, search_domaintxfmrf,
};
AV1_COMMON *const cm = &cpi->common;
struct loopfilter *const lf = &cm->lf;
@@ -962,6 +1198,10 @@
cm->rst_info.sgrproj_info = (SgrprojInfo *)aom_realloc(
cm->rst_info.sgrproj_info, sizeof(*cm->rst_info.sgrproj_info) * ntiles);
assert(cm->rst_info.sgrproj_info != NULL);
+ cm->rst_info.domaintxfmrf_info = (DomaintxfmrfInfo *)aom_realloc(
+ cm->rst_info.domaintxfmrf_info,
+ sizeof(*cm->rst_info.domaintxfmrf_info) * ntiles);
+ assert(cm->rst_info.domaintxfmrf_info != NULL);
for (r = 0; r < RESTORE_SWITCHABLE_TYPES; r++)
tile_cost[r] = (double *)aom_malloc(sizeof(*tile_cost[0]) * ntiles);
@@ -1024,10 +1264,10 @@
}
cm->rst_info.frame_restoration_type = best_restore;
/*
- printf("Frame %d/%d frame_restore_type %d : %f %f %f %f %f\n",
+ printf("Frame %d/%d frame_restore_type %d : %f %f %f %f %f %f\n",
cm->current_video_frame, cm->show_frame,
cm->rst_info.frame_restoration_type, cost_restore[0], cost_restore[1],
- cost_restore[2], cost_restore[3], cost_restore[4]);
+ cost_restore[2], cost_restore[3], cost_restore[4], cost_restore[5]);
*/
for (r = 0; r < RESTORE_SWITCHABLE_TYPES; r++) aom_free(tile_cost[r]);
}