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

@ -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)
{
sampleVec = _vec;
}
std::vector<VertexType *> *sampleVec;
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); typename MeshType::template PerVertexAttributeHandle<VertexPointer> sources;
sources = tri::Allocator<MeshType>:: template AddPerVertexAttribute<VertexPointer> (m,"sources");
// Delete all the (hopefully) small regions that have not been reached by the seeds; for(int iter=0;iter<relaxIter;++iter)
tri::UpdateFlags<MeshType>::VertexClearV(m); {
for(int i=0;i<m.vert.size();++i) if(cb) cb(iter*100/relaxIter,"Voronoi Lloyd Relaxation: First Partitioning");
if(sources[i]==0) m.vert[i].SetV(); // first run: find for each point what is the closest to one of the seeds.
for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) tri::Geodesic<MeshType>::Compute(m,seedVec,std::numeric_limits<ScalarType>::max(),0,&sources);
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. // Delete all the (hopefully) small regions that have not been reached by the seeds;
//The error should have been removed from MSVS2012 tri::UpdateFlags<MeshType>::VertexClearV(m);
std::pair<float,VertexPointer> zz(0.0f,static_cast<VertexPointer>(NULL)); for(int i=0;i<m.vert.size();++i)
std::vector< std::pair<float,VertexPointer> > regionArea(m.vert.size(),zz); if(sources[i]==0) m.vert[i].SetV();
std::vector<VertexPointer> borderVec;
GetAreaAndFrontier(m, sources, regionArea, borderVec); 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);
// Smaller area region are discarded //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.
Distribution<float> H; //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; float areaThreshold;
if(iter==0) areaThreshold = H.Percentile(.1f); if(iter==0) areaThreshold = H.Percentile(.1f);
else areaThreshold = H.Percentile(.001f); 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); //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"); if(cb) cb(iter*100/relaxIter,"Voronoi Lloyd Relaxation: Searching New Seeds");
tri::Geodesic<MeshType>::Compute(m,borderVec); tri::Geodesic<MeshType>::Compute(m,borderVec);
tri::UpdateColor<MeshType>::PerVertexQualityRamp(m); tri::UpdateColor<MeshType>::PerVertexQualityRamp(m);
// Search the local maxima for each region and use them as new seeds // Search the local maxima for each region and use them as new seeds
std::vector< std::pair<float,VertexPointer> > seedMaxima(m.vert.size(),zz); std::vector< std::pair<float,VertexPointer> > seedMaxima(m.vert.size(),zz);
for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
{ {
int seedIndex = tri::Index(m,sources[vi]); int seedIndex = tri::Index(m,sources[vi]);
if(seedMaxima[seedIndex].first < (*vi).Q()) if(seedMaxima[seedIndex].first < (*vi).Q())
{ {
seedMaxima[seedIndex].first=(*vi).Q(); seedMaxima[seedIndex].first=(*vi).Q();
seedMaxima[seedIndex].second=&*vi; seedMaxima[seedIndex].second=&*vi;
} }
} }
std::vector<VertexPointer> newSeeds; 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. // 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. // 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) static void TopologicalVertexColoring(MeshType &m, std::vector<VertexType *> &seedVec)
{ {
std::queue<VertexPointer> VQ; std::queue<VertexPointer> VQ;
tri::UpdateQuality<MeshType>::VertexConstant(m,0); tri::UpdateQuality<MeshType>::VertexConstant(m,0);
for(size_t i=0;i<seedVec.size();++i) for(size_t i=0;i<seedVec.size();++i)
{ {
VQ.push(seedVec[i]); VQ.push(seedVec[i]);
seedVec[i]->Q()=i+1; seedVec[i]->Q()=i+1;
} }
while(!VQ.empty()) while(!VQ.empty())
{ {
VertexPointer vp = VQ.front(); VertexPointer vp = VQ.front();
VQ.pop(); VQ.pop();
std::vector<VertexPointer> vertStar; std::vector<VertexPointer> vertStar;
vcg::face::VVStarVF<FaceType>(vp,vertStar); vcg::face::VVStarVF<FaceType>(vp,vertStar);
for(typename std::vector<VertexPointer>::iterator vv = vertStar.begin();vv!=vertStar.end();++vv) for(typename std::vector<VertexPointer>::iterator vv = vertStar.begin();vv!=vertStar.end();++vv)
{ {
if((*vv)->Q()==0) if((*vv)->Q()==0)
{ {
(*vv)->Q()=vp->Q(); (*vv)->Q()=vp->Q();
VQ.push(*vv); VQ.push(*vv);
} }
} }
} // end while(!VQ.empty()) } // 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.
@ -412,7 +411,7 @@ static void VoronoiClustering(MeshType &mOld, MeshType &mNew, std::vector<Vertex
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; ;