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_); - 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 = 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_); + + 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