5 //also includes opengl if available
11 #define M_PI 3.1415926535897932384626433832795
25 LightMap::LightMap(const LightMap
&a
)
30 map
.setImage(cvCloneImage(a
.map
.getIm()));
39 IplImage
*im
= map
.getIm();
40 if(im
) cvReleaseImage(&im
);
41 if (lightParams
) cvReleaseMat(&lightParams
);
42 if (normals
) delete normals
;
45 void LightMap::clear()
47 IplImage
* im
= map
.getIm();
48 if ( im
) cvReleaseImage( &im
);
51 if (normals
) delete normals
;
55 cvReleaseMat(&lightParams
);
60 bool LightMap::init(int nbCam
, IplImage
*model
, float corners
[4][2], int nx
, int ny
)
62 IplImage
*im
= map
.getIm();
63 if(im
) cvReleaseImage(&im
);
64 map
.setImage(cvCreateImage(cvSize(128,64), IPL_DEPTH_32F
, model
->nChannels
));
65 cvSet(map
.getIm(), cvScalarAll(.1));
66 if (model
->nChannels
== 3) {
68 im
->channelSeq
[0]='B';
69 im
->channelSeq
[1]='G';
70 im
->channelSeq
[2]='R';
73 if (!model
) return false;
75 reflc
.genGrid(corners
, nx
, ny
);
76 reflc
.averageImage(model
,0);
79 FILE *IRef= fopen("IRef.txt","w");
81 for (int i=0; i<reflc.nbTri; i++)
82 fprintf(IRef, "%.6f ", reflc.avg[i]);
89 return reflc
.avg
!= 0;
92 void LightMap::normal2uv(const float n
[3], float uv
[2]) {
93 float l
= 1/sqrt(n
[0]*n
[0] + n
[1]*n
[1] + n
[2]*n
[2]);
97 uv
[0] = n0
*.25f
+.25f
+ (n2
>0 ? 0:.5f
);
101 void LightMap::uv2normal(const float uv
[2], float n
[3]) {
102 n
[0] = (uv
[0]-(uv
[0]>.5f
? .5f
: 0)-.25f
)*4;
104 float len
= n
[0]*n
[0] + n
[1]*n
[1];
106 float f
= 1/sqrt(len
);
109 n
[2]=(uv
[0]>.5f
? -0.0f
: 0.0f
);
111 else n
[2] = sqrt(1-len
)*(uv
[0]>.5f
? -1 : 1);
113 float l
= 1/sqrt(n
[0]*n
[0] + n
[1]*n
[1] + n
[2]*n
[2]);
119 CvScalar
LightMap::readMap(const float normal
[3])
121 if (!isReady()) return cvScalarAll(1);
123 normal2uv(normal
, uv
);
125 IplImage
*im
= map
.getIm();
127 int iu
= cvRound(uv
[0]*(double)im
->width
);
128 int iv
= cvRound(uv
[1]*(double)im
->height
);
131 if (iu
>=im
->width
) iu
= im
->width
-1;
132 if (iv
>=im
->height
) iv
= im
->height
-1;
133 return cvGet2D(im
, iv
, iu
);
136 bool LightMap::addNormal(float normal
[3], LightCollector
&lc
, int cam
)
139 return addNormalLightMap(normal
,lc
,cam
);
141 return addNormalCalib(normal
,lc
,cam
);
144 const float *LightMap::getGain(int cam
)
146 static const float ones
[3] = {1, 1, 1};
147 if (!isReady()) return ones
;
148 return &CV_MAT_ELEM(*lightParams
, float, 2*cam
, 0);
151 const float *LightMap::getBias(int cam
)
153 static const float zeroes
[3] = {0, 0, 0};
154 if (!isReady()) return zeroes
;
155 return &CV_MAT_ELEM(*lightParams
, float, 2*cam
+1, 0);
158 bool LightMap::addNormalLightMap(float normal
[3], LightCollector
&lc
, int cam
)
160 if (lightParams
==0) return false;
161 assert(map
.getIm()!=0);
164 float l
= 1/sqrt(normal
[0]*normal
[0] + normal
[1]*normal
[1] + normal
[2]*normal
[2]);
170 if (lc
.cmpWithRef(reflc
, val
, getGain(cam
), getBias(cam
)))
171 return updateLightMap(normal
, val
);
175 bool LightMap::updateLightMap(float normal
[3], float *val
)
177 if (val
[0]<0) return false;
179 const int nc
= map
.getIm()->nChannels
;
181 for (int y
=0; y
<map
.getIm()->height
; y
++) {
182 float *line
= (float *)(map
.getIm()->imageData
+ y
*map
.getIm()->widthStep
);
184 for (int x
=0; x
<map
.getIm()->width
; x
++) {
186 float uv
[2] = { float(x
)/float(map
.getIm()->width
), float(y
)/float(map
.getIm()->height
) };
188 float dot
= n
[0]*normal
[0] + n
[1]*normal
[1] + n
[2]*normal
[2];
191 dot
= acos(dot
)*2/M_PI
;
193 float f
= (1/sqrt(2*M_PI
))*exp(-(1-dot
)/(2*s
*s
));
194 line
[x
] = (1.0f
-f
)*line
[x
] + f
*val
;
199 //dot = pow(dot, 16)/2;
200 float angle
= (float)(acos(dot
)*2/M_PI
);
201 //dot = (dot-min_dot)/(1-min_dot);
203 //float f = (1/sqrt(2*M_PI))*exp(-(1-dot)/(2*s*s));
204 //float f = (1/sqrt(2*M_PI))*exp(-(angle)/(2*s*s));
205 float f
= exp(-(angle
)/(2*s
*s
));
206 for (int c
=0; c
<nc
; c
++)
207 line
[x
*nc
+c
] = (1.0f
-f
)*line
[x
*nc
+ c
] + f
*val
[c
];
217 static void printShaderInfoLog(GLuint obj
, const char *str
, bool ARB
)
219 GLint infologLength
= 0;
220 GLint charsWritten
= 0;
223 if (ARB
) glGetObjectParameterivARB(obj
, GL_INFO_LOG_LENGTH
,&infologLength
);
224 else glGetShaderiv(obj
, GL_INFO_LOG_LENGTH
,&infologLength
);
226 if (infologLength
> 0)
228 infoLog
= (char *)malloc(infologLength
);
229 if (ARB
) glGetInfoLogARB(obj
, infologLength
, &charsWritten
, infoLog
);
230 else glGetShaderInfoLog(obj
, infologLength
, &charsWritten
, infoLog
);
231 if (strlen(infoLog
)>0) {
232 if (str
) printf("%s\n", str
);
233 printf("%s\n",infoLog
);
241 static std::string
readFile(const char * const file
)
243 std::string ret
= "";
244 FILE * fp
= fopen(file
, "rb");
246 fseek(fp
, 0, SEEK_END
);
247 int size
= (int)ftell(fp
);
248 fseek(fp
, 0, SEEK_SET
);
249 char * buf
= new char[size
+ 1];
250 fread(buf
, size
, 1, fp
);
258 static bool checkErrors(const char *file
, int line
, bool exitOnFailure
=false)
262 while ((error
= glGetError()) != GL_NO_ERROR
) {
263 fprintf(stderr
, "%s:%d: %s\n",
265 (char *) gluErrorString(error
));
274 bool LightMap::initGL()
277 if (initialized
) return true;
281 ARB
= GLEW_ARB_vertex_shader
&& GLEW_ARB_fragment_shader
;
282 if (!ARB
&& !glewIsSupported("GL_VERSION_2_0"))
284 printf("No GLSL support\n");
288 const char * shaderProg
;
291 ret
= readFile("myvert.glsl");
292 shaderProg
= ret
.c_str();
294 if (ARB
) g_vertShader
= glCreateShaderObjectARB(GL_VERTEX_SHADER
);
295 else g_vertShader
= glCreateShader(GL_VERTEX_SHADER
);
296 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
298 if (ARB
) glShaderSourceARB(g_vertShader
, 1, &shaderProg
, 0);
299 else glShaderSource(g_vertShader
, 1, &shaderProg
, 0);
300 printShaderInfoLog(g_vertShader
, "Vertex shader myvert.glsl", ARB
);
301 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
303 if (ARB
) glCompileShaderARB(g_vertShader
);
304 else glCompileShader(g_vertShader
);
306 printShaderInfoLog(g_vertShader
, "Vertex shader myvert.glsl", ARB
);
307 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
309 ret
= readFile("myfrag.glsl");
310 shaderProg
= ret
.c_str();
311 if (ARB
) g_fragShader
= glCreateShaderObjectARB(GL_FRAGMENT_SHADER
);
312 else g_fragShader
= glCreateShader(GL_FRAGMENT_SHADER
);
314 if (ARB
) glShaderSourceARB(g_fragShader
, 1, &shaderProg
, 0);
315 else glShaderSource(g_fragShader
, 1, &shaderProg
, 0);
317 if (ARB
) glCompileShaderARB(g_fragShader
);
318 else glCompileShader(g_fragShader
);
320 printShaderInfoLog(g_fragShader
, "Fragment shader myfrag.glsl", ARB
);
321 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
322 //if (!ok) return false;
324 if (ARB
) g_shaderProgram
= glCreateProgramObjectARB();
325 else g_shaderProgram
= glCreateProgram();
327 if (ARB
) glAttachObjectARB(g_shaderProgram
, g_vertShader
);
328 else glAttachShader(g_shaderProgram
, g_vertShader
);
330 if (ARB
) glAttachObjectARB(g_shaderProgram
, g_fragShader
);
331 else glAttachShader(g_shaderProgram
, g_fragShader
);
333 if (ARB
) glLinkProgramARB(g_shaderProgram
);
334 else glLinkProgram(g_shaderProgram
);
335 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
337 if (ARB
) glValidateProgramARB(g_shaderProgram
);
338 else glValidateProgram(g_shaderProgram
);
339 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
340 printShaderInfoLog(g_shaderProgram
, "glValidateProgram(g_shaderProgram)", ARB
);
342 cout
<< "GLSL shaders sucessfully initialized using "
343 << (ARB
? "ARB extensions" : "OpenGL 2.0") << ".\n";
345 cout
<< "Failed to initialize GLSL shaders.\n";
352 void LightMap::enableShader(int cam
, CvMat
*obj2world
)
357 if (!initGL()) return;
359 if (ARB
) glUseProgramObjectARB(g_shaderProgram
);
360 else glUseProgram(g_shaderProgram
);
361 if (!checkErrors(__FILE__
,__LINE__
)) {
362 printShaderInfoLog(g_shaderProgram
, "glUseProgram(g_shaderProgram)", ARB
);
366 GLuint shaderTexture
, worldToObjectNormal
, gain
, bias
;
368 shaderTexture
= glGetUniformLocationARB(g_shaderProgram
, "main_texture");
369 worldToObjectNormal
= glGetUniformLocationARB(g_shaderProgram
, "worldToObjectNormal");
370 gain
= glGetUniformLocationARB(g_shaderProgram
, "gain");
371 bias
= glGetUniformLocationARB(g_shaderProgram
, "bias");
373 shaderTexture
= glGetUniformLocation(g_shaderProgram
, "main_texture");
374 worldToObjectNormal
= glGetUniformLocation(g_shaderProgram
, "worldToObjectNormal");
375 gain
= glGetUniformLocation(g_shaderProgram
, "gain");
376 bias
= glGetUniformLocation(g_shaderProgram
, "bias");
378 checkErrors(__FILE__
,__LINE__
);
380 glActiveTexture(GL_TEXTURE0
+ 0);
381 glBindTexture(GL_TEXTURE_2D
, shaderTexture
);
382 checkErrors(__FILE__
,__LINE__
);
384 if (ARB
) glUniform1iARB(shaderTexture
, 0);
385 else glUniform1i(shaderTexture
, 0);
386 checkErrors(__FILE__
,__LINE__
);
388 // The sign change is required because we do not like OpenGL's Z axis direction.
389 // Thus, we flip all normals.
391 for (int i
=0; i
<3; ++i
)
392 for (int j
=0; j
<3; ++j
)
393 mat
[j
*3 +i
] = -(float)cvGet2D(obj2world
, i
, j
).val
[0];
396 for (int i
=0;i
<lightParams
->cols
; i
++) {
398 g
[i
] = 1/cvGet2D(lightParams
, cam
*2, i
).val
[0];
399 b
[i
] = g
[i
]*cvGet2D(lightParams
, cam
*2+1, i
).val
[0];
406 glUniform1iARB(shaderTexture
, 0);
407 glUniformMatrix3fvARB(worldToObjectNormal
, 1, GL_FALSE
, mat
);
408 glUniform4fARB(gain
, g
[2], g
[1], g
[0], 1);
409 glUniform4fARB(bias
, b
[2], b
[1], b
[0], 0);
411 glUniform1i(shaderTexture
, 0);
412 glUniformMatrix3fv(worldToObjectNormal
, 1, GL_FALSE
, mat
);
413 glUniform4f(gain
, g
[2], g
[1], g
[0], 1);
414 glUniform4f(bias
, b
[2], b
[1], b
[0], 0);
416 checkErrors(__FILE__
,__LINE__
);
418 checkErrors(__FILE__
,__LINE__
);
422 void LightMap::disableShader()
425 if (ARB
) glUseProgramObjectARB(0);
426 else glUseProgram(0);
427 map
.disableTexture();
431 void LightMap::setCamNum(int n
)
436 if (normals
) delete normals
;
437 normals
= new CvGrowMat(256,3, CV_32FC1
);
438 normals
->resize(0,3);
442 cvReleaseMat(&lightParams
);
446 static void normalize(float normal
[3])
448 float l
= 1/sqrt(normal
[0]*normal
[0] + normal
[1]*normal
[1] + normal
[2]*normal
[2]);
454 bool LightMap::addNormalCalib(float normal
[3], LightCollector
&lc
, int cam
)
457 if (lc
.avg
==0 || reflc
.avg
==0) return false;
461 assert(lc
.nbTri
== reflc
.nbTri
);
463 int normIdx
= normals
->rows
+ nbCam
*2;
469 // is the normal already in the database ?
470 for (int i
=0; i
<normals
->rows
; i
++) {
471 float *n2
= (float *)CV_MAT_ELEM_PTR(*normals
, i
, 0);
472 float ndot
= n2
[0]*normal
[0] +
478 normIdx
= i
+ nbCam
*2;
483 // obsMat->resize(obsMat->rows, obsMat->cols+1);
486 for (int i
=0; i
<lc
.nbTri
; ++i
) {
487 float *rv
= reflc
.avg
+i
*reflc
.avgChannels
;
488 float *v
= lc
.avg
+i
*lc
.avgChannels
;
490 for (int c
=0; c
<reflc
.avgChannels
; c
++)
491 if (!(v
[c
]>5 && rv
[c
] >5 && v
[c
]<250 && rv
[c
] <250))
498 o
.normalCol
= normIdx
;
500 for (int c
=0; c
<reflc
.avgChannels
; c
++) {
501 o
.camVal
[c
] = - v
[c
]/255.0f
;
502 o
.normalVal
[c
] = rv
[c
]/255.0f
;
509 // add the normal vector
510 normals
->resize(normals
->rows
+1, normals
->cols
);
511 float *n
= (float *)CV_MAT_ELEM_PTR(*normals
, normals
->rows
-1,0);
519 static bool saveMat(const CvMat
*m
, const char *fn
)
523 if (!f
.good()) return false;
525 for (int j
=0; j
<m
->rows
; j
++) {
526 for (int i
=0; i
<m
->cols
; i
++) {
527 double v
= cvGet2D(m
, j
, i
).val
[0];
529 if (i
+1 < m
->cols
) f
<<'\t';
537 static CvGrowMat
*loadMat(const char *fn
)
541 if (!f
.good()) return 0;
543 CvGrowMat
*m
= new CvGrowMat(128,3, CV_32FC1
);
549 f
.getline(line
, 4095);
551 if (!f
.good()) break;
556 int len
= strlen(line
);
558 for (int i
=0;i
<len
+1;i
++) {
559 if (line
[i
]==' ' || line
[i
]=='\t' || line
[i
]==0) {
562 if (sscanf(last
, "%f", &val
)==1) {
563 m
->resize(nrow
,max(ncols
, m
->cols
));
564 cvSet2D(m
, nrow
-1, ncols
-1, cvScalarAll(val
));
575 double LightMap::getObsMat(int i
, int j
, int c
)
577 if (j
== obs
[i
].camCol
) return (double)obs
[i
].camVal
[c
];
578 if (j
== (obs
[i
].camCol
+1)) return 1;
579 if (j
== obs
[i
].normalCol
) return (double)obs
[i
].normalVal
[c
];
583 double LightMap::getObsElem(const vector
<Observation
>::iterator
&it
, int i
, int c
)
585 if (it
->camCol
== i
) return it
->camVal
[c
];
586 if (it
->camCol
+1 == i
) return 1;
587 if (it
->normalCol
== i
) return it
->normalVal
[c
];
591 void LightMap::computeAtA(CvMat
*AtA
, int channel
)
593 for (vector
<Observation
>::iterator it
= obs
.begin();
596 int idx
[3] = {it
->camCol
, it
->camCol
+1, it
->normalCol
};
597 for (unsigned i
=0; i
<3; i
++) {
598 for (unsigned j
=0; j
<3; j
++) {
602 double v
= getObsElem(it
,i_
, channel
)*getObsElem(it
,j_
, channel
);
604 double d
= v
+cvGetReal2D(AtA
, i_
, j_
);
605 cvSetReal2D(AtA
, i_
, j_
, d
);
606 cvSetReal2D(AtA
, j_
, i_
, d
);
613 for (int y=1;y<AtA->height; y++)
614 for (int x=0;x<y; x++) {
615 CV_MAT_ELEM(*AtA, float, y, x) = CV_MAT_ELEM(*AtA, float, x, y);
620 bool LightMap::save(const char *lightParamsFN
, const char *normalsFN
)
622 return saveMat(lightParams
,lightParamsFN
) &&
623 saveMat(normals
, normalsFN
);
626 bool LightMap::computeLightParams()
628 int obsMatCols
=normals
->rows
+2*nbCam
;
629 int sizes
[] = {obs
.size(), obsMatCols
};
631 if (lightParams
) cvReleaseMat(&lightParams
);
632 lightParams
= cvCreateMat(obsMatCols
, reflc
.avgChannels
, CV_32FC1
);
633 sizes
[0] = sizes
[1] = obsMatCols
;
635 // create temporary matrices
636 CvMat
*AtA
= cvCreateMat(obsMatCols
,obsMatCols
, CV_32FC1
);
637 CvMat
*w
= cvCreateMat(obsMatCols
, 1, CV_32FC1
);
638 CvMat
*V
= cvCreateMat(obsMatCols
, obsMatCols
, CV_32FC1
);
640 for (int c
=0; c
<reflc
.avgChannels
; c
++) {
645 sprintf(atafn,"ata%d.mat",c);
649 cvSVD(AtA
, w
, 0, V
, CV_SVD_MODIFY_A
);
652 cvGetCol(V
, &sub
, V
->cols
-1);
653 cvGetCol(lightParams
, &lpc
, c
);
654 cvScale(&sub
, &lpc
, 1.0/cvGet2D(&sub
,0,0).val
[0]);
657 std::cout << "Eigen values:";
658 for (int i=0; i<w->rows; ++i) {
659 float v = (float)cvGet2D(w, i, 0).val[0];
660 std::cout << " " << v;
662 std::cout << std::endl;
668 // construct light map
669 cvSet(map
.getIm(), cvScalarAll(.1));
673 std::cout << "Camera Values:\n";
674 for (int c=0; c<nbCam; ++c) {
675 float g = (float)cvGet2D(lightParams, c*2, 0).val[0];
676 float b = (float)cvGet2D(lightParams, c*2+1, 0).val[0];
677 std::cout <<" cam " << c << ": "<< g <<", " << b << std::endl;
680 buildMapFromSamples();
684 void LightMap::buildMapFromSamples()
686 for (int i
=0; i
<normals
->rows
; ++i
) {
687 updateLightMap((float *)CV_MAT_ELEM_PTR(*normals
, i
, 0),
688 &CV_MAT_ELEM(*lightParams
, float, i
+2*nbCam
, 0));
692 bool LightMap::load(const char *lightParamsFN
, const char *normalsFN
)
694 CvGrowMat
*lp
= loadMat(lightParamsFN
);
695 if (!lp
) return false;
696 lightParams
= cvCreateMat(lp
->rows
, lp
->cols
, CV_32FC1
);
697 cvCopy(lp
, lightParams
);
698 printf("Loaded lightParams: %dx%d.\n", lp
->rows
, lp
->cols
);
700 normals
= loadMat(normalsFN
);
701 int nlights
= lightParams
->rows
- 2*nbCam
;
702 if (!normals
|| ((normals
->rows
) < nlights
)) {
704 printf("Not enough normals. Expecting %d cameras. Found %d normals and %d parameters.\n",
705 nbCam
, normals
->rows
, lightParams
->rows
);
707 cvReleaseMat(&lightParams
);
710 if (normals
->rows
> nlights
)
711 normals
->resize(nlights
,3);
714 std::cout << "Camera Values:\n";
715 for (int c=0; c<nbCam; ++c) {
716 float g = (float)cvGet2D(lightParams, c*2, 0).val[0];
717 float b = (float)cvGet2D(lightParams, c*2+1, 0).val[0];
718 std::cout <<" cam " << c << ": "<< g <<", " << b << std::endl;
722 buildMapFromSamples();
727 bool LightMap::saveImage(const char *filename
)
729 IplImage
*image
= map
.getIm();
730 if (image
==0) return false;
731 IplImage
*im
= cvCreateImage(cvGetSize(image
), IPL_DEPTH_8U
,image
->nChannels
);
732 //double min=0, max=2;
733 //cvMinMaxLoc(image, &min, &max);
734 cvConvertScale(image
, im
, 128, 0);
735 int r
= cvSaveImage(filename
, im
);