前言

Meshlab是一个非常实用的工具,无论是在点云处理还是纹理贴图方面都得到了非常广泛的使用,并且其算法的底层主要是使用vcglib头文件库来实现。在某些特定的情况下,我们只需要使用Meshlab中的部分功能而不是全部功能(比如不需要显示功能),此时我们就需要截取Meshlab的部分功能来满足我们的需要。
值得注意的是,PCL是另外一个被广泛使用的点云处理开源代码,但是此处并没有使用它,原因是由于PCL的环境配置十分复杂,令人发指的复杂;而vcglib是一个头文件库,只需要包含头文件就可以完成项目的配置。
下文中,主要通过截取Meshlab中的点云法向量计算工具为例,逐步介绍我是如何完成此功能的截取。
@import "1.png"

准备工作

下载Meshlab和vcglib的源代码,其地址分别如下:
https://github.com/cnr-isti-vclab/meshlab
https://github.com/cnr-isti-vclab/vcglib
解压后使用文本编辑软件打开,此处我使用VS Code(因为需要搜索寻找代码所在处)

代码定位

最重要的工作就是如何找到代码所在地,即明白Meshlab是如何实现本文中对应的功能。此处我采用了一种非常逗比的方式来找代码的位置,即通过查看功能字符串“Compute Normals for Point Sets”来定位代码,在meshlab代码中搜索结果如下:

case FP_NORMAL_EXTRAPOLATION             : return tr("Compute normals for point sets");

看来宏FP_NORMAL_EXTRAPOLATION与法向量计算相关,再在meshlab代码搜索一次:

case FP_NORMAL_EXTRAPOLATION :
{
    tri::PointCloudNormal<CMeshO>::Param p;
    p.fittingAdjNum = par.getInt("K");
    p.smoothingIterNum = par.getInt("smoothIter");
    p.viewPoint = par.getPoint3m("viewPos");
    p.useViewPoint = par.getBool("flipFlag");
    tri::PointCloudNormal<CMeshO>::Compute(m.cm, p,cb);
} break;

BINGO!到目前我就找到了Meshlab的法向量计算实现方法。

分析上文中的代码,发现了一些奇怪的类CMesh0,因此接下来需要找一下该类的定义方法,通过meshlab代码搜索发现其定义在ml_mesh_types.h文件中,贴出部分代码如下:

#ifndef __ML_MESH_TYPE_H
#define __ML_MESH_TYPE_H

#include <vcg/complex/complex.h>

#ifndef MESHLAB_SCALAR
#error "Fatal compilation error: MESHLAB_SCALAR must be defined"
#endif

typedef MESHLAB_SCALAR Scalarm;
typedef vcg::Point2<MESHLAB_SCALAR>   Point2m;
typedef vcg::Point3<MESHLAB_SCALAR>   Point3m;
typedef vcg::Point4<MESHLAB_SCALAR>   Point4m;
typedef vcg::Plane3<MESHLAB_SCALAR>   Plane3m;
typedef vcg::Segment2<MESHLAB_SCALAR> Segment2m;
typedef vcg::Segment3<MESHLAB_SCALAR> Segment3m;
typedef vcg::Box3<MESHLAB_SCALAR>     Box3m;
typedef vcg::Matrix44<MESHLAB_SCALAR> Matrix44m;
typedef vcg::Matrix33<MESHLAB_SCALAR> Matrix33m;
typedef vcg::Shot<MESHLAB_SCALAR>     Shotm;
typedef vcg::Similarity<MESHLAB_SCALAR> Similaritym;

...
...

class CMeshO    : public vcg::tri::TriMesh< vcg::vertex::vector_ocf<CVertexO>, vcg::face::vector_ocf<CFaceO> >
{
public :
    int sfn;    //The number of selected faces.
    int svn;    //The number of selected vertices.

    Matrix44m Tr; // Usually it is the identity. It is applied in rendering and filters can or cannot use it. (most of the filter will ignore this)

    const Box3m &trBB()
    {
        static Box3m bb;
        bb.SetNull();
        bb.Add(Tr,bbox);
        return bb;
    }
};

#endif

简单阅读一下代码,发现还需要定义一下MESHLAB_SCALAR,在meshlab代码搜索得到其被定义为float,因此在上边的代码中添加一句定义代码即可:

typedef float MESHLAB_SCALAR;

代码重构

