From: Yaowu Xu Date: Fri, 5 Feb 2016 21:03:47 +0000 (-0800) Subject: Enable computing PSNRHVS for hbd build X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bb8ca088162144cf092155993a0aa6b64b0aed46;p=libvpx Enable computing PSNRHVS for hbd build 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 --- diff --git a/test/hbd_metrics_test.cc b/test/hbd_metrics_test.cc index fa9cfc158..bf75a2912 100644 --- a/test/hbd_metrics_test.cc +++ b/test/hbd_metrics_test.cc @@ -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 diff --git a/vp10/encoder/encoder.c b/vp10/encoder/encoder.c index 8cd677b9e..5165e9af1 100644 --- a/vp10/encoder/encoder.c +++ b/vp10/encoder/encoder.c @@ -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); } } diff --git a/vp9/encoder/vp9_encoder.c b/vp9/encoder/vp9_encoder.c index 3067afea7..5c67f51df 100644 --- a/vp9/encoder/vp9_encoder.c +++ b/vp9/encoder/vp9_encoder.c @@ -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); } } diff --git a/vpx_dsp/fastssim.c b/vpx_dsp/fastssim.c index 590df9510..ad1fe5e71 100644 --- a/vpx_dsp/fastssim.c +++ b/vpx_dsp/fastssim.c @@ -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) { diff --git a/vpx_dsp/psnrhvs.c b/vpx_dsp/psnrhvs.c index 083cc806a..4d3d6eea0 100644 --- a/vpx_dsp/psnrhvs.c +++ b/vpx_dsp/psnrhvs.c @@ -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); } + diff --git a/vpx_dsp/ssim.h b/vpx_dsp/ssim.h index b1b64301a..6c59540ea 100644 --- a/vpx_dsp/ssim.h +++ b/vpx_dsp/ssim.h @@ -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, diff --git a/vpx_ports/mem.h b/vpx_ports/mem.h index ec7c91b86..ae4aec808 100644 --- a/vpx_ports/mem.h +++ b/vpx_ports/mem.h @@ -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