Ejemplo n.º 1
0
void EpochModel::depthFilter(FloatImage &depthImgf, FloatImage &countImgf, float depthJumpThr, 
														 bool dilation, int dilationNumPasses, int dilationWinsize,
														 bool erosion, int erosionNumPasses, int erosionWinsize)
{
	FloatImage depth;
	FloatImage depth2;
	int w = depthImgf.w;
	int h = depthImgf.h;
	
	depth=depthImgf;

	if (dilation)
	{
		for (int k = 0; k < dilationNumPasses; k++)
		{
			depth.Dilate(depth2, dilationWinsize / 2);
			depth=depth2;
		}
	}

	if (erosion)
	{
		for (int k = 0; k < erosionNumPasses; k++)
		{
			depth.Erode(depth2, erosionWinsize / 2);
			depth=depth2;
		}
	}

  Histogramf HH;
  HH.Clear();
  HH.SetRange(0,depthImgf.MaxVal()-depthImgf.MinVal(),10000);
  for(int i=1; i < static_cast<int>(depthImgf.v.size()); ++i)
    HH.Add(fabs(depthImgf.v[i]-depth.v[i-1]));

  if(logFP) fprintf(logFP,"**** Depth histogram 2 Min %f Max %f Avg %f Percentiles ((10)%f (25)%f (50)%f (75)%f (90)%f)\n",HH.MinV(),HH.MaxV(),HH.Avg(),
        HH.Percentile(.1),HH.Percentile(.25),HH.Percentile(.5),HH.Percentile(.75),HH.Percentile(.9));

  int deletedCnt=0;
  
  depthJumpThr = static_cast<float>(HH.Percentile(0.8));
	for (int y = 0; y < h; y++)
		for (int x = 0; x < w; x++)
		{
				if ((depthImgf.Val(x, y) - depth.Val(x, y)) / depthImgf.Val(x, y) > 0.6)
        {
					countImgf.Val(x, y) = 0.0f;
          ++deletedCnt;
        }
		}

	countImgf.convertToQImage().save("tmp_filteredcount.jpg","jpg");
  
  if(logFP) fprintf(logFP,"**** depthFilter: deleted %i on %i\n",deletedCnt,w*h);

}
Ejemplo n.º 2
0
float Arc3DModel::ComputeDepthJumpThr(FloatImage &depthImgf, float percentile)
{
    Histogramf HH;
    HH.Clear();
    HH.SetRange(0,depthImgf.MaxVal()-depthImgf.MinVal(),10000);
    for(unsigned int i=1; i < static_cast<unsigned int>(depthImgf.v.size()); ++i)
        HH.Add(fabs(depthImgf.v[i]-depthImgf.v[i-1]));

    return HH.Percentile(percentile);
}
Ejemplo n.º 3
0
float EpochModel::ComputeDepthJumpThr(FloatImage &depthImgf, float percentile)
{
  Histogramf HH;
  HH.Clear();
  HH.SetRange(0,depthImgf.MaxVal()-depthImgf.MinVal(),10000);
  for(unsigned int i=1; i < static_cast<unsigned int>(depthImgf.v.size()); ++i)
    HH.Add(fabs(depthImgf.v[i]-depthImgf.v[i-1]));

  if(logFP) fprintf(logFP,"**** Depth histogram Min %f Max %f Avg %f Percentiles ((10)%f (25)%f (50)%f (75)%f (90)%f)\n",HH.MinV(),HH.MaxV(),HH.Avg(),
        HH.Percentile(.1),HH.Percentile(.25),HH.Percentile(.5),HH.Percentile(.75),HH.Percentile(.9));
  
  return HH.Percentile(percentile);
}
Ejemplo n.º 4
0
void Arc3DModel::depthFilter(FloatImage &depthImgf, FloatImage &countImgf, float depthJumpThr,
    bool dilation, int dilationNumPasses, int dilationWinsize,
    bool erosion, int erosionNumPasses, int erosionWinsize)
{
    FloatImage depth;
    FloatImage depth2;
    int w = depthImgf.w;
    int h = depthImgf.h;

    depth=depthImgf;

    if (dilation)
    {
        for (int k = 0; k < dilationNumPasses; k++)
        {
            depth.Dilate(depth2, dilationWinsize / 2);
            depth=depth2;
        }
    }

    if (erosion)
    {
        for (int k = 0; k < erosionNumPasses; k++)
        {
            depth.Erode(depth2, erosionWinsize / 2);
            depth=depth2;
        }
    }

    Histogramf HH;
    HH.Clear();
    HH.SetRange(0,depthImgf.MaxVal()-depthImgf.MinVal(),10000);
    for(int i=1; i < static_cast<int>(depthImgf.v.size()); ++i)
        HH.Add(fabs(depthImgf.v[i]-depth.v[i-1]));

    int deletedCnt=0;

    depthJumpThr = HH.Percentile(0.8f);
    for (int y = 0; y < h; y++)
        for (int x = 0; x < w; x++)
        {
            if ((depthImgf.Val(x, y) - depth.Val(x, y)) / depthImgf.Val(x, y) > 0.6)
            {
                countImgf.Val(x, y) = 0.0f;
                ++deletedCnt;
            }
        }

        countImgf.convertToQImage().save("tmp_filteredcount.jpg","jpg");

}
Ejemplo n.º 5
0
// Core Function doing the actual mesh processing.
bool FilterMeasurePlugin::applyFilter( const QString& filterName,MeshDocument& md,EnvWrap& env, vcg::CallBackPos * /*cb*/ )
{
    if (filterName == "Compute Topological Measures")
    {
        CMeshO &m=md.mm()->cm;
        tri::Allocator<CMeshO>::CompactFaceVector(m);
        tri::Allocator<CMeshO>::CompactVertexVector(m);
        md.mm()->updateDataMask(MeshModel::MM_FACEFACETOPO);
        md.mm()->updateDataMask(MeshModel::MM_VERTFACETOPO);

        int edgeManifNum = tri::Clean<CMeshO>::CountNonManifoldEdgeFF(m,true);
        int faceEdgeManif = tri::UpdateSelection<CMeshO>::FaceCount(m);
        tri::UpdateSelection<CMeshO>::VertexClear(m);
        tri::UpdateSelection<CMeshO>::FaceClear(m);

        int vertManifNum = tri::Clean<CMeshO>::CountNonManifoldVertexFF(m,true);
        tri::UpdateSelection<CMeshO>::FaceFromVertexLoose(m);
        int faceVertManif = tri::UpdateSelection<CMeshO>::FaceCount(m);
        int edgeNum=0,borderNum=0;
        tri::Clean<CMeshO>::CountEdges(m, edgeNum, borderNum);
        int holeNum;
        Log("V: %6i E: %6i F:%6i",m.vn,edgeNum,m.fn);
        int unrefVertNum = tri::Clean<CMeshO>::CountUnreferencedVertex(m);
        Log("Unreferenced Vertices %i",unrefVertNum);
        Log("Boundary Edges %i",borderNum);

        int connectedComponentsNum = tri::Clean<CMeshO>::CountConnectedComponents(m);
        Log("Mesh is composed by %i connected component(s)\n",connectedComponentsNum);

        if(edgeManifNum==0 && vertManifNum==0) {
            Log("Mesh is two-manifold ");
        }

        if(edgeManifNum!=0) Log("Mesh has %i non two manifold edges and %i faces are incident on these edges\n",edgeManifNum,faceEdgeManif);

        if(vertManifNum!=0) Log("Mesh has %i non two manifold vertexes and %i faces are incident on these vertices\n",vertManifNum,faceVertManif);

        // For Manifold meshes compute some other stuff
        if(vertManifNum==0 && edgeManifNum==0)
        {
            holeNum = tri::Clean<CMeshO>::CountHoles(m);
            Log("Mesh has %i holes",holeNum);

            int genus = tri::Clean<CMeshO>::MeshGenus(m.vn-unrefVertNum, edgeNum, m.fn, holeNum, connectedComponentsNum);
            Log("Genus is %i",genus);
        }
        else
        {
            Log("Mesh has a undefined number of holes (non 2-manifold mesh)");
            Log("Genus is undefined (non 2-manifold mesh)");
        }

        return true;
    }

    /************************************************************/
    if (filterName == "Compute Topological Measures for Quad Meshes")
    {
        CMeshO &m=md.mm()->cm;
        md.mm()->updateDataMask(MeshModel::MM_FACEFACETOPO);
        md.mm()->updateDataMask(MeshModel::MM_FACEQUALITY);

        if (! tri::Clean<CMeshO>::IsFFAdjacencyConsistent(m)) {
            this->errorMessage = "Error: mesh has a not consistent FF adjacency";
            return false;
        }
        if (! tri::Clean<CMeshO>::HasConsistentPerFaceFauxFlag(m)) {

            this->errorMessage = "QuadMesh problem: mesh has a not consistent FauxEdge tagging";
            return false;
        }

        int nQuads = tri::Clean<CMeshO>::CountBitQuads(m);
        int nTris = tri::Clean<CMeshO>::CountBitTris(m);
        int nPolys = tri::Clean<CMeshO>::CountBitPolygons(m);
        int nLargePolys = tri::Clean<CMeshO>::CountBitLargePolygons(m);
        if(nLargePolys>0) nQuads=0;

        Log("Mesh has %8i triangles \n",nTris);
        Log("         %8i quads \n",nQuads);
        Log("         %8i polygons \n",nPolys);
        Log("         %8i large polygons (with internal faux vertexes)",nLargePolys);

        if (! tri::Clean<CMeshO>::IsBitTriQuadOnly(m)) {
            this->errorMessage = "QuadMesh problem: the mesh is not TriQuadOnly";
            return false;
        }

        //
        //   i
        //
        //
        //   i+1     i+2
        tri::UpdateFlags<CMeshO>::FaceClearV(m);
        Distribution<float> AngleD; // angle distribution
        Distribution<float> RatioD; // ratio distribution
        tri::UpdateFlags<CMeshO>::FaceClearV(m);
        for(CMeshO::FaceIterator fi=m.face.begin(); fi!=m.face.end(); ++fi)
            if(!fi->IsV())
            {
                fi->SetV();
                // Collect the vertices
                Point3f qv[4];
                bool quadFound=false;
                for(int i=0; i<3; ++i)
                {
                    if((*fi).IsF(i) && !(*fi).IsF((i+1)%3) && !(*fi).IsF((i+2)%3) )
                    {
                        qv[0] = fi->V0(i)->P(),
                                qv[1] = fi->FFp(i)->V2( fi->FFi(i) )->P(),
                                        qv[2] = fi->V1(i)->P(),
                                                qv[3] = fi->V2(i)->P();
                        quadFound=true;
                    }
                }
                assert(quadFound);
                for(int i=0; i<4; ++i)
                    AngleD.Add(fabs(90-math::ToDeg(Angle(qv[(i+0)%4] - qv[(i+1)%4], qv[(i+2)%4] - qv[(i+1)%4]))));
                float edgeLen[4];

                for(int i=0; i<4; ++i)
                    edgeLen[i]=Distance(qv[(i+0)%4],qv[(i+1)%4]);
                std::sort(edgeLen,edgeLen+4);
                RatioD.Add(edgeLen[0]/edgeLen[3]);
            }

        Log("Right Angle Discrepancy  Avg %4.3f Min %4.3f Max %4.3f StdDev %4.3f Percentile 0.05 %4.3f percentile 95 %4.3f",
            AngleD.Avg(), AngleD.Min(), AngleD.Max(),AngleD.StandardDeviation(),AngleD.Percentile(0.05),AngleD.Percentile(0.95));

        Log("Quad Ratio   Avg %4.3f Min %4.3f Max %4.3f", RatioD.Avg(), RatioD.Min(), RatioD.Max());
        return true;
    }
    /************************************************************/
    if(filterName == "Compute Geometric Measures")
    {
        CMeshO &m=md.mm()->cm;
        tri::Inertia<CMeshO> I(m);
        float Area = tri::Stat<CMeshO>::ComputeMeshArea(m);
        float Volume = I.Mass();
        Log("Mesh Bounding Box Size %f %f %f", m.bbox.DimX(), m.bbox.DimY(), m.bbox.DimZ());
        Log("Mesh Bounding Box Diag %f ", m.bbox.Diag());
        Log("Mesh Volume  is %f", Volume);
        Log("Mesh Surface is %f", Area);
        Point3f bc=tri::Stat<CMeshO>::ComputeShellBarycenter(m);
        Log("Thin shell barycenter  %9.6f  %9.6f  %9.6f",bc[0],bc[1],bc[2]);

        if(Volume<=0) Log("Mesh is not 'solid', no information on barycenter and inertia tensor.");
        else
        {
            Log("Center of Mass  is %f %f %f", I.CenterOfMass()[0], I.CenterOfMass()[1], I.CenterOfMass()[2]);

            Matrix33f IT;
            I.InertiaTensor(IT);
            Log("Inertia Tensor is :");
            Log("    | %9.6f  %9.6f  %9.6f |",IT[0][0],IT[0][1],IT[0][2]);
            Log("    | %9.6f  %9.6f  %9.6f |",IT[1][0],IT[1][1],IT[1][2]);
            Log("    | %9.6f  %9.6f  %9.6f |",IT[2][0],IT[2][1],IT[2][2]);

            Matrix33f PCA;
            Point3f pcav;
            I.InertiaTensorEigen(PCA,pcav);
            Log("Principal axes are :");
            Log("    | %9.6f  %9.6f  %9.6f |",PCA[0][0],PCA[0][1],PCA[0][2]);
            Log("    | %9.6f  %9.6f  %9.6f |",PCA[1][0],PCA[1][1],PCA[1][2]);
            Log("    | %9.6f  %9.6f  %9.6f |",PCA[2][0],PCA[2][1],PCA[2][2]);

            Log("axis momenta are :");
            Log("    | %9.6f  %9.6f  %9.6f |",pcav[0],pcav[1],pcav[2]);
        }
        return true;
    }
    /************************************************************/
    if((filterName == "Per Vertex Quality Stat") || (filterName == "Per Face Quality Stat") )
    {
        CMeshO &m=md.mm()->cm;
        Distribution<float> DD;
        if(filterName == "Per Vertex Quality Stat")
            tri::Stat<CMeshO>::ComputePerVertexQualityDistribution(m, DD, false);
        else
            tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m, DD, false);

        Log("   Min %f Max %f",DD.Min(),DD.Max());
        Log("   Avg %f Med %f",DD.Avg(),DD.Percentile(0.5f));
        Log("   StdDev		%f",DD.StandardDeviation());
        Log("   Variance  %f",DD.Variance());
        return true;
    }

    if((filterName == "Per Vertex Quality Histogram") || (filterName == "Per Face Quality Histogram") )
    {
        CMeshO &m=md.mm()->cm;
        float RangeMin = env.evalFloat("HistMin");
        float RangeMax = env.evalFloat("HistMax");
        int binNum     = env.evalInt("binNum");

        Histogramf H;
        H.SetRange(RangeMin,RangeMax,binNum);
        if(filterName == "Per Vertex Quality Histogram")
        {
            for(CMeshO::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
                if(!(*vi).IsD())
                {
                    assert(!math::IsNAN((*vi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)");
                    H.Add((*vi).Q());
                }
        } else {
            for(CMeshO::FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
                if(!(*fi).IsD())
                {
                    assert(!math::IsNAN((*fi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)");
                    H.Add((*fi).Q());
                }
        }
        Log("(         -inf..%15.7f) : %4.0f",RangeMin,H.BinCountInd(0));
        for(int i=1; i<=binNum; ++i)
            Log("[%15.7f..%15.7f) : %4.0f",H.BinLowerBound(i),H.BinUpperBound(i),H.BinCountInd(i));
        Log("[%15.7f..             +inf) : %4.0f",RangeMax,H.BinCountInd(binNum+1));
        return true;
    }
    return false;
}
Ejemplo n.º 6
0
bool AlignPair::Align(
						A2Grid &u,
            A2GridVert &uv,
  		const	Matrix44d &in,					// trasformazione Iniziale (che porta i punti di mov su fix)
						Matrix44d &out,					// trasformazione calcolata
						vector<Point3d> &Pfix,		// vertici corrispondenti su src (rossi)
						vector<Point3d> &Nfix, 		// normali corrispondenti su src (rossi)
						vector<Point3d> &OPmov,		// vertici scelti su trg (verdi) prima della trasformazione in ingresso (Original Point Target)
						vector<Point3d> &ONmov, 		// normali scelti su trg (verdi)
						Histogramf &H,
						AlignPair::Stat &as)
{	
  vector<char> beyondCntVec;    // vettore per marcare i movvert che sicuramente non si devono usare
	                      // ogni volta che un vertice si trova a distanza oltre max dist viene incrementato il suo contatore;
												// i movvert che sono stati scartati piu' di MaxCntDist volte non si guardano piu';
  const int maxBeyondCnt=3;
	vector< Point3d > movvert;
	vector< Point3d > movnorm;
	vector<Point3d> Pmov; // vertici scelti dopo la trasf iniziale
	status=SUCCESS;
	int tt0=clock();  

	out=in;
	
	int i;

	double CosAngleThr=cos(ap.MaxAngleRad);
	double StartMinDist=ap.MinDistAbs;
	int tt1=clock();  
	int ttsearch=0;
	int ttleast=0;
	int nc=0;
	as.clear();
	as.StartTime=clock();
			
	beyondCntVec.resize(mov->size(),0);

  /**************** BEGIN ICP LOOP ****************/
    do
	{	
		Stat::IterInfo ii;
		Box3d movbox;
		InitMov(movvert,movnorm,movbox,out);
		H.SetRange(0,StartMinDist,512,2.5);
		Pfix.clear();
		Nfix.clear();
		Pmov.clear();
		OPmov.clear();
		ONmov.clear();
		int tts0=clock();
		ii.MinDistAbs=StartMinDist;
    int LocSampleNum=min(ap.SampleNum,int(movvert.size()));
    Box3d fixbox;
    if(u.Empty()) fixbox = uv.bbox;
    else fixbox = u.bbox;
    for(i=0;i<LocSampleNum;++i)
    {
      if( beyondCntVec[i] < maxBeyondCnt )
        if(! fixbox.IsIn(movvert[i]) )
          beyondCntVec[i]=maxBeyondCnt+1;
      else
      {
        double error=StartMinDist;
        Point3d closestPoint, closestNormal;
        double maxd= StartMinDist;
        ii.SampleTested++;
        if(u.Empty()) // using the point cloud grid
        {
          A2Mesh::VertexPointer vp = tri::GetClosestVertex(*fix,uv,movvert[i], maxd, error);
          if(error>=StartMinDist) {
            ii.DistanceDiscarded++; ++beyondCntVec[i]; continue;
          }
          if(movnorm[i].dot(vp->N()) < CosAngleThr) {
              ii.AngleDiscarded++; continue;
          }
          closestPoint=vp->P();
          closestNormal=vp->N();
        }
        else // using the standard faces and grid
        {
          A2Mesh::FacePointer f=vcg::tri::GetClosestFaceBase<vcg::AlignPair::A2Mesh, vcg::AlignPair::A2Grid >(*fix, u, movvert[i], maxd, error, closestPoint);
          if(error>=StartMinDist) {
            ii.DistanceDiscarded++; ++beyondCntVec[i]; continue;
          }
          if(movnorm[i].dot(f->N()) < CosAngleThr) {
              ii.AngleDiscarded++; continue;
          }
          Point3d ip;
          InterpolationParameters<A2Face,double>(*f,f->N(),closestPoint, ip);
          const double IP_EPS = 0.00001;
          // If ip[i] == 0 it means that we are on the edge opposite to i
          if(	(fabs(ip[0])<=IP_EPS && f->IsB(1)) ||  (fabs(ip[1])<=IP_EPS && f->IsB(2)) || (fabs(ip[2])<=IP_EPS && f->IsB(0))   ){
            ii.BorderDiscarded++;  continue;
          }
          closestNormal = f->N();
        }
        // The sample was accepted. Store it.
        Pmov.push_back(movvert[i]);
        OPmov.push_back((*mov)[i].P());
        ONmov.push_back((*mov)[i].N());
        Nfix.push_back( closestNormal );
        Pfix.push_back( closestPoint );
        H.Add(float(error));
        ii.SampleUsed++;
      }
    } // End for each pmov
    int tts1=clock();
		//printf("Found %d pairs\n",(int)Pfix.size());
    if(!ChoosePoints(Pfix,Nfix,Pmov,OPmov,ap.PassHiFilter,H))
    if(int(Pfix.size())<ap.MinPointNum)
		{
			status = TOO_FEW_POINTS;
			ii.Time=clock();
			as.I.push_back(ii);
			return false;
		}
		Matrix44d newout;
		switch(ap.MatchMode) {
		case AlignPair::Param::MMSimilarity : ComputeRotoTranslationScalingMatchMatrix(newout,Pfix,OPmov); break;
		case AlignPair::Param::MMRigid   : ComputeRigidMatchMatrix(Pfix,OPmov,newout);   break;
			default :
						status = UNKNOWN_MODE;
						ii.Time=clock();
						as.I.push_back(ii);
						return false;
		}
					
//    double sum_before=0;
//    double sum_after=0;
//    for(unsigned int iii=0;iii<Pfix.size();++iii)
//    {
//      sum_before+=Distance(Pfix[iii], out*OPmov[iii]);
//      sum_after+=Distance(Pfix[iii], newout*OPmov[iii]);
//    }
//    //printf("Distance %f -> %f\n",sum_before/double(Pfix.size()),sum_after/double(Pfix.size()) ) ;
   
		// le passate successive utilizzano quindi come trasformazione iniziale questa appena trovata.
		// Nei prossimi cicli si parte da questa matrice come iniziale.
	  out=newout;

		assert(Pfix.size()==Pmov.size());
		int tts2=clock();
		ttsearch+=tts1-tts0;	
		ttleast +=tts2-tts1;
    ii.pcl50=H.Percentile(.5);
		ii.pclhi=H.Percentile(ap.PassHiFilter);
		ii.AVG=H.Avg();
		ii.RMS=H.RMS();
		ii.StdDev=H.StandardDeviation();
		ii.Time=clock();
		as.I.push_back(ii);
		nc++;
		// The distance of the next points to be considered is lowered according to the <ReduceFactor> parameter. 
		// We use 5 times the <ReduceFactor> percentile of the found points. 
    if(ap.ReduceFactorPerc<1) StartMinDist=max(ap.MinDistAbs*ap.MinMinDistPerc, min(StartMinDist,5.0*H.Percentile(ap.ReduceFactorPerc)));
	} 
	while ( 
		nc<=ap.MaxIterNum && 
		H.Percentile(.5) > ap.TrgDistAbs && 
		(nc<ap.EndStepNum+1 || ! as.Stable(ap.EndStepNum) ) 
		); 
 /**************** END ICP LOOP ****************/
	int tt2=clock();  
	Matrix44d ResCopy=out;
	Point3d scv,shv,rtv,trv;
	Decompose(ResCopy,scv,shv,rtv,trv);
  if(math::Abs(1-scv[0])>ap.MaxScale || math::Abs(1-scv[1])>ap.MaxScale || math::Abs(1-scv[2])>ap.MaxScale ) {
			status = TOO_MUCH_SCALE;
			return false;
		}
	if(shv[0]>ap.MaxShear || shv[1]>ap.MaxShear || shv[2]>ap.MaxShear ) {
			status = TOO_MUCH_SHEAR;
			return false;
		}
	printf("Grid %i %i %i - fn %i\n",u.siz[0],u.siz[1],u.siz[2],fix->fn);
  printf("Init %8.3f Loop %8.3f Search %8.3f least sqrt %8.3f\n",
         float(tt1-tt0)/CLOCKS_PER_SEC, float(tt2-tt1)/CLOCKS_PER_SEC,
         float(ttsearch)/CLOCKS_PER_SEC,float(ttleast)/CLOCKS_PER_SEC );

	return true;
}
Ejemplo n.º 7
0
// Core Function doing the actual mesh processing.
bool FilterMeasurePlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos */*cb*/)
{
	CMeshO::FaceIterator fi;

	switch(ID(filter))
  {
		case FP_MEASURE_TOPO : 
			{
				CMeshO &m=md.mm()->cm;	
				md.mm()->updateDataMask(MeshModel::MM_FACEFACETOPO);				
				md.mm()->updateDataMask(MeshModel::MM_VERTFACETOPO);				
        tri::Allocator<CMeshO>::CompactFaceVector(m);
        tri::Allocator<CMeshO>::CompactVertexVector(m);
        tri::UpdateTopology<CMeshO>::FaceFace(m);
				tri::UpdateTopology<CMeshO>::VertexFace(m);
				
        int edgeManif = tri::Clean<CMeshO>::CountNonManifoldEdgeFF(m,true);
        int faceEdgeManif = tri::UpdateSelection<CMeshO>::CountFace(m);
        tri::UpdateSelection<CMeshO>::ClearVertex(m);
        tri::UpdateSelection<CMeshO>::ClearFace(m);

        int vertManif = tri::Clean<CMeshO>::CountNonManifoldVertexFF(m,true);
        tri::UpdateSelection<CMeshO>::FaceFromVertexLoose(m);
        int faceVertManif = tri::UpdateSelection<CMeshO>::CountFace(m);
				int edgeNum=0,borderNum=0;
				tri::Clean<CMeshO>::CountEdges(m, edgeNum, borderNum);
				int holeNum;
				Log("V: %6i E: %6i F:%6i",m.vn,edgeNum,m.fn);
				Log("Boundary Edges %i",borderNum); 
				
        int connectedComponentsNum = tri::Clean<CMeshO>::CountConnectedComponents(m);
				Log("Mesh is composed by %i connected component(s)",connectedComponentsNum);
				
        if(edgeManif==0 && vertManif==0)
					Log("Mesh has is two-manifold ");
					
        if(edgeManif!=0) Log("Mesh has %i non two manifold edges and %i faces are incident on these edges\n",edgeManif,faceEdgeManif);

        if(vertManif!=0) Log("Mesh has %i non two manifold vertexes and %i faces are incident on these vertices\n",vertManif,faceVertManif);
				
				// For Manifold meshes compute some other stuff
				if(vertManif && edgeManif)
				{
					holeNum = tri::Clean<CMeshO>::CountHoles(m);
					Log("Mesh has %i holes",holeNum);
					
					int genus = tri::Clean<CMeshO>::MeshGenus(m, holeNum, connectedComponentsNum, edgeNum);
					Log("Genus is %i",genus);
				}
			}
		break;
		/************************************************************/ 
		case FP_MEASURE_TOPO_QUAD : 
			{
					CMeshO &m=md.mm()->cm;	
					md.mm()->updateDataMask(MeshModel::MM_FACEFACETOPO);				
					md.mm()->updateDataMask(MeshModel::MM_FACEQUALITY);				

					if (! tri::Clean<CMeshO>::IsFFAdjacencyConsistent(m)){
											Log("Error: mesh has a not consistent FF adjacency");
											return false;
										}
					if (! tri::Clean<CMeshO>::HasConsistentPerFaceFauxFlag(m)) {
											Log("Warning: mesh has a not consistent FauxEdge tagging");
											return false;
										}
					if (! tri::Clean<CMeshO>::IsBitTriQuadOnly(m)) {
											Log("Warning: IsBitTriQuadOnly");
											//return false;
										}
					//										if (! tri::Clean<CMeshO>::HasConsistentEdges(m)) lastErrorDetected |= NOT_EDGES_CONST;
					int nsinglets= tri::BitQuadOptimization< tri::BitQuad<CMeshO> >::MarkSinglets(m);
					if ( nsinglets  )  {
											Log("Warning: MarkSinglets");
											//return false;
										}
					
					if (! tri::BitQuad<CMeshO>::HasConsistentValencyFlag(m))
					 {
											Log("Warning: HasConsistentValencyFlag");
											//return false;
										}
				
			int nQuads = tri::Clean<CMeshO>::CountBitQuads(m);
			int nTris = tri::Clean<CMeshO>::CountBitTris(m);
			int nPolys = tri::Clean<CMeshO>::CountBitPolygons(m);
			
			Log("Mesh has %i tri %i quad and %i polig",nTris,nQuads,nPolys);

			}
			break;
		/************************************************************/ 
		case FP_MEASURE_GEOM : 
			{
				CMeshO &m=md.mm()->cm;
				tri::Inertia<CMeshO> I;
				I.Compute(m);
				
				tri::UpdateBounding<CMeshO>::Box(m); 
				float Area = tri::Stat<CMeshO>::ComputeMeshArea(m);
				float Volume = I.Mass(); 
				Log("Mesh Bounding Box Size %f %f %f", m.bbox.DimX(), m.bbox.DimY(), m.bbox.DimZ());			
				Log("Mesh Bounding Box Diag %f ", m.bbox.Diag());			
				Log("Mesh Volume  is %f", Volume);			
				Log("Center of Mass  is %f %f %f", I.CenterOfMass()[0], I.CenterOfMass()[1], I.CenterOfMass()[2]);		
				
				
				Matrix33f IT;
				I.InertiaTensor(IT);
				Log("Inertia Tensor is :");		
        Log("    | %9.6f  %9.6f  %9.6f |",IT[0][0],IT[0][1],IT[0][2]);
        Log("    | %9.6f  %9.6f  %9.6f |",IT[1][0],IT[1][1],IT[1][2]);
        Log("    | %9.6f  %9.6f  %9.6f |",IT[2][0],IT[2][1],IT[2][2]);
				
				Log("Mesh Surface is %f", Area);
				
        Matrix44f PCA;
        Point4f pcav;
        I.InertiaTensorEigen(PCA,pcav);
        Log("Principal axes are :");
        Log("    | %9.6f  %9.6f  %9.6f |",PCA[0][0],PCA[0][1],PCA[0][2]);
        Log("    | %9.6f  %9.6f  %9.6f |",PCA[1][0],PCA[1][1],PCA[1][2]);
        Log("    | %9.6f  %9.6f  %9.6f |",PCA[2][0],PCA[2][1],PCA[2][2]);

       // Point3f ax0(PCA[0][0],PCA[0][1],PCA[0][2]);
       // Point3f ax1(PCA[1][0],PCA[1][1],PCA[1][2]);
       // Point3f ax2(PCA[2][0],PCA[2][1],PCA[2][2]);

       // Log("ax0*ax1 %f (len ax0 %f) ",ax0*ax1, Norm(ax0));
       // Log("ax1*ax2 %f (len ax1 %f) ",ax1*ax2, Norm(ax1));
       // Log("ax0*ax2 %f (len ax2 %f) ",ax0*ax2, Norm(ax2));

        Log("axis momenta are :");
        Log("    | %9.6f  %9.6f  %9.6f |",pcav[0],pcav[1],pcav[2]);

			}
		break;
        /************************************************************/
        case FP_MEASURE_VERTEX_QUALITY_DISTRIBUTION :
  case FP_MEASURE_FACE_QUALITY_DISTRIBUTION :
			{
				CMeshO &m=md.mm()->cm;
				Distribution<float> DD;
        if(ID(filter)==FP_MEASURE_VERTEX_QUALITY_DISTRIBUTION)
          tri::Stat<CMeshO>::ComputePerVertexQualityDistribution(m, DD, false);
        else
          tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m, DD, false);
				
				Log("   Min %f Max %f",DD.Min(),DD.Max());		
				Log("   Avg %f Med %f",DD.Avg(),DD.Percentile(0.5f));		
				Log("   StdDev		%f",DD.StandardDeviation());		
				Log("   Variance  %f",DD.Variance());						
			}
		break;

		case FP_MEASURE_GAUSSCURV : 
			{
				CMeshO &m=md.mm()->cm;
				SimpleTempData<CMeshO::VertContainer, float> TDArea(m.vert,0.0f);
				SimpleTempData<CMeshO::VertContainer, float> TDAngleSum(m.vert,0);

				tri::UpdateQuality<CMeshO>::VertexConstant(m,0);
				float angle[3];
				CMeshO::FaceIterator fi;
				for(fi=m.face.begin(); fi!= m.face.end(); ++fi)
					{
						angle[0] = math::Abs(Angle(	(*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) ));
						angle[1] = math::Abs(Angle(	(*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) ));
						angle[2] = M_PI-(angle[0]+angle[1]);

						float area= DoubleArea(*fi)/6.0f;
							for(int i=0;i<3;++i)
							{
								TDArea[(*fi).V(i)]+=area;
								TDAngleSum[(*fi).V(i)]+=angle[i];
							}
					}
				CMeshO::VertexIterator vi;
				float totalSum=0;
				for(vi=m.vert.begin(); vi!= m.vert.end(); ++vi)
					{
						(*vi).Q() = (2.0*M_PI-TDAngleSum[vi]);//*TDArea[vi];
						//if((*vi).IsS()) 
						totalSum += (*vi).Q(); 
					}
				Log("integrated is %f (%f*pi)", totalSum,totalSum/M_PI);												
			} break;
  case FP_MEASURE_VERTEX_QUALITY_HISTOGRAM:
  case FP_MEASURE_FACE_QUALITY_HISTOGRAM:
            {
            CMeshO &m=md.mm()->cm;
            float RangeMin = par.getFloat("minVal");
            float RangeMax = par.getFloat("maxVal");
            int binNum = par.getInt("binNum");


            Histogramf H;
            H.SetRange(RangeMin,RangeMax,binNum);
            if(ID(filter)==FP_MEASURE_VERTEX_QUALITY_DISTRIBUTION)
            {
              for(CMeshO::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
                if(!(*vi).IsD())
                {
                assert(!math::IsNAN((*vi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)");
                H.Add((*vi).Q());
              }
            }else{
              for(CMeshO::FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
                if(!(*fi).IsD())
                {
                assert(!math::IsNAN((*fi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)");
                H.Add((*fi).Q());
              }
            }
            Log("(         -inf..%15.7f) : %i",RangeMin,H.BinCountInd(0));
            for(int i=1;i<=binNum;++i)
              Log("[%15.7f..%15.7f) : %i",H.BinLowerBound(i),H.BinUpperBound(i),H.BinCountInd(i));

            Log("[%15.7f..             +inf) : %i",RangeMax,H.BinCountInd(binNum+1));
        } break;

          default: assert(0);
  }
	return true;
}