简介

Geometry++是一个关于三维数据(点云,网格)处理的几何库. 它包含了三维数据处理最基础的算法, 可以作为三维数据处理软件的几何引擎来使用. 它有如下特点:

  • 算法前沿高效, 工程稳定可靠
  • 使用简易, 非常容易集成进原有的系统: Geometry++的算法都是基于ITriMesh和IPointCloud两个接口类来实现的, 您只需要继承实现这两个接口类, 就可以调用所有的API, 三维数据的表示还是基于原有的系统
  • 可维护: Bug反馈后会及时修正, 优于开源库的使用体验

Magic3D

Geometry++开发的使用范例可以参考Magic3D软件的实现. Magic3D提供了点云和网格数据处理的基本功能,底层的几何算法调用了Geometry++ SDK


使用场景

1. 用户已经拥有了一套三维数据处理系统, 想调用Geometry++里面的一些功能. 这种情况只需要实现IPointCloud和ITriMesh两个接口类, 便可以调用所有的算法. 具体的用法可以参考IPointCloud, ITriMesh和相应API的介绍.

2. 用户的三维数据处理模块打算基于Geometry++. 这种情况用户可以使用PointCloud和TriMesh两个类来表示自己的三维数据, 再调用所有的算法. 具体的用法可以参考IPointCloud, ITriMesh以及相应ASPI的介绍.


点云表示
class IPointCloud;

点云数据结构接口. Geometry++点云算法都是基于IPointCloud接口来调用的.

用法:继承这个接口类,实现其成员函数.

优点:可以很方便的在已有的程序中使用Geometry++提供的点云算法,只需要实现这个接口类,就可以调用所有点云相关的算法


class PointCloud;

IPointCloud接口类和IGridPointCloud接口类的一个实现,用户可以直接使用它来表示点云数据


点云导入导出
PointCloud* Parser::ImportPointCloud(std::string fileName);

目前支持的文件格式:obj, asc. 用户也可以导入其它格式的点云数据, 只需要自己写一个Parser, 然后用导入的数据创建点云类(例如PointCloud)即可使用了

fileName: 格式为path/xxx.obj, path/xxx.asc, path可以是绝对路径,也可以相对路径

返回值: 如果导入失败则返回NULL


void Parser::ExportPointCloud(std::string fileName, const IPointCloud* pointCloud);

目前支持的文件格式:obj, asc, ply.

fileName: 格式为path/xxx.obj, path/xxx.asc, path可以是绝对路径,也可以相对路径


有序点云
class IGridPointCloud;

有序点云的数据结构接口。

有序点云是一个方阵,如图所示。点云按照方阵一行一行的,从左上角到右下角排列。

Grid Point Cloud

优点:相比IPointCloud,IGridPointCloud多了点云的位置连接关系信息。相关的计算可以更加快速。


ErrorCode ConsolidateGrid::CalculateGridNormal(IGridPointCloud* pointCloud);

计算有序点云法线


ErrorCode ConsolidateGrid::CalculateGridIsolation(const IGridPointCloud* pointCloud, std::vector< Real >* isolation, Real maxExtendNormalAngle);

用法:计算每个点的孤立值. 孤立值代表了这个点所在局部块占整体点云的比例。其几何意义为,点云被分割为不同的块,每个块内点的孤立值为块内点数占总点数的比例。孤立值越小,孤立项的几率越大,范围是(0, 1). 比如可以认为孤立值小于0.1的点就为孤立项。


ErrorCode ConsolidateGrid::CalculateGridDensity(const IGridPointCloud* pointCloud, Real& density);

计算有序点云密度


ErrorCode ConsolidateGrid::DetectGridBoundary(const IGridPointCloud* pointCloud, Int boundarySize, std::vector< std::pair< Int, Int > >& boundaryIds);

计算有序点云边界点


ErrorCode ConsolidateGrid::SmoothGridGeometry(IGridPointCloud* pointCloud, Int neighborWidth, Int iterationCount);

光滑有序点云几何


ErrorCode ConsolidateGrid::SmoothGridNormal(IGridPointCloud* pointCloud, Int neighborWidth, Int iterationCount);

光滑有序点云法线


ErrorCode SampleGrid::UniformSample(const IGridPointCloud* pointCloud, Int sampleCount, std::vector< std::pair< Int, Int > >* sampleIndex);

有序点云均匀采样


ErrorCode SampleGrid::GeometrySample(const IGridPointCloud* pointCloud, Int sampleCount, Real uniformWeight, std::vector< std::pair< Int, Int > >* sampleIndex);

有序点云几何采样,采样结果在几何特征明显的地方数量会多一些


ErrorCode SampleGrid::Simplify(const IGridPointCloud* pointCloud, Real interval, GridSimplifyType type, IGridPointCloud* simplifiedCloud);

