【VTK】基于VTK的扩展ACVD实现网格重新划分网格Remeshing

很高兴在雪易的CSDN遇见你 

VTK技术爱好者 QQ:870202403      公众号:VTK忠粉


前言

本文分享基于VTK的扩展ACVD实现网格重新划分网格Remeshing,希望对各位小伙伴有所帮助!

感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步!

你的点赞就是我的动力(^U^)ノ~YO

目录

前言

1. 重新划分网格Remeshing的效果

2. 基于VTK实现的ACVD

3. Remeshing源码分享

结论:


1. 重新划分网格Remeshing的效果

在3D图形处理时,通常需要进行网格的重新划分。医学图形处理时通常使用VTK作为图形处理和渲染显示的库。

分享过关于CGAL实现Remeshing的方法,详情见:

【CGAL系列】Remesh—1 Isotropic_remeshing_example_cgal remeshing-CSDN博客

这里,我们分享基于VTK如何进行网格重新划分Remeshing。 

2. 基于VTK实现的ACVD

网站链接:ACVD | Sébastien Valette (insa-lyon.fr)

源码:ACVD

 ACVD库基于VTK进行扩展,为三角形网格提供一种快速高效的网格简化/重采样方法。可以应用于非常复杂的网格(数百万个顶点)。这种方法基于变分框架内的输入网格单元的聚类。这种聚类方法类似于质心的分区Voronoi区域,其中每个区域具有相同的大小,因此表面的采样非常均匀,并且对生成的三角进行测量具有良好的纵横比。

这种方法对于重新划分网格程序也很有用。

基于斯坦福兔子的聚类方法

3. Remeshing源码分享

.h文件

#pragma once
#include <qobject.h>

#include <vtkPolyData.h>

class zxExampleRemeshing :
    public QObject
{

public:
    zxExampleRemeshing(QWidget* parent = nullptr);

    bool ValidateSurface(vtkPolyData* surface);
    vtkPolyData* Remesh(vtkPolyData* surface,
        int numVertices,
        double gradation,
        int subsampling,
        double edgeSplitting,
        int optimizationLevel,
        bool forceManifold,
        bool boundaryFixing);
    void Remeshing();
};

.cpp文件

#include "zxExampleRemeshing.h"

#include "vtkSTLReader.h"
#include "vtkSTLWriter.h"
#include "vtkTriangleFilter.h"
#include "vtkCellData.h"
#include "vtkPointData.h"
#include <vtkIdList.h>
#include <vtkIntArray.h>
#include <vtkPolyDataNormals.h>

#include <array>
#include <vector>

#include "vtkSurface.h"
#include "vtkIsotropicDiscreteRemeshing.h"
#include "vtkIsotropicMetricForClustering.h"
#include "vtkQuadricTools.h"

struct ClustersQuadrics final
{
    explicit ClustersQuadrics(size_t size)
        : Elements(size),
        Size(size)
    {
        for (auto& array : Elements)
            array.fill(0.0);
    }

    ~ClustersQuadrics() = default;

    ClustersQuadrics(const ClustersQuadrics&) = delete;
    ClustersQuadrics& operator=(const ClustersQuadrics&) = delete;

    std::vector<std::array<double, 9>> Elements;
    size_t Size;
};

zxExampleRemeshing::zxExampleRemeshing(QWidget* parent)
{
}

bool zxExampleRemeshing::ValidateSurface(vtkPolyData* surface)
{
    if (nullptr == surface) return false;

    if (surface->GetNumberOfPoints() < 1) return false;

    vtkTriangleFilter* tri = vtkTriangleFilter::New();
    tri->SetInputData(surface);
    tri->Update();

    surface->DeepCopy(tri->GetOutput());

    if (surface->GetNumberOfPolys() < 1) return false;

    return true;
}

