ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/px/pcond3.c
Revision: 3.19
Committed: Tue Apr 13 02:56:42 2021 UTC (3 years ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R4, HEAD
Changes since 3.18: +2 -2 lines
Log Message:
perf(pcond): more aggressive trim criterion for tone curve

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pcond3.c,v 3.18 2020/06/27 03:37:24 greg Exp $";
3 #endif
4 /*
5 * Routines for computing and applying brightness mapping.
6 */
7
8 #include <string.h>
9
10 #include "pcond.h"
11 #include "random.h"
12
13
14 #define CVRATIO 0.025 /* fraction of samples allowed > env. */
15
16 #define LN_10 2.30258509299404568402
17 #define exp10(x) exp(LN_10*(x))
18
19 double modhist[HISTRES]; /* modified histogram */
20 double mhistot; /* modified histogram total */
21 double cumf[HISTRES+1]; /* cumulative distribution function */
22
23 static double centprob(int x, int y);
24 static void mkcumf(void);
25 static double cf(double b);
26 static double BLw(double Lw);
27 static float *getlumsamp(int n);
28 #if ADJ_VEIL
29 static void mkcrfimage(void);
30 #endif
31
32
33
34 void
35 getfixations( /* load fixation history list */
36 FILE *fp
37 )
38 {
39 #define FIXHUNK 128
40 RESOLU fvres;
41 int pos[2];
42 int px, py, i;
43 /* initialize our resolution struct */
44 if ((fvres.rt=inpres.rt)&YMAJOR) {
45 fvres.xr = fvxr;
46 fvres.yr = fvyr;
47 } else {
48 fvres.xr = fvyr;
49 fvres.yr = fvxr;
50 }
51 /* read each picture position */
52 while (fscanf(fp, "%d %d", &pos[0], &pos[1]) == 2) {
53 /* convert to closest index in foveal image */
54 loc2pix(pos, &fvres,
55 (pos[0]+.5)/inpres.xr, (pos[1]+.5)/inpres.yr);
56 /* include nine neighborhood samples */
57 for (px = pos[0]-1; px <= pos[0]+1; px++) {
58 if (px < 0 || px >= fvxr)
59 continue;
60 for (py = pos[1]-1; py <= pos[1]+1; py++) {
61 if (py < 0 || py >= fvyr)
62 continue;
63 for (i = nfixations; i-- > 0; )
64 if (fixlst[i][0] == px &&
65 fixlst[i][1] == py)
66 break;
67 if (i >= 0)
68 continue; /* already there */
69 if (nfixations % FIXHUNK == 0) {
70 if (nfixations)
71 fixlst = (short (*)[2])
72 realloc((void *)fixlst,
73 (nfixations+FIXHUNK)*
74 2*sizeof(short));
75 else
76 fixlst = (short (*)[2])malloc(
77 FIXHUNK*2*sizeof(short)
78 );
79 if (fixlst == NULL)
80 syserror("malloc");
81 }
82 fixlst[nfixations][0] = px;
83 fixlst[nfixations][1] = py;
84 nfixations++;
85 }
86 }
87 }
88 if (!feof(fp)) {
89 fprintf(stderr, "%s: format error reading fixation data\n",
90 progname);
91 exit(1);
92 }
93 #undef FIXHUNK
94 }
95
96
97 void
98 gethisto( /* load precomputed luminance histogram */
99 FILE *fp
100 )
101 {
102 double histo[MAXPREHIST];
103 double histart, histep;
104 double b, lastb, w;
105 int n;
106 int i;
107 /* load data */
108 for (i = 0; i < MAXPREHIST &&
109 fscanf(fp, "%lf %lf", &b, &histo[i]) == 2; i++) {
110 if (i > 1 && fabs(b - lastb - histep) > .001) {
111 fprintf(stderr,
112 "%s: uneven step size in histogram data\n",
113 progname);
114 exit(1);
115 }
116 if (i == 1)
117 if ((histep = b - (histart = lastb)) <= FTINY) {
118 fprintf(stderr,
119 "%s: illegal step in histogram data\n",
120 progname);
121 exit(1);
122 }
123 lastb = b;
124 }
125 if (i < 2 || !feof(fp)) {
126 fprintf(stderr,
127 "%s: format/length error loading histogram (log10L %f at %d)\n",
128 progname, b, i);
129 exit(1);
130 }
131 n = i;
132 histart *= LN_10;
133 histep *= LN_10;
134 /* find extrema */
135 for (i = 0; i < n && histo[i] <= FTINY; i++)
136 ;
137 bwmin = histart + (i-.001)*histep;
138 for (i = n; i-- && histo[i] <= FTINY; )
139 ;
140 bwmax = histart + (i+1.001)*histep;
141 if (bwmax > Bl(LMAX))
142 bwmax = Bl(LMAX);
143 if (bwmin < Bl(LMIN))
144 bwmin = Bl(LMIN);
145 else /* duplicate bottom bin */
146 bwmin = bwmax - (bwmax-bwmin)*HISTRES/(HISTRES-1);
147 /* convert histogram */
148 bwavg = 0.; histot = 0.;
149 for (i = 0; i < HISTRES; i++)
150 bwhist[i] = 0.;
151 for (i = 0, b = histart; i < n; i++, b += histep) {
152 if (b < bwmin+FTINY) continue;
153 if (b >= bwmax-FTINY) break;
154 w = histo[i];
155 bwavg += w*b;
156 bwhist[bwhi(b)] += w;
157 histot += w;
158 }
159 bwavg /= histot;
160 if (bwmin > Bl(LMIN)+FTINY) { /* add false samples at bottom */
161 bwhist[1] *= 0.5;
162 bwhist[0] += bwhist[1];
163 }
164 }
165
166
167 static double
168 centprob( /* center-weighting probability function */
169 int x,
170 int y
171 )
172 {
173 double xr, yr, p;
174 /* paraboloid, 0 at 90 degrees from center */
175 xr = (x - .5*(fvxr-1))/90.; /* 180 degree fisheye has fv?r == 90 */
176 yr = (y - .5*(fvyr-1))/90.;
177 p = 1. - xr*xr - yr*yr;
178 return(p < 0. ? 0. : p);
179 }
180
181
182 static float *
183 getlumsamp(int n) /* get set of random point sample luminances */
184 {
185 float *ls = (float *)malloc(n*sizeof(float));
186 COLR *cscan = (COLR *)malloc(scanlen(&inpres)*sizeof(COLR));
187 long startpos = ftell(infp);
188 long npleft = (long)inpres.xr*inpres.yr;
189 int x;
190
191 if ((ls == NULL) | (cscan == NULL))
192 syserror("malloc");
193 x = 0; /* read/convert samples */
194 while (n > 0) {
195 COLOR col;
196 int sv = 0;
197 double rval, cumprob = 0;
198
199 if (x <= 0 && freadcolrs(cscan, x=scanlen(&inpres), infp) < 0) {
200 fprintf(stderr, "%s: %s: scanline read error\n",
201 progname, infn);
202 exit(1);
203 }
204 rval = frandom(); /* random distance to next sample */
205 while ((cumprob += (1.-cumprob)*n/(npleft-sv)) < rval)
206 sv++;
207 if (x < ++sv) { /* out of pixels in this scanline */
208 npleft -= x;
209 x = 0;
210 continue;
211 }
212 x -= sv;
213 colr_color(col, cscan[x]);
214 ls[--n] = plum(col);
215 npleft -= sv;
216 }
217 free(cscan); /* clean up and reset file pointer */
218 if (fseek(infp, startpos, SEEK_SET) < 0)
219 syserror("fseek");
220 return(ls);
221 }
222
223
224 void
225 comphist(void) /* create foveal sampling histogram */
226 {
227 double l, b, w, lwmin, lwmax;
228 float *lumsamp;
229 int nlumsamp;
230 int x, y;
231 /* check for precalculated histogram */
232 if (what2do&DO_PREHIST)
233 return;
234 lwmin = 1e10; /* find extrema */
235 lwmax = 0.;
236 for (y = 0; y < fvyr; y++)
237 for (x = 0; x < fvxr; x++) {
238 l = plum(fovscan(y)[x]);
239 if (l < lwmin) lwmin = l;
240 if (l > lwmax) lwmax = l;
241 }
242 /* sample luminance pixels */
243 nlumsamp = fvxr*fvyr*16;
244 if (nlumsamp > inpres.xr*inpres.yr)
245 nlumsamp = inpres.xr*inpres.yr;
246 lumsamp = getlumsamp(nlumsamp);
247 for (x = nlumsamp; x--; ) {
248 l = lumsamp[x];
249 if (l < lwmin) lwmin = l;
250 if (l > lwmax) lwmax = l;
251 }
252 lwmax *= 1.01;
253 if (lwmax > LMAX)
254 lwmax = LMAX;
255 bwmax = Bl(lwmax);
256 if (lwmin < LMIN) {
257 lwmin = LMIN;
258 bwmin = Bl(LMIN);
259 } else { /* duplicate bottom bin */
260 bwmin = bwmax - (bwmax-Bl(lwmin))*HISTRES/(HISTRES-1);
261 lwmin = Lb(bwmin);
262 }
263 /* (re)compute histogram */
264 bwavg = 0.;
265 histot = 0.;
266 memset(bwhist, 0, sizeof(bwhist));
267 /* global average */
268 if (!(what2do&DO_FIXHIST) || fixfrac < 1.-FTINY) {
269 for (y = 0; y < fvyr; y++)
270 for (x = 0; x < fvxr; x++) {
271 l = plum(fovscan(y)[x]);
272 if (l < lwmin+FTINY) continue;
273 if (l >= lwmax-FTINY) continue;
274 b = Bl(l);
275 w = what2do&DO_CWEIGHT ? centprob(x,y) : 1.;
276 bwavg += w*b;
277 bwhist[bwhi(b)] += w;
278 histot += w;
279 }
280 /* weight for point luminances */
281 w = 1. * histot / nlumsamp;
282 for (x = nlumsamp; x--; ) {
283 l = lumsamp[x];
284 if (l < lwmin+FTINY) continue;
285 if (l >= lwmax-FTINY) continue;
286 b = Bl(l);
287 bwavg += w*b;
288 bwhist[bwhi(b)] += w;
289 histot += w;
290 }
291 }
292 /* average fixation points */
293 if (what2do&DO_FIXHIST && nfixations > 0) {
294 if (histot > FTINY)
295 w = fixfrac/(1.-fixfrac)*histot/nfixations;
296 else
297 w = 1.;
298 for (x = 0; x < nfixations; x++) {
299 l = plum(fovscan(fixlst[x][1])[fixlst[x][0]]);
300 if (l < lwmin+FTINY) continue;
301 if (l >= lwmax-FTINY) continue;
302 b = Bl(l);
303 bwavg += w*b;
304 bwhist[bwhi(b)] += w;
305 histot += w;
306 }
307 }
308 bwavg /= histot;
309 if (lwmin > LMIN+FTINY) { /* add false samples at bottom */
310 bwhist[1] *= 0.5;
311 bwhist[0] += bwhist[1];
312 }
313 free(lumsamp);
314 }
315
316
317 static void
318 mkcumf(void) /* make cumulative distribution function */
319 {
320 int i;
321 double sum;
322
323 mhistot = 0.; /* compute modified total */
324 for (i = 0; i < HISTRES; i++)
325 mhistot += modhist[i];
326
327 sum = 0.; /* compute cumulative function */
328 for (i = 0; i < HISTRES; i++) {
329 cumf[i] = sum/mhistot;
330 sum += modhist[i];
331 }
332 cumf[HISTRES] = 1.;
333 }
334
335
336 static double
337 cf( /* return cumulative function at b */
338 double b
339 )
340 {
341 double x;
342 int i;
343
344 i = x = HISTRES*(b - bwmin)/(bwmax - bwmin);
345 x -= (double)i;
346 return(cumf[i]*(1.-x) + cumf[i+1]*x);
347 }
348
349
350 static double
351 BLw( /* map world luminance to display brightness */
352 double Lw
353 )
354 {
355 double b;
356
357 if (Lw <= LMIN || (b = Bl(Lw)) <= bwmin+FTINY)
358 return(Bldmin);
359 if (b >= bwmax-FTINY)
360 return(Bldmax);
361 return(Bldmin + cf(b)*(Bldmax-Bldmin));
362 }
363
364
365 double
366 htcontrs( /* human threshold contrast sensitivity, dL(La) */
367 double La
368 )
369 {
370 double l10La, l10dL;
371 /* formula taken from Ferwerda et al. [SG96] */
372 if (La < 1.148e-4)
373 return(1.38e-3);
374 l10La = log10(La);
375 if (l10La < -1.44) /* rod response regime */
376 l10dL = pow(.405*l10La + 1.6, 2.18) - 2.86;
377 else if (l10La < -.0184)
378 l10dL = l10La - .395;
379 else if (l10La < 1.9) /* cone response regime */
380 l10dL = pow(.249*l10La + .65, 2.7) - .72;
381 else
382 l10dL = l10La - 1.255;
383
384 return(exp10(l10dL));
385 }
386
387
388 double
389 clampf( /* histogram clamping function */
390 double Lw
391 )
392 {
393 double bLw, ratio;
394
395 bLw = BLw(Lw); /* apply current brightness map */
396 ratio = what2do&DO_HSENS ? htcontrs(Lb(bLw))/htcontrs(Lw) : Lb(bLw)/Lw;
397 return(ratio/(Lb1(bLw)*(Bldmax-Bldmin)*Bl1(Lw)));
398 }
399
400 double
401 crfactor( /* contrast reduction factor */
402 double Lw
403 )
404 {
405 int i = HISTRES*(Bl(Lw) - bwmin)/(bwmax - bwmin);
406 double bLw, ratio, Tdb;
407
408 if (i <= 0)
409 return(1.0);
410 if (i >= HISTRES)
411 return(1.0);
412 bLw = BLw(Lw);
413 ratio = what2do&DO_HSENS ? htcontrs(Lb(bLw))/htcontrs(Lw) : Lb(bLw)/Lw;
414 Tdb = mhistot * (bwmax - bwmin) / HISTRES;
415 return(modhist[i]*Lb1(bLw)*(Bldmax-Bldmin)*Bl1(Lw)/(Tdb*ratio));
416 }
417
418
419 #if ADJ_VEIL
420 static void
421 mkcrfimage(void) /* compute contrast reduction factor image */
422 {
423 int i;
424 float *crfptr;
425 COLOR *fovptr;
426
427 if (crfimg == NULL)
428 crfimg = (float *)malloc(fvxr*fvyr*sizeof(float));
429 if (crfimg == NULL)
430 syserror("malloc");
431 crfptr = crfimg;
432 fovptr = fovimg;
433 for (i = fvxr*fvyr; i--; crfptr++, fovptr++)
434 crfptr[0] = crfactor(plum(fovptr[0]));
435 }
436 #endif
437
438
439 int
440 mkbrmap(void) /* make dynamic range map */
441 {
442 double Tdb, b, s;
443 double ceiling, trimmings;
444 int i;
445 /* copy initial histogram */
446 memcpy((void *)modhist, (void *)bwhist, sizeof(modhist));
447 s = (bwmax - bwmin)/HISTRES; /* s is delta b */
448 /* loop until satisfactory */
449 do {
450 mkcumf(); /* sync brightness mapping */
451 if (mhistot <= histot*CVRATIO)
452 return(-1); /* no compression needed! */
453 Tdb = mhistot * s;
454 trimmings = 0.; /* clip to envelope */
455 for (i = 0, b = bwmin + .5*s; i < HISTRES; i++, b += s) {
456 ceiling = Tdb*clampf(Lb(b));
457 if (modhist[i] > ceiling) {
458 trimmings += modhist[i] - ceiling;
459 modhist[i] = ceiling;
460 }
461 }
462 } while (trimmings > mhistot*CVRATIO);
463
464 #if ADJ_VEIL
465 mkcrfimage(); /* contrast reduction image */
466 #endif
467
468 return(0); /* we got it */
469 }
470
471
472 void
473 scotscan( /* apply scotopic color sensitivity loss */
474 COLOR *scan,
475 int xres
476 )
477 {
478 COLOR ctmp;
479 double incolor, b, Lw;
480 int i;
481
482 for (i = 0; i < xres; i++) {
483 Lw = plum(scan[i]);
484 if (Lw >= TopMesopic)
485 incolor = 1.;
486 else if (Lw <= BotMesopic)
487 incolor = 0.;
488 else
489 incolor = (Lw - BotMesopic) /
490 (TopMesopic - BotMesopic);
491 if (incolor < 1.-FTINY) {
492 b = (1.-incolor)*slum(scan[i])*inpexp/SWNORM;
493 if (lumf == rgblum) b /= WHTEFFICACY;
494 setcolor(ctmp, b, b, b);
495 if (incolor <= FTINY)
496 setcolor(scan[i], 0., 0., 0.);
497 else
498 scalecolor(scan[i], incolor);
499 addcolor(scan[i], ctmp);
500 }
501 }
502 }
503
504
505 void
506 mapscan( /* apply tone mapping operator to scanline */
507 COLOR *scan,
508 int xres
509 )
510 {
511 double mult, Lw, b;
512 int x;
513
514 for (x = 0; x < xres; x++) {
515 Lw = plum(scan[x]);
516 if (Lw < LMIN) {
517 setcolor(scan[x], 0., 0., 0.);
518 continue;
519 }
520 b = BLw(Lw); /* apply brightness mapping */
521 mult = (Lb(b) - ldmin)/(ldmax - ldmin)/(Lw*inpexp);
522 if (lumf == rgblum) mult *= WHTEFFICACY;
523 scalecolor(scan[x], mult);
524 }
525 }
526
527
528 void
529 putmapping( /* put out mapping function */
530 FILE *fp
531 )
532 {
533 double b, s;
534 int i;
535 double wlum, sf, dlum;
536
537 sf = scalef*inpexp;
538 if (lumf == cielum) sf *= WHTEFFICACY;
539 s = (bwmax - bwmin)/HISTRES;
540 for (i = 0, b = bwmin + .5*s; i < HISTRES; i++, b += s) {
541 wlum = Lb(b);
542 if (what2do&DO_LINEAR) {
543 dlum = sf*wlum;
544 if (dlum > ldmax) dlum = ldmax;
545 else if (dlum < ldmin) dlum = ldmin;
546 fprintf(fp, "%e %e\n", wlum, dlum);
547 } else
548 fprintf(fp, "%e %e\n", wlum, Lb(BLw(wlum)));
549 }
550 }