2 * Copyright (c) 2010 The WebM project authors. All Rights Reserved.
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
10 * This code was originally written by: Gregory Maxwell, at the Daala
17 #include "./vpx_config.h"
18 #include "./vpx_dsp_rtcd.h"
19 #include "vpx_dsp/ssim.h"
22 # define M_PI (3.141592653589793238462643)
26 static void od_bin_fdct8x8(tran_low_t *y, int ystride, const int16_t *x,
29 vpx_fdct8x8(x, y, ystride);
32 /* Normalized inverse quantization matrix for 8x8 DCT at the point of
33 * transparency. This is not the JPEG based matrix from the paper,
34 this one gives a slightly higher MOS agreement.*/
35 static const float csf_y[8][8] = {
36 {1.6193873005, 2.2901594831, 2.08509755623, 1.48366094411, 1.00227514334,
37 0.678296995242, 0.466224900598, 0.3265091542},
38 {2.2901594831, 1.94321815382, 2.04793073064, 1.68731108984, 1.2305666963,
39 0.868920337363, 0.61280991668, 0.436405793551},
40 {2.08509755623, 2.04793073064, 1.34329019223, 1.09205635862, 0.875748795257,
41 0.670882927016, 0.501731932449, 0.372504254596},
42 {1.48366094411, 1.68731108984, 1.09205635862, 0.772819797575,
43 0.605636379554, 0.48309405692, 0.380429446972, 0.295774038565},
44 {1.00227514334, 1.2305666963, 0.875748795257, 0.605636379554,
45 0.448996256676, 0.352889268808, 0.283006984131, 0.226951348204},
46 {0.678296995242, 0.868920337363, 0.670882927016, 0.48309405692,
47 0.352889268808, 0.27032073436, 0.215017739696, 0.17408067321},
48 {0.466224900598, 0.61280991668, 0.501731932449, 0.380429446972,
49 0.283006984131, 0.215017739696, 0.168869545842, 0.136153931001},
50 {0.3265091542, 0.436405793551, 0.372504254596, 0.295774038565,
51 0.226951348204, 0.17408067321, 0.136153931001, 0.109083846276}};
52 static const float csf_cb420[8][8] = {
53 {1.91113096927, 2.46074210438, 1.18284184739, 1.14982565193, 1.05017074788,
54 0.898018824055, 0.74725392039, 0.615105596242},
55 {2.46074210438, 1.58529308355, 1.21363250036, 1.38190029285, 1.33100189972,
56 1.17428548929, 0.996404342439, 0.830890433625},
57 {1.18284184739, 1.21363250036, 0.978712413627, 1.02624506078, 1.03145147362,
58 0.960060382087, 0.849823426169, 0.731221236837},
59 {1.14982565193, 1.38190029285, 1.02624506078, 0.861317501629,
60 0.801821139099, 0.751437590932, 0.685398513368, 0.608694761374},
61 {1.05017074788, 1.33100189972, 1.03145147362, 0.801821139099,
62 0.676555426187, 0.605503172737, 0.55002013668, 0.495804539034},
63 {0.898018824055, 1.17428548929, 0.960060382087, 0.751437590932,
64 0.605503172737, 0.514674450957, 0.454353482512, 0.407050308965},
65 {0.74725392039, 0.996404342439, 0.849823426169, 0.685398513368,
66 0.55002013668, 0.454353482512, 0.389234902883, 0.342353999733},
67 {0.615105596242, 0.830890433625, 0.731221236837, 0.608694761374,
68 0.495804539034, 0.407050308965, 0.342353999733, 0.295530605237}};
69 static const float csf_cr420[8][8] = {
70 {2.03871978502, 2.62502345193, 1.26180942886, 1.11019789803, 1.01397751469,
71 0.867069376285, 0.721500455585, 0.593906509971},
72 {2.62502345193, 1.69112867013, 1.17180569821, 1.3342742857, 1.28513006198,
73 1.13381474809, 0.962064122248, 0.802254508198},
74 {1.26180942886, 1.17180569821, 0.944981930573, 0.990876405848,
75 0.995903384143, 0.926972725286, 0.820534991409, 0.706020324706},
76 {1.11019789803, 1.3342742857, 0.990876405848, 0.831632933426, 0.77418706195,
77 0.725539939514, 0.661776842059, 0.587716619023},
78 {1.01397751469, 1.28513006198, 0.995903384143, 0.77418706195,
79 0.653238524286, 0.584635025748, 0.531064164893, 0.478717061273},
80 {0.867069376285, 1.13381474809, 0.926972725286, 0.725539939514,
81 0.584635025748, 0.496936637883, 0.438694579826, 0.393021669543},
82 {0.721500455585, 0.962064122248, 0.820534991409, 0.661776842059,
83 0.531064164893, 0.438694579826, 0.375820256136, 0.330555063063},
84 {0.593906509971, 0.802254508198, 0.706020324706, 0.587716619023,
85 0.478717061273, 0.393021669543, 0.330555063063, 0.285345396658}};
87 static double convert_score_db(double _score, double _weight) {
88 return 10 * (log10(255 * 255) - log10(_weight * _score));
91 static double calc_psnrhvs(const unsigned char *_src, int _systride,
92 const unsigned char *_dst, int _dystride,
93 double _par, int _w, int _h, int _step,
94 const float _csf[8][8]) {
96 int16_t dct_s[8 * 8], dct_d[8 * 8];
97 tran_low_t dct_s_coef[8 * 8], dct_d_coef[8 * 8];
104 /*In the PSNR-HVS-M paper[1] the authors describe the construction of
105 their masking table as "we have used the quantization table for the
106 color component Y of JPEG [6] that has been also obtained on the
107 basis of CSF. Note that the values in quantization table JPEG have
108 been normalized and then squared." Their CSF matrix (from PSNR-HVS)
109 was also constructed from the JPEG matrices. I can not find any obvious
110 scheme of normalizing to produce their table, but if I multiply their
111 CSF by 0.38857 and square the result I get their masking table.
112 I have no idea where this constant comes from, but deviating from it
113 too greatly hurts MOS agreement.
115 [1] Nikolay Ponomarenko, Flavia Silvestri, Karen Egiazarian, Marco Carli,
116 Jaakko Astola, Vladimir Lukin, "On between-coefficient contrast masking
117 of DCT basis functions", CD-ROM Proceedings of the Third
118 International Workshop on Video Processing and Quality Metrics for Consumer
119 Electronics VPQM-07, Scottsdale, Arizona, USA, 25-26 January, 2007, 4 p.*/
120 for (x = 0; x < 8; x++)
121 for (y = 0; y < 8; y++)
122 mask[x][y] = (_csf[x][y] * 0.3885746225901003)
123 * (_csf[x][y] * 0.3885746225901003);
124 for (y = 0; y < _h - 7; y += _step) {
125 for (x = 0; x < _w - 7; x += _step) {
138 for (i = 0; i < 4; i++)
139 s_means[i] = d_means[i] = s_vars[i] = d_vars[i] = 0;
140 for (i = 0; i < 8; i++) {
141 for (j = 0; j < 8; j++) {
142 int sub = ((i & 12) >> 2) + ((j & 12) >> 1);
143 dct_s[i * 8 + j] = _src[(y + i) * _systride + (j + x)];
144 dct_d[i * 8 + j] = _dst[(y + i) * _dystride + (j + x)];
145 s_gmean += dct_s[i * 8 + j];
146 d_gmean += dct_d[i * 8 + j];
147 s_means[sub] += dct_s[i * 8 + j];
148 d_means[sub] += dct_d[i * 8 + j];
153 for (i = 0; i < 4; i++)
155 for (i = 0; i < 4; i++)
157 for (i = 0; i < 8; i++) {
158 for (j = 0; j < 8; j++) {
159 int sub = ((i & 12) >> 2) + ((j & 12) >> 1);
160 s_gvar += (dct_s[i * 8 + j] - s_gmean) * (dct_s[i * 8 + j] - s_gmean);
161 d_gvar += (dct_d[i * 8 + j] - d_gmean) * (dct_d[i * 8 + j] - d_gmean);
162 s_vars[sub] += (dct_s[i * 8 + j] - s_means[sub])
163 * (dct_s[i * 8 + j] - s_means[sub]);
164 d_vars[sub] += (dct_d[i * 8 + j] - d_means[sub])
165 * (dct_d[i * 8 + j] - d_means[sub]);
168 s_gvar *= 1 / 63.f * 64;
169 d_gvar *= 1 / 63.f * 64;
170 for (i = 0; i < 4; i++)
171 s_vars[i] *= 1 / 15.f * 16;
172 for (i = 0; i < 4; i++)
173 d_vars[i] *= 1 / 15.f * 16;
175 s_gvar = (s_vars[0] + s_vars[1] + s_vars[2] + s_vars[3]) / s_gvar;
177 d_gvar = (d_vars[0] + d_vars[1] + d_vars[2] + d_vars[3]) / d_gvar;
178 od_bin_fdct8x8(dct_s_coef, 8, dct_s, 8);
179 od_bin_fdct8x8(dct_d_coef, 8, dct_d, 8);
180 for (i = 0; i < 8; i++)
181 for (j = (i == 0); j < 8; j++)
182 s_mask += dct_s_coef[i * 8 + j] * dct_s_coef[i * 8 + j] * mask[i][j];
183 for (i = 0; i < 8; i++)
184 for (j = (i == 0); j < 8; j++)
185 d_mask += dct_d_coef[i * 8 + j] * dct_d_coef[i * 8 + j] * mask[i][j];
186 s_mask = sqrt(s_mask * s_gvar) / 32.f;
187 d_mask = sqrt(d_mask * d_gvar) / 32.f;
190 for (i = 0; i < 8; i++) {
191 for (j = 0; j < 8; j++) {
193 err = fabs(dct_s_coef[i * 8 + j] - dct_d_coef[i * 8 + j]);
194 if (i != 0 || j != 0)
195 err = err < s_mask / mask[i][j] ? 0 : err - s_mask / mask[i][j];
196 ret += (err * _csf[i][j]) * (err * _csf[i][j]);
205 double vpx_psnrhvs(const YV12_BUFFER_CONFIG *source,
206 const YV12_BUFFER_CONFIG *dest, double *y_psnrhvs,
207 double *u_psnrhvs, double *v_psnrhvs) {
209 const double par = 1.0;
211 vpx_clear_system_state();
212 *y_psnrhvs = calc_psnrhvs(source->y_buffer, source->y_stride, dest->y_buffer,
213 dest->y_stride, par, source->y_crop_width,
214 source->y_crop_height, step, csf_y);
216 *u_psnrhvs = calc_psnrhvs(source->u_buffer, source->uv_stride, dest->u_buffer,
217 dest->uv_stride, par, source->uv_crop_width,
218 source->uv_crop_height, step, csf_cb420);
220 *v_psnrhvs = calc_psnrhvs(source->v_buffer, source->uv_stride, dest->v_buffer,
221 dest->uv_stride, par, source->uv_crop_width,
222 source->uv_crop_height, step, csf_cr420);
223 psnrhvs = (*y_psnrhvs) * .8 + .1 * ((*u_psnrhvs) + (*v_psnrhvs));
225 return convert_score_db(psnrhvs, 1.0);