有序点云简化:根据点间距做点云简化。


ErrorCode RegistrateGrid::ICPRegistrate(const IGridPointCloud* pointCloudRef, const IGridPointCloud* pointCloudFrom, Matrix4x4* resultTransform, const Matrix4x4* initTransform = NULL, Int maxTryCount = 3);

注意:点云必须带有法线信息。

有序点云的ICP注册对齐:pointCloudRef = resultTransform * initTransform * pointCloudFrom


ErrorCode RegistrateGrid::AlignGrid(const IGridPointCloud* pointCloudRef, const IGridPointCloud* pointCloudFrom, Matrix4x4* resultTransform, Real accuracy = 0);

有序点云的无标记点对齐:pointCloudRef = resultTransform * pointCloudFrom


点云采样
ErrorCode SamplePointCloud::UniformSample(const IPointCloud* pointCloud, Int sampleCount, Int* sampleIndex, Int seedId = 0, SampleQuality quality = SAMPLE_QUALITY_HIGH);

均匀采样:采样结果分布均匀


ErrorCode SamplePointCloud::GeometrySample(const IPointCloud* pointCloud, Int sampleCount, Int* sampleIndex, Real uniformWeight = 0.1, Int neighborCount = 9, Int seedId = 0, SampleQuality quality = SAMPLE_QUALITY_HIGH);

几何采样:采样结果在几何特征明显的地方数量会多一些


ErrorCode SamplePointCloud::GridSample(const IPointCloud* pointCloud, Real interval, std::vector< Int >& sampleIndex);

格栅采样:根据点间距把空间分为一个一个的格子,每个格子采样一个点,并且使得采样的点云尽量均匀。


ErrorCode SamplePointCloud::Simplify(const IPointCloud* pointCloud, Int resolution, IPointCloud* simplifiedCloud, const std::vector< Real >* pointFields = NULL, std::vector< Real >* simplifiedFields = NULL);

点云简化:点云根据分辨率resolution把其所在的包围盒在xyz方向离散化为小方格,每个小方格内的点云会做平均,然后得到一个简化的点云


点云法向量

点云法线的介绍可以参考点云法线,特别是如何给点云法线完美的定向。

ErrorCode ConsolidatePointCloud::CalculatePointCloudNormal(IPointCloud* pointCloud, bool isDepthImage = false, Int neighborCount = 9);

ErrorCode ConsolidatePointCloud::SmoothNormal(IPointCloud* pointCloud, Real normalWeight = 1.0, Int neighborCount = 9, const std::vector< Int >* fixedIndex = NULL);

ErrorCode ConsolidatePointCloud::ReversePatchNormal(IPointCloud* pointCloud, Int pointId, Int neighborCount = 9);

扫描点云数据的相关介绍可以参考有序点云

ErrorCode ConsolidatePointCloud::ConsolidateRawScanData(IPointCloud* pointCloud, Int resolutionX, Int resolutionY, bool removeOutlier, bool zPositive, Real maxZAngle = 90.0 * ONE_RADIAN);

点云光滑
ErrorCode ConsolidatePointCloud::SmoothGeometry(IPointCloud* pointCloud, Int neighborCount, Int iterationCount, const std::vector< Int >* selectIndex = NULL);

点云光滑:这个API把点云不断的往周围邻域拟合的平面上作投影.


ErrorCode ConsolidatePointCloud::SmoothGeometryByNormal(IPointCloud* pointCloud, Int neighborCount = 9, Real sharpAngle = ONE_RADIAN * 20, Int iterationCount = 5);

背景:根据点云法线信息优化点云几何,是其几何位置与法线协调。


点云去除飞点孤立项

点云去除飞点和孤立项,推荐调用CalculateIsolation

ErrorCode ConsolidatePointCloud::CalculateIsolation(const IPointCloud* pointCloud, std::vector< Real >* isolation, Int neighborCount = 20, const std::vector* cloudIds = NULL, Real maxExtendNormalAngle = 90 * ONE_RADIAN);

用法:计算每个点的孤立值. 孤立值代表了这个点所在局部块占整体点云的比例. 其几何意义为,点云被分割为不同的块,每个块内点的孤立值为块内点数占总点数的比例。孤立值越小, 孤立项的几率越大,范围是(0, 1). 比如可以认为孤立值小于0.1的点就为孤立项


ErrorCode ConsolidatePointCloud::CalculateOutlier(const IPointCloud* pointCloud, std::vector< Real >* outlierValue, Int neighborCount = 9);

用法:计算每个点为飞点的概率值,值越大, 飞点的几率越大,范围是(0, 1). 比如可以认为均匀值大于0.8的点就为飞点


ErrorCode ConsolidatePointCloud::CalculateUniformity(const IPointCloud* pointCloud, std::vector< Real >* uniformaity, Int neighborCount = 9);

