1 /*
2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10 *
11 * This code was originally written by: Gregory Maxwell, at the Daala
12 * project.
13 */
14
15 #include <assert.h>
16 #include <math.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19
20 #include "config/aom_config.h"
21 #include "config/aom_dsp_rtcd.h"
22
23 #include "aom_dsp/psnr.h"
24 #include "aom_dsp/ssim.h"
25
od_bin_fdct8x8(tran_low_t * y,int ystride,const int16_t * x,int xstride)26 static void od_bin_fdct8x8(tran_low_t *y, int ystride, const int16_t *x,
27 int xstride) {
28 int i, j;
29 (void)xstride;
30 aom_fdct8x8(x, y, ystride);
31 for (i = 0; i < 8; i++)
32 for (j = 0; j < 8; j++)
33 *(y + ystride * i + j) = (*(y + ystride * i + j) + 4) >> 3;
34 }
35
36 #if CONFIG_AV1_HIGHBITDEPTH
hbd_od_bin_fdct8x8(tran_low_t * y,int ystride,const int16_t * x,int xstride)37 static void hbd_od_bin_fdct8x8(tran_low_t *y, int ystride, const int16_t *x,
38 int xstride) {
39 int i, j;
40 (void)xstride;
41 aom_highbd_fdct8x8(x, y, ystride);
42 for (i = 0; i < 8; i++)
43 for (j = 0; j < 8; j++)
44 *(y + ystride * i + j) = (*(y + ystride * i + j) + 4) >> 3;
45 }
46 #endif // CONFIG_AV1_HIGHBITDEPTH
47
48 /* Normalized inverse quantization matrix for 8x8 DCT at the point of
49 * transparency. This is not the JPEG based matrix from the paper,
50 this one gives a slightly higher MOS agreement.*/
51 static const double csf_y[8][8] = {
52 { 1.6193873005, 2.2901594831, 2.08509755623, 1.48366094411, 1.00227514334,
53 0.678296995242, 0.466224900598, 0.3265091542 },
54 { 2.2901594831, 1.94321815382, 2.04793073064, 1.68731108984, 1.2305666963,
55 0.868920337363, 0.61280991668, 0.436405793551 },
56 { 2.08509755623, 2.04793073064, 1.34329019223, 1.09205635862, 0.875748795257,
57 0.670882927016, 0.501731932449, 0.372504254596 },
58 { 1.48366094411, 1.68731108984, 1.09205635862, 0.772819797575, 0.605636379554,
59 0.48309405692, 0.380429446972, 0.295774038565 },
60 { 1.00227514334, 1.2305666963, 0.875748795257, 0.605636379554, 0.448996256676,
61 0.352889268808, 0.283006984131, 0.226951348204 },
62 { 0.678296995242, 0.868920337363, 0.670882927016, 0.48309405692,
63 0.352889268808, 0.27032073436, 0.215017739696, 0.17408067321 },
64 { 0.466224900598, 0.61280991668, 0.501731932449, 0.380429446972,
65 0.283006984131, 0.215017739696, 0.168869545842, 0.136153931001 },
66 { 0.3265091542, 0.436405793551, 0.372504254596, 0.295774038565,
67 0.226951348204, 0.17408067321, 0.136153931001, 0.109083846276 }
68 };
69 static const double csf_cb420[8][8] = {
70 { 1.91113096927, 2.46074210438, 1.18284184739, 1.14982565193, 1.05017074788,
71 0.898018824055, 0.74725392039, 0.615105596242 },
72 { 2.46074210438, 1.58529308355, 1.21363250036, 1.38190029285, 1.33100189972,
73 1.17428548929, 0.996404342439, 0.830890433625 },
74 { 1.18284184739, 1.21363250036, 0.978712413627, 1.02624506078, 1.03145147362,
75 0.960060382087, 0.849823426169, 0.731221236837 },
76 { 1.14982565193, 1.38190029285, 1.02624506078, 0.861317501629, 0.801821139099,
77 0.751437590932, 0.685398513368, 0.608694761374 },
78 { 1.05017074788, 1.33100189972, 1.03145147362, 0.801821139099, 0.676555426187,
79 0.605503172737, 0.55002013668, 0.495804539034 },
80 { 0.898018824055, 1.17428548929, 0.960060382087, 0.751437590932,
81 0.605503172737, 0.514674450957, 0.454353482512, 0.407050308965 },
82 { 0.74725392039, 0.996404342439, 0.849823426169, 0.685398513368,
83 0.55002013668, 0.454353482512, 0.389234902883, 0.342353999733 },
84 { 0.615105596242, 0.830890433625, 0.731221236837, 0.608694761374,
85 0.495804539034, 0.407050308965, 0.342353999733, 0.295530605237 }
86 };
87 static const double csf_cr420[8][8] = {
88 { 2.03871978502, 2.62502345193, 1.26180942886, 1.11019789803, 1.01397751469,
89 0.867069376285, 0.721500455585, 0.593906509971 },
90 { 2.62502345193, 1.69112867013, 1.17180569821, 1.3342742857, 1.28513006198,
91 1.13381474809, 0.962064122248, 0.802254508198 },
92 { 1.26180942886, 1.17180569821, 0.944981930573, 0.990876405848,
93 0.995903384143, 0.926972725286, 0.820534991409, 0.706020324706 },
94 { 1.11019789803, 1.3342742857, 0.990876405848, 0.831632933426, 0.77418706195,
95 0.725539939514, 0.661776842059, 0.587716619023 },
96 { 1.01397751469, 1.28513006198, 0.995903384143, 0.77418706195, 0.653238524286,
97 0.584635025748, 0.531064164893, 0.478717061273 },
98 { 0.867069376285, 1.13381474809, 0.926972725286, 0.725539939514,
99 0.584635025748, 0.496936637883, 0.438694579826, 0.393021669543 },
100 { 0.721500455585, 0.962064122248, 0.820534991409, 0.661776842059,
101 0.531064164893, 0.438694579826, 0.375820256136, 0.330555063063 },
102 { 0.593906509971, 0.802254508198, 0.706020324706, 0.587716619023,
103 0.478717061273, 0.393021669543, 0.330555063063, 0.285345396658 }
104 };
105
convert_score_db(double _score,double _weight,int16_t pix_max)106 static double convert_score_db(double _score, double _weight, int16_t pix_max) {
107 assert(_score * _weight >= 0.0);
108
109 if (_weight * _score < pix_max * pix_max * 1e-10) return MAX_PSNR;
110 return 10 * (log10(pix_max * pix_max) - log10(_weight * _score));
111 }
112
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 _shift,int buf_is_hbd,int16_t pix_max,int luma)113 static double calc_psnrhvs(const unsigned char *src, int _systride,
114 const unsigned char *dst, int _dystride, double _par,
115 int _w, int _h, int _step, const double _csf[8][8],
116 uint32_t _shift, int buf_is_hbd, int16_t pix_max,
117 int luma) {
118 double ret;
119 const uint8_t *_src8 = src;
120 const uint8_t *_dst8 = dst;
121 const uint16_t *_src16 = CONVERT_TO_SHORTPTR(src);
122 const uint16_t *_dst16 = CONVERT_TO_SHORTPTR(dst);
123 DECLARE_ALIGNED(16, int16_t, dct_s[8 * 8]);
124 DECLARE_ALIGNED(16, int16_t, dct_d[8 * 8]);
125 DECLARE_ALIGNED(16, tran_low_t, dct_s_coef[8 * 8]);
126 DECLARE_ALIGNED(16, tran_low_t, dct_d_coef[8 * 8]);
127 double mask[8][8];
128 int pixels;
129 int x;
130 int y;
131 float sum1;
132 float sum2;
133 float delt;
134 (void)_par;
135 ret = pixels = 0;
136 sum1 = sum2 = delt = 0.0f;
137 for (y = 0; y < _h; y++) {
138 for (x = 0; x < _w; x++) {
139 if (!buf_is_hbd) {
140 sum1 += _src8[y * _systride + x];
141 sum2 += _dst8[y * _dystride + x];
142 } else {
143 sum1 += _src16[y * _systride + x] >> _shift;
144 sum2 += _dst16[y * _dystride + x] >> _shift;
145 }
146 }
147 }
148 if (luma) delt = (sum1 - sum2) / (_w * _h);
149 /*In the PSNR-HVS-M paper[1] the authors describe the construction of
150 their masking table as "we have used the quantization table for the
151 color component Y of JPEG [6] that has been also obtained on the
152 basis of CSF. Note that the values in quantization table JPEG have
153 been normalized and then squared." Their CSF matrix (from PSNR-HVS)
154 was also constructed from the JPEG matrices. I can not find any obvious
155 scheme of normalizing to produce their table, but if I multiply their
156 CSF by 0.3885746225901003 and square the result I get their masking table.
157 I have no idea where this constant comes from, but deviating from it
158 too greatly hurts MOS agreement.
159
160 [1] Nikolay Ponomarenko, Flavia Silvestri, Karen Egiazarian, Marco Carli,
161 Jaakko Astola, Vladimir Lukin, "On between-coefficient contrast masking
162 of DCT basis functions", CD-ROM Proceedings of the Third
163 International Workshop on Video Processing and Quality Metrics for Consumer
164 Electronics VPQM-07, Scottsdale, Arizona, USA, 25-26 January, 2007, 4 p.
165
166 Suggested in aomedia issue#2363:
167 0.3885746225901003 is a reciprocal of the maximum coefficient (2.573509)
168 of the old JPEG based matrix from the paper. Since you are not using that,
169 divide by actual maximum coefficient. */
170 for (x = 0; x < 8; x++)
171 for (y = 0; y < 8; y++)
172 mask[x][y] = (_csf[x][y] / _csf[1][0]) * (_csf[x][y] / _csf[1][0]);
173 for (y = 0; y < _h - 7; y += _step) {
174 for (x = 0; x < _w - 7; x += _step) {
175 int i;
176 int j;
177 int n = 0;
178 double s_gx = 0;
179 double s_gy = 0;
180 double g = 0;
181 double s_gmean = 0;
182 double s_gvar = 0;
183 double s_mask = 0;
184 for (i = 0; i < 8; i++) {
185 for (j = 0; j < 8; j++) {
186 if (!buf_is_hbd) {
187 dct_s[i * 8 + j] = _src8[(y + i) * _systride + (j + x)];
188 dct_d[i * 8 + j] = _dst8[(y + i) * _dystride + (j + x)];
189 } else {
190 dct_s[i * 8 + j] = _src16[(y + i) * _systride + (j + x)] >> _shift;
191 dct_d[i * 8 + j] = _dst16[(y + i) * _dystride + (j + x)] >> _shift;
192 }
193 dct_d[i * 8 + j] += (int)(delt + 0.5f);
194 }
195 }
196 for (i = 1; i < 7; i++) {
197 for (j = 1; j < 7; j++) {
198 s_gx = (dct_s[(i - 1) * 8 + j - 1] * 3 -
199 dct_s[(i - 1) * 8 + j + 1] * 3 + dct_s[i * 8 + j - 1] * 10 -
200 dct_s[i * 8 + j + 1] * 10 + dct_s[(i + 1) * 8 + j - 1] * 3 -
201 dct_s[(i + 1) * 8 + j + 1] * 3) /
202 (pix_max * 16.f);
203 s_gy = (dct_s[(i - 1) * 8 + j - 1] * 3 -
204 dct_s[(i + 1) * 8 + j - 1] * 3 + dct_s[(i - 1) * 8 + j] * 10 -
205 dct_s[(i + 1) * 8 + j] * 10 + dct_s[(i - 1) * 8 + j + 1] * 3 -
206 dct_s[(i + 1) * 8 + j + 1] * 3) /
207 (pix_max * 16.f);
208 g = sqrt(s_gx * s_gx + s_gy * s_gy);
209 if (g > 0.1f) n++;
210 s_gmean += g;
211 }
212 }
213 s_gvar = 1.f / (36 - n + 1) * s_gmean / 36.f;
214 #if CONFIG_AV1_HIGHBITDEPTH
215 if (!buf_is_hbd) {
216 od_bin_fdct8x8(dct_s_coef, 8, dct_s, 8);
217 od_bin_fdct8x8(dct_d_coef, 8, dct_d, 8);
218 } else {
219 hbd_od_bin_fdct8x8(dct_s_coef, 8, dct_s, 8);
220 hbd_od_bin_fdct8x8(dct_d_coef, 8, dct_d, 8);
221 }
222 #else
223 od_bin_fdct8x8(dct_s_coef, 8, dct_s, 8);
224 od_bin_fdct8x8(dct_d_coef, 8, dct_d, 8);
225 #endif // CONFIG_AV1_HIGHBITDEPTH
226 for (i = 0; i < 8; i++)
227 for (j = (i == 0); j < 8; j++)
228 s_mask += dct_s_coef[i * 8 + j] * dct_s_coef[i * 8 + j] * mask[i][j];
229 s_mask = sqrt(s_mask * s_gvar) / 8.f;
230 for (i = 0; i < 8; i++) {
231 for (j = 0; j < 8; j++) {
232 double err;
233 err = fabs((double)(dct_s_coef[i * 8 + j] - dct_d_coef[i * 8 + j]));
234 if (i != 0 || j != 0)
235 err = err < s_mask / mask[i][j] ? 0 : err - s_mask / mask[i][j];
236 ret += (err * _csf[i][j]) * (err * _csf[i][j]);
237 pixels++;
238 }
239 }
240 }
241 }
242 if (pixels <= 0) return 0;
243 ret /= pixels;
244 ret += 0.04 * delt * delt;
245 return ret;
246 }
247
aom_psnrhvs(const YV12_BUFFER_CONFIG * src,const YV12_BUFFER_CONFIG * dst,double * y_psnrhvs,double * u_psnrhvs,double * v_psnrhvs,uint32_t bd,uint32_t in_bd)248 double aom_psnrhvs(const YV12_BUFFER_CONFIG *src, const YV12_BUFFER_CONFIG *dst,
249 double *y_psnrhvs, double *u_psnrhvs, double *v_psnrhvs,
250 uint32_t bd, uint32_t in_bd) {
251 double psnrhvs;
252 const double par = 1.0;
253 const int step = 7;
254 uint32_t bd_shift = 0;
255 assert(bd == 8 || bd == 10 || bd == 12);
256 assert(bd >= in_bd);
257 assert(src->flags == dst->flags);
258 const int buf_is_hbd = src->flags & YV12_FLAG_HIGHBITDEPTH;
259
260 int16_t pix_max = 255;
261 if (in_bd == 10)
262 pix_max = 1023;
263 else if (in_bd == 12)
264 pix_max = 4095;
265
266 bd_shift = bd - in_bd;
267
268 *y_psnrhvs =
269 calc_psnrhvs(src->y_buffer, src->y_stride, dst->y_buffer, dst->y_stride,
270 par, src->y_crop_width, src->y_crop_height, step, csf_y,
271 bd_shift, buf_is_hbd, pix_max, 1);
272 *u_psnrhvs =
273 calc_psnrhvs(src->u_buffer, src->uv_stride, dst->u_buffer, dst->uv_stride,
274 par, src->uv_crop_width, src->uv_crop_height, step,
275 csf_cb420, bd_shift, buf_is_hbd, pix_max, 0);
276 *v_psnrhvs =
277 calc_psnrhvs(src->v_buffer, src->uv_stride, dst->v_buffer, dst->uv_stride,
278 par, src->uv_crop_width, src->uv_crop_height, step,
279 csf_cr420, bd_shift, buf_is_hbd, pix_max, 0);
280 psnrhvs = (*y_psnrhvs) * .8 + .1 * ((*u_psnrhvs) + (*v_psnrhvs));
281 return convert_score_db(psnrhvs, 1.0, pix_max);
282 }
283