vtkPolyData* zxExampleRemeshing::Remesh(vtkPolyData* surface,
    int numVertices,
    double gradation,
    int subsampling,
    double edgeSplitting,
    int optimizationLevel,
    bool forceManifold,
    bool boundaryFixing)
{
    std::cout << "input data: " << surface << std::endl;
    //if (!ValidateSurface(surface)) return nullptr;
    std::cout << "input data validate: " << surface << std::endl;
    std::cout << "Start remeshing...";


    vtkCellArray* VerticesCells = surface->GetVerts();

    vtkSurface* mesh = vtkSurface::New();

    mesh->CreateFromPolyData(surface);
    mesh->GetCellData()->Initialize();
    mesh->GetPointData()->Initialize();
    mesh->DisplayMeshProperties();

    if (numVertices == 0)
    {
        numVertices = surface->GetNumberOfPoints();
    }

    if (edgeSplitting != 0.0)
        mesh->SplitLongEdges(edgeSplitting);

    std::cout << " **************************1111111111111*****************************************" << std::endl;
    vtkIsotropicDiscreteRemeshing* remesher = vtkIsotropicDiscreteRemeshing::New();

    remesher->GetMetric()->SetGradation(gradation);
    remesher->SetBoundaryFixing(boundaryFixing);
    remesher->SetConsoleOutput(1);
    remesher->SetForceManifold(forceManifold);
    remesher->SetInput(mesh);
    remesher->SetNumberOfClusters(numVertices);
    remesher->SetSubsamplingThreshold(subsampling);

    remesher->Remesh();

    std::cout << " **************************222222222222*****************************************" << std::endl;

    // Optimization: Minimize distance between input surface and remeshed surface
    if (optimizationLevel != 0)
    {
        ClustersQuadrics clustersQuadrics(numVertices);

        auto faceList = vtkSmartPointer<vtkIdList>::New();
        vtkSmartPointer<vtkIntArray> clustering = remesher->GetClustering();
        vtkSmartPointer<vtkSurface> remesherInput = remesher->GetInput();
        int clusteringType = remesher->GetClusteringType();
        int numItems = remesher->GetNumberOfItems();
        int numMisclassifiedItems = 0;

        for (int i = 0; i < numItems; ++i)
        {
            int cluster = clustering->GetValue(i);

            if (cluster >= 0 && cluster < numVertices)
            {
                if (clusteringType != 0)
                {
                    remesherInput->GetVertexNeighbourFaces(i, faceList);
                    int numIds = static_cast<int>(faceList->GetNumberOfIds());

                    for (int j = 0; j < numIds; ++j)
                        vtkQuadricTools::AddTriangleQuadric(clustersQuadrics.Elements[cluster].data(), remesherInput, faceList->GetId(j), false);
                }
                else
                {
                    vtkQuadricTools::AddTriangleQuadric(clustersQuadrics.Elements[cluster].data(), remesherInput, i, false);
                }
            }
            else
            {
                ++numMisclassifiedItems;
            }
        }

        std::cout << " **************************333333333333333*****************************************" << std::endl;

        if (numMisclassifiedItems != 0)
            std::cout << numMisclassifiedItems << " items with wrong cluster association" << std::endl;

        vtkSmartPointer<vtkSurface> remesherOutput = remesher->GetOutput();
        double point[3];

        for (int i = 0; i < numVertices; ++i)
        {
            remesherOutput->GetPoint(i, point);
            vtkQuadricTools::ComputeRepresentativePoint(clustersQuadrics.Elements[i].data(), point, optimizationLevel);
            remesherOutput->SetPointCoordinates(i, point);
        }

        std::cout << "After quadrics post-processing:" << std::endl;
        remesherOutput->DisplayMeshProperties();
    }

    std::cout << " **************************444444444444*****************************************" << std::endl;
    auto normals = vtkSmartPointer<vtkPolyDataNormals>::New();

    normals->SetInputData(remesher->GetOutput());
    normals->AutoOrientNormalsOn();
    normals->ComputeCellNormalsOff();
    normals->ComputePointNormalsOn();
    normals->ConsistencyOff();
    normals->FlipNormalsOff();
    normals->NonManifoldTraversalOff();
    normals->SplittingOff();

    normals->Update();

    auto remeshedSurface = vtkPolyData::New();
    remeshedSurface->DeepCopy(normals->GetOutput());

    std::cout << "Finished remeshing";

    return remeshedSurface;
}


