cleaned up a bit the interface and formatting of the code of the voronoiclustering alg

This commit is contained in:
Paolo Cignoni 2013-07-05 14:46:48 +00:00
parent 1c4e43955b
commit 42da29417b
1 changed files with 171 additions and 172 deletions

View File

@ -8,7 +8,7 @@
* \ * * \ *
* All rights reserved. * * All rights reserved. *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or * * the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. * * (at your option) any later version. *
@ -21,11 +21,9 @@
* * * *
****************************************************************************/ ****************************************************************************/
#ifndef VORONOI_PROCESSING_H #ifndef VORONOI_PROCESSING_H
#define VORONOI_PROCESSING_H #define VORONOI_PROCESSING_H
//#include <vcg/simplex/face/topology.h>
#include <vcg/complex/algorithms/geodesic.h> #include <vcg/complex/algorithms/geodesic.h>
#include <vcg/complex/algorithms/update/color.h> #include <vcg/complex/algorithms/update/color.h>
namespace vcg namespace vcg
@ -35,41 +33,38 @@ namespace tri
template <class MeshType> template <class MeshType>
class ClusteringSampler class ClusteringSampler
{ {
public: public:
typedef typename MeshType::VertexType VertexType; typedef typename MeshType::VertexType VertexType;
ClusteringSampler() ClusteringSampler(std::vector<VertexType *> &_vec): sampleVec(_vec)
{ {
sampleVec=0; sampleVec = _vec;
} }
ClusteringSampler(std::vector<VertexType *> *_vec)
{ std::vector<VertexType *> &sampleVec;
sampleVec = _vec;
} void AddVert(const VertexType &p)
std::vector<VertexType *> *sampleVec; {
sampleVec.push_back((VertexType *)(&p));
void AddVert(const VertexType &p) }
{ }; // end class ClusteringSampler
sampleVec->push_back((VertexType *)(&p));
}
}; // end class ClusteringSampler
template <class MeshType > template <class MeshType >
class VoronoiProcessing class VoronoiProcessing
{ {
typedef typename MeshType::CoordType CoordType; typedef typename MeshType::CoordType CoordType;
typedef typename MeshType::ScalarType ScalarType; typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::VertexType VertexType; typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer; typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator; typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::FacePointer FacePointer; typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator; typedef typename MeshType::FaceIterator FaceIterator;
typedef typename MeshType::FaceType FaceType; typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FaceContainer FaceContainer; typedef typename MeshType::FaceContainer FaceContainer;
public: public:
// Given a vector of point3f it finds the closest vertices on the mesh. // Given a vector of point3f it finds the closest vertices on the mesh.
@ -118,18 +113,18 @@ static void VoronoiColoring(MeshType &m, std::vector<VertexType *> &seedVec, boo
tri::Geodesic<MeshType> g; tri::Geodesic<MeshType> g;
VertexPointer farthest; VertexPointer farthest;
if(frontierFlag) if(frontierFlag)
{ {
//static_cast<VertexPointer>(NULL) has been introduced just to avoid an error in the MSVS2010's compiler confusing pointer with int. You could use nullptr to avoid it, but it's not supported by all compilers. //static_cast<VertexPointer>(NULL) has been introduced just to avoid an error in the MSVS2010's compiler confusing pointer with int. You could use nullptr to avoid it, but it's not supported by all compilers.
//The error should have been removed from MSVS2012 //The error should have been removed from MSVS2012
std::pair<float,VertexPointer> zz(0.0f,static_cast<VertexPointer>(NULL)); std::pair<float,VertexPointer> zz(0.0f,static_cast<VertexPointer>(NULL));
std::vector< std::pair<float,VertexPointer> > regionArea(m.vert.size(),zz); std::vector< std::pair<float,VertexPointer> > regionArea(m.vert.size(),zz);
std::vector<VertexPointer> borderVec; std::vector<VertexPointer> borderVec;
GetAreaAndFrontier(m, sources, regionArea, borderVec); GetAreaAndFrontier(m, sources, regionArea, borderVec);
tri::Geodesic<MeshType>::Compute(m,borderVec); tri::Geodesic<MeshType>::Compute(m,borderVec);
} }
tri::UpdateColor<MeshType>::PerVertexQualityRamp(m); tri::UpdateColor<MeshType>::PerVertexQualityRamp(m);
} }
// It associates the faces with a given vertex according to the vertex associations // It associates the faces with a given vertex according to the vertex associations
@ -242,153 +237,157 @@ static void GetAreaAndFrontier(MeshType &m, PerVertexPointerHandle &sources,
std::vector< std::pair<float,VertexPointer> > &regionArea, std::vector< std::pair<float,VertexPointer> > &regionArea,
std::vector<VertexPointer> &borderVec) std::vector<VertexPointer> &borderVec)
{ {
tri::UpdateFlags<MeshType>::VertexClearV(m); tri::UpdateFlags<MeshType>::VertexClearV(m);
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
{ {
if( sources[(*fi).V(0)] != sources[(*fi).V(1)] || if( sources[(*fi).V(0)] != sources[(*fi).V(1)] ||
sources[(*fi).V(0)] != sources[(*fi).V(2)] ) sources[(*fi).V(0)] != sources[(*fi).V(2)] )
{ {
for(int i=0;i<3;++i) for(int i=0;i<3;++i)
{ {
(*fi).V(i)->SetV(); (*fi).V(i)->SetV();
(*fi).V(i)->C() = Color4b::Black; (*fi).V(i)->C() = Color4b::Black;
} }
} }
else // the face belongs to a single region; accumulate area; else // the face belongs to a single region; accumulate area;
{ {
if(sources[(*fi).V(0)] != 0) if(sources[(*fi).V(0)] != 0)
{ {
int seedIndex = sources[(*fi).V(0)] - &*m.vert.begin(); int seedIndex = sources[(*fi).V(0)] - &*m.vert.begin();
regionArea[seedIndex].first+=DoubleArea(*fi); regionArea[seedIndex].first+=DoubleArea(*fi);
regionArea[seedIndex].second=sources[(*fi).V(0)]; regionArea[seedIndex].second=sources[(*fi).V(0)];
} }
} }
} }
// Collect the frontier vertexes // Collect the frontier vertexes
borderVec.clear(); borderVec.clear();
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
if((*vi).IsV()) borderVec.push_back(&*vi); if((*vi).IsV()) borderVec.push_back(&*vi);
} }
static void VoronoiRelaxing(MeshType &m, std::vector<VertexType *> &seedVec, int relaxIter, int /*percentileClamping*/, vcg::CallBackPos *cb=0) static void VoronoiRelaxing(MeshType &m, std::vector<VertexType *> &seedVec, int relaxIter, int /*percentileClamping*/, vcg::CallBackPos *cb=0)
{ {
for(int iter=0;iter<relaxIter;++iter) tri::RequireVFAdjacency(m);
{
if(cb) cb(iter*100/relaxIter,"Voronoi Lloyd Relaxation: First Partitioning");
// first run: find for each point what is the closest to one of the seeds.
typename MeshType::template PerVertexAttributeHandle<VertexPointer> sources;
sources = tri::Allocator<MeshType>:: template AddPerVertexAttribute<VertexPointer> (m,"sources");
tri::Geodesic<MeshType>::Compute(m,seedVec,std::numeric_limits<ScalarType>::max(),0,&sources);
// Delete all the (hopefully) small regions that have not been reached by the seeds; typename MeshType::template PerVertexAttributeHandle<VertexPointer> sources;
tri::UpdateFlags<MeshType>::VertexClearV(m); sources = tri::Allocator<MeshType>:: template AddPerVertexAttribute<VertexPointer> (m,"sources");
for(int i=0;i<m.vert.size();++i)
if(sources[i]==0) m.vert[i].SetV();
for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) for(int iter=0;iter<relaxIter;++iter)
if(fi->V(0)->IsV() || fi->V(1)->IsV() || fi->V(2)->IsV() ) {
{ if(cb) cb(iter*100/relaxIter,"Voronoi Lloyd Relaxation: First Partitioning");
face::VFDetach(*fi); // first run: find for each point what is the closest to one of the seeds.
tri::Allocator<MeshType>::DeleteFace(m,*fi);
}
qDebug("Deleted faces not reached: %i -> %i",int(m.face.size()),m.fn);
tri::Clean<MeshType>::RemoveUnreferencedVertex(m);
tri::Allocator<MeshType>::CompactFaceVector(m);
tri::Allocator<MeshType>::CompactVertexVector(m);
//static_cast<VertexPointer>(NULL) has been introduced just to avoid an error in the MSVS2010's compiler confusing pointer with int. You could use nullptr to avoid it, but it's not supported by all compilers. tri::Geodesic<MeshType>::Compute(m,seedVec,std::numeric_limits<ScalarType>::max(),0,&sources);
//The error should have been removed from MSVS2012
std::pair<float,VertexPointer> zz(0.0f,static_cast<VertexPointer>(NULL));
std::vector< std::pair<float,VertexPointer> > regionArea(m.vert.size(),zz);
std::vector<VertexPointer> borderVec;
GetAreaAndFrontier(m, sources, regionArea, borderVec); // Delete all the (hopefully) small regions that have not been reached by the seeds;
tri::UpdateFlags<MeshType>::VertexClearV(m);
// Smaller area region are discarded for(int i=0;i<m.vert.size();++i)
Distribution<float> H; if(sources[i]==0) m.vert[i].SetV();
for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi)
if(fi->V(0)->IsV() || fi->V(1)->IsV() || fi->V(2)->IsV() )
{
face::VFDetach(*fi);
tri::Allocator<MeshType>::DeleteFace(m,*fi);
}
// qDebug("Deleted faces not reached: %i -> %i",int(m.face.size()),m.fn);
tri::Clean<MeshType>::RemoveUnreferencedVertex(m);
tri::Allocator<MeshType>::CompactFaceVector(m);
tri::Allocator<MeshType>::CompactVertexVector(m);
//static_cast<VertexPointer>(NULL) has been introduced just to avoid an error in the MSVS2010's compiler confusing pointer with int. You could use nullptr to avoid it, but it's not supported by all compilers.
//The error should have been removed from MSVS2012
std::pair<float,VertexPointer> zz(0.0f,static_cast<VertexPointer>(NULL));
std::vector< std::pair<float,VertexPointer> > regionArea(m.vert.size(),zz);
std::vector<VertexPointer> borderVec;
GetAreaAndFrontier(m, sources, regionArea, borderVec);
// Smaller area region are discarded
Distribution<float> H;
for(size_t i=0;i<regionArea.size();++i) for(size_t i=0;i<regionArea.size();++i)
if(regionArea[i].second) H.Add(regionArea[i].first); if(regionArea[i].second) H.Add(regionArea[i].first);
float areaThreshold;
if(iter==0) areaThreshold = H.Percentile(.1f);
else areaThreshold = H.Percentile(.001f);
//qDebug("We have found %i regions range (%f %f), avg area is %f, Variance is %f 10perc is %f",(int)seedVec.size(),H.Min(),H.Max(),H.Avg(),H.StandardDeviation(),areaThreshold);
if(cb) cb(iter*100/relaxIter,"Voronoi Lloyd Relaxation: Searching New Seeds");
tri::Geodesic<MeshType>::Compute(m,borderVec);
tri::UpdateColor<MeshType>::PerVertexQualityRamp(m);
// Search the local maxima for each region and use them as new seeds float areaThreshold;
std::vector< std::pair<float,VertexPointer> > seedMaxima(m.vert.size(),zz); if(iter==0) areaThreshold = H.Percentile(.1f);
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) else areaThreshold = H.Percentile(.001f);
{ //qDebug("We have found %i regions range (%f %f), avg area is %f, Variance is %f 10perc is %f",(int)seedVec.size(),H.Min(),H.Max(),H.Avg(),H.StandardDeviation(),areaThreshold);
int seedIndex = tri::Index(m,sources[vi]);
if(seedMaxima[seedIndex].first < (*vi).Q()) if(cb) cb(iter*100/relaxIter,"Voronoi Lloyd Relaxation: Searching New Seeds");
{
seedMaxima[seedIndex].first=(*vi).Q(); tri::Geodesic<MeshType>::Compute(m,borderVec);
seedMaxima[seedIndex].second=&*vi; tri::UpdateColor<MeshType>::PerVertexQualityRamp(m);
}
} // Search the local maxima for each region and use them as new seeds
std::vector<VertexPointer> newSeeds; std::vector< std::pair<float,VertexPointer> > seedMaxima(m.vert.size(),zz);
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
{
int seedIndex = tri::Index(m,sources[vi]);
if(seedMaxima[seedIndex].first < (*vi).Q())
{
seedMaxima[seedIndex].first=(*vi).Q();
seedMaxima[seedIndex].second=&*vi;
}
}
std::vector<VertexPointer> newSeeds;
for(size_t i=0;i<seedMaxima.size();++i) for(size_t i=0;i<seedMaxima.size();++i)
if(seedMaxima[i].second) if(seedMaxima[i].second)
{ {
seedMaxima[i].second->C() = Color4b::Gray; seedMaxima[i].second->C() = Color4b::Gray;
if(regionArea[i].first >= areaThreshold) if(regionArea[i].first >= areaThreshold)
newSeeds.push_back(seedMaxima[i].second); newSeeds.push_back(seedMaxima[i].second);
} }
tri::UpdateColor<MeshType>::PerVertexQualityRamp(m); tri::UpdateColor<MeshType>::PerVertexQualityRamp(m);
for(size_t i=0;i<seedVec.size();++i) for(size_t i=0;i<seedVec.size();++i)
seedVec[i]->C() = Color4b::Black; seedVec[i]->C() = Color4b::Black;
for(size_t i=0;i<borderVec.size();++i) for(size_t i=0;i<borderVec.size();++i)
borderVec[i]->C() = Color4b::Gray; borderVec[i]->C() = Color4b::Gray;
swap(newSeeds,seedVec); swap(newSeeds,seedVec);
for(size_t i=0;i<seedVec.size();++i) for(size_t i=0;i<seedVec.size();++i)
seedVec[i]->C() = Color4b::White; seedVec[i]->C() = Color4b::White;
}
tri::Allocator<MeshType>::DeletePerVertexAttribute (m,"sources");
tri::Allocator<MeshType>::DeletePerVertexAttribute (m,"sources");
}
} }
// Base vertex voronoi coloring algorithm.
// it assumes VF adjacency. No attempt of computing real geodesic distnace is done. Just a BFS visit starting from the seeds.
static void TopologicalVertexColoring(MeshType &m, std::vector<VertexType *> &seedVec)
{
std::queue<VertexPointer> VQ;
tri::UpdateQuality<MeshType>::VertexConstant(m,0);
for(size_t i=0;i<seedVec.size();++i)
{
VQ.push(seedVec[i]);
seedVec[i]->Q()=i+1;
}
while(!VQ.empty())
{
VertexPointer vp = VQ.front();
VQ.pop();
std::vector<VertexPointer> vertStar;
vcg::face::VVStarVF<FaceType>(vp,vertStar);
for(typename std::vector<VertexPointer>::iterator vv = vertStar.begin();vv!=vertStar.end();++vv)
{
if((*vv)->Q()==0)
{
(*vv)->Q()=vp->Q();
VQ.push(*vv);
}
}
} // end while(!VQ.empty())
}
// Base vertex voronoi coloring algorithm.
// it assumes VF adjacency. No attempt of computing real geodesic distnace is done. Just a BFS visit starting from the seeds.
static void TopologicalVertexColoring(MeshType &m, std::vector<VertexType *> &seedVec)
{
std::queue<VertexPointer> VQ;
tri::UpdateQuality<MeshType>::VertexConstant(m,0);
for(size_t i=0;i<seedVec.size();++i)
{
VQ.push(seedVec[i]);
seedVec[i]->Q()=i+1;
}
while(!VQ.empty())
{
VertexPointer vp = VQ.front();
VQ.pop();
std::vector<VertexPointer> vertStar;
vcg::face::VVStarVF<FaceType>(vp,vertStar);
for(typename std::vector<VertexPointer>::iterator vv = vertStar.begin();vv!=vertStar.end();++vv)
{
if((*vv)->Q()==0)
{
(*vv)->Q()=vp->Q();
VQ.push(*vv);
}
}
} // end while(!VQ.empty())
}
// Drastic Simplification algorithm. // Drastic Simplification algorithm.
// Similar in philosopy to the classic grid clustering but using a voronoi partition instead of the regular grid. // Similar in philosopy to the classic grid clustering but using a voronoi partition instead of the regular grid.
@ -397,22 +396,22 @@ static void TopologicalVertexColoring(MeshType &m, std::vector<VertexType *> &se
// mNew is created by collasping onto a single vertex all the vertices that lies in the same cluster. // mNew is created by collasping onto a single vertex all the vertices that lies in the same cluster.
// Non degenerate triangles are preserved. // Non degenerate triangles are preserved.
static void VoronoiClustering(MeshType &mOld, MeshType &mNew, std::vector<VertexType *> &seedVec) static void VoronoiClustering(MeshType &mOld, MeshType &mNew, std::vector<VertexType *> &seedVec)
{ {
std::set<Point3i> clusteredFace; std::set<Point3i> clusteredFace;
FaceIterator fi; FaceIterator fi;
for(fi=mOld.face.begin();fi!=mOld.face.end();++fi) for(fi=mOld.face.begin();fi!=mOld.face.end();++fi)
{ {
if( (fi->V(0)->Q() != fi->V(1)->Q() ) && if( (fi->V(0)->Q() != fi->V(1)->Q() ) &&
(fi->V(0)->Q() != fi->V(2)->Q() ) && (fi->V(0)->Q() != fi->V(2)->Q() ) &&
(fi->V(1)->Q() != fi->V(2)->Q() ) ) (fi->V(1)->Q() != fi->V(2)->Q() ) )
clusteredFace.insert( Point3i(int(fi->V(0)->Q()), int(fi->V(1)->Q()), int(fi->V(2)->Q()))); clusteredFace.insert( Point3i(int(fi->V(0)->Q()), int(fi->V(1)->Q()), int(fi->V(2)->Q())));
} }
tri::Allocator<MeshType>::AddVertices(mNew,seedVec.size()); tri::Allocator<MeshType>::AddVertices(mNew,seedVec.size());
for(size_t i=0;i< seedVec.size();++i) for(size_t i=0;i< seedVec.size();++i)
mNew.vert[i].ImportLocal(*(seedVec[i])); mNew.vert[i].ImportData(*(seedVec[i]));
tri::Allocator<MeshType>::AddFaces(mNew,clusteredFace.size()); tri::Allocator<MeshType>::AddFaces(mNew,clusteredFace.size());
std::set<Point3i>::iterator fsi; ; std::set<Point3i>::iterator fsi; ;