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