]> granicus.if.org Git - libvpx/commitdiff
Enable computing PSNRHVS for hbd build
authorYaowu Xu <yaowu@google.com>
Fri, 5 Feb 2016 21:03:47 +0000 (13:03 -0800)
committerYaowu Xu <yaowu@google.com>
Thu, 11 Feb 2016 21:17:59 +0000 (13:17 -0800)
This commit adds computation of PSNRHVS for highbitdepth build, it
also adds tests to make sure the calculation of psnrhvs metric for
10 and 12 bit correct.

Change-Id: Iac8a8073d2b3e3ba5d368829d770793212fa63b6

test/hbd_metrics_test.cc
vp10/encoder/encoder.c
vp9/encoder/vp9_encoder.c
vpx_dsp/fastssim.c
vpx_dsp/psnrhvs.c
vpx_dsp/ssim.h
vpx_ports/mem.h

index fa9cfc158d599013fd4228de0eb61ef59c639d86..bf75a2912eed76bd31f6e9008be90742401f4c52 100644 (file)
@@ -32,6 +32,22 @@ typedef double (*HBDMetricFunc)(const YV12_BUFFER_CONFIG *source,
                                 const YV12_BUFFER_CONFIG *dest,
                                 uint32_t bd);
 
+
+double compute_hbd_psnrhvs(const YV12_BUFFER_CONFIG *source,
+  const YV12_BUFFER_CONFIG *dest,
+  uint32_t bit_depth) {
+  double tempy, tempu, tempv;
+  return vpx_psnrhvs(source, dest,
+                     &tempy, &tempu, &tempv, bit_depth);
+}
+
+double compute_psnrhvs(const YV12_BUFFER_CONFIG *source,
+  const YV12_BUFFER_CONFIG *dest) {
+  double tempy, tempu, tempv;
+  return vpx_psnrhvs(source, dest,
+                     &tempy, &tempu, &tempv, 8);
+}
+
 double compute_hbd_fastssim(const YV12_BUFFER_CONFIG *source,
                             const YV12_BUFFER_CONFIG *dest,
                             uint32_t bit_depth) {
@@ -62,6 +78,7 @@ double compute_vpxssim(const YV12_BUFFER_CONFIG *source,
   return 100 * pow(ssim / weight, 8.0);
 }
 
+
 class HBDMetricsTestBase {
  public:
   virtual ~HBDMetricsTestBase() {}
@@ -101,16 +118,40 @@ class HBDMetricsTestBase {
 
     lbd_db = lbd_metric_(&lbd_src, &lbd_dst);
     hbd_db = hbd_metric_(&hbd_src, &hbd_dst, bit_depth_);
+    EXPECT_LE(fabs(lbd_db - hbd_db), threshold_);
+
+    i = 0;
+    while (i < lbd_src.buffer_alloc_sz) {
+      uint16_t dpel;
+      // Create some small distortion for dst buffer.
+      dpel = 120 + (rnd.Rand8() >> 4);
+      lbd_dst.buffer_alloc[i] = (uint8_t)dpel;
+      ((uint16_t*)(hbd_dst.buffer_alloc))[i] = dpel << (bit_depth_ - 8);
+      i++;
+    }
+
+    lbd_db = lbd_metric_(&lbd_src, &lbd_dst);
+    hbd_db = hbd_metric_(&hbd_src, &hbd_dst, bit_depth_);
+    EXPECT_LE(fabs(lbd_db - hbd_db), threshold_);
 
-    printf("%10f \n", lbd_db);
-    printf("%10f \n", hbd_db);
+    i = 0;
+    while (i < lbd_src.buffer_alloc_sz) {
+      uint16_t dpel;
+      // Create some small distortion for dst buffer.
+      dpel = 126 + (rnd.Rand8() >> 6);
+      lbd_dst.buffer_alloc[i] = (uint8_t)dpel;
+      ((uint16_t*)(hbd_dst.buffer_alloc))[i] = dpel << (bit_depth_ - 8);
+      i++;
+    }
+
+    lbd_db = lbd_metric_(&lbd_src, &lbd_dst);
+    hbd_db = hbd_metric_(&hbd_src, &hbd_dst, bit_depth_);
+    EXPECT_LE(fabs(lbd_db - hbd_db), threshold_);
 
     vpx_free_frame_buffer(&lbd_src);
     vpx_free_frame_buffer(&lbd_dst);
     vpx_free_frame_buffer(&hbd_src);
     vpx_free_frame_buffer(&hbd_dst);
-
-    EXPECT_LE(fabs(lbd_db - hbd_db), threshold_);
   }
 
   int bit_depth_;
@@ -140,8 +181,10 @@ TEST_P(HBDMetricsTest, RunAccuracyCheck) {
 
 // Allow small variation due to floating point operations.
 static const double kSsim_thresh = 0.001;
-// Allow some variation from accumulated errors in floating point operations.
-static const double kFSsim_thresh = 0.01;
+// Allow some additional errors accumulated in floating point operations.
+static const double kFSsim_thresh = 0.03;
+// Allow some extra variation due to rounding error accumulated in dct.
+static const double kPhvs_thresh = 0.3;
 
 INSTANTIATE_TEST_CASE_P(
     VPXSSIM, HBDMetricsTest,
@@ -157,5 +200,13 @@ INSTANTIATE_TEST_CASE_P(
                          kFSsim_thresh),
         MetricTestTParam(&compute_fastssim, &compute_hbd_fastssim, 12,
                          kFSsim_thresh)));
+INSTANTIATE_TEST_CASE_P(
+    PSNRHVS, HBDMetricsTest,
+    ::testing::Values(
+        MetricTestTParam(&compute_psnrhvs, &compute_hbd_psnrhvs, 10,
+                         kPhvs_thresh),
+        MetricTestTParam(&compute_psnrhvs, &compute_hbd_psnrhvs, 12,
+                         kPhvs_thresh)));
+
 }  // namespace
 
index 8cd677b9e4ce88fd829aff33485173dae84622a7..5165e9af1c01021aad08f4d8e9663c93d562aaca 100644 (file)
@@ -4509,13 +4509,7 @@ int vp10_get_compressed_data(VP10_COMP *cpi, unsigned int *frame_flags,
         double y, u, v, frame_all;
         frame_all = vpx_calc_fastssim(orig, recon, &y, &u, &v, bit_depth);
         adjust_image_stat(y, u, v, frame_all, &cpi->fastssim);
-      }
-#if CONFIG_VP9_HIGHBITDEPTH
-      if (!cm->use_highbitdepth)
-#endif
-      {
-        double y, u, v, frame_all;
-        frame_all = vpx_psnrhvs(orig, recon, &y, &u, &v);
+        frame_all = vpx_psnrhvs(orig, recon, &y, &u, &v, bit_depth);
         adjust_image_stat(y, u, v, frame_all, &cpi->psnrhvs);
       }
     }
