【CGAL】提取中心线

【CGAL】提取中心线

vtk提取中心线的思路是依次寻找最大内接球,cgal是把模型封闭,让后无线细化成一条线,我测试感觉点会突然离散,不知什么原因

vtkSmartPointer<vtkPolyData> RefinementComputingCenterLines(
    const vtkSmartPointer<vtkPolyData> surface) {
    vtkSmartPointer<vtkPolyData> centerline_input_surface;
    // vtk 表面整理
    vtkNew<vtkCleanPolyData> surface_cleaner;
    surface_cleaner->SetInputData(surface);
    surface_cleaner->Update();
    // vtk 三角形相交检查
    vtkNew<vtkTriangleFilter> surface_triangulator;
    surface_triangulator->SetInputConnection(surface_cleaner->GetOutputPort());
    surface_triangulator->PassLinesOff();
    surface_triangulator->PassVertsOff();
    surface_triangulator->Update();
    // vtk 表面封闭
    vtkNew<vtkvmtkCapPolyData> surface_capper;
    surface_capper->SetInputConnection(surface_triangulator->GetOutputPort());
    surface_capper->SetDisplacement(0);
    surface_capper->SetInPlaneDisplacement(0);
    surface_capper->Update();
    surface_cleaner->SetInputData(surface_capper->GetOutput());
    surface_cleaner->Update();
    surface_triangulator->SetInputConnection(surface_cleaner->GetOutputPort());
    surface_triangulator->PassLinesOff();
    surface_triangulator->PassVertsOff();
    surface_triangulator->Update();
    centerline_input_surface = surface_triangulator->GetOutput();
    // 转换为off模型
    STL2OFF("tmp.off", centerline_input_surface);
    // 提取骨骼
    std::ifstream input("tmp.off");
    Polyhedron tmesh;
    input >> tmesh;
    if (!CGAL::is_triangle_mesh(tmesh)) {
        std::cout << "Input geometry is not triangulated." << std::endl;
        return centerline_input_surface;
    }
    Skeleton skeleton;
    CGAL::extract_mean_curvature_flow_skeleton(tmesh, skeleton);
    vtkNew<vtkPoints> points_tmp;
    vtkNew<vtkCellArray> vertices_tmp;
    for (quint32 i = 0; i < boost::num_edges(skeleton); ++i) {
        points_tmp->InsertNextPoint(skeleton[i].point[0],
                                    skeleton[i].point[1],
                                    skeleton[i].point[2]);
        vertices_tmp->InsertNextCell(1);
        vertices_tmp->InsertCellPoint(i);
    }
    vtkNew<vtkPolyData> centerline_output_surface;
    centerline_output_surface->SetPoints(points_tmp);
    centerline_output_surface->SetVerts(vertices_tmp);
    return centerline_output_surface;
}
void STL2OFF(const QString off_filename,
                      vtkSmartPointer<vtkPolyData> surface_) {
    if (surface_ == nullptr) {
        return;
    }
    if (off_filename.isEmpty()) {
        return;
    }
    double x[3];
    QFile file(off_filename);
    file.open(QIODevice::WriteOnly);
    file.close();
    if (file.open(QIODevice::ReadWrite | QIODevice::Text)) {
        QTextStream stream(&file);
        stream.seek(file.size());
        stream << "OFF" << "\n";
        stream << surface_->GetNumberOfPoints() << " "
               << surface_->GetNumberOfCells() << " 0\n";
        for (qint32 ww = 0; ww < surface_->GetNumberOfPoints() ; ww++) {
            surface_->GetPoint(ww, x);
            stream << x[0] << " " << x[1] << " " << x[2] << "\n";
        }
        for (qint32 ww = 0; ww < surface_->GetNumberOfCells() ; ww++) {
            stream << surface_->GetCell(ww)->GetNumberOfPoints() << " ";
            for (qint32 i = 0; i <
                    surface_->GetCell(ww)->GetNumberOfPoints(); i++) {
                stream << surface_->GetCell(ww)->GetPointId(i) << " ";
            }
            stream << "\n";
        }
        file.close();
    }
}

