| 11 |  | #include "rterror.h" | 
| 12 |  | #include "platform.h" | 
| 13 |  | #include <stdlib.h> | 
| 14 | + | #include <math.h> | 
| 15 |  |  | 
| 16 |  | float   *datarr;                /* our loaded BSDF data array */ | 
| 17 |  | int     ttrank = 4;             /* tensor tree rank */ | 
| 18 |  | int     log2g = 4;              /* log2 of grid resolution */ | 
| 19 |  | int     infmt = 'a';            /* input format ('a','f','d') */ | 
| 20 | < | double  tthresh = .05;          /* relative acceptance threshold */ | 
| 20 | > | double  pctcull = 95.;          /* target culling percentile */ | 
| 21 |  |  | 
| 22 | < | #define dval3(ix,ox,oy)         datarr[((((ix)<<log2g)+(ox))<<log2g)+(oy)] | 
| 23 | < | #define dval4(ix,iy,ox,oy)      datarr[((((((ix)<<log2g)+(iy))<<log2g)+(ox))<<log2g)+(oy)] | 
| 22 | > | #define dval3(ix,ox,oy)         datarr[((size_t)(((ix)<<log2g)+(ox))<<log2g)+(oy)] | 
| 23 | > | #define dval4(ix,iy,ox,oy)      datarr[((((size_t)(((ix)<<log2g)+(iy))<<log2g)+(ox))<<log2g)+(oy)] | 
| 24 |  |  | 
| 24 | – | #define above_threshold(tp)     ((tp)->vmax - (tp)->vmin > 2.*tthresh*(tp)->vavg) | 
| 25 | – |  | 
| 25 |  | /* Tensor tree node */ | 
| 26 |  | typedef struct ttree_s { | 
| 27 |  | float           vmin, vmax;     /* value extrema */ | 
| 29 |  | struct ttree_s  *kid;           /* 2^ttrank children */ | 
| 30 |  | } TNODE; | 
| 31 |  |  | 
| 32 | + | #define HISTLEN         300     /* histogram resolution */ | 
| 33 | + | #define HISTMAX         4.      /* maximum recorded measure in histogram */ | 
| 34 | + |  | 
| 35 | + | int     histo[HISTLEN];         /* histogram freq. of variance measure */ | 
| 36 | + |  | 
| 37 | + | double  tthresh;                /* acceptance threshold (TBD) */ | 
| 38 | + |  | 
| 39 | + | #define var_measure(tp)         sqrt( ((tp)->vmax - (tp)->vmin) / \ | 
| 40 | + | (sqrt((tp)->vavg) + .03) ) | 
| 41 | + | #define above_threshold(tp)     (var_measure(tp) > tthresh) | 
| 42 | + |  | 
| 43 |  | /* Allocate a new set of children for the given node (no checks) */ | 
| 44 |  | static void | 
| 45 |  | new_kids(TNODE *pn) | 
| 46 |  | { | 
| 47 | < | pn->kid = (TNODE *)calloc(1<<ttrank, sizeof(TNODE)); | 
| 47 | > | pn->kid = (TNODE *)calloc((size_t)1<<ttrank, sizeof(TNODE)); | 
| 48 |  | if (pn->kid == NULL) | 
| 49 |  | error(SYSTEM, "out of memory in new_kids"); | 
| 50 |  | } | 
| 87 |  | tp->vavg += val; | 
| 88 |  | } | 
| 89 |  | tp->vavg /= (float)(1<<ttrank); | 
| 90 | + | /* record stats */ | 
| 91 | + | i = (HISTLEN/HISTMAX) * var_measure(tp); | 
| 92 | + | if (i >= HISTLEN) i = HISTLEN-1; | 
| 93 | + | ++histo[i]; | 
| 94 |  | return; | 
| 95 |  | } | 
| 96 |  | --l2s;                          /* else still branching */ | 
| 106 |  | tp->vavg += tp->kid[i].vavg; | 
| 107 |  | } | 
| 108 |  | tp->vavg /= (float)(1<<ttrank); | 
| 95 | – | /* is variation above threshold? */ | 
| 96 | – | if (!above_threshold(tp)) | 
| 97 | – | free_kids(tp);          /* if not, trim branches */ | 
| 109 |  | } | 
| 110 |  |  | 
| 111 | + | /* Set our trimming threshold */ | 
| 112 | + | static void | 
| 113 | + | set_threshold(void) | 
| 114 | + | { | 
| 115 | + | int     hsum = 0; | 
| 116 | + | int     i; | 
| 117 | + |  | 
| 118 | + | for (i = HISTLEN; i--; ) | 
| 119 | + | hsum += histo[i]; | 
| 120 | + | hsum = pctcull*.01 * (double)hsum; | 
| 121 | + | for (i = 0; hsum > 0; i++) | 
| 122 | + | hsum -= histo[i]; | 
| 123 | + | tthresh = (HISTMAX/HISTLEN) * i; | 
| 124 | + | } | 
| 125 | + |  | 
| 126 | + | /* Trim our tree according to the current threshold */ | 
| 127 | + | static void | 
| 128 | + | trim_tree(TNODE *tp) | 
| 129 | + | { | 
| 130 | + | if (tp->kid == NULL) | 
| 131 | + | return; | 
| 132 | + | if (above_threshold(tp)) {      /* keeping branches? */ | 
| 133 | + | int     i = 1<<ttrank; | 
| 134 | + | while (i--) | 
| 135 | + | trim_tree(tp->kid+i); | 
| 136 | + | return; | 
| 137 | + | } | 
| 138 | + | free_kids(tp);                  /* else trim at this point */ | 
| 139 | + | } | 
| 140 | + |  | 
| 141 |  | /* Print a tensor tree from the given hypercube */ | 
| 142 |  | static void | 
| 143 |  | print_tree(const TNODE *tp, const int bmin[], int l2s) | 
| 152 |  | for (i = 0; i < 1<<ttrank; i++) { | 
| 153 |  | float   val; | 
| 154 |  | for (j = ttrank; j--; ) | 
| 155 | < | bkmin[j] = bmin[j] + (i>>j & 1); | 
| 155 | > | bkmin[j] = bmin[j] + (i>>(ttrank-1-j) & 1); | 
| 156 |  | val = (ttrank == 3) ? dval3(bkmin[0],bkmin[1],bkmin[2]) | 
| 157 |  | : dval4(bkmin[0],bkmin[1],bkmin[2],bkmin[3]); | 
| 158 | < | printf(" %.4e", val); | 
| 158 | > | printf((0.001<=val)&(val<10.) ? " %.7f" : " %.3e", val); | 
| 159 |  | } | 
| 160 |  | fputs(" }\n", stdout); | 
| 161 |  | return; | 
| 201 |  |  | 
| 202 |  | if ((rowp == NULL) | (n <= 0)) | 
| 203 |  | return(0); | 
| 204 | < | nread = fread(rowp, sizeof(float), n, stdin); | 
| 204 | > | nread = getbinary(rowp, sizeof(float), n, stdin); | 
| 205 |  | if (nread != n) | 
| 206 |  | error(USER, "unexpected EOF on float input"); | 
| 207 |  | return(nread); | 
| 223 |  | return(0); | 
| 224 |  | } | 
| 225 |  | if (rblen < n) { | 
| 226 | < | rowbuf = (double *)realloc(rowbuf, sizeof(double)*(rblen=n)); | 
| 226 | > | if (rblen) free(rowbuf); | 
| 227 | > | rowbuf = (double *)malloc(sizeof(double)*(rblen=n)); | 
| 228 |  | if (rowbuf == NULL) | 
| 229 |  | error(SYSTEM, "out of memory in read_double"); | 
| 230 |  | } | 
| 231 | < | nread = fread(rowbuf, sizeof(double), n, stdin); | 
| 231 | > | nread = getbinary(rowbuf, sizeof(double), n, stdin); | 
| 232 |  | if (nread != n) | 
| 233 |  | error(USER, "unexpected EOF on double input"); | 
| 234 |  | for (i = 0; i < nread; i++) | 
| 236 |  | return(nread); | 
| 237 |  | } | 
| 238 |  |  | 
| 239 | + | /* Truncate any negative values to zero */ | 
| 240 | + | static void | 
| 241 | + | noneg(float *varr, int n) | 
| 242 | + | { | 
| 243 | + | int     nnan = 0; | 
| 244 | + |  | 
| 245 | + | while (n-- > 0) { | 
| 246 | + | #ifdef isnan | 
| 247 | + | if (isnan(*varr)) { | 
| 248 | + | *varr = 0; | 
| 249 | + | ++nnan; | 
| 250 | + | } else | 
| 251 | + | #endif | 
| 252 | + | if (*varr < 0) *varr = 0; | 
| 253 | + | ++varr; | 
| 254 | + | } | 
| 255 | + | if (nnan) | 
| 256 | + | fprintf(stderr, "Warning: BSDF data contains %d NaN values\n", | 
| 257 | + | nnan); | 
| 258 | + | } | 
| 259 | + |  | 
| 260 |  | /* Load data array, filling zeroes for rank 3 demi-tensor */ | 
| 261 |  | static void | 
| 262 | < | load_data() | 
| 262 | > | load_data(void) | 
| 263 |  | { | 
| 264 |  | int     (*readf)(float *, int) = NULL; | 
| 265 |  |  | 
| 277 |  | error(COMMAND, "unsupported input format"); | 
| 278 |  | break; | 
| 279 |  | } | 
| 280 | < | datarr = (float *)calloc(1<<(log2g*ttrank), sizeof(float)); | 
| 280 | > | datarr = (float *)calloc((size_t)1<<(log2g*ttrank), sizeof(float)); | 
| 281 |  | if (datarr == NULL) | 
| 282 |  | error(SYSTEM, "out of memory in load_data"); | 
| 283 |  | if (ttrank == 3) { | 
| 284 |  | int     ix, ox; | 
| 285 | < | for (ix = 0; ix < 1<<log2g; ix++) | 
| 285 | > | for (ix = 0; ix < 1<<(log2g-1); ix++) | 
| 286 |  | for (ox = 0; ox < 1<<log2g; ox++) | 
| 287 | < | (*readf)(datarr+((((ix)<<log2g)+(ox))<<log2g), | 
| 225 | < | 1<<(log2g-1)); | 
| 287 | > | (*readf)(&dval3(ix,ox,0), 1<<log2g); | 
| 288 |  | } else /* ttrank == 4 */ { | 
| 289 |  | int     ix, iy, ox; | 
| 290 |  | for (ix = 0; ix < 1<<log2g; ix++) | 
| 291 |  | for (iy = 0; iy < 1<<log2g; iy++) | 
| 292 |  | for (ox = 0; ox < 1<<log2g; ox++) | 
| 293 | < | (*readf)(datarr + | 
| 232 | < | ((((((ix)<<log2g)+(iy))<<log2g)+(ox))<<log2g), | 
| 233 | < | 1<<log2g); | 
| 293 | > | (*readf)(&dval4(ix,iy,ox,0), 1<<log2g); | 
| 294 |  | } | 
| 295 |  | (*readf)(NULL, 0);      /* releases any buffers */ | 
| 296 |  | if (infmt == 'a') { | 
| 308 |  | } | 
| 309 |  | } else if (getc(stdin) != EOF) | 
| 310 |  | error(WARNING, "binary data past end of expected input"); | 
| 311 | + |  | 
| 312 | + | noneg(datarr, 1<<(log2g*ttrank));       /* take precautions */ | 
| 313 |  | } | 
| 314 |  |  | 
| 315 | + | /* Enforce reciprocity by averaging data values */ | 
| 316 | + | static void | 
| 317 | + | do_reciprocity(void) | 
| 318 | + | { | 
| 319 | + | const int       siz = 1<<log2g; | 
| 320 | + | float           *v1p, *v2p; | 
| 321 | + |  | 
| 322 | + | if (ttrank == 3) { | 
| 323 | + | int     ix, ox, oy; | 
| 324 | + | for (ix = 0; ix < siz>>1; ix++) | 
| 325 | + | for (ox = 0; ox < siz; ox++) | 
| 326 | + | for (oy = 0; oy < siz>>1; oy++) { | 
| 327 | + | v1p = &dval3(ix,ox,oy); | 
| 328 | + | v2p = &dval3(ix,ox,siz-1-oy); | 
| 329 | + | *v1p = *v2p = .5f*( *v1p + *v2p ); | 
| 330 | + | } | 
| 331 | + | } else /* ttrank == 4 */ { | 
| 332 | + | int     ix, iy, ox, oy; | 
| 333 | + | for (ix = 0; ix < siz; ix++) | 
| 334 | + | for (iy = 0; iy < siz; iy++) { | 
| 335 | + | int     cnt = ix*siz + iy; | 
| 336 | + | for (ox = 0; cnt > 0; ox++) | 
| 337 | + | for (oy = 0; oy < siz; oy++) { | 
| 338 | + | v1p = &dval4(siz-1-ix,siz-1-iy,ox,oy); | 
| 339 | + | v2p = &dval4(siz-1-ox,siz-1-oy,ix,iy); | 
| 340 | + | *v1p = *v2p = .5f*( *v1p + *v2p ); | 
| 341 | + | if (--cnt <= 0) | 
| 342 | + | break; | 
| 343 | + | } | 
| 344 | + | } | 
| 345 | + | } | 
| 346 | + | } | 
| 347 | + |  | 
| 348 |  | /* Load BSDF array, coalesce uniform regions and format as tensor tree */ | 
| 349 |  | int | 
| 350 |  | main(int argc, char *argv[]) | 
| 351 |  | { | 
| 352 |  | int     doheader = 1; | 
| 353 | + | int     recipavg = 0; | 
| 354 |  | int     bmin[4]; | 
| 355 |  | TNODE   gtree; | 
| 356 |  | int     i; | 
| 357 |  | /* get options and parameters */ | 
| 358 |  | for (i = 1; i < argc && argv[i][0] == '-'; i++) | 
| 359 |  | switch (argv[i][1]) { | 
| 360 | + | case 'a': | 
| 361 | + | recipavg = !recipavg; | 
| 362 | + | break; | 
| 363 |  | case 'h': | 
| 364 |  | doheader = !doheader; | 
| 365 |  | break; | 
| 374 |  | goto userr; | 
| 375 |  | break; | 
| 376 |  | case 't': | 
| 377 | < | tthresh = atof(argv[++i]); | 
| 378 | < | if (tthresh <= 0) | 
| 377 | > | pctcull = atof(argv[++i]); | 
| 378 | > | if ((pctcull < 0) | (pctcull >= 100.)) | 
| 379 |  | goto userr; | 
| 380 |  | break; | 
| 381 |  | case 'f': | 
| 389 |  | if (i < argc-1) | 
| 390 |  | goto userr; | 
| 391 |  | /* load input data */ | 
| 392 | < | if (i == argc-1 && freopen(argv[i], "rb", stdin) == NULL) { | 
| 392 | > | if (i == argc-1 && freopen(argv[i], "r", stdin) == NULL) { | 
| 393 |  | sprintf(errmsg, "cannot open input file '%s'", argv[i]); | 
| 394 |  | error(SYSTEM, errmsg); | 
| 395 |  | } | 
| 396 |  | if (infmt != 'a') | 
| 397 |  | SET_FILE_BINARY(stdin); | 
| 398 | + | #ifdef getc_unlocked                    /* avoid lock/unlock overhead */ | 
| 399 | + | flockfile(stdin); | 
| 400 | + | #endif | 
| 401 |  | load_data(); | 
| 402 | + | if (recipavg) | 
| 403 | + | do_reciprocity(); | 
| 404 |  | if (doheader) { | 
| 405 |  | for (i = 0; i < argc; i++) { | 
| 406 |  | fputs(argv[i], stdout); | 
| 411 |  | gtree.kid = NULL;               /* create our tree */ | 
| 412 |  | bmin[0] = bmin[1] = bmin[2] = bmin[3] = 0; | 
| 413 |  | build_tree(>ree, bmin, log2g); | 
| 414 | + | /* compute threshold & trim tree */ | 
| 415 | + | set_threshold(); | 
| 416 | + | trim_tree(>ree); | 
| 417 |  | /* format to stdout */ | 
| 418 |  | print_tree(>ree, bmin, log2g); | 
| 419 |  | /* Clean up isn't necessary for main()... | 
| 422 |  | */ | 
| 423 |  | return(0); | 
| 424 |  | userr: | 
| 425 | < | fprintf(stderr, "Usage: %s [-h][-f{a|f|d}][-r {3|4}][-g log2grid][-t thresh] [input]\n", | 
| 425 | > | fprintf(stderr, "Usage: %s [-h][-a][-f{a|f|d}][-r {3|4}][-g log2grid][-t trim%%] [input]\n", | 
| 426 |  | argv[0]); | 
| 427 |  | return(1); | 
| 428 |  | } |