用法:计算每个点的均匀值. 均匀值越大, 飞点的几率越大,范围是(0, 1)。比如可以认为均匀值大于0.8的点就为飞点。


点云去除重影
ErrorCode ConsolidatePointCloud::RemoveOverlap(const IPointCloud* pointCloud, Real maxOverlapDist, std::vector< Int >& overlapIndex);

用法:去除点云重影部分。如果点法线方向上有点存在,且距离小于maxOverlapDist,则判定为重影点。


点云重建网格
ErrorCode Reconstruct(const IPointCloud* pointCloud, ITriMesh* recontructedMesh, Int quality = 5, bool needFillHole = false, const std::vector< Real >* pointFields = NULL, std::vector< Real >* vertexField = NULL, Real maxHoleAreaRatio = 1);

点云注册对齐

点云拼接注册的相关介绍可以参考点云拼接注册

ErrorCode RegistratePointCloud::_AlignPointPair(const std::vector< Vector3 >& pointsRef, const std::vector< Vector3 >& pointsFrom, Matrix4x4* resultTransform, const Matrix4x4* initTransform = NULL);

计算点对对齐的刚体变换:pointsRef = resultTransform * initTransform * pointsFrom


ErrorCode RegistratePointCloud::ICPRegistrate(const IPointCloud* pointCloudRef, const std::vector< Vector3 >* marksRef, const IPointCloud* pointCloudFrom, const std::vector< Vector3 >* marksFrom, Matrix4x4* resultTransform, const Matrix4x4* initTransform = NULL, bool hasNormalInfo = true);

点云的ICP注册对齐:pointCloudRef = resultTransform * initTransform * pointCloudFrom


ErrorCode RegistratePointCloud::AlignPointCloud(const IPointCloud* pointCloudRef, const IPointCloud* pointCloudFrom, Matrix4x4* resultTransform, Real accuracy = 0);

无标记点的点云初始对齐:pointCloudRef = resultTransform * pointCloudFrom


ErrorCode RegistratePointCloud::AlignPointCloudByMark(const IPointCloud* pointCloudRef, const std::vector< Vector3 >* marksRef, const IPointCloud* pointCloudFrom, const std::vector< Vector3 >* marksFrom, Matrix4x4* resultTransform);

带标记点的点云初始对齐:pointCloudRef = resultTransform * pointCloudFrom

注意: pointCloudRef和pointCloudFrom需要计算好法向量. marksRef和marksFrom不需要一一对应, 但是至少需要3个点

使用场景: 带标记点的初始对齐变换,如果点云有用户标记的标记点,或者扫描时点云有标记点信息,请使用此函数做初始对齐


ErrorCode RegistratePointCloud::GlobalRegistrate(const std::vector< IPointCloud* >* pointCloudList, Int maxIterationCount, std::vector< Matrix4x4 >* resultTransformList, const std::vector< Matrix4x4 >* initTransformList = NULL, bool hasNormalInfo = true, Int fixId = 0, const std::vector< std::vector< Vector3 > >* markList = NULL);

点云全局注册优化

注意: pointCloudList如果带法线信息,注册精度会更好一些

使用场景: 逐帧注册的点云数据,往往有累积误差. 全局注册可以把累积误差分散到每一帧中去,从而减少整体的注册误差


ErrorCode NonRigidICP(const IPointCloud* pointCloudRef, IPointCloud* pointCloudFrom, const Matrix4x4* initTransform = NULL, Int nonRigidSize = 25);

ErrorCode GlobalNonRigidRegistrate(std::vector< IPointCloud* >* pointCloudList, Int maxIterationCount, const std::vector< Matrix4x4 >* initTransformList = NULL, Int nonRigidSize = 25);

多帧点云去重
class SumPointCloud;

目的: 点云在注册对齐后, 往往会有重叠的部分. 如果简单的叠加, 点云的数量会增加很多, 并且也不均匀. 点云去重就是在重叠的地方,只取一帧点云的内容.


SumPointCloud(Real interval, const Vector3& bboxMin, const Vector3& bboxMax, bool hasNormalInfo, Int blendNeighborCount = 25, Int blendIterationCount = 2);

ErrorCode UpdateSumFunction(const IPointCloud* pointCloud, const Matrix4x4* transform, const std::vector< Real >* pointFields = NULL);

ErrorCode ExtractPointCloud(IPointCloud* pointCloud, std::vector< Real >* pointFields = NULL, std::vector< Int >* cloudIds = NULL);

点云变形
ErrorCode ComputeControlInfo(const IPointList* pointList, Int controlPointCount, DeformPointList::ControlInfo* controlInfo);

ErrorCode Deform(ITriMesh* triMesh, ControlInfo* controlInfo, const std::vector< Int >& targetIndex, const std::vector< Vector3 >& targetCoords, const std::vector< bool >& constrainFlags);
ErrorCode Deform(IPointCloud* pointCloud, ControlInfo* controlInfo, const std::vector< Int >& targetIndex, const std::vector< Vector3 >& targetCoords, const std::vector< bool>& constrainFlags);