bool OFF2STL(
    const QString off_filename, vtkSmartPointer<vtkPolyData> surface_) {
    std::string inputFilename = off_filename.toLocal8Bit().data();
    std::ifstream fin(inputFilename.c_str());
    vtkSmartPointer<vtkPolyData> surface = CustomReader(fin);
    vtkSmartPointer<vtkTriangleFilter> triangleFilter =
        vtkSmartPointer<vtkTriangleFilter>::New();
    triangleFilter->SetInputData(surface);
    vtkSmartPointer<vtkPolyDataNormals> normals =
        vtkSmartPointer<vtkPolyDataNormals>::New();
    normals->SetInputConnection(triangleFilter->GetOutputPort());
    normals->ConsistencyOn();
    normals->SplittingOff();
    vtkSmartPointer<vtkMassProperties> massProperties =
        vtkSmartPointer<vtkMassProperties>::New();
    massProperties->SetInputConnection(normals->GetOutputPort());
    massProperties->Update();
    if (massProperties->GetSurfaceArea() > 0.01) {
        surface_ = surface;
        fin.close();
        if (surface_ == nullptr) {
            return false;
        }
        return true;
    }
    return false;
}

vtkSmartPointer<vtkPolyData> CustomReader(istream &infile) {
    char buf[1000];
    infile.getline(buf, 1000);
    if (strcmp(buf, "off") == 0 || strcmp(buf, "OFF") == 0) {
        vtkIdType number_of_points, number_of_triangles, number_of_lines;
        infile >> number_of_points >> number_of_triangles >> number_of_lines;
        vtkSmartPointer<vtkPoints> points
            = vtkSmartPointer<vtkPoints>::New();
        points->SetNumberOfPoints(number_of_points);
        for (vtkIdType i = 0; i < number_of_points; i++) {
            double x, y, z;
            infile >> x >> y >> z;
            points->SetPoint(i, x, y, z);
        }
        vtkSmartPointer<vtkCellArray> polys
            = vtkSmartPointer<vtkCellArray>::New();
        qint32 n;
        vtkIdType type;
        for (vtkIdType i = 0; i < number_of_triangles; i++) {
            infile >> n;
            polys->InsertNextCell(n);
            for (; n > 0; n--) {
                infile >> type;
                polys->InsertCellPoint(type);
            }
        }
        vtkPolyData *polydata = vtkPolyData::New();
        polydata->SetPoints(points);
        polydata->SetPolys(polys);
        return polydata;
    }
    vtkPolyData *polydata = vtkPolyData::New();
    return polydata;
}
首先,需要将地面实况分割转化为一个三维点云。然后,使用CGAL库中的Alpha Shapes算法提取点云的凸壳。接着,使用CGAL库中的Straight Skeleton算法计算凸壳的直线骨架,并将其作为线粒体中心线输出。 下面是一个简单的示例代码: ```c++ #include <CGAL/Exact_predicates_inexact_constructions_kernel.h> #include <CGAL/Alpha_shape_3.h> #include <CGAL/Surface_mesh.h> #include <CGAL/Polyhedron_3.h> #include <CGAL/Polygon_mesh_processing/triangulate_faces.h> #include <CGAL/Polygon_mesh_processing/compute_normal.h> #include <CGAL/Polygon_mesh_processing/orientation.h> #include <CGAL/Straight_skeleton_builder_3.h> #include <CGAL/IO/Ply_reader.h> #include <CGAL/IO/Ply_writer.h> #include <iostream> #include <fstream> #include <vector> #include <algorithm> typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef Kernel::Point_3 Point_3; typedef Kernel::Iso_cuboid_3 Iso_cuboid_3; typedef CGAL::Alpha_shape_vertex_base_3<Kernel> Vb; typedef CGAL::Alpha_shape_cell_base_3<Kernel> Fb; typedef CGAL::Triangulation_data_structure_3<Vb,Fb> Tds; typedef CGAL::Alpha_shape_3<Tds> Alpha_shape_3; typedef Alpha_shape_3::Alpha_iterator Alpha_iterator; typedef Alpha_shape_3::Cell_handle Cell_handle; typedef Alpha_shape_3::Vertex_handle Vertex_handle; typedef Alpha_shape_3::Facet Facet; typedef Alpha_shape_3::Cell Cell; typedef Alpha_shape_3::NT NT; typedef CGAL::Surface_mesh<Point_3> Mesh; typedef CGAL::Polyhedron_3<Kernel> Polyhedron; typedef Polyhedron::HalfedgeDS HalfedgeDS; typedef CGAL::Straight_skeleton_builder_traits_3<Kernel> SsTraits; typedef CGAL::Straight_skeleton_builder_3<SsTraits> SsBuilder; int main(int argc, char** argv) { // 读入点云 std::vector<Point_3> points; std::ifstream in(argv[1]); CGAL::read_xyz_points(in, std::back_inserter(points)); in.close(); // 计算点云的凸壳 Alpha_shape_3 alpha(points.begin(), points.end(), 0.0, Alpha_shape_3::GENERAL); std::vector<std::pair<NT, Cell_handle>> alpha_cells; for(Alpha_iterator it = alpha.alpha_begin(); it != alpha.alpha_end(); ++it) { alpha_cells.clear(); alpha.get_alpha_shape_cells(std::back_inserter(alpha_cells), *it); if(alpha_cells.size() > 0) { Mesh mesh; std::map<Cell_handle, int> cell_to_index; std::vector<std::vector<int>> cell_faces(alpha_cells.size()); for(int i = 0; i < alpha_cells.size(); i++) { Cell_handle ch = alpha_cells[i].second; int index = mesh.add_vertex(ch->voronoi()); cell_to_index[ch] = index; for(int j = 0; j < 4; j++) { Cell_handle neighbor = ch->neighbor(j); if(!alpha.is_infinite(neighbor) && alpha.classify(ch, j) != Alpha_shape_3::EXTERIOR) { if(cell_to_index.find(neighbor) == cell_to_index.end()) { int index2 = mesh.add_vertex(neighbor->voronoi()); cell_to_index[neighbor] = index2; } int v1 = cell_to_index[ch]; int v2 = cell_to_index[neighbor]; cell_faces[i].push_back(mesh.add_face(v1, v2, -1)); } } } // 三角剖分面 std::vector<std::vector<int>> face_indices; CGAL::Polygon_mesh_processing::triangulate_faces(mesh, std::back_inserter(face_indices)); // 计算法向量 std::vector<Point_3> face_normals(face_indices.size()); for(int i = 0; i < face_indices.size(); i++) { std::vector<Point_3> face_points; for(int j = 0; j < face_indices[i].size(); j++) { int index = face_indices[i][j]; face_points.push_back(mesh.point(mesh.vertex_handle(index))); } CGAL::Polygon_mesh_processing::compute_face_normal(face_points[0], face_points[1], face_points[2], face_normals[i]); } // 翻转法向量 for(int i = 0; i < face_indices.size(); i++) { if(CGAL::Polygon_mesh_processing::orientation(face_indices[i], mesh) == CGAL::Polygon_mesh_processing::CLOCKWISE) { std::reverse(face_indices[i].begin(), face_indices[i].end()); face_normals[i] = -face_normals[i]; } } // 输出到PLY文件 std::ofstream out(argv[2]); CGAL::write_PLY(out, mesh, CGAL::make_normal_of_point_with_normal_pmap(face_normals)); out.close(); break; } } // 计算直线骨架 Polyhedron polyhedron; std::ifstream in2(argv[2]); CGAL::read_PLY(in2, polyhedron); in2.close(); SsBuilder builder(polyhedron.facets_begin(), polyhedron.facets_end(), polyhedron.points_begin(), polyhedron.points_end()); Polyhedron skeleton_polyhedron; builder(skeleton_polyhedron); // 输出到PLY文件 std::ofstream out2(argv[3]); CGAL::write_PLY(out2, skeleton_polyhedron); out2.close(); return 0; } ``` 这个示例代码包含了以下步骤: 1. 读入点云数据。 2. 使用Alpha Shapes算法计算点云的凸壳。 3. 将凸壳转化为一个三角网格,并进行三角剖分。 4. 计算每个三角形面的法向量,并翻转面的法向量,使其指向凸壳内部。 5. 将三角网格输出到PLY文件中。 6. 使用Straight Skeleton算法计算凸壳的直线骨架。 7. 将直线骨架输出到PLY文件中。 注意,这个示例代码只适用于简单的点云数据,对于更复杂的点云数据,可能需要进行一些调整。同时,这个示例代码仅供参考,实际使用时需要根据具体情况进行修改。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Beyond欣

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值