1 /****************************************************************************
2 * MeshLab                                                           o o     *
3 * A versatile mesh processing toolbox                             o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2005                                                \/)\/    *
6 * Visual Computing Lab                                            /\/|      *
7 * ISTI - Italian National Research Council                           |      *
8 *                                                                    \      *
9 * All rights reserved.                                                      *
10 *                                                                           *
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.                                       *
15 *                                                                           *
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)          *
20 * for more details.                                                         *
21 *                                                                           *
22 ****************************************************************************/
23 
24 #include "filter_img_patch_param.h"
25 #include <common/GLExtensionsManager.h>
26 #include <QtGui>
27 #include <wrap/gl/shot.h>
28 #include <vcg/space/rect_packer.h>
29 #include "VisibleSet.h"
30 #include "VisibilityCheck.h"
31 #include "TexturePainter.h"
32 #include <cmath>
33 
34 
35 
36 
FilterImgPatchParamPlugin()37 FilterImgPatchParamPlugin::FilterImgPatchParamPlugin() : m_Context(NULL)
38 {
39     typeList << FP_PATCH_PARAM_ONLY
40         << FP_PATCH_PARAM_AND_TEXTURING
41         << FP_RASTER_VERT_COVERAGE
42         << FP_RASTER_FACE_COVERAGE;
43 
44     foreach( FilterIDType tt , types() )
45         actionList << new QAction(filterName(tt), this);
46 }
47 
48 
~FilterImgPatchParamPlugin()49 FilterImgPatchParamPlugin::~FilterImgPatchParamPlugin()
50 {
51     delete m_Context;
52     m_Context = NULL;
53 }
54 
55 
filterName(FilterIDType id) const56 QString FilterImgPatchParamPlugin::filterName( FilterIDType id ) const
57 {
58     switch( id )
59     {
60     case FP_PATCH_PARAM_ONLY:  return QString( "Parameterization from registered rasters" );
61     case FP_PATCH_PARAM_AND_TEXTURING:  return QString( "Parameterization + texturing from registered rasters" );
62     case FP_RASTER_VERT_COVERAGE:  return QString( "Quality from raster coverage (Vertex)" );
63     case FP_RASTER_FACE_COVERAGE:  return QString( "Quality from raster coverage (Face)" );
64     default: assert(0); return QString();
65     }
66 }
67 
68 
filterInfo(FilterIDType id) const69 QString FilterImgPatchParamPlugin::filterInfo( FilterIDType id ) const
70 {
71     switch( id )
72     {
73     case FP_PATCH_PARAM_ONLY:  return QString( "The mesh is parameterized by creating some patches that correspond to projection of portions of surfaces onto the set of registered rasters.");
74     case FP_PATCH_PARAM_AND_TEXTURING:	return QString("The mesh is parameterized and textured by creating some patches that correspond to projection of portions of surfaces onto the set of registered rasters.");
75     case FP_RASTER_VERT_COVERAGE:  return QString( "Compute a quality value representing the number of images into which each vertex of the active mesh is visible." );
76     case FP_RASTER_FACE_COVERAGE:  return QString( "Compute a quality value representing the number of images into which each face of the active mesh is visible." );
77     default: assert(0); return QString();
78     }
79 }
80 
81 
getRequirements(QAction * act)82 int FilterImgPatchParamPlugin::getRequirements( QAction *act )
83 {
84     switch( ID(act) )
85     {
86     case FP_PATCH_PARAM_ONLY:
87     case FP_PATCH_PARAM_AND_TEXTURING:  return MeshModel::MM_WEDGTEXCOORD | MeshModel::MM_FACEFACETOPO | MeshModel::MM_VERTFACETOPO;
88     case FP_RASTER_VERT_COVERAGE:  return MeshModel::MM_VERTQUALITY;
89     case FP_RASTER_FACE_COVERAGE:  return MeshModel::MM_FACEQUALITY;
90     default:  assert(0); return 0;
91     }
92 }
93 
94 
getClass(QAction * act)95 MeshFilterInterface::FilterClass FilterImgPatchParamPlugin::getClass( QAction *act )
96 {
97     switch( ID(act) )
98     {
99     case FP_PATCH_PARAM_ONLY:
100     case FP_PATCH_PARAM_AND_TEXTURING:  return Texture;
101     case FP_RASTER_VERT_COVERAGE:
102     case FP_RASTER_FACE_COVERAGE:  return FilterClass(Quality + Camera + Texture);
103     default:  assert(0); return MeshFilterInterface::Generic;
104     }
105 }
106 
107 
108 //int FilterImgPatchParamPlugin::postCondition( QAction *act ) const
109 //{
110 //    switch( ID(act) )
111 //    {
112 //        case FP_PATCH_PARAM_ONLY:
113 //        case FP_PATCH_PARAM_AND_TEXTURING:  return MeshModel::MM_WEDGTEXCOORD;
114 //        case FP_RASTER_COVERAGE:
115 //        {
116 //            return QString(  );
117 //        }
118 //        default:  assert(0); return 0;
119 //    }
120 //}
121 
122 
initParameterSet(QAction * act,MeshDocument &,RichParameterSet & par)123 void FilterImgPatchParamPlugin::initParameterSet( QAction *act,
124     MeshDocument &/*md*/,
125     RichParameterSet &par )
126 {
127     switch( ID(act) )
128     {
129     case FP_PATCH_PARAM_AND_TEXTURING:
130         {
131             par.addParam( new RichInt( "textureSize",
132                 1024,
133                 "Texture size",
134                 "Specifies the dimension of the generated texture" ) );
135             par.addParam( new RichString( "textureName",
136                 "texture.png",
137                 "Texture name",
138                 "Specifies the name of the file into which the texture image will be saved" ) );
139             par.addParam( new RichBool( "colorCorrection",
140                 true,
141                 "Color correction",
142                 "If true, the final texture is corrected so as to ensure seamless transitions" ) );
143             par.addParam( new RichInt( "colorCorrectionFilterSize",
144                 1,
145                 "Color correction filter",
146                 "It is the radius (in pixel) of the kernel that is used to compute the difference between corresponding texels in different rasters. Default is 1 that generate a 3x3 kernel. Highest values increase the robustness of the color correction process in the case of strong image-to-geometry misalignments" ) );
147         }
148     case FP_PATCH_PARAM_ONLY:
149         {
150             par.addParam( new RichBool( "useDistanceWeight",
151                 true,
152                 "Use distance weight",
153                 "Includes a weight accounting for the distance to the camera during the computation of reference images" ) );
154             par.addParam( new RichBool( "useImgBorderWeight",
155                 true,
156                 "Use image border weight",
157                 "Includes a weight accounting for the distance to the image border during the computation of reference images" ) );
158             par.addParam( new RichBool( "useAlphaWeight",
159                 false,
160                 "Use image alpha weight",
161                 "If true, alpha channel of the image is used as additional weight. In this way it is possible to mask-out parts of the images that should not be projected on the mesh. Please note this is not a transparency effect, but just influences the weigthing between different images" ) );
162             par.addParam( new RichBool( "cleanIsolatedTriangles",
163                 true,
164                 "Clean isolated triangles",
165                 "Remove all patches compound of a single triangle by aggregating them to adjacent patches" ) );
166             par.addParam( new RichBool( "stretchingAllowed",
167                 false,
168                 "UV stretching",
169                 "If true, texture coordinates are stretched so as to cover the full interval [0,1] for both directions" ) );
170             par.addParam( new RichInt( "textureGutter",
171                 4,
172                 "Texture gutter",
173                 "Extra boundary to add to each patch before packing in texture space (in pixels)" ) );
174             break;
175         }
176     case FP_RASTER_VERT_COVERAGE:
177     case FP_RASTER_FACE_COVERAGE:
178         {
179             par.addParam( new RichBool( "normalizeQuality",
180                 false,
181                 "Normalize",
182                 "Rescale quality values to the range [0,1]" ) );
183             break;
184         }
185     }
186 }
187 
188 
applyFilter(QAction * act,MeshDocument & md,RichParameterSet & par,vcg::CallBackPos *)189 bool FilterImgPatchParamPlugin::applyFilter( QAction *act,
190     MeshDocument &md,
191     RichParameterSet &par,
192     vcg::CallBackPos * /*cb*/ )
193 {
194 
195 
196     glContext->makeCurrent();
197     if( !GLExtensionsManager::initializeGLextensions_notThrowing() )
198     {
199         this->errorMessage="Failed GLEW initialization";
200         return false;
201     }
202 
203     glPushAttrib(GL_ALL_ATTRIB_BITS);
204 
205     delete m_Context;
206     m_Context = new glw::Context();
207     m_Context->acquire();
208 
209     if( !VisibilityCheck::GetInstance(*m_Context) )
210     {
211         this->errorMessage="VisibilityCheck failed";
212         return false;
213     }
214     VisibilityCheck::ReleaseInstance();
215 
216 
217     bool retValue = true;
218 
219     CMeshO &mesh = md.mm()->cm;
220 
221     std::list<Shotm> initialShots;
222     QList<RasterModel*> activeRasters;
223     foreach( RasterModel *rm, md.rasterList )
224     {
225         initialShots.push_back( rm->shot );
226         rm->shot.ApplyRigidTransformation( vcg::Inverse(mesh.Tr) );
227         if( rm->visible )
228             activeRasters.push_back( rm );
229     }
230 
231     if( activeRasters.empty() )    {
232         this->errorMessage="No active Raster";
233         {
234             glContext->doneCurrent();
235             errorMessage = "You need to have at least one valid raster layer in your project, to apply this filter"; // text
236             return false;
237         }
238     }
239 
240     switch( ID(act) )
241     {
242     case FP_PATCH_PARAM_ONLY:
243         {
244             if (vcg::tri::Clean<CMeshO>::CountNonManifoldEdgeFF(md.mm()->cm)>0)
245             {
246                 glContext->doneCurrent();
247                 errorMessage = "Mesh has some not 2-manifold faces, this filter requires manifoldness"; // text
248                 return false; // can't continue, mesh can't be processed
249             }
250             vcg::tri::Allocator<CMeshO>::CompactFaceVector(md.mm()->cm);
251             vcg::tri::Allocator<CMeshO>::CompactVertexVector(md.mm()->cm);
252             vcg::tri::UpdateTopology<CMeshO>::FaceFace(md.mm()->cm);
253             vcg::tri::UpdateTopology<CMeshO>::VertexFace(md.mm()->cm);
254             glContext->meshAttributesUpdated(md.mm()->id(),true,MLRenderingData::RendAtts());
255             RasterPatchMap patches;
256             PatchVec nullPatches;
257             patchBasedTextureParameterization( patches,
258                 nullPatches,
259                 md.mm()->id(),
260                 mesh,
261                 activeRasters,
262                 par );
263 
264             break;
265         }
266     case FP_PATCH_PARAM_AND_TEXTURING:
267         {
268             if (vcg::tri::Clean<CMeshO>::CountNonManifoldEdgeFF(md.mm()->cm)>0)
269             {
270                 glContext->doneCurrent();
271                 errorMessage = "Mesh has some not 2-manifold faces, this filter requires manifoldness"; // text
272                 return false; // can't continue, mesh can't be processed
273             }
274             vcg::tri::Allocator<CMeshO>::CompactEveryVector(md.mm()->cm);
275             vcg::tri::UpdateTopology<CMeshO>::FaceFace(md.mm()->cm);
276             vcg::tri::UpdateTopology<CMeshO>::VertexFace(md.mm()->cm);
277             glContext->meshAttributesUpdated(md.mm()->id(),true,MLRenderingData::RendAtts());
278             QString texName = par.getString( "textureName" ).simplified();
279             int pathEnd = std::max( texName.lastIndexOf('/'), texName.lastIndexOf('\\') );
280             if( pathEnd != -1 )
281                 texName = texName.right( texName.size()-pathEnd-1 );
282 
283             if( (retValue = texName.size()!=0) )
284             {
285                 RasterPatchMap patches;
286                 PatchVec nullPatches;
287                 patchBasedTextureParameterization( patches,
288                     nullPatches,
289                     md.mm()->id(),
290                     mesh,
291                     activeRasters,
292                     par );
293 
294                 TexturePainter painter( *m_Context, par.getInt("textureSize") );
295                 if( (retValue = painter.isInitialized()) )
296                 {
297                     QElapsedTimer t; t.start();
298                     painter.paint( patches );
299                     if( par.getBool("colorCorrection") )
300                         painter.rectifyColor( patches, par.getInt("colorCorrectionFilterSize") );
301                     Log( "TEXTURE PAINTING: %.3f sec.", 0.001f*t.elapsed() );
302 
303                     QImage tex = painter.getTexture();
304                     if( tex.save(texName) )
305                     {
306                         mesh.textures.clear();
307                         mesh.textures.push_back( texName.toStdString() );
308                     }
309                 }
310             }
311 
312             break;
313         }
314     case FP_RASTER_VERT_COVERAGE:
315         {
316             VisibilityCheck &visibility = *VisibilityCheck::GetInstance( *m_Context );
317             visibility.setMesh(md.mm()->id(),&mesh );
318             visibility.m_plugcontext = glContext;
319             for( CMeshO::VertexIterator vi=mesh.vert.begin(); vi!=mesh.vert.end(); ++vi )
320                 vi->Q() = 0.0f;
321 
322             foreach( RasterModel *rm, activeRasters )
323             {
324                 visibility.setRaster( rm );
325                 visibility.checkVisibility();
326                 for( CMeshO::VertexIterator vi=mesh.vert.begin(); vi!=mesh.vert.end(); ++vi )
327                     if( visibility.isVertVisible(vi) )
328                         vi->Q() += 1.0f;
329             }
330 
331             if( par.getBool("normalizeQuality") )
332             {
333                 const float normFactor = 1.0f / md.rasterList.size();
334                 for( CMeshO::VertexIterator vi=mesh.vert.begin(); vi!=mesh.vert.end(); ++vi )
335                     vi->Q() *= normFactor;
336             }
337 
338             break;
339         }
340     case FP_RASTER_FACE_COVERAGE:
341         {
342             VisibilityCheck &visibility = *VisibilityCheck::GetInstance( *m_Context );
343             visibility.setMesh(md.mm()->id(),&mesh );
344             visibility.m_plugcontext = glContext;
345 
346             for( CMeshO::FaceIterator fi=mesh.face.begin(); fi!=mesh.face.end(); ++fi )
347                 fi->Q() = 0.0f;
348 
349             foreach( RasterModel *rm, activeRasters )
350             {
351                 visibility.setRaster( rm );
352                 visibility.checkVisibility();
353                 for( CMeshO::FaceIterator fi=mesh.face.begin(); fi!=mesh.face.end(); ++fi )
354                     if( visibility.isFaceVisible(fi) )
355                         fi->Q() += 1.0f;
356             }
357 
358             if( par.getBool("normalizeQuality") )
359             {
360                 const float normFactor = 1.0f / md.rasterList.size();
361                 for( CMeshO::FaceIterator fi=mesh.face.begin(); fi!=mesh.face.end(); ++fi )
362                     fi->Q() *= normFactor;
363             }
364 
365             break;
366         }
367     }
368 
369 
370     foreach( RasterModel *rm, md.rasterList )
371     {
372         rm->shot = *initialShots.begin();
373         initialShots.erase( initialShots.begin() );
374     }
375 
376     VisibilityCheck::ReleaseInstance();
377 
378 
379     delete m_Context;
380     m_Context = NULL;
381 
382     glPopAttrib();
383     glContext->doneCurrent();
384 
385 
386     return retValue;
387 }
388 
389 
getNeighbors(CVertexO * v,NeighbSet & neighb) const390 void FilterImgPatchParamPlugin::getNeighbors( CVertexO *v,
391     NeighbSet &neighb ) const
392 {
393     vcg::face::Pos<CFaceO> p( v->VFp(), v ), ori = p;
394     do
395     {
396         neighb.insert( p.F() );
397         p.FlipF();
398         p.FlipE();
399     } while( ori != p );
400 }
401 
402 
getFaceNeighbors(CFaceO * f,NeighbSet & neighb) const403 void FilterImgPatchParamPlugin::getFaceNeighbors( CFaceO *f,
404     NeighbSet &neighb ) const
405 {
406     getNeighbors( f->V(0), neighb );
407     getNeighbors( f->V(1), neighb );
408     getNeighbors( f->V(2), neighb );
409 }
410 
411 
boundaryOptimization(CMeshO & mesh,VisibleSet & faceVis,bool mostFrontFacing)412 void FilterImgPatchParamPlugin::boundaryOptimization( CMeshO &mesh,
413     VisibleSet &faceVis,
414     bool mostFrontFacing )
415 {
416     std::set<CFaceO*> toOptim;
417 
418 
419     vcg::tri::UpdateFlags<CMeshO>::FaceClearV(mesh);
420 
421     // Collects the faces belonging to boundaries (namely faces for which at least one adjacent
422     // face has a different reference image), so as to initialize the optimization step.
423     for( CMeshO::FaceIterator f=mesh.face.begin(); f!=mesh.face.end(); ++f )
424     {
425         // Checks each of the three edges of the current face. If the opposite face has a different
426         // reference image, the 1-ring neighborhood of this edge is added to the processing queue.
427         vcg::face::Pos<CFaceO> p( &*f, f->V(0) );
428         for( int i=0; i<3; ++i )
429         {
430             const CFaceO *f2 = p.FFlip();
431             if( !f2->IsV() )
432                 if( faceVis[f2].ref() != faceVis[f].ref() )
433                 {
434                     NeighbSet neighb;
435                     getNeighbors( p.V(), neighb );
436                     getNeighbors( p.VFlip(), neighb );
437                     for( NeighbSet::iterator n=neighb.begin(); n!=neighb.end(); ++n )
438                         toOptim.insert( *n );
439                 }
440                 p.FlipV();
441                 p.FlipE();
442         }
443         f->SetV();
444     }
445 
446 
447     // The optimization is a greedy approach that changes the reference image of a face in order to reduce
448     // the number of different images adjacent to that face.
449     while( !toOptim.empty() )
450     {
451         // Extract a face from the queue.
452         CFaceO *f = *toOptim.begin();
453         toOptim.erase( toOptim.begin() );
454 
455 
456         // Counts how many times appears each reference image in the 1-ring neighborhood of this face.
457         NeighbSet neighb;
458         getFaceNeighbors( f, neighb );
459 
460         QMap<RasterModel*,int> neighbRefCount;
461 
462         for( NeighbSet::iterator n=neighb.begin(); n!=neighb.end(); ++n )
463             if( *n && *n!=f )
464             {
465                 RasterModel *neighbRef = faceVis[*n].ref();
466                 QMap<RasterModel*,int>::iterator nFound = neighbRefCount.find( neighbRef );
467                 if( nFound == neighbRefCount.end() )
468                     neighbRefCount[neighbRef] = 1;
469                 else
470                     (*nFound) ++;
471             }
472 
473 
474             if( mostFrontFacing )
475             {
476                 // Look for the one that appears the most, and that belongs to the list of visible rasters
477                 // of the considered face.
478                 std::vector<RasterModel*> appearsMost;
479                 int nbMaxAppear = 0;
480 
481                 for( QMap<RasterModel*,int>::iterator n=neighbRefCount.begin(); n!=neighbRefCount.end(); ++n )
482                     if( n.value()>=nbMaxAppear && faceVis[f].contains(n.key()) )
483                     {
484                         if( n.value() > nbMaxAppear )
485                             appearsMost.clear();
486 
487                         nbMaxAppear = n.value();
488                         appearsMost.push_back( n.key() );
489                     }
490 
491 
492                     // If multiple neighboring reference images have the same number of occurrences, the one with the highest
493                     // weight with respect to the current face is chosen.
494                     RasterModel *candidate = faceVis[f].ref();
495 
496                     if( appearsMost.size() > 1 )
497                     {
498                         float maxWeight = -std::numeric_limits<float>::max();
499                         for( std::vector<RasterModel*>::iterator r=appearsMost.begin(); r!=appearsMost.end(); ++r )
500                         {
501                             float weight = faceVis.getWeight( *r, *f );
502                             if( weight > maxWeight )
503                             {
504                                 maxWeight = weight;
505                                 candidate = *r;
506                             }
507                         }
508                     }
509                     else if( appearsMost.size() == 1 )
510                         candidate = appearsMost.front();
511 
512 
513                     // If the reference image of the current face is different from the candidate image, change it accordingly.
514                     // Triangles of its neighborhood are reintroduced in the queue only if their reference images is different
515                     // from the new one.
516                     if( candidate != faceVis[f].ref() )
517                     {
518                         faceVis[f].setRef( candidate );
519                         for( NeighbSet::iterator n=neighb.begin(); n!=neighb.end(); ++n )
520                             if( *n && *n!=f && faceVis[*n].ref()!=candidate )
521                                 toOptim.insert( *n );
522                     }
523             }
524             else
525             {
526                 // Look for the one that appears the most, and that belongs to the list of visible rasters
527                 // of the considered face.
528                 RasterModel *appearsMost = faceVis[f].ref();
529                 int nbMaxAppear = 0;
530 
531                 for( QMap<RasterModel*,int>::iterator n=neighbRefCount.begin(); n!=neighbRefCount.end(); ++n )
532                     if( n.value()>nbMaxAppear && faceVis[f].contains(n.key()) )
533                     {
534                         nbMaxAppear = n.value();
535                         appearsMost = n.key();
536                     }
537 
538 
539                     // If the reference image of the current face is different from the candidate image, change it accordingly.
540                     // Triangles of its neighborhood are reintroduced in the queue only if their reference images is different
541                     // from the new one.
542                     if( appearsMost != faceVis[f].ref() )
543                     {
544                         faceVis[f].setRef( appearsMost );
545                         for( NeighbSet::iterator n=neighb.begin(); n!=neighb.end(); ++n )
546                             if( *n && *n!=f && faceVis[*n].ref()!=appearsMost )
547                                 toOptim.insert( *n );
548                     }
549             }
550     }
551 }
552 
553 
cleanIsolatedTriangles(CMeshO & mesh,VisibleSet & faceVis)554 int FilterImgPatchParamPlugin::cleanIsolatedTriangles( CMeshO &mesh,
555     VisibleSet &faceVis )
556 {
557     int nbTrianglesChanged = 0;
558 
559 
560     // For each triangle T...
561     for( CMeshO::FaceIterator f=mesh.face.begin(); f!=mesh.face.end(); ++f )
562     {
563         // Each reference image in the immediate edge neighborhood of T is gathered and counted.
564         QMap<RasterModel*,int> neighb;
565         for( int i=0; i<3; ++i )
566             if( f->FFp(i) )
567             {
568                 RasterModel *r = faceVis[ f->FFp(i) ].ref();
569                 if( neighb.contains(r) )
570                     neighb[r] ++;
571                 else
572                     neighb[r] = 1;
573             }
574 
575             // If the reference image of T doesn't appear in its neighborhood, it seems that T is isolated.
576             // In that case, the reference image that appears the most in its neighborhood is chosen as
577             // the new reference image of T.
578             if( !neighb.contains(faceVis[f].ref()) )
579             {
580                 RasterModel *appearsMost = NULL;
581                 int nAppearanceMax = 0;
582 
583                 for( QMap<RasterModel*,int>::iterator n=neighb.begin(); n!=neighb.end(); ++n )
584                     if( n.value() > nAppearanceMax )
585                     {
586                         appearsMost = n.key();
587                         nAppearanceMax = n.value();
588                     }
589 
590                     if( appearsMost )
591                     {
592                         faceVis[f].setRef( appearsMost );
593                         nbTrianglesChanged ++;
594                     }
595             }
596     }
597 
598 
599     return nbTrianglesChanged;
600 }
601 
602 
extractPatches(RasterPatchMap & patches,PatchVec & nullPatches,CMeshO & mesh,VisibleSet & faceVis,QList<RasterModel * > & rasterList)603 int FilterImgPatchParamPlugin::extractPatches( RasterPatchMap &patches,
604     PatchVec &nullPatches,
605     CMeshO &mesh,
606     VisibleSet &faceVis,
607     QList<RasterModel*> &rasterList )
608 {
609     int nbPatches = 0;
610 
611     foreach( RasterModel *rm, rasterList )
612         patches[rm] = PatchVec();
613 
614     for( CMeshO::FaceIterator fSeed=mesh.face.begin(); fSeed!=mesh.face.end(); ++fSeed )
615         if( fSeed->IsV() )
616         {
617             std::queue<CFaceO*> seedFillQueue;
618             seedFillQueue.push( &*fSeed );
619             fSeed->ClearV();
620 
621             Patch patch;
622             patch.ref = faceVis[fSeed].ref();
623 
624             do
625             {
626                 CFaceO *f = seedFillQueue.front();
627                 seedFillQueue.pop();
628 
629                 patch.faces.push_back( f );
630 
631                 for( int i=0; i<3; ++i )
632                 {
633                     CFaceO *fAdj = f->FFp(i);
634                     if( fAdj && fAdj->IsV() && faceVis[fAdj].ref()==patch.ref )
635                     {
636                         fAdj->ClearV();
637                         seedFillQueue.push( fAdj );
638                     }
639                 }
640             } while( !seedFillQueue.empty() );
641 
642             if( patch.ref )
643             {
644                 patches[patch.ref].push_back( patch );
645                 ++ nbPatches;
646             }
647             else
648                 nullPatches.push_back( patch );
649         }
650 
651         return nbPatches;
652 }
653 
654 
constructPatchBoundary(Patch & p,VisibleSet & faceVis)655 void FilterImgPatchParamPlugin::constructPatchBoundary( Patch &p,
656     VisibleSet &faceVis )
657 {
658     for( std::vector<CFaceO*>::iterator f=p.faces.begin(); f!=p.faces.end(); ++f )
659     {
660         RasterModel *fRef = faceVis[*f].ref();
661         vcg::face::Pos<CFaceO> pos( *f, (*f)->V(0) );
662 
663         for( int i=0; i<3; ++i )
664         {
665             const CFaceO *f2 = pos.FFlip();
666             if(faceVis[f2].ref() && faceVis[f2].ref()!=fRef )
667             {
668                 NeighbSet neighb;
669                 getNeighbors( pos.V(), neighb );
670                 getNeighbors( pos.VFlip(), neighb );
671                 for( NeighbSet::iterator n=neighb.begin(); n!=neighb.end(); ++n )
672                     if( !(*n)->IsV() && faceVis[*n].ref()!=fRef && faceVis[*n].contains(fRef))
673                     {
674                         p.boundary.push_back( *n );
675                         (*n)->SetV();
676                     }
677             }
678             pos.FlipV();
679             pos.FlipE();
680         }
681     }
682 
683     for( std::vector<CFaceO*>::iterator f=p.boundary.begin(); f!=p.boundary.end(); ++f )
684         (*f)->ClearV();
685 }
686 
687 
computePatchUV(CMeshO & mesh,RasterModel * rm,PatchVec & patches)688 void FilterImgPatchParamPlugin::computePatchUV( CMeshO &mesh,
689     RasterModel *rm,
690     PatchVec &patches )
691 {
692     // Recovers the view frustum of the current raster.
693     CMeshO::ScalarType zNear, zFar;
694     GlShot< Shotm >::GetNearFarPlanes( rm->shot, mesh.bbox, zNear, zFar );
695     if( zNear < 0.0001f )
696         zNear = 0.1f;
697     if( zFar < zNear )
698         zFar = zNear + 1000.0f;
699 
700     CMeshO::ScalarType l, r, b, t, focal;
701     rm->shot.Intrinsics.GetFrustum( l, r, b, t, focal );
702 
703     // Computes the camera perspective projection matrix from the frustum values.
704     Matrix44m camProj;
705     camProj.SetZero();
706     camProj[0][0] = 2.0f*focal / (r-l);
707     camProj[0][2] = (r+l) / (r-l);
708     camProj[1][1] = 2.0f*focal / (t-b);
709     camProj[1][2] = (t+b) / (t-b);
710     camProj[2][2] = (zNear+zFar) / (zNear-zFar);
711     camProj[2][3] = 2.0f*zNear*zFar / (zNear-zFar);
712     camProj[3][2] = -1.0f;
713 
714     Matrix44m cam2clip;
715     cam2clip.SetZero();
716     cam2clip[0][0] = cam2clip[0][3] = 0.5f * rm->shot.Intrinsics.ViewportPx.X();
717     cam2clip[1][1] = cam2clip[1][3] = 0.5f * rm->shot.Intrinsics.ViewportPx.Y();
718     cam2clip[2][2] = cam2clip[3][3] = 1.0f;
719 
720     // Computes the full transform that goes from the mesh local space to the camera clipping space.
721     Matrix44m mesh2clip = cam2clip * camProj * rm->shot.GetWorldToExtrinsicsMatrix();
722 
723     for( PatchVec::iterator p=patches.begin(); p!=patches.end(); ++p )
724     {
725         // Resets the UV bounding box of the patch, and allocate the array containing the UV coordinates
726         // for the boundary faces.
727         p->bbox.SetNull();
728         p->boundaryUV.clear();
729         p->boundaryUV.reserve( p->boundary.size() );
730 
731         // Computes UV coordinates for internal patch faces, and update the bounding box accordingly.
732         for( std::vector<CFaceO*>::iterator f=p->faces.begin(); f!=p->faces.end(); ++f )
733             for( int i=0; i<3; ++i )
734             {
735                 Point3m &vp = (*f)->V(i)->P();
736 
737                 (*f)->WT(i).U() = mesh2clip.GetRow3(0)*vp + mesh2clip[0][3];
738                 (*f)->WT(i).V() = mesh2clip.GetRow3(1)*vp + mesh2clip[1][3];
739                 (*f)->WT(i).P() *= 1.0f / (mesh2clip.GetRow3(3)*vp + mesh2clip[3][3]);
740 
741                 p->bbox.Add( (*f)->WT(i).P() );
742             }
743 
744             // Computes UV coordinates for boundary patch faces, and update the bounding box accordingly.
745             for( std::vector<CFaceO*>::iterator f=p->boundary.begin(); f!=p->boundary.end(); ++f )
746             {
747                 TriangleUV fuv;
748                 for( int i=0; i<3; ++i )
749                 {
750                     Point3m &vp = (*f)->V(i)->P();
751 
752                     fuv.v[i].U() = mesh2clip.GetRow3(0)*vp + mesh2clip[0][3];
753                     fuv.v[i].V() = mesh2clip.GetRow3(1)*vp + mesh2clip[1][3];
754                     fuv.v[i].P() *= 1.0f / (mesh2clip.GetRow3(3)*vp + mesh2clip[3][3]);
755 
756                     p->bbox.Add( fuv.v[i].P() );
757                 }
758                 p->boundaryUV.push_back( fuv );
759             }
760     }
761 }
762 
763 
mergeOverlappingPatches(PatchVec & patches)764 void FilterImgPatchParamPlugin::mergeOverlappingPatches( PatchVec &patches )
765 {
766     if( patches.size() <= 1 )
767         return;
768 
769 
770     for( PatchVec::iterator p=patches.begin(); p!=patches.end(); ++p )
771         p->valid = true;
772 
773 
774     float globalGain = 0.0f;
775     for( PatchVec::iterator p1=patches.begin(); p1!=patches.end(); ++p1 )
776         if( p1->valid )
777         {
778             float maxOccupancyGain = -globalGain;
779             PatchVec::iterator candidate = patches.end();
780 
781             for( PatchVec::iterator p2=patches.begin(); p2!=patches.end(); ++p2 )
782                 if( p2!=p1 && p2->valid && p2->bbox.Collide(p1->bbox) )
783                 {
784                     vcg::Box2f boxMerge = p1->bbox;
785                     boxMerge.Add( p2->bbox );
786                     float occupancyGain = p1->bbox.Area() + p2->bbox.Area() - boxMerge.Area();
787 
788                     if( occupancyGain > maxOccupancyGain )
789                     {
790                         maxOccupancyGain = occupancyGain;
791                         candidate = p2;
792                     }
793                 }
794 
795                 if( candidate != patches.end() )
796                 {
797                     p1->faces.insert( p1->faces.end(), candidate->faces.begin(), candidate->faces.end() );
798                     p1->boundary.insert( p1->boundary.end(), candidate->boundary.begin(), candidate->boundary.end() );
799                     p1->boundaryUV.insert( p1->boundaryUV.end(), candidate->boundaryUV.begin(), candidate->boundaryUV.end() );
800                     p1->bbox.Add( candidate->bbox );
801                     candidate->valid = false;
802                     globalGain += maxOccupancyGain;
803                 }
804         }
805 
806 
807         for( PatchVec::iterator p=patches.begin(); p!=patches.end(); )
808             if( p->valid )
809                 ++ p;
810             else
811             {
812                 *p = patches.back();
813                 patches.pop_back();
814             }
815 }
816 
817 
patchPacking(RasterPatchMap & patches,int textureGutter,bool allowUVStretching)818 void FilterImgPatchParamPlugin::patchPacking( RasterPatchMap &patches,
819     int textureGutter,
820     bool allowUVStretching )
821 {
822     std::vector<vcg::Box2f> patchRect;
823     std::vector<vcg::Similarity2f> patchPackingTr;
824 
825 
826     // Computes the foreseen texture edge length based on the total area covered by patches' boxes.
827     float totalArea = 0;
828 
829     for( RasterPatchMap::iterator rp=patches.begin(); rp!=patches.end(); ++rp )
830         for( PatchVec::iterator p=rp->begin(); p!=rp->end(); ++p )
831         {
832             p->bbox.Offset( vcg::Point2f(textureGutter,textureGutter) );
833             patchRect.push_back( p->bbox );
834             totalArea += p->bbox.Area();
835         }
836 
837         if( patchRect.empty() )
838             return;
839 
840         float edgeLen = std::sqrt( totalArea );
841 
842 
843         // Performs the packing.
844         vcg::Point2f coveredArea(0,0);
845         vcg::RectPacker<float>::Pack( patchRect, vcg::Point2i(edgeLen,edgeLen), patchPackingTr, coveredArea );
846 
847 
848         // Applies to the UV coordinates the transformations computed by the packing algorithm, as well as a scaling
849         // so as to make them ranging the interval [0,1]x[0,1].
850         float scaleU, scaleV;
851 
852         if( allowUVStretching )
853         {
854             scaleU = 1.0f / coveredArea.X();
855             scaleV = 1.0f / coveredArea.Y();
856         }
857         else
858             scaleU = scaleV = 1.0f / std::max( coveredArea.X(), coveredArea.Y() );
859 
860         int n = 0;
861         for( RasterPatchMap::iterator rp=patches.begin(); rp!=patches.end(); ++rp )
862             for( PatchVec::iterator p=rp->begin(); p!=rp->end(); ++p, ++n )
863             {
864                 vcg::Similarity2f &tr = patchPackingTr[n];
865                 float c = std::cos( tr.rotRad );
866                 float s = std::sin( tr.rotRad );
867 
868                 p->img2tex.SetIdentity();
869                 p->img2tex[0][0] =  c * tr.sca * scaleU;
870                 p->img2tex[0][1] = -s * tr.sca * scaleU;
871                 p->img2tex[0][3] =  tr.tra.X() * scaleU;
872                 p->img2tex[1][0] =  s * tr.sca * scaleV;
873                 p->img2tex[1][1] =  c * tr.sca * scaleV;
874                 p->img2tex[1][3] =  tr.tra.Y() * scaleV;
875 
876                 for( std::vector<CFaceO*>::iterator f=p->faces.begin(); f!=p->faces.end(); ++f )
877                     for( int i=0; i<3; ++i )
878                     {
879                         (*f)->WT(i).P() = tr * (*f)->WT(i).P();
880                         (*f)->WT(i).U() *= scaleU;
881                         (*f)->WT(i).V() *= scaleV;
882                     }
883 
884                     for( std::vector<TriangleUV>::iterator f=p->boundaryUV.begin(); f!=p->boundaryUV.end(); ++f )
885                         for( int i=0; i<3; ++i )
886                         {
887                             f->v[i].P() = tr * f->v[i].P();
888                             f->v[i].U() *= scaleU;
889                             f->v[i].V() *= scaleV;
890                         }
891             }
892 }
893 
894 
patchBasedTextureParameterization(RasterPatchMap & patches,PatchVec & nullPatches,int meshid,CMeshO & mesh,QList<RasterModel * > & rasterList,RichParameterSet & par)895 void FilterImgPatchParamPlugin::patchBasedTextureParameterization( RasterPatchMap &patches,
896     PatchVec &nullPatches,
897     int meshid,
898     CMeshO &mesh,
899     QList<RasterModel*> &rasterList,
900     RichParameterSet &par )
901 {
902     // Computes the visibility set for all mesh faces. It contains the set of all images
903     // into which the face is visible, as well as a reference image, namely the one with
904     // the most orthogonal viewing angle.
905     QElapsedTimer t; t.start();
906     int weightMask = VisibleSet::W_ORIENTATION;
907     if( par.getBool("useDistanceWeight") )
908         weightMask |= VisibleSet::W_DISTANCE;
909     if( par.getBool("useImgBorderWeight") )
910         weightMask |= VisibleSet::W_IMG_BORDER;
911     if( par.getBool("useAlphaWeight") )
912         weightMask |= VisibleSet::W_IMG_ALPHA;
913     VisibleSet faceVis( *m_Context,glContext,meshid, mesh, rasterList, weightMask );
914     Log( "VISIBILITY CHECK: %.3f sec.", 0.001f*t.elapsed() );
915 
916 
917     // Boundary optimization: the goal is to produce more regular boundaries between surface regions
918     // associated to different reference images.
919     t.start();
920     boundaryOptimization( mesh, faceVis, true );
921     Log( "BOUNDARY OPTIMIZATION: %.3f sec.", 0.001f*t.elapsed() );
922 
923 
924     // Incorporates patches compounds of only one triangles to one of their neighbours.
925     if( par.getBool("cleanIsolatedTriangles") )
926     {
927         t.start();
928         int triCleaned = cleanIsolatedTriangles( mesh, faceVis );
929         Log( "CLEANING ISOLATED TRIANGLES: %.3f sec.", 0.001f*t.elapsed() );
930         Log( "  * %i triangles cleaned.", triCleaned );
931     }
932 
933 
934     // Recovers patches by extracting connected components of faces having the same reference image.
935     t.start();
936     float oldArea = computeTotalPatchArea( patches );
937     int nbPatches = extractPatches( patches, nullPatches, mesh, faceVis, rasterList );
938     Log( "PATCH EXTRACTION: %.3f sec.", 0.001f*t.elapsed() );
939     Log( "  * %i patches extracted, %i null patches.", nbPatches, nullPatches.size() );
940 
941 
942     // Extends each patch so as to include faces that belong to the other side of its boundary.
943     t.start();
944     oldArea = computeTotalPatchArea( patches );
945     for( RasterPatchMap::iterator rp=patches.begin(); rp!=patches.end(); ++rp )
946         for( PatchVec::iterator p=rp->begin(); p!=rp->end(); ++p )
947             constructPatchBoundary( *p, faceVis );
948     Log( "PATCH EXTENSION: %.3f sec.", 0.001f*t.elapsed() );
949 
950 
951     // Compute the UV coordinates of all patches by projecting them onto their reference images.
952     // UV are then defined in image space, ranging from [0,0] to [w,h].
953     t.start();
954     oldArea = computeTotalPatchArea( patches );
955     for( RasterPatchMap::iterator rp=patches.begin(); rp!=patches.end(); ++rp )
956         computePatchUV( mesh, rp.key(), rp.value() );
957     Log( "PATCHES UV COMPUTATION: %.3f sec.", 0.001f*t.elapsed() );
958 
959 
960     // Merge patches so as to reduce the occupied texture area when their bounding boxes overlap.
961     t.start();
962     oldArea = computeTotalPatchArea( patches );
963     for( RasterPatchMap::iterator rp=patches.begin(); rp!=patches.end(); ++rp )
964         mergeOverlappingPatches( *rp );
965     Log( "PATCH MERGING: %.3f sec.", 0.001f*t.elapsed() );
966     Log( "  * Area reduction: %.1f%%.", 100.0f*computeTotalPatchArea(patches)/oldArea );
967     Log( "  * Patches number reduced from %i to %i.", nbPatches, computePatchCount(patches) );
968 
969 
970     // Patches' bounding boxes are packed in texture space. After this operation, boxes are still defined
971     // in the space of their patches' reference images but UV coordinates are all defined in a common texture
972     // space, ranging from [0,0] to [1,1].
973     t.start();
974     patchPacking( patches, par.getInt("textureGutter"), par.getBool("stretchingAllowed") );
975     Log( "PATCH TEXTURE PACKING: %.3f sec.", 0.001f*t.elapsed() );
976 
977 
978     // Clear the UV coordinates for patches that are not visible in any image.
979     for( PatchVec::iterator p=nullPatches.begin(); p!=nullPatches.end(); ++p )
980         for( std::vector<CFaceO*>::iterator f=p->faces.begin(); f!=p->faces.end(); ++f )
981             for( int i=0; i<3; ++i )
982                 (*f)->WT(i).P() = vcg::Point2f(0.0f,0.0f);
983 
984     for(CMeshO::FaceIterator fi=mesh.face.begin(); fi!=mesh.face.end();++fi)
985         for(int i=0;i<3;++i) fi->WT(i).N()=0;
986 }
987 
988 
computeTotalPatchArea(RasterPatchMap & patches)989 float FilterImgPatchParamPlugin::computeTotalPatchArea( RasterPatchMap &patches )
990 {
991     float totalArea = 0;
992 
993     for( RasterPatchMap::iterator rp=patches.begin(); rp!=patches.end(); ++rp )
994         for( PatchVec::iterator p=rp->begin(); p!=rp->end(); ++p )
995             totalArea += p->bbox.Area();
996 
997     return totalArea;
998 }
999 
1000 
computePatchCount(RasterPatchMap & patches)1001 int FilterImgPatchParamPlugin::computePatchCount( RasterPatchMap &patches )
1002 {
1003     int nbPatches = 0;
1004 
1005     for( RasterPatchMap::iterator rp=patches.begin(); rp!=patches.end(); ++rp )
1006         nbPatches += rp->size();
1007 
1008     return nbPatches;
1009 }
1010 
1011 
1012 
1013 
1014 MESHLAB_PLUGIN_NAME_EXPORTER(FilterImgPatchParamPlugin)
1015