基于控制点的点云变形


三角网格表示
class ITriMesh;

网格数据结构接口. Geometry++网格算法都是基于ITriMesh接口来调用的.

优点:可以很方便的在已有的程序中使用Geometry++提供的网格算法,只需要实现这个接口类,就可以调用所有网格相关的算法


网格半边结构
class HalfMesh;
Half Edge Structure
图示:半边数据结构示例. 左边红点为内点,右边红点为边界点

网格导入导出
TriMesh* Parser::ImportTriMesh(std::string fileName);

目前支持的文件格式: obj, stl, off, ply. 用户也可以导入其它格式的网格数据, 只需要自己写一个Parser, 然后用导入的数据创建网格类(例如TriMesh)即可使用了

fileName: 格式为path/xxx.obj, path/xxx.stl, path可以是绝对路径,也可以相对路径

返回值: 如果导入失败则返回NULL


void Parser::ExportTriMesh(std::string fileName, const ITriMesh* triMesh);

目前支持的文件格式: obj, stl, ply

fileName: 格式为path/xxx.obj, path/xxx.stl, path可以是绝对路径,也可以相对路径


网格去噪
ErrorCode RemoveGeometryNoise(ITriMesh* triMesh, Real sharpAngle = 70.0 * ONE_RADIAN, Real positionWeight = 1.0);

ErrorCode LaplaceSmooth(ITriMesh* triMesh, bool keepBoundary = true, Real positionWeight = 1.0);

网格去除孤立项
ErrorCode CalculateIsolation(ITriMesh* triMesh, std::vector< Real >* isolation);

用法:计算网格每个顶点的孤立值. 孤立值越小, 孤立项的几率越大,范围是(0, 1). 比如可以认为孤立值小于0.1的点就为孤立项


几何细节增强
ErrorCode EnhanceDetail(ITriMesh* triMesh, Real intensity = 2.0);

网格拓扑修整

网格拓扑的相关介绍可以参考从STL文件到网格拓扑

bool _IsTriMeshManifold(ITriMesh* triMesh);

ErrorCode MakeTriMeshManifold(ITriMesh* triMesh, std::map< Int, Int >* insertVertexIdMap = NULL);

移除非流形结构


网格几何修整
ErrorCode ConsolidateGeometry(ITriMesh* triMesh, Real minTriangleAngle, Real minEdgeLength, Real foldoverAngleTol);

优化退化三角形角, 退化三角边,折叠三角形,不改变网格的拓扑结构


网格补洞
ErrorCode FindHoles(const ITriMesh* triMesh, std::vector< std::vector< Int > > *holesIds);

找网格的洞


ErrorCode FillHoles(ITriMesh* triMesh, const std::vector< Int >* boundarySeedIds = NULL, FillMeshHoleType method = FILL_MESH_HOLE_FLAT,
            const std::vector< Real >* vertexFields = NULL, std::vector< Real >* insertedVertexFields = NULL);

补网格的洞


ErrorCode BridgeEdges(ITriMesh* triMesh, const Int edge1VertexIds[2], const Int edge2VertexIds[2], const std::vector< Real >* vertexFields = NULL, std::vector< Real >* insertedVertexFields = NULL);

桥接网格任意边界上的一对边


Loop细分
ErrorCode LoopSubdivideMesh(ITriMesh* triMesh, const std::vector< Real >* vertexFields = NULL, std::vector< Real >* insertedVertexFields = NULL);

网格加密
ErrorCode DensifyMesh(ITriMesh* triMesh, Int targetVertexCount, const std::vector< Real >* vertexFields = NULL, std::vector< Real >* insertedVertexFields = NULL);

网格简化
ErrorCode QuadricSimplify(ITriMesh* triMesh, Int targetVertexCount, bool keepBoundary = true, const std::vector< Real >* vertexFields = NULL, std::vector< Real >* simplifiedVertexFields = NULL);

ErrorCode SimplifyWithTextureCoords(ITriMesh* triMesh, Int targetVertexCount, const std::vector< Vector3 >* texCoords, std::vector< Vector3 >* simplifiedTexCoords, const std::vector< Real >* vertexFields = NULL, std::vector< Real >* simplifiedVertexFields = NULL);

重新网格化
ErrorCode UniformRemesh(ITriMesh* triMesh, Int targetVertexCount, Real sharpAngle, Int optimiseCount = 2, const std::vector< Real >* vertexFields = NULL, std::vector< Real >* remeshVertexFields = NULL);

重新网格化(Remesh):网格顶点分布更加均匀。相关概念可以参考从Delaunay三角化到网格质量