void zxExampleRemeshing::Remeshing()
{
	vtkNew< vtkSTLReader > reader;
	reader->SetFileName("D:\\Data\\Remeshing\\implant.stl");
	reader->Update();

    vtkPolyData* polyData = vtkPolyData::New();
    polyData->DeepCopy(reader->GetOutput());
    std::cout << "Points: " << polyData->GetNumberOfPoints() << std::endl;

	

    /*vtkPolyData* remeshPD = Remesh(polyData, numVertices,
        gradation,
        subsampling,
        0.0,
        1.0,
        false,
        boundingFixing);*/
    vtkSurface* mesh = vtkSurface::New();

    mesh->CreateFromPolyData(polyData);
    mesh->GetCellData()->Initialize();
    mesh->GetPointData()->Initialize();
    mesh->DisplayMeshProperties();


    int density = 50;
    int numVertices = std::max(100, static_cast<int>(polyData->GetNumberOfPoints() * density * 0.01));

    QString remeshingType = "Adaptive"; // Adaptive or Regular
    double gradation = remeshingType == QStringLiteral("Adaptive") ? 1.0 : 0.0;

    const QString quality = "Medium (fast)";
    int subsampling;

    if (QStringLiteral("High (slow)") == quality)
    {
        subsampling = 50;
    }
    else if (QStringLiteral("Maximum (very slow)") == quality)
    {
        subsampling = 500;
    }
    else // The default is "Medium (fast)".
    {
        subsampling = 10;
    }

    bool boundingFixing = true;
    double edgeSplitting = 0.;
    double optimizationLevel = 1.0;

    if (numVertices == 0)
    {
        numVertices = polyData->GetNumberOfPoints();
    }

    if (edgeSplitting != 0.0)
        mesh->SplitLongEdges(edgeSplitting);

    std::cout << " **************************1111111111111*****************************************" << std::endl;
    vtkIsotropicDiscreteRemeshing* remesher = vtkIsotropicDiscreteRemeshing::New();

    remesher->GetMetric()->SetGradation(gradation);
    remesher->SetBoundaryFixing(boundingFixing);
    remesher->SetConsoleOutput(1);
    remesher->SetForceManifold(false);
    remesher->SetInput(mesh);
    remesher->SetNumberOfClusters(numVertices);
    remesher->SetSubsamplingThreshold(subsampling);

    remesher->Remesh();

    std::cout << " **************************222222222222*****************************************" << std::endl;

    // Optimization: Minimize distance between input surface and remeshed surface
    if (optimizationLevel != 0)
    {
        ClustersQuadrics clustersQuadrics(numVertices);

        auto faceList = vtkSmartPointer<vtkIdList>::New();
        vtkSmartPointer<vtkIntArray> clustering = remesher->GetClustering();
        vtkSmartPointer<vtkSurface> remesherInput = remesher->GetInput();
        int clusteringType = remesher->GetClusteringType();
        int numItems = remesher->GetNumberOfItems();
        int numMisclassifiedItems = 0;

        for (int i = 0; i < numItems; ++i)
        {
            int cluster = clustering->GetValue(i);

            if (cluster >= 0 && cluster < numVertices)
            {
                if (clusteringType != 0)
                {
                    remesherInput->GetVertexNeighbourFaces(i, faceList);
                    int numIds = static_cast<int>(faceList->GetNumberOfIds());

                    for (int j = 0; j < numIds; ++j)
                        vtkQuadricTools::AddTriangleQuadric(clustersQuadrics.Elements[cluster].data(), remesherInput, faceList->GetId(j), false);
                }
                else
                {
                    vtkQuadricTools::AddTriangleQuadric(clustersQuadrics.Elements[cluster].data(), remesherInput, i, false);
                }
            }
            else
            {
                ++numMisclassifiedItems;
            }
        }

        std::cout << " **************************333333333333333*****************************************" << std::endl;

        if (numMisclassifiedItems != 0)
            std::cout << numMisclassifiedItems << " items with wrong cluster association" << std::endl;

        vtkSmartPointer<vtkSurface> remesherOutput = remesher->GetOutput();
        double point[3];

        for (int i = 0; i < numVertices; ++i)
        {
            remesherOutput->GetPoint(i, point);
            vtkQuadricTools::ComputeRepresentativePoint(clustersQuadrics.Elements[i].data(), point, optimizationLevel);
            remesherOutput->SetPointCoordinates(i, point);
        }

        std::cout << "After quadrics post-processing:" << std::endl;
        remesherOutput->DisplayMeshProperties();
    }

    std::cout << " **************************444444444444*****************************************" << std::endl;
    auto normals = vtkSmartPointer<vtkPolyDataNormals>::New();

    normals->SetInputData(remesher->GetOutput());
    normals->AutoOrientNormalsOn();
    normals->ComputeCellNormalsOff();
    normals->ComputePointNormalsOn();
    normals->ConsistencyOff();
    normals->FlipNormalsOff();
    normals->NonManifoldTraversalOff();
    normals->SplittingOff();

    normals->Update();

    auto remeshedSurface = vtkPolyData::New();
    remeshedSurface->DeepCopy(normals->GetOutput());

    std::cout << "Finished remeshing";

    std::cout << "remeshPD: " << remeshedSurface <<std::endl;

    vtkNew< vtkSTLWriter > writer;
    writer->SetFileName("D:/Data/Remeshing/implantRemeshCode2.stl");
    writer->SetInputData(remeshedSurface);
    writer->Update();
}

结论:

感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步!

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪易

给我来点鼓励吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值