1
/****************************************************************************
3
* A versatile mesh processing toolbox o o *
5
* Copyright(C) 2005 \/)\/ *
6
* Visual Computing Lab /\/| *
7
* ISTI - Italian National Research Council | *
9
* All rights reserved. *
11
* This program 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 2 of the License, or *
14
* (at your option) any later version. *
16
* This program 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 (http://www.gnu.org/licenses/gpl.txt) *
22
****************************************************************************/
31
#include <QtXml/QDomDocument>
32
#include <QtXml/QDomElement>
33
#include <QtXml/QDomNode>
35
#include <src/PhotoTexturer.h>
36
#include <src/UVFaceTexture.h>
37
#include <src/CameraCalibration.h>
38
#include <src/TextureFilter.h>
39
#include <src/TextureMerger.h>
40
#include <src/Tsai/TsaiCameraCalibration.h>
42
#include<vcg/complex/trimesh/allocate.h>
43
#include <vcg/math/matrix44.h>
44
#include <src/QuadTree/QuadTreeNode.h>
46
#include "photo_texture_tools.h"
48
#include <src/WinnerTakesAllTextureMerger.h>
49
#include <src/SmartBlendTextureMerger.h>
52
#include <vcg/complex/trimesh/update/position.h>
53
#include <vcg/complex/trimesh/update/bounding.h>
57
const QString PhotoTexturer::XML_PHOTOTEXTURING = "photoTexturing";
59
//const std::string PhotoTexturer::ORIGINALUVTEXTURECOORDS = "OriginalUVTextureCoords";
60
const std::string PhotoTexturer::UVTEXTURECOORDS = "UVTextureCoords";
62
const QString PhotoTexturer::TEXTURE_SIZE_WIDTH = "pt_texture_width";
63
const QString PhotoTexturer::TEXTURE_SIZE_HEIGHT = "pt_texture_height";
65
const QString PhotoTexturer::UNPROJECT_ENABLE_ANGLE = "pt_enable_angle";
66
const QString PhotoTexturer::UNPROJECT_ANGLE = "pt_angle";
67
const QString PhotoTexturer::UNPROJECT_ANGLE_WEIGHT = "pt_angle_weight";
68
const QString PhotoTexturer::UNPROJECT_ANGLE_SHARPNESS = "pt_angle_sharpness";
70
const QString PhotoTexturer::UNPROJECT_ENABLE_DISTANCE = "pt_enable_distance";
71
const QString PhotoTexturer::UNPROJECT_DISTANCE_WEIGHT = "pt_distance_weight";
72
const QString PhotoTexturer::UNPROJECT_DISTANCE_SHARPNESS = "pt_distance_shjarpness";
74
const QString PhotoTexturer::UNPROJECT_ENABLE_EDGE_STRETCHING = "pt_enable_edge_stretching";
75
const QString PhotoTexturer::UNPROJECT_EDGE_STRETCHING_PASS = "pt_edge_stretching_pass";
77
const QString PhotoTexturer::UNPROJECT_TEXTURE_FILENAME = "pt_unproject_texture_name";
80
const QString PhotoTexturer::BAKE_SAVE_UNPROJECT = "pt_save_unproject";
82
const QString PhotoTexturer::BAKE_MERGE_TEXTURES = "pt_merge_textures";
83
const QString PhotoTexturer::BAKE_MERGE_TYPE = "pt_merge_type";
84
const QString PhotoTexturer::BAKE_MERGED_TEXTURE = "pt_merged_texture_file";
85
const QString PhotoTexturer::BAKE_SMARTBLEND = "pt_smartblend_command";
87
#define ZB_EPSILON 1e-2
90
PhotoTexturer::PhotoTexturer(){
96
PhotoTexturer::~PhotoTexturer(){
100
void PhotoTexturer::loadConfigurationFile(QString cfgFile){
105
QString errorMessage;
106
if (file.open(QIODevice::ReadOnly) && doc.setContent(&file, &errorMessage)){
108
QDomElement root = doc.documentElement();
109
if (root.nodeName() == XML_PHOTOTEXTURING){
110
for(QDomElement element = root.firstChildElement(Camera::XML_CAMERA); !element.isNull(); element = element.nextSiblingElement(Camera::XML_CAMERA)){
111
Camera* cam = new Camera();
112
cam->loadFromXml(&element);
113
cameras.push_back(cam);
119
int PhotoTexturer::generateTextureId(){
123
void PhotoTexturer::saveConfigurationFile(QString cfgFile){
124
QDomDocument doc(XML_PHOTOTEXTURING);
125
QDomElement root = doc.createElement(XML_PHOTOTEXTURING);
126
doc.appendChild(root);
129
for (i=0;i<cameras.size();i++){
131
Camera *cam = cameras.at(i);
132
cam->saveAsXml(&doc,&root);
137
file.open(QIODevice::WriteOnly);
138
QTextStream qstream(&file);
143
void PhotoTexturer::addCamera(QString camFile){
146
QString errorMessage;
148
if (file.open(QIODevice::ReadOnly) && doc.setContent(&file, &errorMessage)){
150
QDomElement root = doc.documentElement();
151
qDebug() << root.nodeName() ;
152
if (root.nodeName() == Camera::XML_CAMERADOCUMENT){
154
QDomElement xml_cam = root.firstChildElement(Camera::XML_CAMERA);
155
if (!xml_cam.isNull()){
156
Camera* cam = new Camera();
157
cam->loadFromXml(&xml_cam);
158
cameras.push_back(cam);
161
qDebug("root is not camera \n");
164
qDebug()<< "errorMessage: " << errorMessage;
168
void PhotoTexturer::removeCamera(int i){
169
//checks if i is a valid index of the cameras list
170
//and deletes the camera on position i
171
if (i>=0 && i < cameras.size()){
176
void PhotoTexturer::storeOriginalTextureCoordinates(MeshModel *m){
177
qDebug()<<"storeOriginalTextureCoordinates";
178
// see http://vcg.sourceforge.net/index.php/Tutorial#User-defined_attributes
179
//if (m->cm.HasPerWedgeTexCoord()){ //Problem HasPerWedgeTexCoord() returns true even if MeshModel
180
//has no texture information
182
//checks if the MeshModel has texture coordinates
183
if(m->hasDataMask(MeshModel::MM_WEDGTEXCOORD) ){
184
//qDebug()<<"HasPerWedgeTexCoord";
185
CMeshO::PerFaceAttributeHandle<QMap<int,UVFaceTexture*> >ih;
186
if (!vcg::tri::HasPerFaceAttribute(m->cm,UVTEXTURECOORDS)){
187
//qDebug()<<"has no PhotoTexturingUVCoords";
188
ih = vcg::tri::Allocator<CMeshO>::AddPerFaceAttribute<QMap<int,UVFaceTexture*> >(m->cm,UVTEXTURECOORDS);
190
ih = vcg::tri::Allocator<CMeshO>::AddPerFaceAttribute<QMap<int,UVFaceTexture*> >(m->cm,UVTEXTURECOORDS);
192
//checks if the original texture coordinates has been saved before
194
if (origTextureID == -1){
195
//qDebug()<<"has no OriginalTextureCoords";
196
origTextureID = generateTextureId();
197
//CMeshO::PerFaceAttributeHandle<QMap<int,UVFaceTexture*> >ih = vcg::tri::Allocator<CMeshO>::AddPerFaceAttribute<QMap<int,UVFaceTexture*> >(m->cm,UVTEXTURECOORDS);
199
//saves the texture information for each face as perFaceAttribute
200
CMeshO::FaceIterator fi; int i = 0;
201
for(fi = m->cm.face.begin(); fi != m->cm.face.end(); ++fi,i++){
202
UVFaceTexture* uvft = new UVFaceTexture();
203
uvft->u[0] = (*fi).WT(0).u();
204
uvft->v[0] = (*fi).WT(0).v();
206
uvft->u[1] = (*fi).WT(1).u();
207
uvft->v[1] = (*fi).WT(1).v();
209
uvft->u[2] = (*fi).WT(2).u();
210
uvft->v[2] = (*fi).WT(2).v();
212
uvft->textureindex = (*fi).WT(0).n();
215
ih[i][origTextureID] = uvft; // [] operator takes a iterator
217
textureList[origTextureID]="original";
219
//qDebug()<<"has OriginalTextureCoords";
222
//qDebug()<<"!HasPerWedgeTexCoord";
227
void PhotoTexturer::restoreOriginalTextureCoordinates(MeshModel *m){
228
//qDebug() << "restoreOriginalTextureCoordinates";
229
// see http://vcg.sourceforge.net/index.php/Tutorial#User-defined_attributes
231
//checks if the original texture informationof the MeshModel were stored
232
if (origTextureID!= -1 && vcg::tri::HasPerFaceAttribute(m->cm,UVTEXTURECOORDS)){
233
applyTextureToMesh(m,origTextureID);
236
//qDebug()<<"has no OriginalTextureCoords";
242
void PhotoTexturer::calculateMeshTextureForAllCameras(MeshModel *m, bool calcZBuffer){
243
//checks if the MeshModel already has the perfaceAttribute PhotoTexturer::CAMERAUVTEXTURECOORDS
244
//if not it creates one
247
//enables texture information for the MeshModel
248
//m->updateDataMask(MeshModel::MM_WEDGTEXCOORD);
249
//makes sure that the mesh model mask enabels texture coordinates (needed to save the uv coorinates later)
250
//m->ioMask |= MeshModel::IOM_WEDGTEXCOORD;
253
//checks if special transformation data is stored asMeshData
257
//calculates the texture information (uv coordinates and texture index) for each camera
259
for (i=0;i<cameras.size();i++){
260
Camera *cam = cameras.at(i);
261
calculateMeshTextureForCamera(m,cam, calcZBuffer);
265
void PhotoTexturer::calculateMeshTextureForCamera(MeshModel *m, Camera* cam,bool calcZBuffer){
267
unsigned int size = static_cast<unsigned int>(m->cm.textures.size());
271
//gets the perFaceAttributeHandler for the perFaceAttribute PhotoTexturer::CAMERAUVTEXTURECOORDS
272
CMeshO::PerFaceAttributeHandle<QMap<int ,UVFaceTexture*> > ih;
274
if (!vcg::tri::HasPerFaceAttribute(m->cm,UVTEXTURECOORDS)){
275
//qDebug()<<"has no PhotoTexturingUVCoords";
276
ih = vcg::tri::Allocator<CMeshO>::AddPerFaceAttribute<QMap<int,UVFaceTexture*> > (m->cm,UVTEXTURECOORDS);
278
ih = vcg::tri::Allocator<CMeshO>::GetPerFaceAttribute<QMap<int,UVFaceTexture*> > (m->cm,UVTEXTURECOORDS);
281
vcg::Matrix44f matrixTr = m->cm.Tr;
282
vcg::Matrix44f matrix;
283
vcg::Matrix44f matrixInv;
284
if(vcg::tri::HasPerMeshAttribute(m->cm, PhotoTextureTools::TransformForPhoto)){
285
CMeshO::PerMeshAttributeHandle<vcg::Matrix44f> transformHandle = vcg::tri::Allocator<CMeshO>::GetPerMeshAttribute<vcg::Matrix44f> (m->cm, PhotoTextureTools::TransformForPhoto);
286
matrix = transformHandle();
287
matrixInv =vcg::Inverse(matrix);
289
vcg::tri::UpdatePosition<CMeshO>::Matrix(m->cm, m->cm.Tr,true);
290
//vcg::tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m->cm);
291
vcg::tri::UpdateBounding<CMeshO>::Box(m->cm);
293
qDebug()<< "transformHandle";
295
qDebug()<<"Identity";
296
matrix = vcg::Matrix44f();
297
matrix.SetIdentity();
303
//stdout << "matrix" << matrix;
304
qDebug() << "matrix2: " << matrix[0][0]<< matrix[0][1]<< matrix[0][2]<< matrix[0][3]<< matrix[1][0]<< matrix[1][1]<< matrix[1][2]<< matrix[1][3]<< matrix[2][0]<< matrix[2][1]<< matrix[2][2]<< matrix[2][3]<< matrix[3][0]<< matrix[3][1]<< matrix[3][2]<< matrix[3][3];
306
//loads the texture image and gets its dimensions
307
QImage *img = new QImage(cam->textureImage);
308
int imgw = img->width();
309
int imgh = img->height();
311
//looks if the texture image is allready loaded and stores the index of the texture
313
while (!found && (j < size))
315
if (cam->textureImage.toStdString().compare(m->cm.textures[j])==0)
325
m->cm.textures.push_back(cam->textureImage.toStdString());
330
//cam->calibration->calibrateToTsai(m);
331
int textureId = generateTextureId();
332
cam->textureId = textureId;
333
QList<QuadTreeLeaf*> buildQuadTree;
334
//calculates the uv coordinates for each face and saves them as UVFaceTexture as
335
//perFaceAttribute of the MeshModel
336
CMeshO::FaceIterator fi;
338
for(fi=m->cm.face.begin(); fi!=m->cm.face.end(); ++fi, count++) {
340
UVFaceTexture *ft = new UVFaceTexture();
342
//calculating angle between the camera direction and the face normal
343
vcg::Matrix33f rMatrix = vcg::Matrix33f(matrix,3);
344
//vcg::Point3f tmpN = rMatrix*(*fi).N();
345
vcg::Point3f tmpN = (*fi).N();
347
double angle = ((-1*cam->calibration->cameraDirection[0])*tmpN[0])
348
+((-1*cam->calibration->cameraDirection[1])*tmpN[1])
349
+((-1*cam->calibration->cameraDirection[2])*tmpN[2]);
351
angle = (angle/M_PI)*180.0;
353
//qDebug()<< "angle: " << angle;
354
ft->faceAngleToCamera = angle;
359
//vcg::Point3f tmpVector = matrix*(*fi).V(i)->cP();
360
vcg::Point3f tmpVector = (*fi).V(i)->cP();
361
cam->calibration->getUVforPoint(tmpVector[0],tmpVector[1],tmpVector[2],&u,&v);
363
ft->u[i] = u/cam->resolution[0];
364
ft->v[i] = 1.0-v/cam->resolution[1];
367
ft->textureindex =tindx;
368
ft->faceIndex = count;
370
ih[ft->faceIndex][textureId] = ft;
371
buildQuadTree.push_back(ft);
375
if (cam->zBuffer!= NULL){
380
QuadTreeNode zBufferTree = QuadTreeNode(0.0,0.0,1.0,1.0);
381
zBufferTree.buildQuadTree(&buildQuadTree,1.0/imgw,1.0/imgh);
382
cam->zBuffer = new TextureFilterZB(imgw,imgh,1);
383
calculateZBuffer(m,cam,&zBufferTree,cam->zBuffer);
384
cam->zBuffer->normalize();
386
//save Z-Buffer as image
387
//cam->zBuffer->SaveAsImage("zbuffer_",cam->name);
388
cam->calculatedTextures = true;
389
textureList[textureId]= cam->name;
392
if(vcg::tri::HasPerMeshAttribute(m->cm, PhotoTextureTools::TransformForPhoto)){
393
m->cm.Tr = matrixInv;
394
vcg::tri::UpdatePosition<CMeshO>::Matrix(m->cm, m->cm.Tr);
395
//vcg::tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m->cm);
396
vcg::tri::UpdateBounding<CMeshO>::Box(m->cm);
398
qDebug()<< "transformHandle";
402
void PhotoTexturer::applyTextureToMesh(MeshModel *m,int textureIdx, bool use_different_tidx, int tidx){
404
if(!m->hasDataMask(MeshModel::MM_WEDGTEXCOORD)){
405
m->updateDataMask(MeshModel::MM_WEDGTEXCOORD);
407
QMap<int, QString>::const_iterator i = textureList.find(textureIdx);
408
if (i != textureList.end()){
410
if (vcg::tri::HasPerFaceAttribute(m->cm,UVTEXTURECOORDS)){
412
CMeshO::PerFaceAttributeHandle<QMap<int,UVFaceTexture*> > ih = vcg::tri::Allocator<CMeshO>::GetPerFaceAttribute<QMap<int,UVFaceTexture*> > (m->cm,UVTEXTURECOORDS);
413
CMeshO::FaceIterator fi;
415
for(fi=m->cm.face.begin(); fi!=m->cm.face.end(); ++fi) {
417
UVFaceTexture* ft = ih[fi][textureIdx];
420
(*fi).WT(i).u() = ft->u[i];
421
(*fi).WT(i).v() = ft->v[i];
422
if (use_different_tidx && tidx >-1){
423
(*fi).WT(i).n() = tidx;
425
(*fi).WT(i).n() = ft->textureindex;
437
void PhotoTexturer::unprojectToOriginalTextureMap(MeshModel *m, Camera* camera, QuadTreeNode &qtree, ImageFilterContainer *container ,bool use_distance_filter, int distance_weight, bool use_angle_filter, int angle_weight , int angle_map_sharpness, double min_angle, int imgResX, int imgResY){
438
qDebug() <<"unprojectToOriginalTextureMap"<< min_angle;
440
//checks if the MeshModel has original uv coordinates and camera projected uv coordinates.
441
if (origTextureID != -1 && vcg::tri::HasPerFaceAttribute(m->cm,UVTEXTURECOORDS)){
443
//CMeshO::PerFaceAttributeHandle<UVFaceTexture*> oth = vcg::tri::Allocator<CMeshO>::GetPerFaceAttribute<UVFaceTexture*>(m->cm,ORIGINALUVTEXTURECOORDS);
444
CMeshO::PerFaceAttributeHandle<QMap<int,UVFaceTexture*> > cth = vcg::tri::Allocator<CMeshO>::GetPerFaceAttribute<QMap<int,UVFaceTexture*> > (m->cm,UVTEXTURECOORDS);
448
vcg::Matrix44f matrixTr = m->cm.Tr;
449
vcg::Matrix44f matrix;
450
vcg::Matrix44f matrixInv;
451
if(vcg::tri::HasPerMeshAttribute(m->cm, PhotoTextureTools::TransformForPhoto)){
452
CMeshO::PerMeshAttributeHandle<vcg::Matrix44f> transformHandle = vcg::tri::Allocator<CMeshO>::GetPerMeshAttribute<vcg::Matrix44f> (m->cm, PhotoTextureTools::TransformForPhoto);
453
matrix = transformHandle();
454
matrixInv =vcg::Inverse(matrix);
456
vcg::tri::UpdatePosition<CMeshO>::Matrix(m->cm, m->cm.Tr,true);
457
//vcg::tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m->cm);
458
vcg::tri::UpdateBounding<CMeshO>::Box(m->cm);
460
qDebug()<< "transformHandle";
463
QString camname = camera->name;
465
//creates a new RGBA image for saving the new texture
466
//QImage image(res_x, res_y, QImage::Format_ARGB32);
468
container->image = new QImage(imgResX,imgResY,QImage::Format_ARGB32);
469
TextureFilterSTD *distance_filter = NULL;
470
TextureFilterSTD *angle_filter = NULL;
472
if(use_angle_filter){
473
angle_filter = new TextureFilterSTD(imgResX,imgResY,angle_weight);
475
if (use_distance_filter){
476
distance_filter = new TextureFilterSTD(imgResX,imgResY,distance_weight);
479
//loading the texture corresponding to the camera
480
QImage tmp_texture(camera->textureImage);
481
QRgb* utimg = (QRgb*)tmp_texture.bits();
482
int twidth = tmp_texture.width();
483
int theight = tmp_texture.height();
489
//CMeshO::FaceIterator fi;
493
//goes pixelwise over the whole new texture image and looks if it lies inside
494
//a texture face of the original texture coordinates. If the pixel lies inside
495
//a textured face it looks in the corresponding camera texture for the color value
496
//of this pixel and stores it at the current pixel position in the new texture image.
498
QRgb* ucimg = (QRgb*)container->image->bits();
500
for (y=0;y<imgResY;y++){
501
for (x=0;x<imgResX;x++){
503
//sets the current pixel to black with an alpha value of 0
504
cpixel = QColor(0, 0, 0, 0);
505
//container->image->setPixel(x,imgResY-(y+1), cpixel.rgba());
506
ucimg[(imgResY-(y+1))*imgResX+x] = cpixel.rgba();
508
//searches the QuadTree for matching faces
509
QList<QuadTreeLeaf*> list;
510
qtree.getLeafs(((double)x/(double)(imgResX-1)),((double)y/(double)(imgResY-1)),list);
511
int ns = list.size();
515
while(!found && idx <ns){
517
tmp = dynamic_cast<UVFaceTexture*>(list.at(idx));
522
UVFaceTexture* ct = cth[tmp->faceIndex][camera->textureId];
523
tmp->getBarycentricCoordsForUV(((double)x/(double)(imgResX-1)),((double)y/(double)(imgResY-1)),a,b,c,d);
525
ct->getUVatBarycentricCoords(u,v,a,b,c);
526
int ix = (int)(((double)twidth-1)*u);
527
int iy = theight-(int)((((double)theight-1)*v)+1);
529
if(ix>=0 && ix<twidth && iy>=0 && iy<theight){
532
//calculating alpha value of the pixel by using the angle
535
f = m->cm.face.at(tmp->faceIndex);
538
p = f.V(0)->cP()*a+ f.V(1)->cP()*b+f.V(2)->cP()*c;
539
double distance = sqrt(pow(p[0]-camera->calibration->cameraPosition[0],2)+pow(p[1]-camera->calibration->cameraPosition[1],2)+pow(p[2]-camera->calibration->cameraPosition[2],2));
541
if((camera->zBuffer!= 0 && (camera->zBuffer->normalizeValue(distance)-ZB_EPSILON)<=camera->zBuffer->getValue(ix,tmp_texture.height()-(iy+1)))|| (camera->zBuffer== 0 )){
545
//cpixel = QColor(tmp_texture.pixel(ix,iy));
546
cpixel = QColor(utimg[iy*twidth+ix]);
547
cpixel.setAlpha(255);
548
//container->image->setPixel(x,imgResY-(y+1), cpixel.rgba());
549
ucimg[(imgResY-(y+1))*imgResX+x] = cpixel.rgba();
552
if(use_angle_filter){
553
//calculate normal vector for pixel
555
n1 = a*f.V(0)->N()[0]+ b*f.V(1)->N()[0]+c*f.V(2)->N()[0];
556
n2 = a*f.V(0)->N()[1]+ b*f.V(1)->N()[1]+c*f.V(2)->N()[1];
557
n3 = a*f.V(0)->N()[2]+ b*f.V(1)->N()[2]+c*f.V(2)->N()[2];
559
angle = ((-1*camera->calibration->cameraDirection[0])*n1)
560
+((-1*camera->calibration->cameraDirection[1])*n2)
561
+((-1*camera->calibration->cameraDirection[2])*n3);
563
//qDebug() << "angle["<<ix<<"]["<<iy<<"]: " <<angle;
567
angle = (angle/M_PI)*180.0;
569
if(angle<= min_angle){
570
double wangle = (angle/180.0)*M_PI;
571
wangle = sin(wangle);
572
wangle = pow(wangle,angle_map_sharpness);
574
angle_filter->setValue(x,imgResY-(y+1),wangle);
578
if(use_distance_filter){
579
//calculate distance for pixel
580
distance_filter->setValue(x,imgResY-(y+1),distance);
584
if (angle<= min_angle){
585
//cpixel = QColor(tmp_texture.pixel(ix,iy));
586
cpixel = QColor(utimg[iy*twidth+ix]);
587
//int rgb = (int)((angle/90.0*255.0));
588
//cpixel = QColor(rgb,rgb,rgb);
589
cpixel.setAlpha((int)((angle/90.0*255.0)));
590
//cpixel.setAlpha(255);
593
cpixel = QColor(0,0,0,0);
596
//container->image->setPixel(x,imgResY-(y+1), cpixel.rgba());
597
ucimg[(imgResY-(y+1))*imgResX+x] = cpixel.rgba();
606
//qDebug() << "not found ";
612
if(use_angle_filter){
613
container->addFilter(angle_filter);
615
if(use_distance_filter){
616
container->addFilter(distance_filter);
620
if(vcg::tri::HasPerMeshAttribute(m->cm, PhotoTextureTools::TransformForPhoto)){
621
m->cm.Tr = matrixInv;
622
vcg::tri::UpdatePosition<CMeshO>::Matrix(m->cm, m->cm.Tr);
623
//vcg::tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m->cm);
624
vcg::tri::UpdateBounding<CMeshO>::Box(m->cm);
626
qDebug()<< "transformHandle";
630
void PhotoTexturer::getSurrundingMeanColor(QRgb* uimg, int iwidth, int iheight, int x, int y, QColor &surcolor){
631
//qDebug()<<"getSurrundingMeanColor: "<<x<<y;
633
if((x>=0) && (x< iwidth) && (y>=0) && (y< iheight)){
635
c[0] = QColor(0,0,0,0);
636
c[1] = QColor(0,0,0,0);
637
c[2] = QColor(0,0,0,0);
638
c[3] = QColor(0,0,0,0);
639
c[4] = QColor(0,0,0,0);
640
c[5] = QColor(0,0,0,0);
641
c[6] = QColor(0,0,0,0);
642
c[7] = QColor(0,0,0,0);
643
if((x-1>=0) && (x-1< iwidth) && (y-1>=0) && (y-1< iheight)){
644
//c[0]=QColor::fromRgba(image.pixel(x-1,y-1));
645
c[0]=QColor::fromRgba(uimg[(y-1)*iwidth+(x-1)]);
648
if((x>=0) && (x< iwidth) && (y-1>=0) && (y-1< iheight)){
649
//c[1]=QColor::fromRgba(image.pixel(x,y-1));
650
c[1]=QColor::fromRgba(uimg[(y-1)*iwidth+(x)]);
652
if((x+1>=0) && (x+1< iwidth) && (y-1>=0) && (y-1< iheight)){
653
//c[2]=QColor::fromRgba(image.pixel(x+1,y-1));
654
c[2]=QColor::fromRgba(uimg[(y-1)*iwidth+(x+1)]);
656
if((x+1>=0) && (x+1< iwidth) && (y>=0) && (y< iheight)){
657
//c[3]=QColor::fromRgba(image.pixel(x+1,y));
658
c[3]=QColor::fromRgba(uimg[(y)*iwidth+(x+1)]);
660
if((x+1>=0) && (x+1< iwidth) && (y+1>=0) && (y+1< iheight)){
661
//c[4]=QColor::fromRgba(image.pixel(x+1,y+1));
662
c[4]=QColor::fromRgba(uimg[(y+1)*iwidth+(x+1)]);
664
if((x>=0) && (x< iwidth) && (y+1>=0) && (y+1< iheight)){
665
//c[5]=QColor::fromRgba(image.pixel(x,y+1));
666
c[5]=QColor::fromRgba(uimg[(y+1)*iwidth+(x)]);
668
if((x-1>=0) && (x-1< iwidth) && (y+1>=0) && (y+1< iheight)){
669
//c[6]=QColor::fromRgba(image.pixel(x-1,y+1));
670
c[6]=QColor::fromRgba(uimg[(y+1)*iwidth+(x-1)]);
672
if((x-1>=0) && (x-1< iwidth) && (y>=0) && (y< iheight)){
673
//c[7]=QColor::fromRgba(image.pixel(x-1,y));
674
c[7]=QColor::fromRgba(uimg[(y)*iwidth+(x-1)]);
692
surcolor.setRed(r/count);
693
surcolor.setGreen(g/count);
694
surcolor.setBlue(b/count);
695
surcolor.setAlpha(a/count);
696
surcolor.setAlpha(255);
699
surcolor.setGreen(0);
701
surcolor.setAlpha(0);
710
void PhotoTexturer::edgeTextureStretching(QImage *image, int pass){
711
QRgb* uimg = (QRgb*) image->bits();
712
int width = image->width();
713
int height = image->height();
717
//qDebug()<< "edgeTextureStretching pass:" <<++count;
718
QImage tmp_image = image->copy(0,0,width,height);
719
QRgb* utimg = (QRgb*) tmp_image.bits();
722
for(y=0;y<height;y++){
723
for(x=0;x<width;x++){
724
//QColor test = QColor::fromRgba(image->pixel(x,y));
725
QColor test = QColor::fromRgba(uimg[y*width+x]);
727
//qDebug()<< "alpha == 0";
729
getSurrundingMeanColor(utimg,width,height,x,y,surcolor);
730
//image->setPixel(x,y,surcolor.rgba());
731
uimg[y*width+x] = surcolor.rgba();
743
int PhotoTexturer::unprojectTextures(MeshModel *m, int textureID, FilterParameterSet *paraSet){
744
int width = paraSet->getInt(TEXTURE_SIZE_WIDTH);
745
int height = paraSet->getInt(TEXTURE_SIZE_HEIGHT);
746
int ets = paraSet->getInt(UNPROJECT_EDGE_STRETCHING_PASS);
747
//bool enable_angle_map = paraSet->getBool(UNPROJECT_ENABLE_ANGLE);
748
//int angle_weight = paraSet->getInt(UNPROJECT_ANGLE_WEIGHT);
749
//int angle_map_sharpness = paraSet->getInt(UNPROJECT_ANGLE_SHARPNESS);
750
//double min_angle = paraSet->getFloat(UNPROJECT_ANGLE);
751
//bool enable_distance_map = paraSet->getBool(UNPROJECT_ENABLE_DISTANCE);
752
//int distance_weight = paraSet->getInt(UNPROJECT_DISTANCE_WEIGHT);
753
QString smartblend = paraSet->getString(BAKE_SMARTBLEND);
755
QList<QuadTreeLeaf*> *list = new QList<QuadTreeLeaf*>();
756
CMeshO::PerFaceAttributeHandle<QMap<int,UVFaceTexture*> >oth = vcg::tri::Allocator<CMeshO>::GetPerFaceAttribute<QMap<int,UVFaceTexture*> >(m->cm,UVTEXTURECOORDS);
757
CMeshO::FaceIterator fi;
758
for(fi = m->cm.face.begin();fi!=m->cm.face.end();fi++) {
759
list->push_back(oth[fi][origTextureID]);
762
QuadTreeNode qtree = QuadTreeNode(0.0,0.0,1.0,1.0);
763
//qDebug() << "list->size(): "<<list->size();
764
//qDebug()<< "buildQuadTree";
765
qtree.buildQuadTree(list, 0.50/(double)width,0.50/(double)height);
767
ImageFilterContainer *ifc = new ImageFilterContainer();
768
unprojectToOriginalTextureMap(m,camera,qtree,ifc, enable_distance_map, distance_weight, enable_angle_map,angle_weight,angle_map_sharpness,min_angle,width,height);
770
ifc->image->save(unprojectTextureName,"PNG");
776
int PhotoTexturer::bakeTextures(MeshModel *m, FilterParameterSet *paraSet){
777
int width = paraSet->getInt(TEXTURE_SIZE_WIDTH);
778
int height = paraSet->getInt(TEXTURE_SIZE_HEIGHT);
779
bool enable_ets = paraSet->getBool(UNPROJECT_ENABLE_EDGE_STRETCHING);
780
int ets = paraSet->getInt(UNPROJECT_EDGE_STRETCHING_PASS);
781
bool enable_angle_map = paraSet->getBool(UNPROJECT_ENABLE_ANGLE);
782
int angle_weight = paraSet->getInt(UNPROJECT_ANGLE_WEIGHT);
783
int angle_map_sharpness = paraSet->getInt(UNPROJECT_ANGLE_SHARPNESS);
784
double min_angle = paraSet->getFloat(UNPROJECT_ANGLE);
785
bool enable_distance_map = paraSet->getBool(UNPROJECT_ENABLE_DISTANCE);
786
int distance_weight = paraSet->getInt(UNPROJECT_DISTANCE_WEIGHT);
787
QString smartblend = paraSet->getString(BAKE_SMARTBLEND);
788
int merger_type = paraSet->getEnum(BAKE_MERGE_TYPE);
789
bool saveUnprojected = paraSet->getBool(BAKE_SAVE_UNPROJECT);
790
//creating a list of all UVFaceTexture
791
QList<QuadTreeLeaf*> list;
792
CMeshO::PerFaceAttributeHandle<QMap<int,UVFaceTexture*> >oth = vcg::tri::Allocator<CMeshO>::GetPerFaceAttribute<QMap<int,UVFaceTexture*> >(m->cm,UVTEXTURECOORDS);
793
CMeshO::FaceIterator fi;
794
for(fi = m->cm.face.begin();fi!=m->cm.face.end();fi++) {
795
list.push_back(oth[fi][origTextureID]);
798
//creating a quadtree from list UVFaceTexture
799
QuadTreeNode qtree = QuadTreeNode(0.0,0.0,1.0,1.0);
800
qtree.buildQuadTree(&list, 0.50/(double)width,0.50/(double)height);
801
TextureMerger *texMerger = NULL;
802
//deciedes which TextureMerger to use
803
if (merger_type == 0){
804
texMerger = new WinnerTakesAllTextureMerger();
805
}else if(merger_type == 1){
806
texMerger = new SmartBlendTextureMerger(smartblend);
808
if (texMerger != NULL){
809
for (int i=0;i<cameras.size();i++){
810
Camera *camera = cameras.at(i);
811
if(camera->textureId >-1){
812
ImageFilterContainer *ifc = new ImageFilterContainer();
813
ifc->tag = camera->name;
814
unprojectToOriginalTextureMap(m,camera,qtree,ifc, enable_distance_map, distance_weight, enable_angle_map,angle_weight,angle_map_sharpness,min_angle,width,height);
815
texMerger->ifcList.push_back(ifc);
818
texMerger->normalizeFilterContainerList();
820
if (saveUnprojected){
821
ImageFilterContainer *ifc;
822
QFileInfo fi = QFileInfo(m->fileName.c_str());
823
for (int i=0;i<texMerger->ifcList.size();i++){
824
ifc = texMerger->ifcList.at(i);
825
QString upTextureName = fi.baseName()+"_unproject_"+ifc->tag+".png";
826
ifc->image->save(upTextureName,"PNG");
830
QImage *image = texMerger->merge(width,height);
835
edgeTextureStretching(image,ets);
837
QString filename = paraSet->getString(BAKE_MERGED_TEXTURE);
839
QImage final_image = image->convertToFormat(QImage::Format_RGB32);
841
final_image.save(filename,"PNG");
843
//create new UVTexture set
844
qDebug()<<"filename:"<<filename;
845
int textureId = generateTextureId();
847
unsigned int size = static_cast<unsigned int>(m->cm.textures.size());
850
while (!found && (j < size)){
852
if (filename.toStdString().compare(m->cm.textures[j])==0)
862
m->cm.textures.push_back(filename.toStdString());
866
qDebug()<<"tindx:"<<tindx;
868
if (origTextureID>-1){
869
qDebug()<<"has OriginalTextureCoords";
870
//CMeshO::PerFaceAttributeHandle<UVFaceTexture*> ihot = vcg::tri::Allocator<CMeshO>::GetPerFaceAttribute<UVFaceTexture*> (m->cm,ORIGINALUVTEXTURECOORDS);
871
CMeshO::PerFaceAttributeHandle<QMap<int,UVFaceTexture*> > cth = vcg::tri::Allocator<CMeshO>::GetPerFaceAttribute<QMap<int,UVFaceTexture*> > (m->cm,UVTEXTURECOORDS);
873
//overwrites the current texture information with the original texture information
874
// from the perFaceAttribute "ORIGINALUVTEXTURECOORDS"
875
CMeshO::FaceIterator fi; int i = 0;
876
for(fi = m->cm.face.begin(); fi != m->cm.face.end(); ++fi,++i){
878
UVFaceTexture* uvft = cth[fi][origTextureID] ;
879
UVFaceTexture* tmp = new UVFaceTexture(*uvft);
880
tmp->textureindex = tindx;
882
cth[fi][textureId] = tmp;
887
//qDebug()<<"has no OriginalTextureCoords";
889
qDebug()<<"textureId:"<<textureId;
891
textureList[textureId]="baked_win_"+QString::number(bakeCounter);
892
}else if(merger_type==1){
893
textureList[textureId]="baked_sb_"+QString::number(bakeCounter);
906
QImage PhotoTexturer::mergeTextureImagesWinnerTakesAll(int imgWidth, int imgHeight, QList<QImage> imgList){
907
QImage image = QImage(imgWidth,imgHeight,QImage::Format_RGB32);
910
for(x=0; x<imgWidth;x++){
911
for(y=0;y<imgHeight;y++){
912
QColor cpixel = QColor(0, 0, 0, 0);
914
for(i=0;i<imgList.size();i++){
915
QImage tmpImg = imgList.at(i);
916
QColor tmpPixel = QColor::fromRgba(tmpImg.pixel(x,y));
917
if(cpixel.alpha()<tmpPixel.alpha()){
918
cpixel = QColor(tmpPixel);
921
image.setPixel(x,y,cpixel.rgba());
928
void PhotoTexturer::convertToTsaiCamera(int camIdx, bool optimize, QString filename,MeshModel *mm){
929
if(camIdx>=0 && camIdx< cameras.size()){
930
Camera* newCam = new Camera();
931
Camera* oldCam = cameras.at(camIdx);
933
newCam->name = oldCam->name;
934
newCam->resolution[0] = oldCam->resolution[0];
935
newCam->resolution[1] = oldCam->resolution[1];
936
newCam->textureImage = oldCam->textureImage;
937
newCam->calibration = oldCam->calibration->calibrateToTsai(mm,optimize);
939
QDomDocument doc(Camera::XML_CAMERADOCUMENT);
940
QDomElement root = doc.createElement(Camera::XML_CAMERADOCUMENT);
941
newCam->saveAsXml(&doc,&root);
942
doc.appendChild(root);
943
QFile file(filename);
944
file.open(QIODevice::WriteOnly);
945
QTextStream qstream(&file);
952
void PhotoTexturer::exportMaxScript(QString filename,MeshModel *mm){
954
QFile* ms = new QFile(filename);
955
if (ms->open(QIODevice::WriteOnly)){
959
for (i=0;i<cameras.size();i++){
960
Camera* cam = cameras.at(i);
961
//TsaiCameraCalibration* tsai = dynamic_cast<TsaiCameraCalibration*>(cam->calibration);
962
TsaiCameraCalibration* tsai = dynamic_cast<TsaiCameraCalibration*>(cam->calibration->calibrateToTsai(mm,false));
964
out << "-- kappa1: "<< tsai->calib_const.kappa1<<"\n";
965
out << "-- Cx: "<< tsai->cam_para.Cx << "\tCy: "<<tsai->cam_para.Cy<<"\n";
966
out << "-- sx: "<< tsai->cam_para.sx<<"\n";
967
out << "-- p1: "<< tsai->calib_const.p1 << "\tp2: "<<tsai->calib_const.p2<<"\n";
969
out << "r1 = [" << tsai->calib_const.r1<<",\t"<< -tsai->calib_const.r4<<",\t"<<-tsai->calib_const.r7<<"]\n";
970
out << "r2 = [" << tsai->calib_const.r2<<",\t"<< -tsai->calib_const.r5<<",\t"<<-tsai->calib_const.r8<<"]\n";
971
out << "r3 = [" << tsai->calib_const.r3<<",\t"<< -tsai->calib_const.r6<<",\t"<<-tsai->calib_const.r9<<"]\n";
972
out << "r4 = [" << tsai->calib_const.Tx<<",\t"<< -tsai->calib_const.Ty<<",\t"<<-tsai->calib_const.Tz<<"]\n";
974
out<< "m = matrix3 r1 r2 r3 r4\n";
976
out << "setRendApertureWidth ("<< tsai->cam_para.dpx * cam->resolution[0]<<")\n";// -- horizontal size of sensor/filmback
977
out << "renderPixelAspect = " << tsai->cam_para.dpx/tsai->cam_para.dpy<<"\n"; // -- non-square pixels?
978
out << "renderWidth = "<< cam->resolution[0] <<"\n";
979
out << "renderHeight = "<< cam->resolution[1] <<"\n";
981
out << "freecamera name:\""<< cam->name <<"\" fov:(cameraFOV.MMtoFOV "<<tsai->calib_const.f<<") transform:(inverse m)\n";
988
qDebug()<< "Could not open max script file." <<filename;
996
int PhotoTexturer::combineTextures(MeshModel* m){
998
if (vcg::tri::HasPerFaceAttribute(m->cm,UVTEXTURECOORDS)){
1000
CMeshO::PerFaceAttributeHandle<QMap<int,UVFaceTexture*> > ih = vcg::tri::Allocator<CMeshO>::GetPerFaceAttribute<QMap<int,UVFaceTexture*> > (m->cm,UVTEXTURECOORDS);
1001
CMeshO::FaceIterator fi;
1002
textureId = generateTextureId();
1005
for(fi=m->cm.face.begin(); fi!=m->cm.face.end(); ++fi) {
1006
UVFaceTexture* ft =0;
1008
for(j=0;j<cameras.size();j++){
1009
UVFaceTexture* tmp = ih[fi][cameras.at(j)->textureId];
1012
if (ft==0 || ft->faceAngleToCamera>tmp->faceAngleToCamera){
1014
if(((tmp->u[0]>=0.0 && tmp->u[0] <=1.0)&&(tmp->v[0]>=0.0 && tmp->v[0] <=1.0))&&
1015
((tmp->u[1]>=0.0 && tmp->u[1] <=1.0)&&(tmp->v[1]>=0.0 && tmp->v[1] <=1.0))&&
1016
((tmp->u[2]>=0.0 && tmp->u[2] <=1.0)&&(tmp->v[2]>=0.0 && tmp->v[2] <=1.0))){
1024
UVFaceTexture* tmp2 = NULL;
1026
tmp2 = new UVFaceTexture(*ft);
1029
ih[fi][textureId]=tmp2;
1035
textureList[textureId] = "combined_"+QString::number(combineCounter);
1040
void PhotoTexturer::calculateZBuffer(MeshModel *mm,Camera* camera,QuadTreeNode *qtree, TextureFilterZB *zbuffer){
1042
qDebug()<< "zbuffer->vm_height:"<<zbuffer->vm_height<<"zbuffer->vm_width:" <<zbuffer->vm_width;
1044
for (y=0;y<zbuffer->vm_height;y++){
1045
for (x=0;x<zbuffer->vm_width;x++){
1046
double dx = ((double)x/(double)(zbuffer->vm_width-1));
1047
double dy = ((double)y/(double)(zbuffer->vm_height-1));
1048
QList<QuadTreeLeaf*>list;
1049
qtree->getLeafs(dx,dy,list);
1052
for (i=0;i<list.size();i++){
1053
QuadTreeLeaf* leaf = list.at(i);
1054
UVFaceTexture *uvft = dynamic_cast<UVFaceTexture*>(leaf);
1057
uvft->getBarycentricCoordsForUV(dx,dy,a,b,c,d);
1058
//qDebug() << "d:" << d;
1060
CFaceO face = mm->cm.face.at(uvft->faceIndex);
1061
vcg::Point3f point = face.V(0)->cP()*a+face.V(1)->cP()*b+face.V(2)->cP()*c;
1062
double distance = sqrt(pow(camera->calibration->cameraPosition[0]-point[0],2)+pow(camera->calibration->cameraPosition[1]-point[1],2)+pow(camera->calibration->cameraPosition[2]-point[2],2));
1063
//qDebug()<< "distance: " << distance;
1064
if(zbuffer->getValue(x,y)== DBL_MIN||zbuffer->getValue(x,y)>distance){
1065
zbuffer->setValue(x,y,distance);
1066
//qDebug()<< "found z";
1080
void PhotoTexturer::reset(MeshModel *mm){
1081
if (origTextureID > -1){
1082
restoreOriginalTextureCoordinates(mm);
1084
mm->clearDataMask(MeshModel::MM_WEDGTEXCOORD);
1088
QList<int> keys = textureList.keys();
1091
for(int i =0; i< keys.size();i++){
1093
if(key != origTextureID){
1094
QMap<int, QString>::const_iterator it = textureList.find(key);
1095
if (it != textureList.end()){
1096
if (vcg::tri::HasPerFaceAttribute(mm->cm,UVTEXTURECOORDS)){
1098
CMeshO::PerFaceAttributeHandle<QMap<int,UVFaceTexture*> > ih = vcg::tri::Allocator<CMeshO>::GetPerFaceAttribute<QMap<int,UVFaceTexture*> > (mm->cm,UVTEXTURECOORDS);
1099
CMeshO::FaceIterator fi;
1100
for(fi=mm->cm.face.begin(); fi!=mm->cm.face.end(); ++fi) {
1102
UVFaceTexture* ft = ih[fi][key];
1109
textureList.remove(key);
1113
for(int i = 0;i<cameras.size();i++){
1114
Camera *c = cameras.at(i);