由于所有需要的代码都已经完全准备好,接下来就需要构建一个工程来计算点云的法向量。下边以VS2015为例,当然CMake也是一样的,只需要加一个include_path就好了。
首先创建一个空的工程,然后新建一个pointset_normal.cpp,把CMesh0定义拷贝进去,然后将法向量计算的代码放到main函数中。
然后在include path中添加vcglib的根目录,大概如下:
@import "2.png"
但是此时还缺少点云的读取和保存方式,此处可以翻看一下vcglib/apps/sample下边的代码即可,我贴一下我找到的读取相关的代码:

// input output
#include <wrap/io_trimesh/import_ply.h>
#include <wrap/io_trimesh/export_ply.h>

//open a mesh
tri::io::ImporterPLY<CMeshO>::Open(m,in_file.c_str());
//save a mesh
tri::io::ExporterPLY<CMeshO>::Save(m,out_file.c_str());

注意需要手动添加vcglib/wrap/ply/plylib.cpp,工程目录如下:
@import "3.png"
完成以上操作后,还有一个地方需要注意,ExporterPLY默认并不会保存法向量,我阅读了该函数的定义然后添加了一些代码来保存法向量:

tri::io::PlyInfo pi;
pi.mask = tri::io::Mask::IOM_VERTNORMAL;
//save a mesh
tri::io::ExporterPLY<CMeshO>::Save(m,out_file.c_str(),true, pi);

因此我们终于完成了Meshlab的功能截取。

实验结果

程序调用

mesh_normal.exe" cloud.ply cloud_normal.ply

结果展示
@import "4.png"


完整代码

/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004-2016                                           \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* This program is free software; you can redistribute it and/or modify      *
* it under the terms of the GNU General Public License as published by      *
* the Free Software Foundation; either version 2 of the License, or         *
* (at your option) any later version.                                       *
*                                                                           *
* This program is distributed in the hope that it will be useful,           *
* but WITHOUT ANY WARRANTY; without even the implied warranty of            *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/
/*! \file trimesh_smooth.cpp
\ingroup code_sample

\brief the minimal example of using the lib

This file contain a minimal example of the library

*/

#include<vcg/complex/complex.h>
#include<vcg/complex/algorithms/pointcloud_normal.h>

// input output
#include <wrap/io_trimesh/import_ply.h>
#include <wrap/io_trimesh/export_ply.h>

using namespace vcg;

#include <iostream>
using namespace std;

typedef double MESHLAB_SCALAR;

typedef MESHLAB_SCALAR Scalarm;
typedef vcg::Point2<MESHLAB_SCALAR>   Point2m;
typedef vcg::Point3<MESHLAB_SCALAR>   Point3m;
typedef vcg::Point4<MESHLAB_SCALAR>   Point4m;
typedef vcg::Plane3<MESHLAB_SCALAR>   Plane3m;
typedef vcg::Segment2<MESHLAB_SCALAR> Segment2m;
typedef vcg::Segment3<MESHLAB_SCALAR> Segment3m;
typedef vcg::Box3<MESHLAB_SCALAR>     Box3m;
typedef vcg::Matrix44<MESHLAB_SCALAR> Matrix44m;
typedef vcg::Matrix33<MESHLAB_SCALAR> Matrix33m;
typedef vcg::Shot<MESHLAB_SCALAR>     Shotm;
typedef vcg::Similarity<MESHLAB_SCALAR> Similaritym;

template<typename T>
struct MeshLabScalarTest
{

};

template<>
struct MeshLabScalarTest<float>
{
    static const char* floatingPointPrecision() { return "fp"; }
    static const char* floatingPointPrecisionIOToken() { return "%f"; }
    static bool doublePrecision() { return false; }
};

template<>
struct MeshLabScalarTest<double>
{
    static const char* floatingPointPrecision() { return "dp"; }
    static const char* floatingPointPrecisionIOToken() { return "%lf"; }
    static bool doublePrecision() { return true; }
};

namespace vcg
{
    namespace vertex
    {
        template <class T> class Coord3m : public Coord<vcg::Point3<MESHLAB_SCALAR>, T> {
        public: static void Name(std::vector<std::string> & name) { name.push_back(std::string("Coord3m")); T::Name(name); }
        };

        template <class T> class Normal3m : public Normal<vcg::Point3<MESHLAB_SCALAR>, T> {
        public: static void Name(std::vector<std::string> & name) { name.push_back(std::string("Normal3m")); T::Name(name); }
        };

        template <class T> class CurvatureDirmOcf : public CurvatureDirOcf<CurvatureDirTypeOcf<MESHLAB_SCALAR>, T> {
        public: static void Name(std::vector<std::string> & name) { name.push_back(std::string("CurvatureDirmOcf")); T::Name(name); }
        };