网格优化
ErrorCode ConstrainedDelaunayOptimization(ITriMesh* triMesh, Real* sharpAngle, const std::vector< std::vector< Int > >* polylineList, const std::vector< Int >* forbiddenEdges);

带约束的Delaunay网格优化:优化网格三角形为Delaunay三角形。相关概念可以参考从Delaunay三角化到网格质量


ErrorCode CentroidVoronoiOptimization(ITriMesh* triMesh, Real* sharpAngle, std::vector< Real >* vertexFields, const std::vector< Int >* forbiddenEdges, const std::vector< Int >* fixVertices, const std::vector< std::vector< Int > >* fixPolylines);

重心Voronoi优化:优化网格三角形为Delaunay三角形,并且三角形分布更加均匀。相关概念可以参考从Delaunay三角化到网格质量


高度场压缩
ErrorCode CompressHeightField(std::vector< Real >* heightField, Int resolutionX, Int resolutionY, Real compressRatio);

切割网格
ErrorCode SplitByPlane(ITriMesh* triMesh, const Plane3* plane, std::vector< bool >* triangleFlags, const std::vector< Real >* vertexFields = NULL, std::vector< Real >* insertedVertexFields = NULL);

ErrorCode InsertSplitLinesByEdgePoints(ITriMesh* triMesh, const std::vector< std::vector< PointOnEdge > >& edgePointLines, std::vector< std::vector< Int> >* newSplitLineVertexIds = NULL, const std::vector< Vector3 >* texCoords = NULL, std::vector< Vector3 >* newTexCoords = NULL);
ErrorCode InsertSplitLinesByFacePoints(ITriMesh* triMesh, const std::vector< std::vector< PointOnFace> >& facePointLines, std::vector< std::vector< Int > >* newSplitLinesVertexIds = NULL, const std::vector< Vector3 >* texCoords = NULL, std::vector< Vector3 >* newTexCoords = NULL);

ErrorCode SplitByLines(ITriMesh* triMesh, const std::vector< std::vector< Int > >& splitLines, std::vector< Int >* insertVertexIdMap = NULL);

网格抽壳
ErrorCode UniformApproximate(ITriMesh* triMesh, Real offsetValue);

网格均匀抽壳:计算网格的近似等距网格,如果是开网格,会把原始网格和等距网格的边界连接起来形成一个封闭网格。


网格采样
ErrorCode UniformSample(const ITriMesh* triMesh, Int sampleCount, Real sharpAngle, std::vector< PointOnFace >& pointsOnFace, std::vector< PointOnEdge >& pointsOnEdge, std::vector< Int >& pointsOnVertex, bool excludeBoundary);

网格采样:在网格上均匀采样点。


网格变形
class DeformMesh

ErrorCode Init(ITriMesh* triMesh, const std::vector< bool >& vertexFixFlags, bool consolidateGeometry = true);

初始化网格变形


ErrorCode Deform(const std::vector< Int >& targetVertexIds, const std::vector< Vector3 >& targetCoords, DeformMeshType deformType);

网格变形


网格UV展开

网格UV展开的相关介绍可以参考网格UV展开

ErrorCode ConformalMap(const ITriMesh* triMesh, const std::vector< Int >* fixedVertexIndices, const std::vector< Real >* fixedVertexCoords, std::vector< Real >* texCoords);

ErrorCode OptimizeIsometric(const ITriMesh* triMesh, std::vector< Real >* texCoords, Int iterationCount, const std::vector< Int >* fixedVertexIndex = NULL);

free_fix_boundary
ErrorCode PackUVAtlas(const std::vector< ITriMesh* >& singleRegionList, std::vector< std::vector< Real > >& texCoordList);

纹理坐标打包:把多个网格的纹理坐标打包到一个方形区域内。


ErrorCode GenerateUVAtlas(const ITriMesh* triMesh, Int initChartCount, std::vector< Real >* texCoords, std::vector< Int >* faceTexIds, bool needInitSplit, bool needSplitFoldOver, bool needSplitOverlap);

点像对应
ErrorCode TransferMappingToMesh(const IPointCloud* pointCloud, const std::vector< ImageColorId >& pointColorIds, ITriMesh* triMesh, std::vector< ImageColorId >& meshColorIds, Real densityScale = 1.0, bool smoothGeometry = false);

在点云重建成网格后,把点云的点像对应信息传递到网格顶点上


ErrorCode InterpolateImageColorIdsOnMesh(const ITriMesh* triMesh, const std::vector< Int >& validFlags, std::vector< ImageColorId >& imageColorIds);

ErrorCode ProjectImageColorIdToMesh(const IPointCloud* pointCloud, const Matrix4x4* transform, const std::vector< ImageColorId >& pointColorIds, Real pointDensity, const ITriMesh* triMesh, std::vector< std::pair< Int, ImageColorId > >& meshColorIds);

