mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-20 11:26:11 +00:00
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:
parent
e4670ab703
commit
7d0ae09de9
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user