        template <class T> class RadiusmOcf : public RadiusOcf<MESHLAB_SCALAR, T> {
        public: static void Name(std::vector<std::string> & name) { name.push_back(std::string("RadiusmOcf")); T::Name(name); }
        };

    }//end namespace vertex
    namespace face
    {
        template <class T> class Normal3m : public NormalAbs<vcg::Point3<MESHLAB_SCALAR>, T> {
        public:  static void Name(std::vector<std::string> & name) { name.push_back(std::string("Normal3m")); T::Name(name); }
        };

        template <class T> class CurvatureDirmOcf : public CurvatureDirOcf<CurvatureDirOcfBaseType<MESHLAB_SCALAR>, T> {
        public: static void Name(std::vector<std::string> & name) { name.push_back(std::string("CurvatureDirdOcf")); T::Name(name); }
        };

    }//end namespace face
}//end namespace vcg

 // Forward declarations needed for creating the used types
class CVertexO;
class CEdgeO;
class CFaceO;

// Declaration of the semantic of the used types
class CUsedTypesO : public vcg::UsedTypes < vcg::Use<CVertexO>::AsVertexType,
    vcg::Use<CEdgeO   >::AsEdgeType,
    vcg::Use<CFaceO  >::AsFaceType > {};


// The Main Vertex Class
// Most of the attributes are optional and must be enabled before use.
// Each vertex needs 40 byte, on 32bit arch. and 44 byte on 64bit arch.

class CVertexO : public vcg::Vertex< CUsedTypesO,
    vcg::vertex::InfoOcf,           /*  4b */
    vcg::vertex::Coord3m,           /* 12b */
    vcg::vertex::BitFlags,          /*  4b */
    vcg::vertex::Normal3m,          /* 12b */
    vcg::vertex::Qualityf,          /*  4b */
    vcg::vertex::Color4b,           /*  4b */
    vcg::vertex::VFAdjOcf,          /*  0b */
    vcg::vertex::MarkOcf,           /*  0b */
    vcg::vertex::TexCoordfOcf,      /*  0b */
    vcg::vertex::CurvaturefOcf,     /*  0b */
    vcg::vertex::CurvatureDirmOcf,  /*  0b */
    vcg::vertex::RadiusmOcf         /*  0b */
> {
};


// The Main Edge Class
class CEdgeO : public vcg::Edge<CUsedTypesO,
    vcg::edge::BitFlags,          /*  4b */
    vcg::edge::EVAdj,
    vcg::edge::EEAdj
> {
};

// Each face needs 32 byte, on 32bit arch. and 48 byte on 64bit arch.
class CFaceO : public vcg::Face<  CUsedTypesO,
    vcg::face::InfoOcf,              /* 4b */
    vcg::face::VertexRef,            /*12b */
    vcg::face::BitFlags,             /* 4b */
    vcg::face::Normal3m,             /*12b */
    vcg::face::QualityfOcf,          /* 0b */
    vcg::face::MarkOcf,              /* 0b */
    vcg::face::Color4bOcf,           /* 0b */
    vcg::face::FFAdjOcf,             /* 0b */
    vcg::face::VFAdjOcf,             /* 0b */
    vcg::face::CurvatureDirmOcf,     /* 0b */
    vcg::face::WedgeTexCoordfOcf     /* 0b */
> {};


class CMeshO : public vcg::tri::TriMesh< vcg::vertex::vector_ocf<CVertexO>, vcg::face::vector_ocf<CFaceO> >
{
public:
    int sfn;    //The number of selected faces.
    int svn;    //The number of selected vertices.

    Matrix44m Tr; // Usually it is the identity. It is applied in rendering and filters can or cannot use it. (most of the filter will ignore this)

    const Box3m &trBB()
    {
        static Box3m bb;
        bb.SetNull();
        bb.Add(Tr, bbox);
        return bb;
    }
};


int main(int argc, char *argv[])
{
    string in_file = argv[1];
    string out_file = argv[2];

    CMeshO m;

    //open a mesh
    tri::io::ImporterPLY<CMeshO>::Open(m,in_file.c_str());

    tri::PointCloudNormal<CMeshO>::Param p;
    //p.fittingAdjNum = 10;
    p.smoothingIterNum = 2;
    p.useViewPoint = true;
    tri::PointCloudNormal<CMeshO>::Compute(m, p);

    tri::io::PlyInfo pi;
    pi.mask = tri::io::Mask::IOM_VERTNORMAL;
    //save a mesh
    tri::io::ExporterPLY<CMeshO>::Save(m,out_file.c_str(),true, pi);
    return 0;
}
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