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]);
 }