1 |
/*===========================================================================* |
2 |
* opts.c * |
3 |
* * |
4 |
* Special C code to handle TUNEing options * |
5 |
* * |
6 |
* EXPORTED PROCEDURES: * |
7 |
* Tune_Init * |
8 |
* CollectQuantStats * |
9 |
* * |
10 |
*===========================================================================*/ |
11 |
|
12 |
|
13 |
/* |
14 |
* Copyright (c) 1995 The Regents of the University of California. |
15 |
* All rights reserved. |
16 |
* |
17 |
* Permission to use, copy, modify, and distribute this software and its |
18 |
* documentation for any purpose, without fee, and without written agreement is |
19 |
* hereby granted, provided that the above copyright notice and the following |
20 |
* two paragraphs appear in all copies of this software. |
21 |
* |
22 |
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR |
23 |
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT |
24 |
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF |
25 |
* CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 |
* |
27 |
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, |
28 |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
29 |
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
30 |
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO |
31 |
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
32 |
*/ |
33 |
|
34 |
/*==============* |
35 |
* HEADER FILES * |
36 |
*==============*/ |
37 |
|
38 |
#include <stdio.h> |
39 |
#include <string.h> |
40 |
#if !defined(__APPLE__) |
41 |
#include <malloc.h> |
42 |
#else |
43 |
#include <stdlib.h> |
44 |
#endif |
45 |
#include <math.h> |
46 |
|
47 |
#include "opts.h" |
48 |
|
49 |
/*==============* |
50 |
* EXTERNALS * |
51 |
*==============*/ |
52 |
|
53 |
extern char outputFileName[]; |
54 |
extern boolean pureDCT; |
55 |
extern int32 qtable[], niqtable[]; |
56 |
extern int ZAG[]; |
57 |
extern boolean printSNR, decodeRefFrames; |
58 |
|
59 |
void init_idctref _ANSI_ARGS_((void)); |
60 |
void init_fdct _ANSI_ARGS_((void)); |
61 |
|
62 |
|
63 |
/*===================* |
64 |
* GLOBALS MADE HERE * |
65 |
*===================*/ |
66 |
|
67 |
boolean tuneingOn = FALSE; |
68 |
int block_bound = 128; |
69 |
boolean collect_quant = FALSE; |
70 |
int collect_quant_detailed = 0; |
71 |
FILE *collect_quant_fp; |
72 |
int kill_dim = FALSE; |
73 |
int kill_dim_break, kill_dim_end; |
74 |
float kill_dim_slope; |
75 |
int SearchCompareMode = DEFAULT_SEARCH; |
76 |
boolean squash_small_differences = FALSE; |
77 |
int SquashMaxLum, SquashMaxChr; |
78 |
float LocalDCTRateScale = 1.0, LocalDCTDistortScale = 1.0; |
79 |
boolean IntraPBAllowed = TRUE; |
80 |
boolean WriteDistortionNumbers = FALSE; |
81 |
int collect_distortion_detailed = 0; |
82 |
FILE *distortion_fp; |
83 |
FILE *fp_table_rate[31], *fp_table_dist[31]; |
84 |
boolean DoLaplace = FALSE; |
85 |
double **L1, **L2, **Lambdas; |
86 |
int LaplaceNum, LaplaceCnum; |
87 |
boolean BSkipBlocks = TRUE; |
88 |
|
89 |
/*====================* |
90 |
* Internal Prototypes* |
91 |
*====================*/ |
92 |
void SetupCollectQuantStats _ANSI_ARGS_((char *charPtr)); |
93 |
void SetupSquashSmall _ANSI_ARGS_ ((char *charPtr)); |
94 |
void SetupKillDimAreas _ANSI_ARGS_((char *charPtr)); |
95 |
void SetupLocalDCT _ANSI_ARGS_((char *charPtr)); |
96 |
void SetupWriteDistortions _ANSI_ARGS_((char *charPtr)); |
97 |
void SetupLaplace _ANSI_ARGS_((void)); |
98 |
void CalcLambdas _ANSI_ARGS_((void)); |
99 |
void Mpost_UnQuantZigBlockLaplace _ANSI_ARGS_((FlatBlock in, Block out, int qscale, boolean iblock)); |
100 |
|
101 |
/* define this as it too much of a pain to find toupper on different arch'es */ |
102 |
#define ASCII_TOUPPER(c) ((c>='a') && (c<='z')) ? c-'a'+'A' : c |
103 |
|
104 |
/*=====================* |
105 |
* EXPORTED PROCEDURES * |
106 |
*=====================*/ |
107 |
|
108 |
/*===========================================================================* |
109 |
* |
110 |
* Tune_Init |
111 |
* |
112 |
* Do any setup needed before coding stream |
113 |
* |
114 |
* RETURNS: nothing |
115 |
* |
116 |
* SIDE EFFECTS: varies |
117 |
* |
118 |
*===========================================================================*/ |
119 |
void Tune_Init() |
120 |
{ |
121 |
int i; |
122 |
|
123 |
/* Just check for each, and do whats needed */ |
124 |
if (collect_quant) { |
125 |
if (!pureDCT) { |
126 |
pureDCT = TRUE; |
127 |
init_idctref(); |
128 |
init_fdct(); |
129 |
} |
130 |
fprintf(collect_quant_fp, "# %s\n", outputFileName); |
131 |
fprintf(collect_quant_fp, "#"); |
132 |
for (i=0; i<64; i++) |
133 |
fprintf(collect_quant_fp, " %d", qtable[i]); |
134 |
fprintf(collect_quant_fp, "\n#"); |
135 |
for (i=0; i<64; i++) |
136 |
fprintf(collect_quant_fp, " %d", niqtable[i]); |
137 |
fprintf(collect_quant_fp, "\n# %d %d %d\n\n", |
138 |
GetIQScale(), GetPQScale(), GetBQScale()); |
139 |
|
140 |
} |
141 |
|
142 |
if (DoLaplace) { |
143 |
if (!pureDCT) { |
144 |
pureDCT = TRUE; |
145 |
init_idctref(); |
146 |
init_fdct(); |
147 |
} |
148 |
decodeRefFrames = TRUE; |
149 |
printSNR = TRUE; |
150 |
} |
151 |
|
152 |
} |
153 |
|
154 |
/*===========================================================================* |
155 |
* |
156 |
* ParseTuneParam |
157 |
* |
158 |
* Handle the strings following TUNE |
159 |
* |
160 |
* RETURNS: nothing |
161 |
* |
162 |
* SIDE EFFECTS: varies |
163 |
* |
164 |
*===========================================================================*/ |
165 |
void ParseTuneParam(charPtr) |
166 |
char *charPtr; |
167 |
{ |
168 |
switch (ASCII_TOUPPER(*charPtr)) { |
169 |
case 'B': |
170 |
if (1 != sscanf(charPtr+2, "%d", &block_bound)) { |
171 |
fprintf(stderr, "Invalid tuning parameter (b) in parameter file.\n"); |
172 |
} |
173 |
break; |
174 |
case 'C': |
175 |
SetupCollectQuantStats(charPtr+2); |
176 |
break; |
177 |
case 'D': |
178 |
SetupLocalDCT(SkipSpacesTabs(charPtr+1)); |
179 |
break; |
180 |
case 'K': |
181 |
SetupKillDimAreas(SkipSpacesTabs(charPtr+1)); |
182 |
break; |
183 |
case 'L': |
184 |
SetupLaplace(); |
185 |
break; |
186 |
case 'N': |
187 |
SearchCompareMode = NO_DC_SEARCH; |
188 |
break; |
189 |
case 'Q': |
190 |
SearchCompareMode = DO_Mean_Squared_Distortion; |
191 |
break; |
192 |
case 'S': |
193 |
SetupSquashSmall(SkipSpacesTabs(charPtr+1)); |
194 |
break; |
195 |
case 'W': |
196 |
SetupWriteDistortions(SkipSpacesTabs(charPtr+1)); |
197 |
break; |
198 |
case 'U': |
199 |
BSkipBlocks = FALSE; |
200 |
break; |
201 |
case 'Z': |
202 |
IntraPBAllowed = FALSE; |
203 |
break; |
204 |
default: |
205 |
fprintf(stderr, "Unknown tuning (%s) in parameter file.\n",charPtr); |
206 |
break; |
207 |
} |
208 |
} |
209 |
|
210 |
|
211 |
/*===============* |
212 |
* Internals * |
213 |
*===============*/ |
214 |
|
215 |
/*===========================================================================* |
216 |
* |
217 |
* SetupCollectQuantStats |
218 |
* |
219 |
* Setup variables to collect statistics on quantization values |
220 |
* |
221 |
* RETURNS: nothing |
222 |
* |
223 |
* SIDE EFFECTS: sets collect_quant and collect_quant_fp |
224 |
* |
225 |
*===========================================================================*/ |
226 |
void SetupCollectQuantStats(charPtr) |
227 |
char *charPtr; |
228 |
{ |
229 |
char fname[256], *cp; |
230 |
|
231 |
cp = charPtr; |
232 |
while ( (*cp != ' ') && (*cp != '\t') && (*cp != '\n')) { |
233 |
cp++; |
234 |
} |
235 |
|
236 |
strncpy(fname, charPtr, cp-charPtr); |
237 |
fname[cp-charPtr] = '\0'; |
238 |
collect_quant = TRUE; |
239 |
if ((collect_quant_fp = fopen(fname,"w")) == NULL) { |
240 |
fprintf(stderr, "Error opening %s for quant statistics\n", fname); |
241 |
fprintf(stderr, "Using stdout (ick!)\n"); |
242 |
collect_quant_fp = stdout; |
243 |
} |
244 |
|
245 |
cp = SkipSpacesTabs(cp); |
246 |
if (*cp != '\n') { |
247 |
switch (*cp) { |
248 |
case 'c': |
249 |
collect_quant_detailed = 1; |
250 |
break; |
251 |
default: |
252 |
fprintf(stderr, "Unknown TUNE parameter setting format %s\n", cp); |
253 |
}} |
254 |
} |
255 |
|
256 |
|
257 |
|
258 |
|
259 |
/*===========================================================================* |
260 |
* |
261 |
* SetupKillDimAreas |
262 |
* |
263 |
* Do a transform on small lum values |
264 |
* |
265 |
* RETURNS: nothing |
266 |
* |
267 |
* SIDE EFFECTS: sets kill_dim, kill_dim_break, kill_dim_end |
268 |
* |
269 |
*===========================================================================*/ |
270 |
void SetupKillDimAreas(charPtr) |
271 |
char *charPtr; |
272 |
{ |
273 |
int items_scanned; |
274 |
|
275 |
kill_dim = TRUE; |
276 |
items_scanned = sscanf(charPtr, "%d %d %f", |
277 |
&kill_dim_break, &kill_dim_end, &kill_dim_slope); |
278 |
if (items_scanned != 3) { |
279 |
kill_dim_slope = 0.25; |
280 |
items_scanned = sscanf(charPtr, "%d %d", |
281 |
&kill_dim_break, &kill_dim_end); |
282 |
if (items_scanned != 2) { |
283 |
/* Use defaults */ |
284 |
kill_dim_break = 20; |
285 |
kill_dim_end = 25; |
286 |
} |
287 |
} |
288 |
/* check values */ |
289 |
if (kill_dim_break > kill_dim_end) { |
290 |
fprintf(stderr, "TUNE parameter k: break > end is illegal.\n"); |
291 |
exit(-1); |
292 |
} |
293 |
if (kill_dim_slope < 0) { |
294 |
fprintf(stderr, "TUNE parameter k: slope < 0 is illegal.\n"); |
295 |
exit(-1); |
296 |
} |
297 |
} |
298 |
|
299 |
|
300 |
|
301 |
/*===========================================================================* |
302 |
* |
303 |
* SetupSquashSmall |
304 |
* |
305 |
* Setup encoder to squash small changes in Y or Cr/Cb values |
306 |
* |
307 |
* RETURNS: nothing |
308 |
* |
309 |
* SIDE EFFECTS: sets squash_max_differences SquashMaxLum SquashMaxChr |
310 |
* |
311 |
*===========================================================================*/ |
312 |
void SetupSquashSmall(charPtr) |
313 |
char *charPtr; |
314 |
{ |
315 |
squash_small_differences = TRUE; |
316 |
|
317 |
if (sscanf(charPtr, "%d %d", &SquashMaxLum, &SquashMaxChr) == 1) { |
318 |
/* Only set one, do both */ |
319 |
SquashMaxChr = SquashMaxLum; |
320 |
} |
321 |
} |
322 |
|
323 |
|
324 |
/*===========================================================================* |
325 |
* |
326 |
* SetupLocalDCT |
327 |
* |
328 |
* Setup encoder to use DCT for rate-distortion estimat ein Psearches |
329 |
* |
330 |
* RETURNS: nothing |
331 |
* |
332 |
* SIDE EFFECTS: sets SearchCompareMode and |
333 |
* can change LocalDCTRateScale, LocalDCTDistortScale |
334 |
* |
335 |
*===========================================================================*/ |
336 |
void SetupLocalDCT(charPtr) |
337 |
char *charPtr; |
338 |
{ |
339 |
int num_scales=0; |
340 |
|
341 |
SearchCompareMode = LOCAL_DCT; |
342 |
|
343 |
/* Set scaling factors if present */ |
344 |
num_scales = sscanf(charPtr, "%f %f", &LocalDCTRateScale, &LocalDCTDistortScale); |
345 |
if (num_scales == 1) { |
346 |
fprintf(stderr, "Invalid number of scaling factors for local DCT\n"); |
347 |
fprintf(stderr, "Must specify Rate Scale and Distorion scale (both floats)\n"); |
348 |
fprintf(stderr, "Continuing with 1.0 1.0\n"); |
349 |
LocalDCTRateScale = 1.0; |
350 |
LocalDCTDistortScale = 1.0; |
351 |
} |
352 |
} |
353 |
|
354 |
|
355 |
/*===========================================================================* |
356 |
* |
357 |
* SetupLaplace |
358 |
* |
359 |
* Setup encoder to find distrubution for I-frames, and use for -snr |
360 |
* |
361 |
* RETURNS: nothing |
362 |
* |
363 |
* SIDE EFFECTS: sets DoLaplace, L1, L2, and Lambdas |
364 |
* |
365 |
*===========================================================================*/ |
366 |
void SetupLaplace() |
367 |
{ |
368 |
int i; |
369 |
|
370 |
DoLaplace = TRUE; |
371 |
LaplaceNum = 0; |
372 |
L1 = (double **)malloc(sizeof(double *)*3); |
373 |
L2 = (double **)malloc(sizeof(double *)*3); |
374 |
Lambdas = (double **)malloc(sizeof(double *)*3); |
375 |
if (L1 == NULL || L2 == NULL || Lambdas == NULL) { |
376 |
fprintf(stderr,"Out of memory!!!\n"); |
377 |
exit(1); |
378 |
} |
379 |
for (i = 0; i < 3; i++) { |
380 |
L1[i] = (double *)calloc(64, sizeof(double)); |
381 |
L2[i] = (double *)calloc(64, sizeof(double)); |
382 |
Lambdas[i] = (double *)malloc(sizeof(double) * 64); |
383 |
if (L1[i] == NULL || L2[i] == NULL || Lambdas[i] == NULL) { |
384 |
fprintf(stderr,"Out of memory!!!\n"); |
385 |
exit(1); |
386 |
} |
387 |
} |
388 |
} |
389 |
|
390 |
void CalcLambdas() |
391 |
{ |
392 |
int i,j,n; |
393 |
double var; |
394 |
|
395 |
n = LaplaceNum; |
396 |
for (i = 0; i < 3; i++) { |
397 |
for (j = 0; j < 64; j++) { |
398 |
var = (n*L1[i][j] + L2[i][j]*L2[i][j]) / (n*(n-1)); |
399 |
Lambdas[i][j] = sqrt(2.0) / sqrt(var); |
400 |
} |
401 |
} |
402 |
} |
403 |
|
404 |
|
405 |
/*===========================================================================* |
406 |
* |
407 |
* Mpost_UnQuantZigBlockLaplace |
408 |
* |
409 |
* unquantize and zig-zag (decode) a single block, using the distrib to get vals |
410 |
* Iblocks only now |
411 |
* |
412 |
* RETURNS: nothing |
413 |
* |
414 |
* SIDE EFFECTS: none |
415 |
* |
416 |
*===========================================================================*/ |
417 |
void |
418 |
Mpost_UnQuantZigBlockLaplace(in, out, qscale, iblock) |
419 |
FlatBlock in; |
420 |
Block out; |
421 |
int qscale; |
422 |
boolean iblock; |
423 |
{ |
424 |
register int index; |
425 |
int position; |
426 |
register int qentry; |
427 |
int level, coeff; |
428 |
double low, high; |
429 |
double mid,lam; |
430 |
|
431 |
/* qtable[0] must be 8 */ |
432 |
out[0][0] = (int16)(in[0] * 8); |
433 |
|
434 |
for ( index = 1; index < DCTSIZE_SQ; index++ ) { |
435 |
position = ZAG[index]; |
436 |
level = in[index]; |
437 |
|
438 |
if (level == 0) { |
439 |
((int16 *)out)[position] = 0; |
440 |
continue; |
441 |
} |
442 |
qentry = qtable[position] * qscale; |
443 |
coeff = (level*qentry)/8; |
444 |
low = ((ABS(level)-.5)*qentry)/8; |
445 |
high = ((ABS(level)+.5)*qentry)/8; |
446 |
lam = Lambdas[LaplaceCnum][position]; |
447 |
mid = (1.0/lam) * log(0.5*(exp(-lam*low)+exp(-lam*high))); |
448 |
mid = ABS(mid); |
449 |
if (mid - floor(mid) > .4999) { |
450 |
mid = ceil(mid); |
451 |
} else { |
452 |
mid = floor(mid); |
453 |
} |
454 |
if (level<0) {mid = -mid;} |
455 |
/*printf("(%2.1lf-%2.1lf): old: %d vs %d\n",low,high,coeff,(int) mid);*/ |
456 |
coeff = mid; |
457 |
if ( (coeff & 1) == 0 ) { |
458 |
if ( coeff < 0 ) { |
459 |
coeff++; |
460 |
} else if ( coeff > 0 ) { |
461 |
coeff--; |
462 |
} |
463 |
} |
464 |
((int16 *)out)[position] = coeff; |
465 |
} |
466 |
} |
467 |
|
468 |
void |
469 |
SetupWriteDistortions(charPtr) |
470 |
char *charPtr; |
471 |
{ |
472 |
char fname[256], *cp; |
473 |
int i; |
474 |
|
475 |
WriteDistortionNumbers = TRUE; |
476 |
cp = charPtr; |
477 |
while ( (*cp != ' ') && (*cp != '\t') && (*cp != '\n')) { |
478 |
cp++; |
479 |
} |
480 |
|
481 |
strncpy(fname, charPtr, cp-charPtr); |
482 |
fname[cp-charPtr] = '\0'; |
483 |
collect_quant = TRUE; |
484 |
if ((distortion_fp = fopen(fname,"w")) == NULL) { |
485 |
fprintf(stderr, "Error opening %s for quant statistics\n", fname); |
486 |
fprintf(stderr, "Using stdout (ick!)\n"); |
487 |
distortion_fp = stdout; |
488 |
} |
489 |
|
490 |
cp = SkipSpacesTabs(cp); |
491 |
if (*cp != '\n') { |
492 |
switch (*cp) { |
493 |
case 'c': |
494 |
collect_distortion_detailed = TRUE; |
495 |
break; |
496 |
case 't': { |
497 |
char scratch[256]; |
498 |
collect_distortion_detailed = 2; |
499 |
for (i = 1; i < 32; i++) { |
500 |
sprintf(scratch, "%srate%d", fname, i); |
501 |
fp_table_rate[i-1] = fopen(scratch, "w"); |
502 |
sprintf(scratch, "%sdist%d", fname, i); |
503 |
fp_table_dist[i-1] = fopen(scratch, "w"); |
504 |
}} |
505 |
break; |
506 |
default: |
507 |
fprintf(stderr, "Unknown TUNE parameter setting format %s\n", cp); |
508 |
}} |
509 |
} |
510 |
|
511 |
int mse(blk1, blk2) |
512 |
Block blk1, blk2; |
513 |
{ |
514 |
register int index, error, tmp; |
515 |
int16 *bp1, *bp2; |
516 |
|
517 |
bp1 = (int16 *)blk1; |
518 |
bp2 = (int16 *)blk2; |
519 |
error = 0; |
520 |
for ( index = 0; index < DCTSIZE_SQ; index++ ) { |
521 |
tmp = *bp1++ - *bp2++; |
522 |
error += tmp*tmp; |
523 |
} |
524 |
return error; |
525 |
} |