blob: d01aa29dade5a5f6e4f12ae6d3bb28bc0804cfbd
1 | /* |
2 | * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at> |
3 | * |
4 | * This file is part of FFmpeg. |
5 | * |
6 | * FFmpeg is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * FFmpeg is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with FFmpeg; if not, write to the Free Software |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | */ |
20 | |
21 | #include <stdio.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <inttypes.h> |
25 | #include <math.h> |
26 | #include <float.h> |
27 | #include <limits.h> |
28 | |
29 | #include "libavutil/intfloat.h" |
30 | #include "libavutil/intreadwrite.h" |
31 | |
32 | #define FFMIN(a, b) ((a) > (b) ? (b) : (a)) |
33 | #define F 100 |
34 | #define SIZE 2048 |
35 | |
36 | uint64_t exp16_table[21] = { |
37 | 65537, |
38 | 65538, |
39 | 65540, |
40 | 65544, |
41 | 65552, |
42 | 65568, |
43 | 65600, |
44 | 65664, |
45 | 65793, |
46 | 66050, |
47 | 66568, |
48 | 67616, |
49 | 69763, |
50 | 74262, |
51 | 84150, |
52 | 108051, |
53 | 178145, |
54 | 484249, |
55 | 3578144, |
56 | 195360063, |
57 | 582360139072LL, |
58 | }; |
59 | |
60 | #if 0 |
61 | // 16.16 fixpoint exp() |
62 | static unsigned int exp16(unsigned int a){ |
63 | int i; |
64 | int out= 1<<16; |
65 | |
66 | for(i=19;i>=0;i--){ |
67 | if(a&(1<<i)) |
68 | out= (out*exp16_table[i] + (1<<15))>>16; |
69 | } |
70 | |
71 | return out; |
72 | } |
73 | #endif |
74 | |
75 | // 16.16 fixpoint log() |
76 | static int64_t log16(uint64_t a) |
77 | { |
78 | int i; |
79 | int out = 0; |
80 | |
81 | if (a < 1 << 16) |
82 | return -log16((1LL << 32) / a); |
83 | a <<= 16; |
84 | |
85 | for (i = 20; i >= 0; i--) { |
86 | int64_t b = exp16_table[i]; |
87 | if (a < (b << 16)) |
88 | continue; |
89 | out |= 1 << i; |
90 | a = ((a / b) << 16) + (((a % b) << 16) + b / 2) / b; |
91 | } |
92 | return out; |
93 | } |
94 | |
95 | static uint64_t int_sqrt(uint64_t a) |
96 | { |
97 | uint64_t ret = 0; |
98 | uint64_t ret_sq = 0; |
99 | int s; |
100 | |
101 | for (s = 31; s >= 0; s--) { |
102 | uint64_t b = ret_sq + (1ULL << (s * 2)) + (ret << s) * 2; |
103 | if (b <= a) { |
104 | ret_sq = b; |
105 | ret += 1ULL << s; |
106 | } |
107 | } |
108 | return ret; |
109 | } |
110 | |
111 | static int16_t get_s16l(uint8_t *p) |
112 | { |
113 | union { |
114 | uint16_t u; |
115 | int16_t s; |
116 | } v; |
117 | v.u = p[0] | p[1] << 8; |
118 | return v.s; |
119 | } |
120 | |
121 | static float get_f32l(uint8_t *p) |
122 | { |
123 | union av_intfloat32 v; |
124 | v.i = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; |
125 | return v.f; |
126 | } |
127 | |
128 | static double get_f64l(uint8_t *p) |
129 | { |
130 | return av_int2double(AV_RL64(p)); |
131 | } |
132 | |
133 | static int run_psnr(FILE *f[2], int len, int shift, int skip_bytes) |
134 | { |
135 | uint64_t i, j; |
136 | uint64_t sse = 0; |
137 | double sse_d = 0.0; |
138 | uint8_t buf[2][SIZE]; |
139 | int64_t max = (1LL << (8 * len)) - 1; |
140 | uint64_t size0 = 0; |
141 | uint64_t size1 = 0; |
142 | uint64_t maxdist = 0; |
143 | double maxdist_d = 0.0; |
144 | int noseek; |
145 | |
146 | noseek = fseek(f[0], 0, SEEK_SET) || |
147 | fseek(f[1], 0, SEEK_SET); |
148 | |
149 | if (!noseek) { |
150 | for (i = 0; i < 2; i++) { |
151 | uint8_t *p = buf[i]; |
152 | if (fread(p, 1, 12, f[i]) != 12) |
153 | return -1; |
154 | if (!memcmp(p, "RIFF", 4) && |
155 | !memcmp(p + 8, "WAVE", 4)) { |
156 | if (fread(p, 1, 8, f[i]) != 8) |
157 | return -1; |
158 | while (memcmp(p, "data", 4)) { |
159 | int s = p[4] | p[5] << 8 | p[6] << 16 | p[7] << 24; |
160 | fseek(f[i], s, SEEK_CUR); |
161 | if (fread(p, 1, 8, f[i]) != 8) |
162 | return -1; |
163 | } |
164 | } else { |
165 | fseek(f[i], -12, SEEK_CUR); |
166 | } |
167 | } |
168 | |
169 | fseek(f[shift < 0], abs(shift), SEEK_CUR); |
170 | |
171 | fseek(f[0], skip_bytes, SEEK_CUR); |
172 | fseek(f[1], skip_bytes, SEEK_CUR); |
173 | } |
174 | |
175 | for (;;) { |
176 | int s0 = fread(buf[0], 1, SIZE, f[0]); |
177 | int s1 = fread(buf[1], 1, SIZE, f[1]); |
178 | |
179 | for (j = 0; j < FFMIN(s0, s1); j += len) { |
180 | switch (len) { |
181 | case 1: |
182 | case 2: { |
183 | int64_t a, b; |
184 | int dist; |
185 | if (len == 2) { |
186 | a = get_s16l(buf[0] + j); |
187 | b = get_s16l(buf[1] + j); |
188 | } else { |
189 | a = buf[0][j]; |
190 | b = buf[1][j]; |
191 | } |
192 | sse += (a - b) * (a - b); |
193 | dist = llabs(a - b); |
194 | if (dist > maxdist) |
195 | maxdist = dist; |
196 | break; |
197 | } |
198 | case 4: |
199 | case 8: { |
200 | double dist, a, b; |
201 | if (len == 8) { |
202 | a = get_f64l(buf[0] + j); |
203 | b = get_f64l(buf[1] + j); |
204 | } else { |
205 | a = get_f32l(buf[0] + j); |
206 | b = get_f32l(buf[1] + j); |
207 | } |
208 | dist = fabs(a - b); |
209 | sse_d += (a - b) * (a - b); |
210 | if (dist > maxdist_d) |
211 | maxdist_d = dist; |
212 | break; |
213 | } |
214 | } |
215 | } |
216 | size0 += s0; |
217 | size1 += s1; |
218 | if (s0 + s1 <= 0) |
219 | break; |
220 | } |
221 | |
222 | i = FFMIN(size0, size1) / len; |
223 | if (!i) |
224 | i = 1; |
225 | switch (len) { |
226 | case 1: |
227 | case 2: { |
228 | uint64_t psnr; |
229 | uint64_t dev = int_sqrt(((sse / i) * F * F) + (((sse % i) * F * F) + i / 2) / i); |
230 | if (sse) |
231 | psnr = ((2 * log16(max << 16) + log16(i) - log16(sse)) * |
232 | 284619LL * F + (1LL << 31)) / (1LL << 32); |
233 | else |
234 | psnr = 1000 * F - 1; // floating point free infinity :) |
235 | |
236 | printf("stddev:%5d.%02d PSNR:%3d.%02d MAXDIFF:%5"PRIu64" bytes:%9"PRIu64"/%9"PRIu64"\n", |
237 | (int)(dev / F), (int)(dev % F), |
238 | (int)(psnr / F), (int)(psnr % F), |
239 | maxdist, size0, size1); |
240 | return psnr; |
241 | } |
242 | case 4: |
243 | case 8: { |
244 | char psnr_str[64]; |
245 | double psnr = INT_MAX; |
246 | double dev = sqrt(sse_d / i); |
247 | uint64_t scale = (len == 4) ? (1ULL << 24) : (1ULL << 32); |
248 | |
249 | if (sse_d) { |
250 | psnr = 2 * log(DBL_MAX) - log(i / sse_d); |
251 | snprintf(psnr_str, sizeof(psnr_str), "%5.02f", psnr); |
252 | } else |
253 | snprintf(psnr_str, sizeof(psnr_str), "inf"); |
254 | |
255 | maxdist = maxdist_d * scale; |
256 | |
257 | printf("stddev:%10.2f PSNR:%s MAXDIFF:%10"PRIu64" bytes:%9"PRIu64"/%9"PRIu64"\n", |
258 | dev * scale, psnr_str, maxdist, size0, size1); |
259 | return psnr; |
260 | } |
261 | } |
262 | return -1; |
263 | } |
264 | |
265 | int main(int argc, char *argv[]) |
266 | { |
267 | FILE *f[2]; |
268 | int len = 1; |
269 | int shift_first= argc < 5 ? 0 : atoi(argv[4]); |
270 | int skip_bytes = argc < 6 ? 0 : atoi(argv[5]); |
271 | int shift_last = shift_first + (argc < 7 ? 0 : atoi(argv[6])); |
272 | int shift; |
273 | int max_psnr = -1; |
274 | int max_psnr_shift = 0; |
275 | |
276 | if (shift_last > shift_first) |
277 | shift_first -= shift_last - shift_first; |
278 | |
279 | if (argc > 3) { |
280 | if (!strcmp(argv[3], "u8")) { |
281 | len = 1; |
282 | } else if (!strcmp(argv[3], "s16")) { |
283 | len = 2; |
284 | } else if (!strcmp(argv[3], "f32")) { |
285 | len = 4; |
286 | } else if (!strcmp(argv[3], "f64")) { |
287 | len = 8; |
288 | } else { |
289 | char *end; |
290 | len = strtol(argv[3], &end, 0); |
291 | if (*end || len < 1 || len > 2) { |
292 | fprintf(stderr, "Unsupported sample format: %s\nSupported: u8, s16, f32, f64\n", argv[3]); |
293 | return 1; |
294 | } |
295 | } |
296 | } |
297 | |
298 | if (argc < 3) { |
299 | printf("tiny_psnr <file1> <file2> [<elem size>|u8|s16|f32|f64 [<shift> [<skip bytes> [<shift search range>]]]]\n"); |
300 | printf("WAV headers are skipped automatically.\n"); |
301 | return 1; |
302 | } |
303 | |
304 | f[0] = fopen(argv[1], "rb"); |
305 | f[1] = fopen(argv[2], "rb"); |
306 | if (!f[0] || !f[1]) { |
307 | fprintf(stderr, "Could not open input files.\n"); |
308 | return 1; |
309 | } |
310 | |
311 | for (shift = shift_first; shift <= shift_last; shift++) { |
312 | int psnr = run_psnr(f, len, shift, skip_bytes); |
313 | if (psnr > max_psnr || (shift < 0 && psnr == max_psnr)) { |
314 | max_psnr = psnr; |
315 | max_psnr_shift = shift; |
316 | } |
317 | } |
318 | if (max_psnr < 0) |
319 | return 2; |
320 | |
321 | if (shift_last > shift_first) |
322 | printf("Best PSNR is %3d.%02d for shift %i\n", (int)(max_psnr / F), (int)(max_psnr % F), max_psnr_shift); |
323 | return 0; |
324 | } |
325 |