index 3067afea703e7a4c2e2cd417f6fd1d09c088b53b..5c67f51df4a957b8178211fece110b8baf5edbc8 100644 (file)
@@ -4681,12 +4681,10 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags,
                                       &v, bit_depth);
         adjust_image_stat(y, u, v, frame_all, &cpi->fastssim);
       }
-#if CONFIG_VP9_HIGHBITDEPTH
-      if (!cm->use_highbitdepth)
-#endif
       {
         double y, u, v, frame_all;
-        frame_all = vpx_psnrhvs(cpi->Source, cm->frame_to_show, &y, &u, &v);
+        frame_all = vpx_psnrhvs(cpi->Source, cm->frame_to_show, &y, &u, &v,
+                                bit_depth);
         adjust_image_stat(y, u, v, frame_all, &cpi->psnrhvs);
       }
     }
index 590df951083b3f3519859b8dcd6fcd1d25241ae7..ad1fe5e712c2c89314046cb9858f9948ca7cfc74 100644 (file)
@@ -506,8 +506,6 @@ static double calc_ssim(const unsigned char *_src, int _systride,
 }
 
 
-#define CONVERT_TO_SHORTPTR(x) ((uint16_t*)(((uintptr_t)(x)) << 1))
-
 static double calc_hbd_ssim(const uint8_t *_src, int _systride,
                             const uint8_t *_dst, int _dystride,
                             int _w, int _h, uint32_t bit_depth) {
index 083cc806a1026c57f13b48fe009ab1cf77838c80..4d3d6eea001d7fa48a52e32abb203ae27729d428 100644 (file)
@@ -35,6 +35,17 @@ static void od_bin_fdct8x8(tran_low_t *y, int ystride, const int16_t *x,
     for (j = 0; j< 8; j++)
       *(y + ystride*i + j) = (*(y + ystride*i + j) + 4) >> 3;
 }
+#if CONFIG_VP9_HIGHBITDEPTH
+static void hbd_od_bin_fdct8x8(tran_low_t *y, int ystride, const int16_t *x,
+                           int xstride) {
+  int i, j;
+  (void) xstride;
+  vpx_highbd_fdct8x8(x, y, ystride);
+  for (i = 0; i < 8; i++)
+    for (j = 0; j< 8; j++)
+      *(y + ystride*i + j) = (*(y + ystride*i + j) + 4) >> 3;
+}
+#endif
 
 /* Normalized inverse quantization matrix for 8x8 DCT at the point of
  * transparency. This is not the JPEG based matrix from the paper,
@@ -91,18 +102,28 @@ static const double csf_cr420[8][8] = {
     {0.593906509971, 0.802254508198, 0.706020324706, 0.587716619023,
      0.478717061273, 0.393021669543, 0.330555063063, 0.285345396658}};
 
-static double convert_score_db(double _score, double _weight) {
+static double convert_score_db(double _score, double _weight, int bit_depth) {
+  int16_t pix_max = 255;
   assert(_score * _weight >= 0.0);
-  if (_weight * _score < 255 * 255 * 1e-10)
+  if (bit_depth == 10)
+    pix_max = 1023;
+  else if (bit_depth == 12)
+    pix_max = 4095;
+
+  if (_weight * _score < pix_max * pix_max * 1e-10)
     return MAX_PSNR;
-  return 10 * (log10(255 * 255) - log10(_weight * _score));
+  return 10 * (log10(pix_max * pix_max) - log10(_weight * _score));
 }
 
-static double calc_psnrhvs(const unsigned char *_src, int _systride,
-                           const unsigned char *_dst, int _dystride,
-                           double _par, int _w, int _h, int _step,
-                           const double _csf[8][8]) {
+static double calc_psnrhvs(const unsigned char *src, int _systride,
+                               const unsigned char *dst, int _dystride,
+                               double _par, int _w, int _h, int _step,
+                               const double _csf[8][8], uint32_t bit_depth) {
   double ret;
+  const uint8_t *_src8 = src;
+  const uint8_t *_dst8 = dst;
+  const uint16_t *_src16 = CONVERT_TO_SHORTPTR(src);
+  const uint16_t *_dst16 = CONVERT_TO_SHORTPTR(dst);
   int16_t dct_s[8 * 8], dct_d[8 * 8];
   tran_low_t dct_s_coef[8 * 8], dct_d_coef[8 * 8];
   double mask[8][8];
@@ -111,6 +132,7 @@ static double calc_psnrhvs(const unsigned char *_src, int _systride,
   int y;
   (void) _par;
   ret = pixels = 0;
+
   /*In the PSNR-HVS-M paper[1] the authors describe the construction of
    their masking table as "we have used the quantization table for the
    color component Y of JPEG [6] that has been also obtained on the
@@ -150,8 +172,13 @@ static double calc_psnrhvs(const unsigned char *_src, int _systride,
       for (i = 0; i < 8; i++) {
         for (j = 0; j < 8; j++) {
           int sub = ((i & 12) >> 2) + ((j & 12) >> 1);
-          dct_s[i * 8 + j] = _src[(y + i) * _systride + (j + x)];
-          dct_d[i * 8 + j] = _dst[(y + i) * _dystride + (j + x)];
+          if (bit_depth == 8) {
+            dct_s[i * 8 + j] = _src8[(y + i) * _systride + (j + x)];
+            dct_d[i * 8 + j] = _dst8[(y + i) * _dystride + (j + x)];
+          } else if (bit_depth == 10 || bit_depth == 12) {
+            dct_s[i * 8 + j] = _src16[(y + i) * _systride + (j + x)];
+            dct_d[i * 8 + j] = _dst16[(y + i) * _dystride + (j + x)];
+          }
           s_gmean += dct_s[i * 8 + j];
           d_gmean += dct_d[i * 8 + j];
           s_means[sub] += dct_s[i * 8 + j];
@@ -185,8 +212,16 @@ static double calc_psnrhvs(const unsigned char *_src, int _systride,
         s_gvar = (s_vars[0] + s_vars[1] + s_vars[2] + s_vars[3]) / s_gvar;
       if (d_gvar > 0)
         d_gvar = (d_vars[0] + d_vars[1] + d_vars[2] + d_vars[3]) / d_gvar;
-      od_bin_fdct8x8(dct_s_coef, 8, dct_s, 8);
-      od_bin_fdct8x8(dct_d_coef, 8, dct_d, 8);
+#if CONFIG_VP9_HIGHBITDEPTH
+      if (bit_depth == 10 || bit_depth == 12) {
+        hbd_od_bin_fdct8x8(dct_s_coef, 8, dct_s, 8);
+        hbd_od_bin_fdct8x8(dct_d_coef, 8, dct_d, 8);
+      }
+#endif
+      if (bit_depth == 8) {
+        od_bin_fdct8x8(dct_s_coef, 8, dct_s, 8);
+        od_bin_fdct8x8(dct_d_coef, 8, dct_d, 8);
+      }
       for (i = 0; i < 8; i++)
         for (j = (i == 0); j < 8; j++)
           s_mask += dct_s_coef[i * 8 + j] * dct_s_coef[i * 8 + j] * mask[i][j];
@@ -200,7 +235,7 @@ static double calc_psnrhvs(const unsigned char *_src, int _systride,
       for (i = 0; i < 8; i++) {
         for (j = 0; j < 8; j++) {
           double err;
-          err = fabs((float)(dct_s_coef[i * 8 + j] - dct_d_coef[i * 8 + j]));
+          err = fabs((double)(dct_s_coef[i * 8 + j] - dct_d_coef[i * 8 + j]));
           if (i != 0 || j != 0)
             err = err < s_mask / mask[i][j] ? 0 : err - s_mask / mask[i][j];
           ret += (err * _csf[i][j]) * (err * _csf[i][j]);
@@ -212,25 +247,28 @@ static double calc_psnrhvs(const unsigned char *_src, int _systride,
   ret /= pixels;
   return ret;
 }
+
 double vpx_psnrhvs(const YV12_BUFFER_CONFIG *source,
                    const YV12_BUFFER_CONFIG *dest, double *y_psnrhvs,
-                   double *u_psnrhvs, double *v_psnrhvs) {
+                   double *u_psnrhvs, double *v_psnrhvs, uint32_t bit_depth) {
   double psnrhvs;
   const double par = 1.0;
   const int step = 7;
   vpx_clear_system_state();
+
+  assert(bit_depth == 8 || bit_depth == 10 || bit_depth == 12);
+
   *y_psnrhvs = calc_psnrhvs(source->y_buffer, source->y_stride, dest->y_buffer,
                             dest->y_stride, par, source->y_crop_width,
-                            source->y_crop_height, step, csf_y);
-
+                            source->y_crop_height, step, csf_y, bit_depth);
   *u_psnrhvs = calc_psnrhvs(source->u_buffer, source->uv_stride, dest->u_buffer,
                             dest->uv_stride, par, source->uv_crop_width,
-                            source->uv_crop_height, step, csf_cb420);
+                            source->uv_crop_height, step, csf_cb420, bit_depth);
 
   *v_psnrhvs = calc_psnrhvs(source->v_buffer, source->uv_stride, dest->v_buffer,
                             dest->uv_stride, par, source->uv_crop_width,
-                            source->uv_crop_height, step, csf_cr420);
+                            source->uv_crop_height, step, csf_cr420, bit_depth);
   psnrhvs = (*y_psnrhvs) * .8 + .1 * ((*u_psnrhvs) + (*v_psnrhvs));
-
-  return convert_score_db(psnrhvs, 1.0);
+  return convert_score_db(psnrhvs, 1.0, bit_depth);
 }
+
index b1b64301a13bac60f12510b3c9b0a4a5683cf411..6c59540ead605c8a834ba6aebc3faf9738e71013 100644 (file)
@@ -77,7 +77,8 @@ double vpx_calc_fastssim(const YV12_BUFFER_CONFIG *source,
 
 double vpx_psnrhvs(const YV12_BUFFER_CONFIG *source,
                    const YV12_BUFFER_CONFIG *dest,
-                   double *ssim_y, double *ssim_u, double *ssim_v);
+                   double *phvs_y, double *phvs_u,
+                   double *phvs_v, uint32_t bit_depth);
 
 #if CONFIG_VP9_HIGHBITDEPTH
 double vpx_highbd_calc_ssim(const YV12_BUFFER_CONFIG *source,
index ec7c91b86ad72e515aa5eb453ffa23ba83afa956..ae4aec808ddd5df27978c229e819d2d6f1c87afc 100644 (file)
@@ -45,8 +45,8 @@
 #define ALIGN_POWER_OF_TWO(value, n) \
     (((value) + ((1 << (n)) - 1)) & ~((1 << (n)) - 1))
 
-#if CONFIG_VP9_HIGHBITDEPTH
 #define CONVERT_TO_SHORTPTR(x) ((uint16_t*)(((uintptr_t)(x)) << 1))
+#if CONFIG_VP9_HIGHBITDEPTH
 #define CONVERT_TO_BYTEPTR(x) ((uint8_t*)(((uintptr_t)(x)) >> 1))
 #endif  // CONFIG_VP9_HIGHBITDEPTH