ErrorCode FuseImageColorIds(const ITriMesh* triMesh, const std::vector< std::vector< ImageColorId > >& imageColorIdList, std::vector< ImageColorId >& imageColorIds, std::vector< Int >& faceColorIds, FuseImageColorIdType fuseType = FUSE_IMAGECOLORID_SEQUENTIAL, const std::vector< Vector3 >* cameraDirList = NULL, const std::vector< std::vector< Color4> >* refImageList = NULL, const std::vector< Int >* refImageInfos = NULL, const std::vector< Real >* refImageWeights = NULL);
fuse_imagecolorid
ErrorCode InterpolateFaceImageColorIds(const ITriMesh* triMesh, std::vector< ImageColorId >& imageColorIds, std::vector< Int >& faceColorIds);

插值网格三角片顶点的点像对应。


纹理图制作

纹理图制作的相关介绍可以参考彩色网格

ErrorCode CreateTextureImageByVertexColors(const std::vector< Real >& texCoordinates, const std::vector< Int >& faceTextureIds, const std::vector< Color4 >& texColors, Int outputImageWidth, Int outputImageHeight, std::vector< Color4 >& outputImageData, std::vector< Int >* pixelResultInfos = NULL);

应用网格顶点颜色制作纹理贴图


ErrorCode CreateTextureImageByRefImages(const std::vector< Real >& texCoordinates, const std::vector< Int >& faceTextureIds, const std::vector< ImageColorId >& texColorIds, const std::vector< std::vector< Color4 > > refImageListData, const std::vector< Int >& refImageInfos, Int outputImageWidth, Int outputImageHeight, std::vector< Color4 >& outputImageData, std::vector< Int >* pixelResultInfos = NULL);

应用网格对应的图片制作纹理贴图


纹理色彩融合
ErrorCode TuneColorFromMultiFrame(const IPointCloud* pointCloud, Int neighborCount, const std::vector< Int >& colorIds, std::vector< Vector3 >& pointColors, const Vector3& sharpColorDiff = Vector3(0.1, 1.0, 0.5));

多角度点云颜色融合:详细介绍可以参考多角度点云颜色融合


ErrorCode TuneMeshColorFromMultiPatch(const ITriMesh* triMesh, const std::vector< Int >& colorIds, std::vector< Vector3 >& vertexColors, const Vector3& sharpColorDiff = Vector3(0.1, 1.0, 0.5));

网格颜色融合:原理和TuneColorFromMultiFrame一样。不同的地方是,网格有顶点的连接关系,可以自然得到邻域信息,并且局部邻域成片状。点云如果不均匀,其局部邻域不成片状,比如线激光数据。所以颜色融合在网格上计算更加稳定。


ErrorCode TuneColorFromSingleLight(const std::vector< IPointCloud* >& pointCloudList, std::vector< std::vector< Vector3 > >& colorList, bool needBlend, const Real* pointDensity, const std::vector< std::vector< Int > >* originColorIdList, std::vector< std::vector< Int > >* tunedColorIdList);

单光源光照的点云颜色融合:详细介绍可以参考单光源点云颜色修正


ErrorCode TuneTextureImageColor(const ITriMesh* triMesh, const std::vector< Real >& textureCoords, const std::vector< Color4 >& faceVertexColors, const std::vector< Int >& faceVertexImageIds, Int imageWidth, Int imageHeight, std::vector< Color4 >& textureColors, const Vector3& sharpColorDiff = Vector3(0.1, 0.5, 0.5));

ErrorCode TuneImageByPointColor(const std::vector< Int >& pointCoords, const std::vector< Vector3 & pointColors, Int imageWidth, Int imageHeight, std::vector< Color4 >& imageColors);

ErrorCode TuneImageByTriangleColor(const std::vector< Int >& vertexCoords, const std::vector< Vector3 >& vertexColors, const std::vector< Int > vertexFlags, const std::vector< Int >& faceVertexIds, Int imageWidth, Int imageHeight, std::vector< Color4 >& imageColors);

ErrorCode TuneTextureImageByVertexColor(const std::vector< Real >& textureCoords, const std::vector< Vector3 >& vertexColors, Int imageWidth, Int imageHeight, const std::vector< PixelType > pixelTypes, std::vector< Color4 >& textureColors);

网格测地线
ErrorCode ComputeApproximateGeodesics(const ITriMesh* triMesh, const std::vector< Int >& sectionVertexIds, bool isSectionClose, std::vector< Int >& pathVertexIds, Real& distance, IMeshDistance* meshDistance);

网格测地线的近似求解,测地线经过网格顶点,求解速度很快


ErrorCode ComputeExactGeodesics(const ITriMesh* triMesh, const std::vector< Int >& sectionVertexIds, bool isSectionClose, std::vector& pathPointPositions, Real& distance, std::vector< PointOnEdge > *pathPointInfos);

