Commit bb8ca088 authored by Yaowu Xu's avatar Yaowu Xu

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
parent 00380700
......@@ -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
......@@ -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);
}
}
......
......@@ -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);
}
}
......
......@@ -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) {
......
......@@ -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);
}
......@@ -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,
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment