Added APSSCurvatureFeature. It works on point clouds and mesh as well. Pointers to features have been replaced with objects.

Some code cleaning and refactoring to make it shorter.
This commit is contained in:
Paolo Cignoni cignoni 2009-07-19 22:02:46 +00:00
parent e4670ab703
commit 7d0ae09de9
5 changed files with 691 additions and 146 deletions

View File

@ -64,7 +64,6 @@ class VertexPointerSampler
void AddTextureSample(const FaceType &, const CoordType &, const Point2i &){}
};
//puo darsi che serva solo al consenso. in quel caso mettila privata della classe
template<class MESH_TYPE> void SampleVertUniform(MESH_TYPE& m, vector<typename MESH_TYPE::VertexPointer>& vert, int sampleNum)
{
typedef MESH_TYPE MeshType;
@ -74,20 +73,6 @@ template<class MESH_TYPE> void SampleVertUniform(MESH_TYPE& m, vector<typename M
for(unsigned int i=0; i<sampler.sampleVec.size(); i++) vert.push_back(sampler.sampleVec[i]);
}
template<class FEATURE_TYPE, class SCALAR_TYPE> struct MData {
typedef FEATURE_TYPE FeatureType;
typedef SCALAR_TYPE ScalarType;
int shortCons;
float summedPointDist;
Matrix44<ScalarType> tr;
FeatureType** matchPtr;
FeatureType** basePtr;
};
template<class FEATURE_TYPE, class SCALAR_TYPE> bool MDataCompareDist(MData<FEATURE_TYPE,SCALAR_TYPE> i,MData<FEATURE_TYPE,SCALAR_TYPE> j) { return (i.summedPointDist<j.summedPointDist); }
template<class FEATURE_TYPE, class SCALAR_TYPE> bool MDataCompareCons(MData<FEATURE_TYPE,SCALAR_TYPE> i,MData<FEATURE_TYPE,SCALAR_TYPE> j) { return (i.shortCons>j.shortCons); }
template<class MESH_TYPE> class Consensus
{
public:
@ -140,8 +125,6 @@ template<class MESH_TYPE> class Consensus
void SetFix(MeshType& m){ mFix = &m; }
//void ClearFix(MeshType& mFix){}
void SetMove(MeshType& m){ mMov = &m; }
bool Init(Parameters& param){
@ -182,7 +165,7 @@ template<class MESH_TYPE> class Consensus
assert(queryVert);
//init variables for consensus
float consDist = param.consensusDist*(mMov->bbox.Diag()/100.0f); //consensus distance
float consDist = param.consensusDist*(mMov->bbox.Diag()/100.0f); //consensus distance
int cons_succ = int(param.threshold*(param.samples/100.0f)); //score needed to pass consensus
int consensus = 0; //counts vertices in consensus
float dist; //holds the distance of the closest vertex found
@ -190,11 +173,10 @@ template<class MESH_TYPE> class Consensus
Point3<ScalarType> queryNrm; //the query point normal for consensus
CoordType queryPnt; //the query point for consensus
CoordType closestPnt; //the closest point found in consensus
Matrix33<ScalarType> inv33_matMov(mMov->Tr,3); //3x3 matrix needed to transform normals
Matrix33<ScalarType> inv33_matFix(Inverse(mFix->Tr),3); //3x3 matrix needed to transform normals
Matrix33<ScalarType> inv33_matMov(mMov->Tr,3); //3x3 matrix needed to transform normals
Matrix33<ScalarType> inv33_matFix(Inverse(mFix->Tr),3); //3x3 matrix needed to transform normals
//Colors handling: white = not tested; blue = not in consensus; red = in consensus;
//yellow = not in consensus becouse of normals
//Colors handling: white = not tested; blue = not in consensus; red = in consensus; yellow = not in consensus becouse of normals
//Colors are stored in a buffer vector; at the end of consensus loop they are applied
//to the mesh ONLY IF full consensus overcome succes threshold AND the best consensus.
vector<Color4b>* colorBuf = NULL;
@ -210,7 +192,6 @@ template<class MESH_TYPE> class Consensus
//set query point; vertex coord is transformed properly in fix mesh coordinates space; the same for normals
queryPnt = Inverse(mFix->Tr) * (mMov->Tr * (*vi)->P());
queryNrm = inv33_matFix * (inv33_matMov * (*vi)->N());
//if query point is bbox, the look for a vertex in cDist from the query point
if(mFix->bbox.IsIn(queryPnt)) closestVertex = gridFix->GetClosest(PDistFunct,markerFunctorFix,queryPnt,consDist,dist,closestPnt);
else closestVertex=NULL; //out of bbox, we consider the point not in consensus...
@ -327,7 +308,7 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
class Parameters{
public:
int samplingStrategy; //strategy to extract features from range maps
int samplingStrategy; //strategy to extract features from range maps
int numFixFeatureSelected; //number of feature sampled on mFix, to choose a base
int numMovFeatureSelected; //number of feature sampled on mMov, to match base
int ransacIter; //number of ransac iterations
@ -421,6 +402,21 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
}
};
struct CandidateType
{
//typedef FEATURE_TYPE FeatureType;
//typedef SCALAR_TYPE ScalarType;
int shortCons;
float summedPointDist;
Matrix44<ScalarType> tr;
FeatureType** matchPtr;
FeatureType** basePtr;
static bool SortByDistance(CandidateType i,CandidateType j) { return (i.summedPointDist<j.summedPointDist); }
static bool SortByScore(CandidateType i,CandidateType j) { return (i.shortCons>j.shortCons); }
};
Result res; //structure to hold result and stats
vector<FeatureType*>* vecFFix; //vector to hold pointers to features extracted from mFix
vector<FeatureType*>* vecFMov; //vector to hold pointers to features extracted from mMov
@ -429,14 +425,8 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
ConsensusType cons;
ConsensusParam consParam;
FeatureAlignment(){
//set pointers to NULL for safety
fkdTree = NULL;
vecFFix = NULL;
vecFMov = NULL;
//create struct for result and stats
res = Result();
}
//set pointers to NULL for safety and create struct for result and stats
FeatureAlignment():fkdTree(NULL),vecFFix(NULL),vecFMov(NULL),res(Result()){}
~FeatureAlignment(){
//Cleaning extracted features
@ -460,7 +450,7 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
return res;
}
//copy descriptors of mMov into ANN structures, then build kdtree...
//copy descriptors of mFix into ANN structures, then build kdtree...
FeatureAlignment::SetupKDTreePoints(*vecFFix, &fdataPts, FeatureType::getFeatureDimension());
fkdTree = new ANNkd_tree(fdataPts,vecFFix->size(),FeatureType::getFeatureDimension());
assert(fkdTree);
@ -478,7 +468,7 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
}
Result& align(MeshType& mFix, MeshType& mMov, Parameters& param, CallBackPos *cb=NULL)
{
{
time_t start, end, start_loop, end_loop; //timers
time(&start);
@ -486,16 +476,18 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
if(param.k>int(vecFFix->size())){ setError(3, res); return res; } //not enough features to pick k neighboors
//normalize normals
tri::UpdateNormals<MeshType>::PerVertexNormalized(mFix);
tri::UpdateNormals<MeshType>::PerVertexNormalized(mMov);
assert(mFix.HasPerVertexNormal()); //mesh don't have per vertex normals!!!
assert(mMov.HasPerVertexNormal());
tri::UpdateNormals<MeshType>::NormalizeVertex(mFix);
tri::UpdateNormals<MeshType>::NormalizeVertex(mMov);
//variables declaration
res.exitCode = Result::NOT_ALIGNED;
int bestConsensus = 0; //best consensus score
int short_cons_succ = int((param.shortConsOffset*param.overlap/100.0f)*(param.short_cons_samples/100.0f)); //number of vertices to win short consensus
int cons_succ = int((param.consOffset*param.overlap/100.0f)*(param.fullConsensusSamples/100.0f)); //number of vertices to win consensus
int ransac_succ = int((param.succOffset*param.overlap/100.0f)*(param.fullConsensusSamples/100.0f)); //number of vertices to win ransac
int bestConsIdx = -1; //index of the best MData, needed to store picked points
int cons_succ = int((param.consOffset*param.overlap/100.0f)*(param.fullConsensusSamples/100.0f)); //number of vertices to win consensus
int ransac_succ = int((param.succOffset*param.overlap/100.0f)*(param.fullConsensusSamples/100.0f)); //number of vertices to win ransac
int bestConsIdx = -1; //index of the best candidate, needed to store picked points
//set up params for consensus
consParam.consensusDist=param.consensusDist;
@ -505,7 +497,7 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
//auxiliary vectors needed inside the loop
vector<FeatureType**>* baseVec = new vector<FeatureType**>();
vector<FeatureType**>* matchesVec = new vector<FeatureType**>();
vector<MData<FeatureType,ScalarType> >* candidates = new vector<MData<FeatureType,ScalarType> >();
vector<CandidateType>* candidates = new vector<CandidateType>();
//variables needed for progress bar callback
float progBar = 0.0f;
@ -529,9 +521,9 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
//scan all the new matches found and computes datas for each one
for(unsigned int j=candidates->size(); j<matchesVec->size(); j++)
{
MData<FeatureType,ScalarType> data; //alloca struct
data.shortCons = -1; //init is necessary to get structures well sorted later
data.matchPtr = (*matchesVec)[j]; //set the pointer to the match
CandidateType data;
data.shortCons = -1; //this is needed to get structures well sorted later
data.matchPtr = (*matchesVec)[j]; //set the pointer to the match
data.basePtr = (*baseVec)[currBase]; //set the pointer to the relative base
//computes the rigid transformation matrix that overlaps the two points sets
@ -543,7 +535,7 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
Matrix44Type oldTr = ApplyTransformation(mMov, tr); //apply transformation
data.summedPointDist = SummedPointDistances(mFix, mMov, (*baseVec)[currBase], (*matchesVec)[j], param.nBase); //compute and store the sum of points distances
ResetTransformation(mMov, oldTr); //restore old tranformation
ResetTransformation(mMov, oldTr); //restore old tranformation
candidates->push_back(data);
}
@ -555,7 +547,7 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
res.numMatches = candidates->size();
//sort candidates by summed point distances
sort(candidates->begin(), candidates->end(),MDataCompareDist<FeatureType,ScalarType>);
sort(candidates->begin(), candidates->end(), CandidateType::SortByDistance);
//variable needed for progress bar callback
offset = (40.0f/math::Min(param.maxNumShortConsensus,int(candidates->size())));
@ -571,8 +563,8 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
Matrix44Type oldTr = ApplyTransformation(mMov, currTr); //apply transformation
consParam.samples=param.short_cons_samples;
consParam.threshold = param.shortConsOffset*param.overlap/100.0f;
(*candidates)[j].shortCons = cons.Check(consParam); //compute short consensus
ResetTransformation(mMov, oldTr); //restore old tranformation
(*candidates)[j].shortCons = cons.Check(consParam); //compute short consensus
ResetTransformation(mMov, oldTr); //restore old tranformation
if((*candidates)[j].shortCons >= short_cons_succ) res.numWonShortCons++; //count how many won, and use this as bound later
}
@ -581,7 +573,7 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
res.shortConsTime = int(difftime(end_loop,start_loop));
//sort candidates by short consensus
sort(candidates->begin(), candidates->end(),MDataCompareCons<FeatureType,ScalarType>);
sort(candidates->begin(), candidates->end(), CandidateType::SortByScore);
//variables needed for progress bar callback
offset = (40.0f/math::Min(param.maxNumFullConsensus,int(candidates->size())));
@ -598,8 +590,8 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
consParam.samples=param.fullConsensusSamples;
consParam.threshold = param.consOffset*param.overlap/100.0f;
consParam.bestScore = bestConsensus;
int consensus = cons.Check(consParam); //compute full consensus
ResetTransformation(mMov, oldTr); //restore old tranformation
int consensus = cons.Check(consParam); //compute full consensus
ResetTransformation(mMov, oldTr); //restore old tranformation
if(consensus >= cons_succ) res.numWonFullCons++;
@ -614,10 +606,11 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
if(consensus >= ransac_succ) break; //very good alignment, no more iterations are done
}
}
time(&end_loop);
time(&end);
res.fullConsTime = int(difftime(end_loop,start_loop));
res.time = int(difftime(end,start));
res.time = (difftime(end,start));
//if flag 'points' is checked, clear old picked points and save the new points
if(param.pickPoints){
@ -692,7 +685,7 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
{
typedef MESH_TYPE MeshType;
typedef FEATURE_TYPE FeatureType;
typedef typename MeshType::template PerVertexAttributeHandle<FeatureType*> PVAttributeHandle;
typedef typename MeshType::template PerVertexAttributeHandle<FeatureType> PVAttributeHandle;
VertexPointerSampler<MeshType> samp = VertexPointerSampler<MeshType>();
SampleVertPoissonDisk(samplingMesh, samp, *sampleNum);
@ -700,7 +693,7 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
PVAttributeHandle fh = FeatureAlignment::GetFeatureAttribute(samplingMesh);
if(!tri::Allocator<MeshType>::IsValidHandle(samplingMesh,fh)) return NULL;
FeatureType** sampler = new FeatureType*[*sampleNum];
for(int i=0; i<*sampleNum; i++) sampler[i]=fh[samp.sampleVec[i]];
for(int i=0; i<*sampleNum; i++) sampler[i]=&(fh[samp.sampleVec[i]]);
return sampler;
}
@ -722,20 +715,20 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
tri::SurfaceSampling<MeshType,VertexPointerSampler<MeshType> >::Poissondisk(m, sampler, m, radius, pp);
}
static typename MESH_TYPE::template PerVertexAttributeHandle<FEATURE_TYPE*> GetFeatureAttribute(MESH_TYPE& m, bool createAttribute = false)
static typename MESH_TYPE::template PerVertexAttributeHandle<FEATURE_TYPE> GetFeatureAttribute(MESH_TYPE& m, bool createAttribute = false)
{
typedef MESH_TYPE MeshType;
typedef FEATURE_TYPE FeatureType;
typedef typename MeshType::template PerVertexAttributeHandle<FeatureType*> PVAttributeHandle;
typedef typename MeshType::template PerVertexAttributeHandle<FeatureType> PVAttributeHandle;
//checks if the attribute exist
if(!tri::HasPerVertexAttribute(m,std::string(FeatureType::getName()))){
//if createAttribute is true and attribute doesn't exist, we add it; else return a NULL handle
if(createAttribute) tri::Allocator<MeshType>::template AddPerVertexAttribute<FeatureType*>(m,std::string(FeatureType::getName()));
if(createAttribute) tri::Allocator<MeshType>::template AddPerVertexAttribute<FeatureType>(m,std::string(FeatureType::getName()));
else return PVAttributeHandle(NULL,0);
}
//now we can get a handle to the attribute and return it
return tri::Allocator<MeshType>::template GetPerVertexAttribute<FeatureType*> (m,std::string(FeatureType::getName()));
return tri::Allocator<MeshType>::template GetPerVertexAttribute<FeatureType> (m,std::string(FeatureType::getName()));
}
static vector<FEATURE_TYPE*>* extractFeatures(int numRequested, MESH_TYPE &m, int samplingStrategy, CallBackPos *cb = NULL)
@ -753,7 +746,6 @@ template<class MESH_TYPE, class FEATURE_TYPE> class FeatureAlignment
return featureSubset;
}
static void SetupKDTreePoints(vector<FEATURE_TYPE*>& vecF, ANNpointArray* dataPts, int pointDim)
{
//fill dataPts with feature's descriptions in vecF

View File

@ -8,9 +8,14 @@
#include <vcg/complex/trimesh/stat.h>
#include <vcg/math/histogram.h>
#include <vcg/complex/trimesh/smooth.h>
#include <vcg/complex/trimesh/clustering.h>
#include <meshlabplugins/filter_mls/apss.h>
#include <meshlabplugins/filter_mls/implicits.h>
using namespace vcg;
using namespace std;
using namespace GaelMls;
//class for a multiscale feature based on mean curvature: curvature is computed at differents levels of smoothness
template<class MESH_TYPE, int dim>
@ -24,37 +29,22 @@ class SmoothCurvatureFeature
enum CurvatureType {GAUSSIAN, MEAN, ABSOLUTE};
class Item
struct Item
{
public:
CurvatureType type;
int scale;
float lower_bound;
float upper_bound;
float lower_bound, upper_bound;
Item(CurvatureType _type, int _scale, float _lower_bound = 0.0f, float _upper_bound = 1.0f){
type = _type;
scale = _scale;
lower_bound = _lower_bound;
upper_bound = _upper_bound;
}
Item(CurvatureType _type, int _scale, float _lower_bound = 0.0f, float _upper_bound = 1.0f):
type(_type),scale(_scale),lower_bound(_lower_bound),upper_bound(_upper_bound){}
};
vector<Item>* featureDesc;
Parameters(){
featureDesc = new vector<Item>();
}
~Parameters(){
delete featureDesc;
}
vector<Item> featureDesc;
bool add(CurvatureType cType, int smoothStep, float lower_bound = 0.0f, float upper_bound = 1.0f){
assert(smoothStep>=0 & featureDesc->size()<dim & lower_bound>=0.0f & upper_bound<=1.0f & lower_bound<upper_bound);
featureDesc->push_back(Item(cType, smoothStep, lower_bound, upper_bound));
assert(smoothStep>=0 & featureDesc.size()<dim & lower_bound>=0.0f & upper_bound<=1.0f & lower_bound<upper_bound);
featureDesc.push_back(Item(cType, smoothStep, lower_bound, upper_bound));
return true;
}
};
@ -65,13 +55,13 @@ class SmoothCurvatureFeature
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::template PerVertexAttributeHandle<FeatureType*> PVAttributeHandle;
typedef typename vector<FeatureType*>::iterator VecFeatureIterator;
typedef typename MeshType::template PerVertexAttributeHandle<FeatureType> PVAttributeHandle;
Point3<ScalarType> pos;
Point3<ScalarType> normal;
float description[dim];
SmoothCurvatureFeature();
SmoothCurvatureFeature(VertexType& v);
static char* getName();
static int getRequirements();
@ -81,11 +71,11 @@ class SmoothCurvatureFeature
static void SetupParameters( ParamType& param );
static bool ComputeFeature( MeshType&, ParamType& param, CallBackPos *cb=NULL);
static bool Subset(int, MeshType&, vector<FeatureType*>&, int, CallBackPos *cb=NULL);
static bool CheckPersistency(FeatureType* f);
static bool CheckPersistency(FeatureType f);
private:
static MESH_TYPE* CreateSamplingMesh();
static int SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType*>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures);
static int SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures);
static pair<float,float> FindMinMax(vector<FeatureType*>& vec, int scale);
static void SortByBinCount(vector<FeatureType*>& vecFeatures, float min, float max, int histSize, vector<FeatureType*>& sorted);
static int SpatialSelection(vector<FeatureType*>& container, vector<FeatureType*>& vec, int k, float radius);
@ -93,11 +83,13 @@ class SmoothCurvatureFeature
};
template<class MESH_TYPE, int dim>
inline SmoothCurvatureFeature<MESH_TYPE,dim>::SmoothCurvatureFeature(VertexType& v)
inline SmoothCurvatureFeature<MESH_TYPE,dim>::SmoothCurvatureFeature(){}
template<class MESH_TYPE, int dim>
inline SmoothCurvatureFeature<MESH_TYPE,dim>::SmoothCurvatureFeature(VertexType& v):pos(v.P()),normal(v.N())
{
pos = v.P();
normal = v.N();
normal.Normalize();
for(int i=0; i<dim; i++) SmoothCurvatureFeature::getNullValue();
}
template<class MESH_TYPE, int dim> inline char* SmoothCurvatureFeature<MESH_TYPE,dim>::getName()
@ -135,11 +127,11 @@ template<class MESH_TYPE, int dim> inline int SmoothCurvatureFeature<MESH_TYPE,d
}
//check persistence beetween scales: return true if description is valid for all scales, false otherwise
template<class MESH_TYPE, int dim> bool SmoothCurvatureFeature<MESH_TYPE,dim>::CheckPersistency(FeatureType* f)
template<class MESH_TYPE, int dim> bool SmoothCurvatureFeature<MESH_TYPE,dim>::CheckPersistency(FeatureType f)
{
for(int i = 0; i<FeatureType::getFeatureDimension(); i++)
{
if( FeatureType::isNullValue(f->description[i]) ) return false;
if( FeatureType::isNullValue(f.description[i]) ) return false;
}
return true;
}
@ -153,7 +145,7 @@ template<class MESH_TYPE, int dim> void SmoothCurvatureFeature<MESH_TYPE,dim>::S
param.add(Parameters::MEAN, 10, 0.4f, 0.9f);
param.add(Parameters::GAUSSIAN, 15, 0.4f, 0.9f);
param.add(Parameters::MEAN, 15, 0.4f, 0.9f);
assert(param.featureDesc->size()==getFeatureDimension());
assert(int(param.featureDesc.size())==getFeatureDimension());
}
template<class MESH_TYPE, int dim> void SmoothCurvatureFeature<MESH_TYPE,dim>::PreCleaning(MeshType& m)
@ -178,11 +170,6 @@ template<class MESH_TYPE, int dim> bool SmoothCurvatureFeature<MESH_TYPE,dim>::C
PVAttributeHandle fh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(m, true);
if(!tri::Allocator<MeshType>::IsValidHandle(m,fh)) return false;
//for each vertex delete the related attribute
for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi){
if(fh[vi] != NULL){ delete fh[vi]; fh[vi] = NULL; }
}
//clear the mesh to avoid wrong values during curvature computations
PreCleaning(m);
@ -196,19 +183,19 @@ template<class MESH_TYPE, int dim> bool SmoothCurvatureFeature<MESH_TYPE,dim>::C
assert(int(oldVertCoords.size())==m.VertexNumber());
//for all vertexes create feature object in the heap and assign it to the vertex attribute; in the meantime sets trivial values
for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi) fh[vi] = new FeatureType(*vi);
//creates a feature for each vertex
for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi) fh[vi] = FeatureType(*vi);
//loop trough scale levels
int smooth_step = 0, smooth_accum = 0;
for (unsigned int i = 0; i<FeatureType::getFeatureDimension(); i++, smooth_accum+=smooth_step)
{
smooth_step = (*param.featureDesc)[i].scale - smooth_accum;
smooth_step = param.featureDesc[i].scale - smooth_accum;
tri::Smooth<MeshType>::VertexCoordLaplacian(m, smooth_step);//smooth mesh
tri::UpdateCurvature<MeshType>::MeanAndGaussian(m); //compute curvature
//copy curvature values in the quality attributes; then build the histogram and take lower and upper bounds.
switch((*param.featureDesc)[i].type){
switch(param.featureDesc[i].type){
case Parameters::GAUSSIAN:{
tri::UpdateQuality<MeshType>::VertexFromGaussianCurvature(m);
break;
@ -225,16 +212,15 @@ template<class MESH_TYPE, int dim> bool SmoothCurvatureFeature<MESH_TYPE,dim>::C
}
Histogram<ScalarType> hist = Histogram<ScalarType>();
tri::Stat<MeshType>::ComputePerVertexQualityHistogram(m, hist);
float vmin = hist.Percentile((*param.featureDesc)[i].lower_bound); float vmax = hist.Percentile((*param.featureDesc)[i].upper_bound);
float vmin = hist.Percentile(param.featureDesc[i].lower_bound); float vmax = hist.Percentile(param.featureDesc[i].upper_bound);
//for each vertex, creates a feature, assign it to the attribute, and set its common values. If curvature is beetween bounds
//and vertex is not a boundary, curvature is stored in the feature, otherwise the feature is set to an empty value.
//If curvature is beetween bounds and vertex is not a boundary, curvature is stored in the feature, otherwise the feature is set to an empty value.
for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
{
if( (!(*vi).IsB()) & ((*vi).Q()<=vmin) | ((*vi).Q()>=vmax) & (!FeatureType::isNullValue((*vi).Q())) )
fh[vi]->description[i] = (*vi).Q();
fh[vi].description[i] = (*vi).Q();
else
fh[vi]->description[i] = FeatureType::getNullValue();
fh[vi].description[i] = FeatureType::getNullValue();
if(cb){ progBar+=offset; cb((int)progBar,"Computing features..."); }
}
@ -262,7 +248,7 @@ MESH_TYPE* SmoothCurvatureFeature<MESH_TYPE,dim>::CreateSamplingMesh()
}
template<class MESH_TYPE, int dim>
int SmoothCurvatureFeature<MESH_TYPE,dim>::SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType*>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures)
int SmoothCurvatureFeature<MESH_TYPE,dim>::SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures)
{
int countFeatures = 0;
PVAttributeHandle pmfh;
@ -274,9 +260,9 @@ int SmoothCurvatureFeature<MESH_TYPE,dim>::SetupSamplingStructures(MeshType& m,
//fill the vector with all persistent features.
for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi){
//check persistence beetween scales: if feature is persistent, add a pointer in vecFeatures
if( FeatureType::CheckPersistency(fh[vi]) ){
if( FeatureType::CheckPersistency(fh[vi])){
countFeatures++; //increment counter of valid features
if(vecFeatures) vecFeatures->push_back(fh[vi]);
if(vecFeatures) vecFeatures->push_back(&(fh[vi]));
if(samplingMesh){
tri::Allocator<MeshType>::AddVertices(*samplingMesh, 1);
samplingMesh->vert.back().ImportLocal(*vi);
@ -521,3 +507,544 @@ template<class MESH_TYPE, int dim> int SmoothCurvatureFeature<MESH_TYPE,dim>::Sp
return vec.size();
}
//class for a multiscale feature based on APSS curvature. Works with point clouds.
template<class MESH_TYPE, int dim>
class APSSCurvatureFeature
{
public:
class Parameters
{
public:
enum CurvatureType { MEAN, GAUSSIAN, K1, K2, APSS};
struct Item
{
public:
CurvatureType type;
float lower_bound, upper_bound, scale; //indicates how much sub sample the mesh. 1 = whole mesh, 0.5 = half mesh, etc
Item(CurvatureType _type, float _scale, float _lower_bound = 0.0f, float _upper_bound = 1.0f):
type(_type),scale(_scale),lower_bound(_lower_bound),upper_bound(_upper_bound){}
};
vector<Item> featureDesc;
bool add(CurvatureType cType, float subSampAmount, float lower_bound = 0.0f, float upper_bound = 1.0f)
{
assert(subSampAmount>=0 & subSampAmount<=1 & featureDesc.size()<dim & lower_bound>=0.0f & upper_bound<=1.0f & lower_bound<upper_bound);
featureDesc.push_back(Item(cType, subSampAmount, lower_bound, upper_bound));
return true;
}
};
typedef MESH_TYPE MeshType;
typedef APSSCurvatureFeature<MeshType,dim> FeatureType;
typedef typename FeatureType::Parameters ParamType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::template PerVertexAttributeHandle<FeatureType> PVAttributeHandle;
Point3<ScalarType> pos;
Point3<ScalarType> normal;
float description[dim];
APSSCurvatureFeature();
APSSCurvatureFeature(VertexType& v);
static char* getName();
static int getRequirements();
static float getNullValue();
static bool isNullValue(float);
static int getFeatureDimension();
static void SetupParameters( ParamType& param );
static bool ComputeFeature( MeshType&, ParamType& param, CallBackPos *cb=NULL);
static bool Subset(int, MeshType&, vector<FeatureType*>&, int, CallBackPos *cb=NULL);
static bool CheckPersistency(FeatureType f);
private:
static bool ComputeAPSS(MeshType& m, int type, float filterScale, int maxProjIter, float projAcc, float sphPar, bool accNorm, bool selectionOnly);
static MESH_TYPE* CreateSamplingMesh();
static int SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures);
static pair<float,float> FindMinMax(vector<FeatureType*>& vec, int scale);
static void SortByBinCount(vector<FeatureType*>& vecFeatures, float min, float max, int histSize, vector<FeatureType*>& sorted);
static int SpatialSelection(vector<FeatureType*>& container, vector<FeatureType*>& vec, int k, float radius);
static void PreCleaning(MeshType& m);
};
template<class MESH_TYPE, int dim>
inline APSSCurvatureFeature<MESH_TYPE,dim>::APSSCurvatureFeature(){}
template<class MESH_TYPE, int dim>
inline APSSCurvatureFeature<MESH_TYPE,dim>::APSSCurvatureFeature(VertexType& v):pos(v.P()),normal(v.N())
{
normal.Normalize();
for(int i=0; i<dim; i++) APSSCurvatureFeature::getNullValue();
}
template<class MESH_TYPE, int dim> inline char* APSSCurvatureFeature<MESH_TYPE,dim>::getName()
{
return "APSSCurvatureFeature";
}
/* Provides needed attribute to compute feature. A detailed list follows:
MM_VERTCURV required by curvature computation
MM_VERTQUALITY required by curvature and histogram computation
MM_VERTRADIUS required by APSS curvature computation
MM_VERTCURVDIR required by APSS curvature computation
*/
template<class MESH_TYPE, int dim> inline int APSSCurvatureFeature<MESH_TYPE,dim>::getRequirements()
{
return (MeshModel::MM_VERTCURVDIR | MeshModel::MM_VERTQUALITY | MeshModel::MM_VERTRADIUS);
}
template<class MESH_TYPE, int dim> inline bool APSSCurvatureFeature<MESH_TYPE,dim>::isNullValue(float val)
{
return ( ((val==FeatureType::getNullValue()) | (math::IsNAN(val))) );
}
template<class MESH_TYPE, int dim> inline float APSSCurvatureFeature<MESH_TYPE,dim>::getNullValue()
{
return -std::numeric_limits<float>::max();
}
template<class MESH_TYPE, int dim> inline int APSSCurvatureFeature<MESH_TYPE,dim>::getFeatureDimension()
{
return dim;
}
//check persistence beetween scales: return true if description is valid for all scales, false otherwise
template<class MESH_TYPE, int dim> bool APSSCurvatureFeature<MESH_TYPE,dim>::CheckPersistency(FeatureType f)
{
for(int i = 0; i<FeatureType::getFeatureDimension(); i++)
{
if( FeatureType::isNullValue(f.description[i]) ) return false;
}
return true;
}
//parameters must be ordered according to smooth iterations
template<class MESH_TYPE, int dim> void APSSCurvatureFeature<MESH_TYPE,dim>::SetupParameters(ParamType& param)
{
param.add(Parameters::MEAN, 1.0f, 0.3f, 0.9f);
param.add(Parameters::MEAN, 0.75f, 0.3f, 0.9f);
param.add(Parameters::MEAN, 0.5f, 0.3f, 0.9f);
assert(int(param.featureDesc.size())==getFeatureDimension());
}
template<class MESH_TYPE, int dim> void APSSCurvatureFeature<MESH_TYPE,dim>::PreCleaning(MeshType& m)
{
//if we are not working on point clouds, clean up faces
if(m.fn>0)
{
tri::Clean<MeshType>::RemoveZeroAreaFace(m);
tri::Clean<MeshType>::RemoveDuplicateFace(m);
tri::Clean<MeshType>::RemoveDuplicateVertex(m);
tri::Clean<MeshType>::RemoveUnreferencedVertex(m);
}
tri::Allocator<MeshType>::CompactVertexVector(m);
}
template<class MESH_TYPE, int dim> bool APSSCurvatureFeature<MESH_TYPE,dim>::ComputeAPSS(MeshType& m, int type = 0, float filterScale = 2.0f, int maxProjIter = 15, float projAcc = 1e-4f, float sphPar = 1.0f, bool accNorm = true, bool selectionOnly=true)
{
// create the MLS surface
APSS<MeshType>* mls = new APSS<MeshType>(m);
// We require a per vertex radius so as a first thing compute it
mls->computeVertexRaddi();
mls->setFilterScale(filterScale);
mls->setMaxProjectionIters(maxProjIter);
mls->setProjectionAccuracy(projAcc);
mls->setSphericalParameter(sphPar);
mls->setGradientHint(accNorm ? GaelMls::MLS_DERIVATIVE_ACCURATE : GaelMls::MLS_DERIVATIVE_APPROX);
uint size = m.vert.size();
vcg::Point3f grad;
vcg::Matrix33f hess;
//computes curvatures and statistics
for (unsigned int i = 0; i< size; i++)
{
if ( (!selectionOnly) || (m.vert[i].IsS()) )
{
Point3f p = mls->project(m.vert[i].P());
float c = 0;
if (type==Parameters::APSS) c = mls->approxMeanCurvature(p);
else
{
int errorMask;
grad = mls->gradient(p, &errorMask);
if (errorMask == MLS_OK && grad.Norm() > 1e-8)
{
hess = mls->hessian(p, &errorMask);
implicits::WeingartenMap<float> W(grad,hess);
m.vert[i].PD1() = W.K1Dir();
m.vert[i].PD2() = W.K2Dir();
m.vert[i].K1() = W.K1();
m.vert[i].K2() = W.K2();
switch(type){
case Parameters::MEAN: c = W.MeanCurvature(); break;
case Parameters::GAUSSIAN: c = W.GaussCurvature(); break;
case Parameters::K1: c = W.K1(); break;
case Parameters::K2: c = W.K2(); break;
default: assert(0 && "invalid curvature type");
}
}
assert(!math::IsNAN(c) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)");
}
m.vert[i].Q() = c;
}
}
delete mls;
return true;
}
template<class MESH_TYPE, int dim> bool APSSCurvatureFeature<MESH_TYPE,dim>::ComputeFeature(MeshType &m, ParamType& param, CallBackPos *cb)
{
//variables needed for progress bar callback
float progBar = 0.0f;
float offset = 100.0f/((FeatureType::getFeatureDimension())*m.VertexNumber());
if(cb) cb(0,"Computing features...");
//allocates a custom per vertex attribute in which we can store pointers to features in the heap.
PVAttributeHandle fh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(m, true);
if(!tri::Allocator<MeshType>::IsValidHandle(m,fh)) return false;
//clear the mesh to avoid wrong values during curvature computations
PreCleaning(m);
//creates a feature for each vertex
for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi) fh[vi] = FeatureType(*vi);
//loop trough scale levels
for (unsigned int i = 0; i<FeatureType::getFeatureDimension(); i++)
{
//compute the amount of verteces desidered for this scale
int targetSize = int(m.VertexNumber()*param.featureDesc[i].scale);
//set up the clustering structure and perform a logaritmic search in order to get a number
//of clustered vertex very close to the target number
tri::Clustering<MeshType, tri::NearestToCenter<MeshType> > ClusteringGrid;
bool done = false;
bool onlySelected = (i==0) ? false : true; //we subsample the whole mesh just the first time, then we subsample from the previous scale!
int size = 10*targetSize, errSize = int(0.02f*targetSize), inf = 0, sup = 0;
do{
ClusteringGrid.Init(m.bbox,size);
ClusteringGrid.AddPointSet(m,onlySelected);
int sel = ClusteringGrid.CountPointSet();
if(sel<targetSize-errSize){
inf = size;
if(sup) size+=(sup-inf)/2;
else size*=2;
}
else if(sel>targetSize+errSize){
sup = size;
if(inf) size-=(sup-inf)/2;
else size/=2;
}
else done=true;
}while(!done);
//perform clustering. this set some vertesec of m as selected
ClusteringGrid.SelectPointSet(m);
ComputeAPSS(m); //compute curvature
//compute curvature histogram just on selected verteces
Histogram<ScalarType> hist = Histogram<ScalarType>();
tri::Stat<MeshType>::ComputePerVertexQualityHistogram(m, hist, true);
float vmin = hist.Percentile(param.featureDesc[i].lower_bound); float vmax = hist.Percentile(param.featureDesc[i].upper_bound);
//If curvature is beetween bounds and vertex is selected and it is not a boundary, curvature is stored in the feature, otherwise the feature is set to an empty value.
for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
{
if( ((*vi).IsS()) & ((!(*vi).IsB()) & ((*vi).Q()<=vmin) | ((*vi).Q()>=vmax) & (!FeatureType::isNullValue((*vi).Q()))) )
fh[vi].description[i] = (*vi).Q();
else
fh[vi].description[i] = FeatureType::getNullValue();
if(cb){ progBar+=offset; cb((int)progBar,"Computing features..."); }
}
}
return true;
}
template<class MESH_TYPE, int dim>
MESH_TYPE* APSSCurvatureFeature<MESH_TYPE,dim>::CreateSamplingMesh()
{
MeshType* m = new MeshType();
PVAttributeHandle fh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(*m, true);
if(!tri::Allocator<MeshType>::IsValidHandle(*m,fh)){
if(m) delete m;
return NULL;
}
return m;
}
template<class MESH_TYPE, int dim>
int APSSCurvatureFeature<MESH_TYPE,dim>::SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures)
{
int countFeatures = 0;
PVAttributeHandle pmfh;
if(samplingMesh){
pmfh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(*samplingMesh);
if(!tri::Allocator<MeshType>::IsValidHandle(*samplingMesh,pmfh)) return 0;
}
//fill the vector with all persistent features.
for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi){
//check persistence beetween scales: if feature is persistent, add a pointer in vecFeatures
if( FeatureType::CheckPersistency(fh[vi])){
countFeatures++; //increment counter of valid features
if(vecFeatures) vecFeatures->push_back(&(fh[vi]));
if(samplingMesh){
tri::Allocator<MeshType>::AddVertices(*samplingMesh, 1);
samplingMesh->vert.back().ImportLocal(*vi);
pmfh[samplingMesh->vert.back()] = fh[vi];
}
}
}
return countFeatures;
}
template<class MESH_TYPE, int dim>
bool APSSCurvatureFeature<MESH_TYPE,dim>::Subset(int k, MeshType &m, vector<FeatureType*> &vecSubset, int sampType, CallBackPos *cb)
{
//variables needed for progress bar callback
float progBar = 0.0f;
float offset = 100.0f/(m.VertexNumber() + k);
//if attribute doesn't exist, return; else we can get a handle to the attribute
PVAttributeHandle fh = FeatureAlignment<MeshType, FeatureType>::GetFeatureAttribute(m);
if(!tri::Allocator<MeshType>::IsValidHandle(m,fh)) return false;
//create a vector to hold valid features that later will be sampled
vector<FeatureType*>* vecFeatures = NULL;
MeshType* poissonMesh = NULL;
if(sampType==0) vecFeatures = new vector<FeatureType*>();
else poissonMesh = CreateSamplingMesh();
//fill the vector with all persistent features.
int countFeatures = SetupSamplingStructures(m, fh, poissonMesh, vecFeatures);
if(countFeatures<k) k = countFeatures; //we can't extract more of what we got!
//perform different kinds of sampling
FeatureType** sampler = NULL;
switch(sampType){
case 0:{ //uniform sampling: uses vecFeatures
sampler = FeatureAlignment<MeshType, FeatureType>::FeatureUniform(*vecFeatures, &k);
break;
}
case 1:{ //poisson disk sampling: uses poissonMesh
sampler = FeatureAlignment<MeshType, FeatureType>::FeaturePoisson(*poissonMesh, &k);
break;
}
default: assert(0);
}
//store features into the returned vector
for(int i=0; i<k; ++i){
vecSubset.push_back(sampler[i]);
if(cb){ progBar+=offset; cb(int(progBar),"Extracting features..."); }
}
if(vecFeatures) delete vecFeatures; //clear useless data
if(poissonMesh) delete poissonMesh;
if(sampler) delete[] sampler;
return true;
////UNCOMMENT FOLLOW TO GET A RARE FEATURE SELECTION////
/*
int histSize = 10000; //number of bins of the histogram
vector<FeatureType*>* sorted = new vector<FeatureType*>(); //vector that holds features sorted by bin cont
//compute min val e max val between all features; min and max are needed to bound the histogram
pair<float,float> minmax = FindMinMax(*vecFeatures, 0);
//fill multimap with features sorted by bin count
SortByBinCount(*vecFeatures, minmax.first, minmax.second, histSize, *sorted);
//select the first k entries from mulptimap, and put them into vecSubset
typename vector<FeatureType*>::iterator it = sorted->begin();
///UNCOMMENT THIS LOOP FOR A SORTED SELECTION
for(int i=0; i<k & it!=sorted->end(); i++, it++)
{
(*it)->selected = true; //mark features as selected
vecSubset.push_back(*it);
}
///UNCOMMENT THIS LOOP FOR A EQUALIZED SELECTION
int off = int(sorted->size()/k);
for(it = sorted->begin(); it<sorted->end(); it=it+off)
{
(*it)->selected = true; //mark features as selected
vecSubset.push_back(*it);
}
//delete vector and set pointer to NULL for safety
if(vecFeatures) delete vecFeatures; //clear useless data
sorted->clear();
delete sorted;
sorted = NULL;
return true;
*/
}
//scan the vector of features and return a pair containig the min and max description values
template<class MESH_TYPE, int dim> pair<float,float> APSSCurvatureFeature<MESH_TYPE,dim>::FindMinMax(vector<FeatureType*>& vec, int scale)
{
assert(scale>=0 && scale<FeatureType::GetFeatureDimension());
typename vector<FeatureType*>::iterator vi;
pair<float,float> minmax = make_pair(numeric_limits<float>::max(),-numeric_limits<float>::max());
for(vi = vec.begin(); vi!=vec.end(); ++vi)
{
if( !FeatureType::isNullValue((*vi)->description[scale])) //invalid values are discarded
{
if( (*vi)->description[scale] < minmax.first) minmax.first=(*vi)->description[scale];
if( (*vi)->description[scale] > minmax.second ) minmax.second=(*vi)->description[scale];
}
}
return minmax;
}
//fill a vector that holds features pointers sorted by bin count; i.e first all the very infrequent features and last all the very frequent ones.
template<class MESH_TYPE, int dim> void APSSCurvatureFeature<MESH_TYPE,dim>::SortByBinCount(vector<FeatureType*>& vecFeatures, float min, float max, int histSize, vector<FeatureType*>& sorted)
{
//vector to hold bins ranges
vector<float>* bins = new vector<float>(histSize, 0);
//vector to count content of each bin
vector<int> *hToC = new vector<int>(histSize, 0);
//multimap to keep track of features for each bin
multimap<int, FeatureType* >* hToF = new multimap<int, FeatureType* >();
//multimap to store pairs of (count, FeatureType*). multimap is naturally sorted by count.
multimap<int, FeatureType* >* cToF = new multimap<int, FeatureType* >();
//offset beetween min and max is divided into histSize uniform bins
for(unsigned int i = 0; i<bins->size(); i++) bins->at(i) = min + i*(max-min)/float(histSize);
//build histogram, hToF and hToC; all with just one features scan
typename multimap<int, FeatureType* >::iterator hfIt = hToF->begin();
typename vector<FeatureType*>::iterator vi;
for(vi = vecFeatures.begin(); vi!=vecFeatures.end(); ++vi)
{
float val = (*vi)->description[0];
// lower_bound returns an iterator pointing to the first element "not less than" val, or end() if every element is less than val.
typename vector<float>::iterator it = lower_bound(bins->begin(),bins->end(),val);
//if val is out of range, skip iteration and take in account next val
if(it==bins->begin() || it==bins->end()) continue;
//determine in which bin got to insert val
int binId = (it - bins->begin())-1;
assert(binId>=0);
assert (bins->at(binId) < val);
assert (val <= bins->at(binId+1));
//increment bin count
hToC->at(binId)++;
//remember in which bin has been inserted this feature
hfIt = hToF->insert(hfIt,make_pair(binId, (*vi)));
}
//delete bins and set pointer to NULL for safety
bins->clear();
delete bins;
bins = NULL;
//for all bin index insert in the multimap an entry with key bin count and value a feature. Entries are naturally
//sorted by key in the multimap
typename multimap<int, FeatureType* >::iterator cfIt = cToF->begin();
pair< typename multimap<int, FeatureType* >::iterator, typename multimap<int, FeatureType* >::iterator> range;
for(unsigned int i=0; i<hToC->size(); i++)
{
//if bin count is zero, then skip; nothing to put in the multimap
if(hToC->at(i)!=0)
{
range = hToF->equal_range(i);
assert(range.first!=range.second);
for (; range.first!=range.second; ++range.first)
{
cfIt = cToF->insert( cfIt, make_pair(hToC->at(i), (*range.first).second) );
}
}
}
typename multimap<int, FeatureType* >::iterator it;
for(it = cToF->begin(); it != cToF->end(); it++)
sorted.push_back((*it).second);
assert(sorted.size()==cToF->size());
//delete all ausiliary structures and set pointers to NULL for safety
hToF->clear();
delete hToF;
hToF = NULL;
hToC->clear();
delete hToC;
hToC = NULL;
cToF->clear();
delete cToF;
cToF = NULL;
}
template<class MESH_TYPE, int dim> int APSSCurvatureFeature<MESH_TYPE,dim>::SpatialSelection(vector<FeatureType*>& container, vector<FeatureType*>& vec, int k, float radius)
{
//variables to manage the kDTree which works on features position
ANNpointArray dataPts = NULL; // data points
ANNpoint queryPnt = NULL; // query points
ANNkd_tree* kdTree = NULL; // search structure
queryPnt = annAllocPt(3);
typename vector<FeatureType*>::iterator ci = container.begin();
while(ci != container.end() && vec.size()<k)
{
if(vec.size()==0)
{
vec.push_back(*ci);
(*ci)->selected = true;
ci++;
continue;
}
if(dataPts){ annDeallocPts(dataPts); dataPts = NULL; }
if(kdTree){ delete kdTree; kdTree = NULL; }
dataPts = annAllocPts(vec.size(), 3);
for(int i = 0; i < vec.size(); i++)
{
for (int j = 0; j < 3; j++)
{
(dataPts[i])[j] = (ANNcoord)(vec.at(i)->pos[j]);
}
}
//build search structure
kdTree = new ANNkd_tree(dataPts,vec.size(),3);
for (int j = 0; j < 3; j++)
{
queryPnt[j] = (ANNcoord)((*ci)->pos[j]);
}
//if there aren't features yet selected in the range distance
if(!kdTree->annkFRSearch(queryPnt, math::Sqr(radius), 0, NULL, NULL, 0.0))
{
vec.push_back(*ci);
(*ci)->selected = true;
}
ci++;
}
if(dataPts){ annDeallocPts(dataPts); dataPts = NULL; }
if(queryPnt){ annDeallocPt(queryPnt); queryPnt = NULL; }
if(kdTree){ delete kdTree; kdTree = NULL; }
return vec.size();
}

View File

@ -18,19 +18,11 @@ class FeatureRGB
enum RGBAType {RED, GREEN, BLUE, ALPHA};
vector<RGBAType>* featureDesc;
Parameters(){
featureDesc = new vector<RGBAType>();
}
~Parameters(){
delete featureDesc;
}
vector<RGBAType> featureDesc;
bool add(RGBAType cType){
if(featureDesc->size()>=dim) return false;
featureDesc->push_back(cType);
if(featureDesc.size()>=dim) return false;
featureDesc.push_back(cType);
return true;
}
};
@ -40,36 +32,37 @@ class FeatureRGB
typedef typename FeatureType::Parameters ParamType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::template PerVertexAttributeHandle<FeatureType*> PVAttributeHandle;
typedef typename vector<FeatureType*>::iterator VecFeatureIterator;
typedef typename MeshType::template PerVertexAttributeHandle<FeatureType> PVAttributeHandle;
Point3<ScalarType> pos;
Point3<ScalarType> normal;
float description[dim];
FeatureRGB();
FeatureRGB(VertexType& v);
static char* getName();
static int getRequirements();
static float getNullValue();
static bool isNullValue(float);
static int getFeatureDimension();
static bool CheckPersistency(FeatureType* f);
static bool CheckPersistency(FeatureType f);
static void SetupParameters(ParamType& param);
static bool ComputeFeature( MeshType&, ParamType& param, CallBackPos *cb=NULL);
static bool Subset(int, MeshType&, vector<FeatureType*>&, int, CallBackPos *cb=NULL);
private:
static MESH_TYPE* CreateSamplingMesh();
static int SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType*>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures);
static int SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures);
};
template<class MESH_TYPE, int dim> inline FeatureRGB<MESH_TYPE,dim>::FeatureRGB(VertexType& v)
template<class MESH_TYPE, int dim> inline FeatureRGB<MESH_TYPE,dim>::FeatureRGB(){}
template<class MESH_TYPE, int dim>
inline FeatureRGB<MESH_TYPE,dim>::FeatureRGB(VertexType& v):pos(v.P()),normal(v.N())
{
pos = v.P();
normal = v.N();
normal.Normalize();
for(int i=0; i<dim; i++) FeatureRGB::getNullValue();
}
template<class MESH_TYPE, int dim> inline char* FeatureRGB<MESH_TYPE,dim>::getName()
@ -98,11 +91,11 @@ template<class MESH_TYPE, int dim> inline int FeatureRGB<MESH_TYPE,dim>::getFeat
}
//check persistence beetween scales: return true if description is valid for all scales, false otherwise
template<class MESH_TYPE, int dim> bool FeatureRGB<MESH_TYPE,dim>::CheckPersistency(FeatureType* f)
template<class MESH_TYPE, int dim> bool FeatureRGB<MESH_TYPE,dim>::CheckPersistency(FeatureType f)
{
for(int i = 0; i<FeatureType::getFeatureDimension(); i++)
{
if( FeatureType::isNullValue(f->description[i]) ) return false;
if( FeatureType::isNullValue(f.description[i]) ) return false;
}
return true;
}
@ -112,7 +105,7 @@ template<class MESH_TYPE, int dim> void FeatureRGB<MESH_TYPE,dim>::SetupParamete
param.add(FeatureType::Parameters::RED);
param.add(FeatureType::Parameters::GREEN);
param.add(FeatureType::Parameters::BLUE);
assert(param.featureDesc->size()==getFeatureDimension());
assert(param.featureDesc.size()==getFeatureDimension());
}
template<class MESH_TYPE, int dim> bool FeatureRGB<MESH_TYPE,dim>::ComputeFeature(MeshType &m, ParamType& param, CallBackPos *cb)
@ -132,14 +125,11 @@ template<class MESH_TYPE, int dim> bool FeatureRGB<MESH_TYPE,dim>::ComputeFeatur
VertexIterator vi;
for(vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
{
//if custom attributes holds an old pointer, first delete it to avoid memory leak, then add a fresh pointer to f
if(fh[vi] != NULL){ delete fh[vi]; fh[vi] = NULL; }
fh[vi] = new FeatureType(*vi); //Create feature object in the heap
fh[vi] = FeatureType(*vi);
//copy vertex color into feature values
for(unsigned int i=0; i<FeatureType::getFeatureDimension(); i++)
fh[vi]->description[i] = float((*vi).C()[(*param.featureDesc)[i]]);
fh[vi].description[i] = float((*vi).C()[param.featureDesc[i]]);
//advance progress bar
progBar+=offset;
@ -162,7 +152,7 @@ MESH_TYPE* FeatureRGB<MESH_TYPE,dim>::CreateSamplingMesh()
}
template<class MESH_TYPE, int dim>
int FeatureRGB<MESH_TYPE,dim>::SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType*>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures)
int FeatureRGB<MESH_TYPE,dim>::SetupSamplingStructures(MeshType& m, typename MeshType::template PerVertexAttributeHandle<FeatureType>& fh, MeshType* samplingMesh, vector<FeatureType*>* vecFeatures)
{
int countFeatures = 0;
PVAttributeHandle pmfh;
@ -176,7 +166,7 @@ int FeatureRGB<MESH_TYPE,dim>::SetupSamplingStructures(MeshType& m, typename Mes
//check persistence beetween scales: if feature is persistent, add a pointer in vecFeatures
if( FeatureType::CheckPersistency(fh[vi]) ){
countFeatures++; //increment counter of valid features
if(vecFeatures) vecFeatures->push_back(fh[vi]);
if(vecFeatures) vecFeatures->push_back(&(fh[vi]));
if(samplingMesh){
tri::Allocator<MeshType>::AddVertices(*samplingMesh, 1);
samplingMesh->vert.back().ImportLocal(*vi);

View File

@ -138,6 +138,7 @@ void FilterFeatureAlignment::initParameterSet(QAction *a, MeshDocument& md, Filt
{
QStringList l;
l << "GMSmooth curvature"
<< "APSS curvature"
<< "RGB";
par.addEnum("featureType", 0, l,"Feature type:", "The feature that you want to compute for the current mesh.");
break;
@ -194,6 +195,7 @@ void FilterFeatureAlignment::initParameterSet(QAction *a, MeshDocument& md, Filt
{
QStringList l;
l << "GMSmooth curvature"
<< "APSS curvature"
<< "RGB";
par.addEnum("featureType", 0, l,"Feature type:", "The feature that you want to compute for the current mesh.");
par.addMesh("mFix", 0, "Fix mesh:", "The mesh that stays still and grow large after alignment.");
@ -322,6 +324,12 @@ bool FilterFeatureAlignment::applyFilter(QAction *filter, MeshDocument &md, Filt
return ComputeFeatureOperation<MeshType,FeatureType>(*currMesh, param, cb);
}
case 1:{
typedef APSSCurvatureFeature<MeshType, 3> FeatureType; //define needed typedef FeatureType
FeatureType::Parameters param;
FeatureType::SetupParameters(param);
return ComputeFeatureOperation<MeshType,FeatureType>(*currMesh, param, cb);
}
case 2:{
typedef FeatureRGB<MeshType, 3> FeatureType; //define needed typedef FeatureType
FeatureType::Parameters param;
FeatureType::SetupParameters(param);
@ -436,8 +444,17 @@ bool FilterFeatureAlignment::applyFilter(QAction *filter, MeshDocument &md, Filt
setAlignmentParameters<AlignerType>(mFix->cm, mMov->cm, par, alignerParam);
ResultType res = RansacOperation<AlignerType>(*mFix, *mMov, alignerParam, cb);
return logResult<AlignerType>(ID(filter), res, errorMessage);
}
}
case 1:{
typedef APSSCurvatureFeature<MeshType, 3> FeatureType; //define needed typedef FeatureType
typedef FeatureAlignment<MeshType, FeatureType> AlignerType; //define the Aligner class
typedef AlignerType::Result ResultType;
AlignerType::Parameters alignerParam(mFix->cm, mMov->cm);
setAlignmentParameters<AlignerType>(mFix->cm, mMov->cm, par, alignerParam);
ResultType res = RansacOperation<AlignerType>(*mFix, *mMov, alignerParam, cb);
return logResult<AlignerType>(ID(filter), res, errorMessage);
}
case 2:{
typedef FeatureRGB<MeshType, 3> FeatureType; //define needed typedef FeatureType
typedef FeatureAlignment<MeshType, FeatureType> AlignerType; //define the Aligner class
typedef AlignerType::Result ResultType;

View File

@ -5,14 +5,33 @@ LIBS += -L$$ANNDIR/lib \
-lANN
QT += opengl
QT += xml
HEADERS = ../../meshlab/interfaces.h \
filter_feature_alignment.h \
feature_alignment.h \
feature_rgb.h \
feature_msc.h
SOURCES = filter_feature_alignment.cpp \
../../meshlab/filterparameter.cpp \
../../meshlabplugins/edit_pickpoints/pickedPoints.cpp \
$$GLEWCODE
HEADERS = ../../meshlab/interfaces.h \
../../meshlab/meshmodel.h \
apss.h \
mlsmarchingcube.h \
priorityqueue.h \
balltree.h \
rimls.h \
implicits.h \
mlssurface.h \
smallcomponentselection.h \
kdtree.h \
mlsutils.h \
filter_feature_alignment.h \
feature_alignment.h \
feature_rgb.h \
feature_msc.h
SOURCES = filter_feature_alignment.cpp \
apss.cpp \
balltree.cpp \
rimls.cpp \
apss.tpp \
kdtree.cpp \
mlssurface.tpp \
rimls.tpp \
../../meshlab/filterparameter.cpp \
../../meshlab/meshmodel.cpp \
../edit_pickpoints/pickedPoints.cpp \
$$GLEWCODE
TARGET = filter_feature_alignment
CONFIG += opengl