网格测地线的精确求解,测地线经过网格边,求解速度较慢


 ErrorCode FastComputeExactGeodesics(const ITriMesh* triMesh, const std::vector< Int >& sectionVertexIds, bool isSectionClose, std::vector< Vector3 >& pathPointPositions, Real& distance, std::vector< PointOnEdge > *pathPointInfos, Real accuracy = 0.5);

网格测地线的快速求解,测地线经过网格边,求解速度很快


 ErrorCode FastComputeExactGeodesics(const ITriMesh* triMesh, const std::vector< PointOnFace >& sectionPofs, bool isSectionClose, std::vector< Vector3 >& pathPointPositions, Real& distance, std::vector< PointOnFace > *pathPointInfos, Real accuracy = 0.5);

平面截线
 ErrorCode ConnectVertexByCuttingPlane(const ITriMesh* triMesh, Int startId, Int endId, const Plane3& cuttingPlane, std::vector< PointOnEdge >& pathPointInfos);
 ErrorCode ConnectFacePointByCuttingPlane(const ITriMesh* triMesh, const PointOnFace& startPof, const PointOnFace& endPof, const Plane3& cuttingPlane, std::vector< PointOnFace >& pathPointInfos);

生成连接两个网格顶点/网格面点的截面线。最终生成的曲线是经过给定的两个点并在给定平面上的网格曲线。


网格曲线光滑
ErrorCode SmoothCurveOnMesh(ITriMesh* triMesh, const std::vector< Int >& curve, Real sharpAngle, Real smoothWeight, Int iterationCount, bool optimizeTopology = false, std::vector< Int >* optimizedCurve = NULL);

光滑网格顶点上的曲线:通过改变曲线所在顶点的位置,来达到smooth的效果


ErrorCode SmoothCurveCrossMesh(const ITriMesh* triMesh, const std::vector< PointOnEdge >& curve, Real smoothWeight, Int iterationCount, std::vector< PointOnEdge >& smoothCurve);

光滑PointOnEdge类型的曲线:通过移动curve在网格上的位置,来达到smooth的效果,输入网格几何不变。


网格面积和体积
ErrorCode ComputeArea(const ITriMesh* triMesh, Real& area);

ErrorCode ComputeVolume(const ITriMesh* triMesh, Real& volume);

网格曲率
ErrorCode ComputeMeanCurvature(const ITriMesh* triMesh, std::vector< Real >& curvature);

网格平均曲率测量


ErrorCode ComputeGaussCurvature(const ITriMesh* triMesh, std::vector< Real >& curvature);

网格高斯曲率测量


ErrorCode ComputePrincipalCurvature(const ITriMesh* triMesh, std::vector< Real >& minCurvature, std::vector< Real >& maxCurvature, std::vector< Vector3 >& minDirs, std::vector< Vector3 >& maxDirs);

网格主曲率测量


点到网格距离
class MeshQueryTool;

背景:当需要频繁查询点到某固定网格的距离时,可以预先创建该特殊的数据结构来加速这一查询过程。


ErrorCode Init(const ITriMesh* refITriMesh);

ErrorCode QueryNearestTriangle(const Vector3& queryCoord, PointOnFace* projectPoint, Real* distance = NULL, Vector3* projectCoord = NULL) const;

ErrorCode QueryNearestTriangles(const std::vector< Vector3 >& queryCoords, std::vector< PointOnFace >* projectPoints, std::vector< Real >* distances = NULL, std::vector< Vector3 >* projectCoords = NULL) const;

射线与网格求交点
class MeshQueryTool;

ErrorCode Init(const ITriMesh* refITriMesh);

ErrorCode RayIntersections(const Vector3& rayOrigin, const Vector3& rayDirection, Int maxIntersectCount, std::vector< PointOnFace >* intersectPoints, std::vector< Real >* distances = NULL, std::vector< Vector3 >* intersectCoords = NULL) const;

网格厚度
ErrorCode ComputeThickness(const ITriMesh* triMesh, std::vector< Real >& thickness, std::vector< bool > *isInfiniteValue = NULL);

调试
请先看看常见问题
这里的调试特指某个API的调试。对于一系列API的调用后出现的问题,请先分析定位出是哪个API出了问题,一般是合理的输入没有得到合理的输出。
Geometry++ API的调试。首先查看API的返回码和Log文件,如果不能解决,则之后的调试一般分3种情况:

1. 可以在Magic3D里重现操作步骤,并且可以重现错误.

2. 可以在Magic3D里重现操作步骤,但是重现不了错误:这种情况一般是api的用法有问题,请仔细对比Magic3D的用法。如果用户采用的是自定义的网格和点云类(没有采用PointCloud和TriMesh),可以在自己程序里先用TriMesh或者PointCloud调用API,如果调用成功,则说明自定义类有问题。请仔细参考PointCloud和TriMesh的函数实现。特别是对一些退化情况的处理。

