2 Copyright 2005, 2006 Computer Vision Lab,
3 Ecole Polytechnique Federale de Lausanne (EPFL), Switzerland.
4 Modified by Damian Stewart <damian@frey.co.nz> 2009-2010;
5 modifications Copyright 2009, 2010 Damian Stewart <damian@frey.co.nz>.
7 Distributed under the terms of the GNU General Public License v3.
9 This file is part of The Artvertiser.
11 The Artvertiser is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 The Artvertiser is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public License
22 along with The Artvertiser. If not, see <http://www.gnu.org/licenses/>.
30 //also includes opengl if available
36 #define M_PI 3.1415926535897932384626433832795
50 LightMap::LightMap(const LightMap
&a
)
55 map
.setImage(cvCloneImage(a
.map
.getIm()));
64 IplImage
*im
= map
.getIm();
65 if(im
) cvReleaseImage(&im
);
66 if (lightParams
) cvReleaseMat(&lightParams
);
67 if (normals
) delete normals
;
70 void LightMap::clear()
72 IplImage
* im
= map
.getIm();
73 if ( im
) cvReleaseImage( &im
);
76 if (normals
) delete normals
;
80 cvReleaseMat(&lightParams
);
85 bool LightMap::init(int nbCam
, IplImage
*model
, float corners
[4][2], int nx
, int ny
)
87 IplImage
*im
= map
.getIm();
88 if(im
) cvReleaseImage(&im
);
89 map
.setImage(cvCreateImage(cvSize(128,64), IPL_DEPTH_32F
, model
->nChannels
));
90 cvSet(map
.getIm(), cvScalarAll(.1));
91 if (model
->nChannels
== 3) {
93 im
->channelSeq
[0]='B';
94 im
->channelSeq
[1]='G';
95 im
->channelSeq
[2]='R';
98 if (!model
) return false;
100 reflc
.genGrid(corners
, nx
, ny
);
101 reflc
.averageImage(model
,0);
104 FILE *IRef= fopen("IRef.txt","w");
106 for (int i=0; i<reflc.nbTri; i++)
107 fprintf(IRef, "%.6f ", reflc.avg[i]);
114 return reflc
.avg
!= 0;
117 void LightMap::normal2uv(const float n
[3], float uv
[2]) {
118 float l
= 1/sqrt(n
[0]*n
[0] + n
[1]*n
[1] + n
[2]*n
[2]);
122 uv
[0] = n0
*.25f
+.25f
+ (n2
>0 ? 0:.5f
);
126 void LightMap::uv2normal(const float uv
[2], float n
[3]) {
127 n
[0] = (uv
[0]-(uv
[0]>.5f
? .5f
: 0)-.25f
)*4;
129 float len
= n
[0]*n
[0] + n
[1]*n
[1];
131 float f
= 1/sqrt(len
);
134 n
[2]=(uv
[0]>.5f
? -0.0f
: 0.0f
);
136 else n
[2] = sqrt(1-len
)*(uv
[0]>.5f
? -1 : 1);
138 float l
= 1/sqrt(n
[0]*n
[0] + n
[1]*n
[1] + n
[2]*n
[2]);
144 CvScalar
LightMap::readMap(const float normal
[3])
146 if (!isReady()) return cvScalarAll(1);
148 normal2uv(normal
, uv
);
150 IplImage
*im
= map
.getIm();
152 int iu
= cvRound(uv
[0]*(double)im
->width
);
153 int iv
= cvRound(uv
[1]*(double)im
->height
);
156 if (iu
>=im
->width
) iu
= im
->width
-1;
157 if (iv
>=im
->height
) iv
= im
->height
-1;
158 return cvGet2D(im
, iv
, iu
);
161 bool LightMap::addNormal(float normal
[3], LightCollector
&lc
, int cam
)
164 return addNormalLightMap(normal
,lc
,cam
);
166 return addNormalCalib(normal
,lc
,cam
);
169 const float *LightMap::getGain(int cam
)
171 static const float ones
[3] = {1, 1, 1};
172 if (!isReady()) return ones
;
173 return &CV_MAT_ELEM(*lightParams
, float, 2*cam
, 0);
176 const float *LightMap::getBias(int cam
)
178 static const float zeroes
[3] = {0, 0, 0};
179 if (!isReady()) return zeroes
;
180 return &CV_MAT_ELEM(*lightParams
, float, 2*cam
+1, 0);
183 bool LightMap::addNormalLightMap(float normal
[3], LightCollector
&lc
, int cam
)
185 if (lightParams
==0) return false;
186 assert(map
.getIm()!=0);
189 float l
= 1/sqrt(normal
[0]*normal
[0] + normal
[1]*normal
[1] + normal
[2]*normal
[2]);
195 if (lc
.cmpWithRef(reflc
, val
, getGain(cam
), getBias(cam
)))
196 return updateLightMap(normal
, val
);
200 bool LightMap::updateLightMap(float normal
[3], float *val
)
202 if (val
[0]<0) return false;
204 const int nc
= map
.getIm()->nChannels
;
206 for (int y
=0; y
<map
.getIm()->height
; y
++) {
207 float *line
= (float *)(map
.getIm()->imageData
+ y
*map
.getIm()->widthStep
);
209 for (int x
=0; x
<map
.getIm()->width
; x
++) {
211 float uv
[2] = { float(x
)/float(map
.getIm()->width
), float(y
)/float(map
.getIm()->height
) };
213 float dot
= n
[0]*normal
[0] + n
[1]*normal
[1] + n
[2]*normal
[2];
216 dot
= acos(dot
)*2/M_PI
;
218 float f
= (1/sqrt(2*M_PI
))*exp(-(1-dot
)/(2*s
*s
));
219 line
[x
] = (1.0f
-f
)*line
[x
] + f
*val
;
224 //dot = pow(dot, 16)/2;
225 float angle
= (float)(acos(dot
)*2/M_PI
);
226 //dot = (dot-min_dot)/(1-min_dot);
228 //float f = (1/sqrt(2*M_PI))*exp(-(1-dot)/(2*s*s));
229 //float f = (1/sqrt(2*M_PI))*exp(-(angle)/(2*s*s));
230 float f
= exp(-(angle
)/(2*s
*s
));
231 for (int c
=0; c
<nc
; c
++)
232 line
[x
*nc
+c
] = (1.0f
-f
)*line
[x
*nc
+ c
] + f
*val
[c
];
242 static void printShaderInfoLog(GLuint obj
, const char *str
, bool ARB
)
244 GLint infologLength
= 0;
245 GLint charsWritten
= 0;
248 if (ARB
) glGetObjectParameterivARB(obj
, GL_INFO_LOG_LENGTH
,&infologLength
);
249 else glGetShaderiv(obj
, GL_INFO_LOG_LENGTH
,&infologLength
);
251 if (infologLength
> 0)
253 infoLog
= (char *)malloc(infologLength
);
254 if (ARB
) glGetInfoLogARB(obj
, infologLength
, &charsWritten
, infoLog
);
255 else glGetShaderInfoLog(obj
, infologLength
, &charsWritten
, infoLog
);
256 if (strlen(infoLog
)>0) {
257 if (str
) printf("%s\n", str
);
258 printf("%s\n",infoLog
);
266 static std::string
readFile(const char * const file
)
268 std::string ret
= "";
269 FILE * fp
= fopen(file
, "rb");
271 fseek(fp
, 0, SEEK_END
);
272 int size
= (int)ftell(fp
);
273 fseek(fp
, 0, SEEK_SET
);
274 char * buf
= new char[size
+ 1];
275 fread(buf
, size
, 1, fp
);
283 static bool checkErrors(const char *file
, int line
, bool exitOnFailure
=false)
287 while ((error
= glGetError()) != GL_NO_ERROR
) {
288 fprintf(stderr
, "%s:%d: %s\n",
290 (char *) gluErrorString(error
));
299 bool LightMap::initGL()
302 if (initialized
) return true;
306 ARB
= GLEW_ARB_vertex_shader
&& GLEW_ARB_fragment_shader
;
307 if (!ARB
&& !glewIsSupported("GL_VERSION_2_0"))
309 printf("No GLSL support\n");
313 const char * shaderProg
;
316 ret
= readFile("myvert.glsl");
317 shaderProg
= ret
.c_str();
319 if (ARB
) g_vertShader
= glCreateShaderObjectARB(GL_VERTEX_SHADER
);
320 else g_vertShader
= glCreateShader(GL_VERTEX_SHADER
);
321 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
323 if (ARB
) glShaderSourceARB(g_vertShader
, 1, &shaderProg
, 0);
324 else glShaderSource(g_vertShader
, 1, &shaderProg
, 0);
325 printShaderInfoLog(g_vertShader
, "Vertex shader myvert.glsl", ARB
);
326 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
328 if (ARB
) glCompileShaderARB(g_vertShader
);
329 else glCompileShader(g_vertShader
);
331 printShaderInfoLog(g_vertShader
, "Vertex shader myvert.glsl", ARB
);
332 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
334 ret
= readFile("myfrag.glsl");
335 shaderProg
= ret
.c_str();
336 if (ARB
) g_fragShader
= glCreateShaderObjectARB(GL_FRAGMENT_SHADER
);
337 else g_fragShader
= glCreateShader(GL_FRAGMENT_SHADER
);
339 if (ARB
) glShaderSourceARB(g_fragShader
, 1, &shaderProg
, 0);
340 else glShaderSource(g_fragShader
, 1, &shaderProg
, 0);
342 if (ARB
) glCompileShaderARB(g_fragShader
);
343 else glCompileShader(g_fragShader
);
345 printShaderInfoLog(g_fragShader
, "Fragment shader myfrag.glsl", ARB
);
346 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
347 //if (!ok) return false;
349 if (ARB
) g_shaderProgram
= glCreateProgramObjectARB();
350 else g_shaderProgram
= glCreateProgram();
352 if (ARB
) glAttachObjectARB(g_shaderProgram
, g_vertShader
);
353 else glAttachShader(g_shaderProgram
, g_vertShader
);
355 if (ARB
) glAttachObjectARB(g_shaderProgram
, g_fragShader
);
356 else glAttachShader(g_shaderProgram
, g_fragShader
);
358 if (ARB
) glLinkProgramARB(g_shaderProgram
);
359 else glLinkProgram(g_shaderProgram
);
360 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
362 if (ARB
) glValidateProgramARB(g_shaderProgram
);
363 else glValidateProgram(g_shaderProgram
);
364 ok
= ok
&& checkErrors(__FILE__
,__LINE__
);
365 printShaderInfoLog(g_shaderProgram
, "glValidateProgram(g_shaderProgram)", ARB
);
367 cout
<< "GLSL shaders sucessfully initialized using "
368 << (ARB
? "ARB extensions" : "OpenGL 2.0") << ".\n";
370 cout
<< "Failed to initialize GLSL shaders.\n";
377 void LightMap::enableShader(int cam
, CvMat
*obj2world
)
382 if (!initGL()) return;
384 if (ARB
) glUseProgramObjectARB(g_shaderProgram
);
385 else glUseProgram(g_shaderProgram
);
386 if (!checkErrors(__FILE__
,__LINE__
)) {
387 printShaderInfoLog(g_shaderProgram
, "glUseProgram(g_shaderProgram)", ARB
);
391 GLuint shaderTexture
, worldToObjectNormal
, gain
, bias
;
393 shaderTexture
= glGetUniformLocationARB(g_shaderProgram
, "main_texture");
394 worldToObjectNormal
= glGetUniformLocationARB(g_shaderProgram
, "worldToObjectNormal");
395 gain
= glGetUniformLocationARB(g_shaderProgram
, "gain");
396 bias
= glGetUniformLocationARB(g_shaderProgram
, "bias");
398 shaderTexture
= glGetUniformLocation(g_shaderProgram
, "main_texture");
399 worldToObjectNormal
= glGetUniformLocation(g_shaderProgram
, "worldToObjectNormal");
400 gain
= glGetUniformLocation(g_shaderProgram
, "gain");
401 bias
= glGetUniformLocation(g_shaderProgram
, "bias");
403 checkErrors(__FILE__
,__LINE__
);
405 glActiveTexture(GL_TEXTURE0
+ 0);
406 glBindTexture(GL_TEXTURE_2D
, shaderTexture
);
407 checkErrors(__FILE__
,__LINE__
);
409 if (ARB
) glUniform1iARB(shaderTexture
, 0);
410 else glUniform1i(shaderTexture
, 0);
411 checkErrors(__FILE__
,__LINE__
);
413 // The sign change is required because we do not like OpenGL's Z axis direction.
414 // Thus, we flip all normals.
416 for (int i
=0; i
<3; ++i
)
417 for (int j
=0; j
<3; ++j
)
418 mat
[j
*3 +i
] = -(float)cvGet2D(obj2world
, i
, j
).val
[0];
421 for (int i
=0;i
<lightParams
->cols
; i
++) {
423 g
[i
] = 1/cvGet2D(lightParams
, cam
*2, i
).val
[0];
424 b
[i
] = g
[i
]*cvGet2D(lightParams
, cam
*2+1, i
).val
[0];
431 glUniform1iARB(shaderTexture
, 0);
432 glUniformMatrix3fvARB(worldToObjectNormal
, 1, GL_FALSE
, mat
);
433 glUniform4fARB(gain
, g
[2], g
[1], g
[0], 1);
434 glUniform4fARB(bias
, b
[2], b
[1], b
[0], 0);
436 glUniform1i(shaderTexture
, 0);
437 glUniformMatrix3fv(worldToObjectNormal
, 1, GL_FALSE
, mat
);
438 glUniform4f(gain
, g
[2], g
[1], g
[0], 1);
439 glUniform4f(bias
, b
[2], b
[1], b
[0], 0);
441 checkErrors(__FILE__
,__LINE__
);
443 checkErrors(__FILE__
,__LINE__
);
447 void LightMap::disableShader()
450 if (ARB
) glUseProgramObjectARB(0);
451 else glUseProgram(0);
452 map
.disableTexture();
456 void LightMap::setCamNum(int n
)
461 if (normals
) delete normals
;
462 normals
= new CvGrowMat(256,3, CV_32FC1
);
463 normals
->resize(0,3);
467 cvReleaseMat(&lightParams
);
471 static void normalize(float normal
[3])
473 float l
= 1/sqrt(normal
[0]*normal
[0] + normal
[1]*normal
[1] + normal
[2]*normal
[2]);
479 bool LightMap::addNormalCalib(float normal
[3], LightCollector
&lc
, int cam
)
482 if (lc
.avg
==0 || reflc
.avg
==0) return false;
486 assert(lc
.nbTri
== reflc
.nbTri
);
488 int normIdx
= normals
->rows
+ nbCam
*2;
494 // is the normal already in the database ?
495 for (int i
=0; i
<normals
->rows
; i
++) {
496 float *n2
= (float *)CV_MAT_ELEM_PTR(*normals
, i
, 0);
497 float ndot
= n2
[0]*normal
[0] +
503 normIdx
= i
+ nbCam
*2;
508 // obsMat->resize(obsMat->rows, obsMat->cols+1);
511 for (int i
=0; i
<lc
.nbTri
; ++i
) {
512 float *rv
= reflc
.avg
+i
*reflc
.avgChannels
;
513 float *v
= lc
.avg
+i
*lc
.avgChannels
;
515 for (int c
=0; c
<reflc
.avgChannels
; c
++)
516 if (!(v
[c
]>5 && rv
[c
] >5 && v
[c
]<250 && rv
[c
] <250))
523 o
.normalCol
= normIdx
;
525 for (int c
=0; c
<reflc
.avgChannels
; c
++) {
526 o
.camVal
[c
] = - v
[c
]/255.0f
;
527 o
.normalVal
[c
] = rv
[c
]/255.0f
;
534 // add the normal vector
535 normals
->resize(normals
->rows
+1, normals
->cols
);
536 float *n
= (float *)CV_MAT_ELEM_PTR(*normals
, normals
->rows
-1,0);
544 static bool saveMat(const CvMat
*m
, const char *fn
)
548 if (!f
.good()) return false;
550 for (int j
=0; j
<m
->rows
; j
++) {
551 for (int i
=0; i
<m
->cols
; i
++) {
552 double v
= cvGet2D(m
, j
, i
).val
[0];
554 if (i
+1 < m
->cols
) f
<<'\t';
562 static CvGrowMat
*loadMat(const char *fn
)
566 if (!f
.good()) return 0;
568 CvGrowMat
*m
= new CvGrowMat(128,3, CV_32FC1
);
574 f
.getline(line
, 4095);
576 if (!f
.good()) break;
581 int len
= strlen(line
);
583 for (int i
=0;i
<len
+1;i
++) {
584 if (line
[i
]==' ' || line
[i
]=='\t' || line
[i
]==0) {
587 if (sscanf(last
, "%f", &val
)==1) {
588 m
->resize(nrow
,max(ncols
, m
->cols
));
589 cvSet2D(m
, nrow
-1, ncols
-1, cvScalarAll(val
));
600 double LightMap::getObsMat(int i
, int j
, int c
)
602 if (j
== obs
[i
].camCol
) return (double)obs
[i
].camVal
[c
];
603 if (j
== (obs
[i
].camCol
+1)) return 1;
604 if (j
== obs
[i
].normalCol
) return (double)obs
[i
].normalVal
[c
];
608 double LightMap::getObsElem(const vector
<Observation
>::iterator
&it
, int i
, int c
)
610 if (it
->camCol
== i
) return it
->camVal
[c
];
611 if (it
->camCol
+1 == i
) return 1;
612 if (it
->normalCol
== i
) return it
->normalVal
[c
];
616 void LightMap::computeAtA(CvMat
*AtA
, int channel
)
618 for (vector
<Observation
>::iterator it
= obs
.begin();
621 int idx
[3] = {it
->camCol
, it
->camCol
+1, it
->normalCol
};
622 for (unsigned i
=0; i
<3; i
++) {
623 for (unsigned j
=0; j
<3; j
++) {
627 double v
= getObsElem(it
,i_
, channel
)*getObsElem(it
,j_
, channel
);
629 double d
= v
+cvGetReal2D(AtA
, i_
, j_
);
630 cvSetReal2D(AtA
, i_
, j_
, d
);
631 cvSetReal2D(AtA
, j_
, i_
, d
);
638 for (int y=1;y<AtA->height; y++)
639 for (int x=0;x<y; x++) {
640 CV_MAT_ELEM(*AtA, float, y, x) = CV_MAT_ELEM(*AtA, float, x, y);
645 bool LightMap::save(const char *lightParamsFN
, const char *normalsFN
)
647 return saveMat(lightParams
,lightParamsFN
) &&
648 saveMat(normals
, normalsFN
);
651 bool LightMap::computeLightParams()
653 int obsMatCols
=normals
->rows
+2*nbCam
;
654 int sizes
[] = {obs
.size(), obsMatCols
};
656 if (lightParams
) cvReleaseMat(&lightParams
);
657 lightParams
= cvCreateMat(obsMatCols
, reflc
.avgChannels
, CV_32FC1
);
658 sizes
[0] = sizes
[1] = obsMatCols
;
660 // create temporary matrices
661 CvMat
*AtA
= cvCreateMat(obsMatCols
,obsMatCols
, CV_32FC1
);
662 CvMat
*w
= cvCreateMat(obsMatCols
, 1, CV_32FC1
);
663 CvMat
*V
= cvCreateMat(obsMatCols
, obsMatCols
, CV_32FC1
);
665 for (int c
=0; c
<reflc
.avgChannels
; c
++) {
670 sprintf(atafn,"ata%d.mat",c);
674 cvSVD(AtA
, w
, 0, V
, CV_SVD_MODIFY_A
);
677 cvGetCol(V
, &sub
, V
->cols
-1);
678 cvGetCol(lightParams
, &lpc
, c
);
679 cvScale(&sub
, &lpc
, 1.0/cvGet2D(&sub
,0,0).val
[0]);
682 std::cout << "Eigen values:";
683 for (int i=0; i<w->rows; ++i) {
684 float v = (float)cvGet2D(w, i, 0).val[0];
685 std::cout << " " << v;
687 std::cout << std::endl;
693 // construct light map
694 cvSet(map
.getIm(), cvScalarAll(.1));
698 std::cout << "Camera Values:\n";
699 for (int c=0; c<nbCam; ++c) {
700 float g = (float)cvGet2D(lightParams, c*2, 0).val[0];
701 float b = (float)cvGet2D(lightParams, c*2+1, 0).val[0];
702 std::cout <<" cam " << c << ": "<< g <<", " << b << std::endl;
705 buildMapFromSamples();
709 void LightMap::buildMapFromSamples()
711 for (int i
=0; i
<normals
->rows
; ++i
) {
712 updateLightMap((float *)CV_MAT_ELEM_PTR(*normals
, i
, 0),
713 &CV_MAT_ELEM(*lightParams
, float, i
+2*nbCam
, 0));
717 bool LightMap::load(const char *lightParamsFN
, const char *normalsFN
)
719 CvGrowMat
*lp
= loadMat(lightParamsFN
);
720 if (!lp
) return false;
721 lightParams
= cvCreateMat(lp
->rows
, lp
->cols
, CV_32FC1
);
722 cvCopy(lp
, lightParams
);
723 printf("Loaded lightParams: %dx%d.\n", lp
->rows
, lp
->cols
);
725 normals
= loadMat(normalsFN
);
726 int nlights
= lightParams
->rows
- 2*nbCam
;
727 if (!normals
|| ((normals
->rows
) < nlights
)) {
729 printf("Not enough normals. Expecting %d cameras. Found %d normals and %d parameters.\n",
730 nbCam
, normals
->rows
, lightParams
->rows
);
732 cvReleaseMat(&lightParams
);
735 if (normals
->rows
> nlights
)
736 normals
->resize(nlights
,3);
739 std::cout << "Camera Values:\n";
740 for (int c=0; c<nbCam; ++c) {
741 float g = (float)cvGet2D(lightParams, c*2, 0).val[0];
742 float b = (float)cvGet2D(lightParams, c*2+1, 0).val[0];
743 std::cout <<" cam " << c << ": "<< g <<", " << b << std::endl;
747 buildMapFromSamples();
752 bool LightMap::saveImage(const char *filename
)
754 IplImage
*image
= map
.getIm();
755 if (image
==0) return false;
756 IplImage
*im
= cvCreateImage(cvGetSize(image
), IPL_DEPTH_8U
,image
->nChannels
);
757 //double min=0, max=2;
758 //cvMinMaxLoc(image, &min, &max);
759 cvConvertScale(image
, im
, 128, 0);
760 int r
= cvSaveImage(filename
, im
);