ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrbf.c
Revision: 2.27
Committed: Fri Jan 29 16:21:55 2016 UTC (8 years, 3 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.26: +81 -11 lines
Log Message:
Updated pabopto2bsdf to handle tristimulus color -- change in .sir format

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.27 static const char RCSid[] = "$Id: bsdfrbf.c,v 2.26 2014/08/22 05:38:44 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * Radial basis function representation for BSDF data.
6     *
7     * G. Ward
8     */
9    
10 greg 2.13 /****************************************************************
11     1) Collect samples into a grid using the Shirley-Chiu
12     angular mapping from a hemisphere to a square.
13    
14     2) Compute an adaptive quadtree by subdividing the grid so that
15     each leaf node has at least one sample up to as many
16     samples as fit nicely on a plane to within a certain
17     MSE tolerance.
18    
19     3) Place one Gaussian lobe at each leaf node in the quadtree,
20     sizing it to have a radius equal to the leaf size and
21     a volume equal to the energy in that node.
22     *****************************************************************/
23    
24 greg 2.1 #define _USE_MATH_DEFINES
25     #include <stdio.h>
26     #include <stdlib.h>
27     #include <string.h>
28     #include <math.h>
29     #include "bsdfrep.h"
30    
31 greg 2.12 #ifndef RSCA
32 greg 2.25 #define RSCA 2.0 /* radius scaling factor (empirical) */
33 greg 2.9 #endif
34 greg 2.12 #ifndef SMOOTH_MSE
35 greg 2.19 #define SMOOTH_MSE 5e-5 /* acceptable mean squared error */
36 greg 2.1 #endif
37 greg 2.12 #ifndef SMOOTH_MSER
38 greg 2.18 #define SMOOTH_MSER 0.03 /* acceptable relative MSE */
39 greg 2.7 #endif
40 greg 2.12 #define MAX_RAD (GRIDRES/8) /* maximum lobe radius */
41    
42     #define RBFALLOCB 10 /* RBF allocation block size */
43    
44 greg 2.25 /* loaded grid or comparison DSFs */
45 greg 2.1 GRIDVAL dsf_grid[GRIDRES][GRIDRES];
46 greg 2.27 /* allocated chrominance sums if any */
47     float (*spec_grid)[GRIDRES][GRIDRES];
48     int nspec_grid = 0;
49    
50     /* Set up visible spectrum sampling */
51     void
52     set_spectral_samples(int nspec)
53     {
54     if (rbf_colorimetry == RBCunknown) {
55     if (nspec_grid > 0) {
56     free(spec_grid);
57     spec_grid = NULL;
58     nspec_grid = 0;
59     }
60     if (nspec == 1) {
61     rbf_colorimetry = RBCphotopic;
62     return;
63     }
64     if (nspec == 3) {
65     rbf_colorimetry = RBCtristimulus;
66     spec_grid = (float (*)[GRIDRES][GRIDRES])calloc(
67     2*GRIDRES*GRIDRES, sizeof(float) );
68     if (spec_grid == NULL)
69     goto mem_error;
70     nspec_grid = 2;
71     return;
72     }
73     fprintf(stderr,
74     "%s: only 1 or 3 spectral samples currently supported\n",
75     progname);
76     exit(1);
77     }
78     if (nspec != nspec_grid+1) {
79     fprintf(stderr,
80     "%s: number of spectral samples cannot be changed\n",
81     progname);
82     exit(1);
83     }
84     return;
85     mem_error:
86     fprintf(stderr, "%s: out of memory in set_spectral_samples()\n",
87     progname);
88     exit(1);
89     }
90 greg 2.1
91     /* Start new DSF input grid */
92     void
93     new_bsdf_data(double new_theta, double new_phi)
94     {
95     if (!new_input_direction(new_theta, new_phi))
96     exit(1);
97     memset(dsf_grid, 0, sizeof(dsf_grid));
98 greg 2.27 if (nspec_grid > 0)
99     memset(spec_grid, 0, sizeof(float)*GRIDRES*GRIDRES*nspec_grid);
100 greg 2.1 }
101    
102     /* Add BSDF data point */
103     void
104 greg 2.27 add_bsdf_data(double theta_out, double phi_out, const double val[], int isDSF)
105 greg 2.1 {
106     FVECT ovec;
107 greg 2.27 double cfact, Yval;
108 greg 2.1 int pos[2];
109    
110 greg 2.27 if (nspec_grid > 2) {
111     fprintf(stderr, "%s: unsupported color space\n", progname);
112     exit(1);
113     }
114 greg 2.1 if (!output_orient) /* check output orientation */
115     output_orient = 1 - 2*(theta_out > 90.);
116     else if (output_orient > 0 ^ theta_out < 90.) {
117 greg 2.27 fprintf(stderr,
118     "%s: cannot handle output angles on both sides of surface\n",
119     progname);
120 greg 2.1 exit(1);
121     }
122     ovec[2] = sin((M_PI/180.)*theta_out);
123     ovec[0] = cos((M_PI/180.)*phi_out) * ovec[2];
124     ovec[1] = sin((M_PI/180.)*phi_out) * ovec[2];
125     ovec[2] = sqrt(1. - ovec[2]*ovec[2]);
126 greg 2.27 /* BSDF to DSF correction */
127     cfact = isDSF ? 1. : ovec[2];
128 greg 2.1
129 greg 2.27 Yval = cfact * val[rbf_colorimetry==RBCtristimulus];
130 greg 2.4 /* update BSDF histogram */
131 greg 2.27 if (BSDF2SML*ovec[2] < Yval && Yval < BSDF2BIG*ovec[2])
132     ++bsdf_hist[histndx(Yval/ovec[2])];
133 greg 2.4
134 greg 2.1 pos_from_vec(pos, ovec);
135    
136 greg 2.27 dsf_grid[pos[0]][pos[1]].sum.v += Yval;
137 greg 2.20 dsf_grid[pos[0]][pos[1]].sum.n++;
138 greg 2.27 /* add in X and Z values */
139     if (rbf_colorimetry == RBCtristimulus) {
140     spec_grid[0][pos[0]][pos[1]] += val[0];
141     spec_grid[1][pos[0]][pos[1]] += val[2];
142     }
143 greg 2.1 }
144    
145 greg 2.11 /* Compute minimum BSDF from histogram (does not clear) */
146 greg 2.5 static void
147     comp_bsdf_min()
148     {
149 greg 2.17 unsigned long cnt, target;
150     int i;
151 greg 2.5
152     cnt = 0;
153     for (i = HISTLEN; i--; )
154     cnt += bsdf_hist[i];
155     if (!cnt) { /* shouldn't happen */
156     bsdf_min = 0;
157     return;
158     }
159     target = cnt/100; /* ignore bottom 1% */
160     cnt = 0;
161     for (i = 0; cnt <= target; i++)
162     cnt += bsdf_hist[i];
163     bsdf_min = histval(i-1);
164     }
165    
166 greg 2.12 /* Determine if the given region is empty of grid samples */
167 greg 2.6 static int
168 greg 2.12 empty_region(int x0, int x1, int y0, int y1)
169 greg 2.6 {
170 greg 2.12 int x, y;
171    
172     for (x = x0; x < x1; x++)
173     for (y = y0; y < y1; y++)
174 greg 2.20 if (dsf_grid[x][y].sum.n)
175 greg 2.12 return(0);
176     return(1);
177     }
178    
179     /* Determine if the given region is smooth enough to be a single lobe */
180     static int
181     smooth_region(int x0, int x1, int y0, int y1)
182     {
183     RREAL rMtx[3][3];
184     FVECT xvec;
185     double A, B, C, nvs, sqerr;
186     int x, y, n;
187     /* compute planar regression */
188     memset(rMtx, 0, sizeof(rMtx));
189     memset(xvec, 0, sizeof(xvec));
190     for (x = x0; x < x1; x++)
191     for (y = y0; y < y1; y++)
192 greg 2.20 if ((n = dsf_grid[x][y].sum.n) > 0) {
193     double z = dsf_grid[x][y].sum.v;
194 greg 2.13 rMtx[0][0] += x*x*(double)n;
195     rMtx[0][1] += x*y*(double)n;
196     rMtx[0][2] += x*(double)n;
197     rMtx[1][1] += y*y*(double)n;
198     rMtx[1][2] += y*(double)n;
199     rMtx[2][2] += (double)n;
200 greg 2.12 xvec[0] += x*z;
201     xvec[1] += y*z;
202     xvec[2] += z;
203     }
204     rMtx[1][0] = rMtx[0][1];
205 greg 2.15 rMtx[2][0] = rMtx[0][2];
206 greg 2.12 rMtx[2][1] = rMtx[1][2];
207     nvs = rMtx[2][2];
208     if (SDinvXform(rMtx, rMtx) != SDEnone)
209 greg 2.16 return(1); /* colinear values */
210 greg 2.12 A = DOT(rMtx[0], xvec);
211     B = DOT(rMtx[1], xvec);
212     C = DOT(rMtx[2], xvec);
213     sqerr = 0.0; /* compute mean squared error */
214     for (x = x0; x < x1; x++)
215     for (y = y0; y < y1; y++)
216 greg 2.20 if ((n = dsf_grid[x][y].sum.n) > 0) {
217     double d = A*x + B*y + C - dsf_grid[x][y].sum.v/n;
218 greg 2.12 sqerr += n*d*d;
219 greg 2.6 }
220 greg 2.12 if (sqerr <= nvs*SMOOTH_MSE) /* below absolute MSE threshold? */
221     return(1);
222 greg 2.13 /* OR below relative MSE threshold? */
223 greg 2.12 return(sqerr*nvs <= xvec[2]*xvec[2]*SMOOTH_MSER);
224     }
225    
226     /* Create new lobe based on integrated samples in region */
227 greg 2.21 static int
228 greg 2.12 create_lobe(RBFVAL *rvp, int x0, int x1, int y0, int y1)
229     {
230     double vtot = 0.0;
231 greg 2.27 double CIEXtot = 0.0, CIEZtot = 0.0;
232 greg 2.12 int nv = 0;
233 greg 2.25 double wxsum = 0.0, wysum = 0.0, wtsum = 0.0;
234 greg 2.12 double rad;
235     int x, y;
236     /* compute average for region */
237     for (x = x0; x < x1; x++)
238 greg 2.25 for (y = y0; y < y1; y++)
239     if (dsf_grid[x][y].sum.n) {
240     const double v = dsf_grid[x][y].sum.v;
241     const int n = dsf_grid[x][y].sum.n;
242    
243     if (v > 0) {
244 greg 2.27 const double wt = v / (double)n;
245 greg 2.25 wxsum += wt * x;
246     wysum += wt * y;
247     wtsum += wt;
248     }
249     vtot += v;
250     nv += n;
251 greg 2.27 if (rbf_colorimetry == RBCtristimulus) {
252     CIEXtot += spec_grid[0][x][y];
253     CIEZtot += spec_grid[1][x][y];
254     }
255 greg 2.25 }
256 greg 2.12 if (!nv) {
257     fprintf(stderr, "%s: internal - missing samples in create_lobe\n",
258     progname);
259     exit(1);
260 greg 2.6 }
261 greg 2.21 if (vtot <= 0) /* only create positive lobes */
262     return(0);
263 greg 2.27 /* assign color */
264     if (rbf_colorimetry == RBCtristimulus) {
265     const double df = 1.0 / (CIEXtot + vtot + CIEZtot);
266     C_COLOR cclr;
267     c_cset(&cclr, CIEXtot*df, vtot*df);
268     rvp->chroma = c_encodeChroma(&cclr);
269     } else
270     rvp->chroma = c_dfchroma;
271 greg 2.12 /* peak value based on integral */
272     vtot *= (x1-x0)*(y1-y0)*(2.*M_PI/GRIDRES/GRIDRES)/(double)nv;
273     rad = (RSCA/(double)GRIDRES)*(x1-x0);
274     rvp->peak = vtot / ((2.*M_PI) * rad*rad);
275 greg 2.25 rvp->crad = ANG2R(rad); /* put peak at centroid */
276     rvp->gx = (int)(wxsum/wtsum + .5);
277     rvp->gy = (int)(wysum/wtsum + .5);
278 greg 2.21 return(1);
279 greg 2.6 }
280    
281 greg 2.12 /* Recursive function to build radial basis function representation */
282 greg 2.6 static int
283 greg 2.12 build_rbfrep(RBFVAL **arp, int *np, int x0, int x1, int y0, int y1)
284 greg 2.6 {
285 greg 2.12 int xmid = (x0+x1)>>1;
286     int ymid = (y0+y1)>>1;
287     int branched[4];
288     int nadded, nleaves;
289     /* need to make this a leaf? */
290     if (empty_region(x0, xmid, y0, ymid) ||
291     empty_region(xmid, x1, y0, ymid) ||
292     empty_region(x0, xmid, ymid, y1) ||
293     empty_region(xmid, x1, ymid, y1))
294     return(0);
295     /* add children (branches+leaves) */
296     if ((branched[0] = build_rbfrep(arp, np, x0, xmid, y0, ymid)) < 0)
297     return(-1);
298     if ((branched[1] = build_rbfrep(arp, np, xmid, x1, y0, ymid)) < 0)
299     return(-1);
300     if ((branched[2] = build_rbfrep(arp, np, x0, xmid, ymid, y1)) < 0)
301     return(-1);
302     if ((branched[3] = build_rbfrep(arp, np, xmid, x1, ymid, y1)) < 0)
303     return(-1);
304     nadded = branched[0] + branched[1] + branched[2] + branched[3];
305     nleaves = !branched[0] + !branched[1] + !branched[2] + !branched[3];
306     if (!nleaves) /* nothing but branches? */
307     return(nadded);
308     /* combine 4 leaves into 1? */
309 greg 2.14 if ((nleaves == 4) & (x1-x0 <= MAX_RAD) &&
310     smooth_region(x0, x1, y0, y1))
311 greg 2.12 return(0);
312     /* need more array space? */
313     if ((*np+nleaves-1)>>RBFALLOCB != (*np-1)>>RBFALLOCB) {
314     *arp = (RBFVAL *)realloc(*arp,
315     sizeof(RBFVAL)*(*np+nleaves-1+(1<<RBFALLOCB)));
316     if (*arp == NULL)
317     return(-1);
318 greg 2.6 }
319 greg 2.12 /* create lobes for leaves */
320 greg 2.21 if (!branched[0] && create_lobe(*arp+*np, x0, xmid, y0, ymid)) {
321     ++(*np); ++nadded;
322     }
323     if (!branched[1] && create_lobe(*arp+*np, xmid, x1, y0, ymid)) {
324     ++(*np); ++nadded;
325     }
326     if (!branched[2] && create_lobe(*arp+*np, x0, xmid, ymid, y1)) {
327     ++(*np); ++nadded;
328     }
329     if (!branched[3] && create_lobe(*arp+*np, xmid, x1, ymid, y1)) {
330     ++(*np); ++nadded;
331     }
332 greg 2.12 return(nadded);
333 greg 2.6 }
334    
335 greg 2.1 /* Count up filled nodes and build RBF representation from current grid */
336     RBFNODE *
337 greg 2.12 make_rbfrep()
338 greg 2.1 {
339 greg 2.12 RBFNODE *newnode;
340     RBFVAL *rbfarr;
341 greg 2.1 int nn;
342 greg 2.5 /* compute minimum BSDF */
343     comp_bsdf_min();
344 greg 2.12 /* create RBF node list */
345     rbfarr = NULL; nn = 0;
346 greg 2.22 if (build_rbfrep(&rbfarr, &nn, 0, GRIDRES, 0, GRIDRES) <= 0) {
347     if (nn)
348     goto memerr;
349 greg 2.24 fprintf(stderr,
350     "%s: warning - skipping bad incidence (%.1f,%.1f)\n",
351     progname, theta_in_deg, phi_in_deg);
352     return(NULL);
353 greg 2.22 }
354 greg 2.12 /* (re)allocate RBF array */
355     newnode = (RBFNODE *)realloc(rbfarr,
356     sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
357 greg 2.2 if (newnode == NULL)
358     goto memerr;
359 greg 2.12 /* copy computed lobes into RBF node */
360     memmove(newnode->rbfa, newnode, sizeof(RBFVAL)*nn);
361 greg 2.1 newnode->ord = -1;
362     newnode->next = NULL;
363     newnode->ejl = NULL;
364     newnode->invec[2] = sin((M_PI/180.)*theta_in_deg);
365     newnode->invec[0] = cos((M_PI/180.)*phi_in_deg)*newnode->invec[2];
366     newnode->invec[1] = sin((M_PI/180.)*phi_in_deg)*newnode->invec[2];
367     newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
368 greg 2.12 newnode->vtotal = .0;
369 greg 2.1 newnode->nrbf = nn;
370 greg 2.12 /* compute sum for normalization */
371     while (nn-- > 0)
372     newnode->vtotal += rbf_volume(&newnode->rbfa[nn]);
373 greg 2.3 #ifdef DEBUG
374 greg 2.12 fprintf(stderr, "Built RBF with %d lobes\n", newnode->nrbf);
375 greg 2.3 fprintf(stderr, "Integrated DSF at (%.1f,%.1f) deg. is %.2f\n",
376     get_theta180(newnode->invec), get_phi360(newnode->invec),
377     newnode->vtotal);
378     #endif
379 greg 2.1 insert_dsf(newnode);
380     return(newnode);
381 greg 2.2 memerr:
382     fprintf(stderr, "%s: Out of memory in make_rbfrep()\n", progname);
383     exit(1);
384 greg 2.1 }