3. 没法在Magic3D里重现操作步骤:采用dump api输入的方式来反馈错误。dump数据的生成步骤是:

  • a) 在需要Dump调试信息的API前使用函数GPP::DumpOnce. 比如需要Dump GlobalRegistrate的调试信息:
  •     GPP::DumpOnce();
        GPP::ErrorCode res = GPP::RegistratePointCloud::GlobalRegistrate(pointCloudRef, pointCloudFrom, transform);
    
  • b) 调试信息在调用API时会输出到程序可执行文件的当前目录下
  • 问题反馈请发送到邮箱threeparkhelp@qq.com,文件请压缩后发送
    问题反馈的内容:
  • 一次只反馈一个问题,多个问题请分多次反馈。
  • 问题的详细描述:请准确,定量的,用专业术语描述问题。
  • 数据:Magic3D的操作模型,或者Geometry++的dump文件。
  • Log文件:请保证是操作出错时的Log
  • API返回码
  • 结果描述:形式可以是多种多样,如导出结果文件,结果截图,操作视频等。
  • 代码片断:可以截取api调用时的代码片断,注意不是整个代码文件
  • 其它:任何可以帮助问题重现的资料
  • 注意:问题反馈的内容,主要是为了重现问题。反馈的内容越精确,问题越能得到重现。解决问题是建立在问题重现的基础之上的。
    问题反馈,请用准确,定量的专业术语来描述。定性的,或者不准确的的问题反馈,很难得到及时有效的回复。比如“我的SDK为什么不能用呢?”“某某功能为什么不能用啊?为什么出错啊?”“我的程序崩溃是怎么回事呢?”

    程序调试的相关介绍也可以参考程序调试


    常见问题
    请不要在release的环境下debug程序,因为release环境下面的调试信息是不准确的。
    编译有错误
  • 请仔细检查头文件,lib库,宏定义是否都配置好了,详细请参考使用简介。如果工程中链接了很多第三方库,可以新建一个简单的工程来链接Geometry++库,来确认Geometry++库是否有问题。
  • API调用出现问题
  • 检查API的返回码:-6属于没有激活开发包;-8属于函数没有定义,如果你有这个函数的授权,可以前来联系。
  • 查看log文件,看能不能得到提示
  • 其它情况,可以详细参考调试部分的介绍。
  • 一般性问题还是特例问题
  • 某个api调用出现问题时,可以多试试几个例子,看看是对所有例子都有问题,还是个例有问题。如果对所有例子都有问题,一般都是用法有问题。
  • 程序崩溃了(Crash)怎么办
  • 请仔细确认程序的崩溃点!如果有异常发生,请在软件程序里Catch住异常。异常的捕获处理属于软件部分的内容。
  • 我程序里的结果没有Magic3D的结果好
  • 请确认你的开发包和Magic3D的开发包是同一个版本
  • 请确认API的调用参数是一样的,Magic3D的调用参数可以参考其源代码
  • 请确认API的输入是一样的
  • 总之,肯定是某个地方有区别。这个问题就是一个比较差异的问题,需要一些耐心。问题本身是可以解决的。
  • 网格为什么需要拓扑修复
  • 网格相关的API一般有流形结构的假设前提。软件可以设计成在网格生成或导入时,就进行拓扑修复。特别需要注意的是,拓扑修复可能会改变网格顶点和三角片的个数和顺序,进行拓扑修复后,请及时更新网格相关的各类信息。
  • 自定义网格类的注意事项
  • 用户如果没有丰富的网格处理经验,建议使用TriMesh类来表达三角网格
  • 如果用户没有使用TriMesh类,而是从ITriMesh继承,自定义了三角网格类。请仔细参考TriMesh的函数实现。特别是对一些退化情况的处理,比如UpdateNormal函数的实现。
  • 调试自定义类的方法,对比自定义类和TriMesh调用同一个API的结果差别。
  • 自定义类的最大问题是,有时候问题很难重现:开发者这边用的TriMesh,用户这边用的自定义网格类。遇到这类问题,用户可以先试试TriMesh的结果。如果TriMesh的结果是正确的,则可以比较自定义类和TriMesh类的实现差别。
  • 反馈问题时模型的导出
  • 导出模型坐标时,确保导出的坐标精度是没有截断的,如std::ofstream导出时可以调用precision来设置精度。
  • 网格导出时,一般使用OBJ格式,不要使用STL,因为STL没有网格拓扑信息。不同软件系统重构STL拓扑的实现可能不一样。具体可以参考为什么不建议使用STL格式。
  • 总之,最重要的环节是问题重现。只有问题能够重现出来,才能得到有效的解决。反馈问题前,请先想想反馈的信息是否能够得到问题重现。