Marching Cubes算法再回顾

2024-01-09 15:04
文章标签 算法 回顾 marching cubes

本文主要是介绍Marching Cubes算法再回顾,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1,确定包含等值面的体元

首先介绍一下 体元的概念,体元是三维图像中由相邻的八个体素点组成的正方体方格,英语也叫 Cube,体元中角点函数值分为两种情况,一种是大于等于给定等值面的值 C0 ,则将角点设为 1 称该角点在等值面内部,否则设为0,在等值面之外,

一般来说,会出现一个角点在内,一个角点在外,则角点之间的连线(也就是体元的边)必然与等值面相交,根据这个原理就能判断等值面与哪些体元相交。

——————————————————————————————————————

三维空间中,平行且相邻的两个二维图像(每个图像中的正方形四个像素顶点组成一个基本的像素图像单元)组成一个基本的三维图像单元。,下图中由6个这样的基本三维图像单元:

vtkImageData结构由尺寸、间距和原点来定义。尺寸标注是沿着每个主轴的体素或像素的数量。原点是数据的第一个切片的左下角的世界坐标位置。间距是沿三个主要轴的像素之间的距离。

原点是数据集左下角的世界坐标位置。

尺寸是沿着三个主要轴的体素或像素的数量。

间距是体素的高度、长度和宽度,或相邻像素之间的距离,这取决于是将数据视为相同的方框还是连续函数中的样本点。
——————————————————————————————————————————

Marching Cubes算法根据一个立方体的8个顶点,判断这8个顶点的每个顶点在等值面的内部还是外部(每个顶点只有“在等值面内”和“在等值面外”这两种状态,设为0和1)从而根据这8个顶点的状态,建立一个包含共256种状态的查找表(根据平面对称性、中心对称性,256种最终降到15种)。

顶点值高于等值在表面的内部,等于等值在表面上,低于等值在表面外。

体元的每个顶点有两种状态,总共有256种,可以制作一个查找表(look up table)

但由于反转状态不变,所以可以减少一半,为128种。

再根据旋转不变形,又可以减少到15种情况。

可以认为这15种情况类似于基,经过旋转,反转可以得到256种状态对应的结果Triangulated Cubes:

根据每个顶点的状态,我们可以为每类制作一个8位索引Cube Numbering:

索引指向边表,给出了边的交叉情况。

相交边的编码——通过编码记录对应的cube,相交边的编号

二进制:00000010

十进制:2

Table[256]表示哪些边有交点

Table[2]=0x103=0000 0010 0000 0011

表示0,1,9号边上有交点

为了避免每次转化成二进制进行解码,可以直接记录与哪些边有交点,之后直接查表即可。

Table2[256][16]

Table2[2]=(0, 1, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

因为每个正方体中最多有1个或4个三角形,所以线性插值就足够了。

2,确定等值面与体元边界的交点

找到含有等值面的体元之后,接下来就是确定等值面与体元边界的交点,体元间的数值都是呈线性变化,求交点时一般采用的是线性插值,如 Case0 中等值面的两个端点 一个在外为( 标记0) ,一个在内 ( 标记为1 ) 则交点为0.5;

3,求等值面的法向量

以上步骤 1,2,3 为实现 MC 算法步骤流程,但利用 VTK ,不需要这么繁琐,主要算法步骤都已经封装到 vtkMarchingCube 类中,使用 vtkMarchingCube 时,需要设置三个参数:

  • SetValue(int i,double value) 设置第i 个等值面的值b,(提醒一下,医学图像中的灰度值范围不是 0-256 而是0-65326,但大部分取值范围都在0-1000)。
  • SetNumberofContours(int number),设置等值面的个数
  • ComputerNormalsOn() 设置计算等值面的法向量,提高渲染质量;

dab9fdfd6e307b681d5c9054c536065e.png

上面这张图显示的就是 vtk 呈像的基本流程,下面是仿照官网写的用面绘制来对图像重建的代码部分:

#include<vtkRenderWindow.h>
#include<vtkRenderWindowInteractor.h>
#include<vtkDICOMImageReader.h>
#include<vtkMarchingCubes.h>
#include<vtkPolyDataMapper.h>
#include<vtkStripper.h>
#include<vtkActor.h>
#include<vtkProperty.h>
#include<vtkCamera.h>
#include<vtkOutlineFilter.h>
#include<vtkOBJExporter.h>
#include<vtkRenderer.h>
#include<vtkMetaImageReader.h>
#include<vtkInteractorStyleTrackballCamera.h>#include<iostream>
#include<string.h>
//需要进行初始化,否则会报错
#include <vtkAutoInit.h> 
#include<vtkRenderingVolumeOpenGL2ObjectFactory.h>
#include<vtkRenderingOpenGL2ObjectFactory.h>using namespace std;
int main()
{///Marching Cube; vtkObjectFactory::RegisterFactory(vtkRenderingOpenGL2ObjectFactory::New());vtkObjectFactory::RegisterFactory(vtkRenderingVolumeOpenGL2ObjectFactory::New());vtkSmartPointer<vtkRenderer> ren = vtkSmartPointer<vtkRenderer>::New();vtkSmartPointer<vtkRenderWindow> renWin = vtkSmartPointer<vtkRenderWindow>::New();//WINDOW;renWin->AddRenderer(ren);vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New();//wininteratcor;iren->SetRenderWindow(renWin);vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();reader->SetDirectoryName("E:/DIcom_Data/DICOM");reader->SetDataByteOrderToLittleEndian();reader->Update();/*vtkDICOMImageReader *reader = vtkDICOMImageReader::New();reader->SetDirectoryName("E:/Coding Pra/VTK/VTK_Examples_StandardFormats_Input_DicomTestImages/DICOM");reader->SetDataByteOrderToLittleEndian();reader->Update();*/cout << "读取数据完毕" << endl;cout << "The width is" << reader->GetWidth() << endl;cout << "The height is" << reader->GetHeight() << endl;cout << "The depth is" << reader->GetPixelSpacing() << endl;cout << "The Output port is" << reader->GetOutputPort() << endl;vtkSmartPointer<vtkMarchingCubes> marchingcube = vtkSmartPointer<vtkMarchingCubes>::New();marchingcube->SetInputConnection(reader->GetOutputPort());//获得读取的数据的点集;marchingcube->SetValue(0, 200);//Setting the threshold;marchingcube->ComputeNormalsOn();//计算表面法向量;vtkSmartPointer<vtkStripper> Stripper = vtkSmartPointer<vtkStripper>::New();Stripper->SetInputConnection(marchingcube->GetOutputPort());//获取三角片vtkSmartPointer<vtkPolyDataMapper> Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();//将三角片映射为几何数据;Mapper->SetInputConnection(Stripper->GetOutputPort());Mapper->ScalarVisibilityOff();//vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();//Created a actor;actor->SetMapper(Mapper);//获得皮肤几何数据actor->GetProperty()->SetDiffuseColor(1, .49, .25);//设置皮肤颜色;actor->GetProperty()->SetSpecular(0.3);//反射率;actor->GetProperty()->SetOpacity(1.0);//透明度;actor->GetProperty()->SetSpecularPower(20);//反射光强度;actor->GetProperty()->SetColor(1, 0, 0);//设置角的颜色;actor->GetProperty()->SetRepresentationToWireframe();//线框;//vtkSmartPointer<vtkCamera> camera = vtkSmartPointer<vtkCamera>::New();//Setting the Camera;//camera->SetViewUp(0, 0, -1);//设置相机向上方向;//camera->SetPosition(0, 1, 0);//位置:世界坐标系,相机位置;//camera->SetFocalPoint(0, 0, 0);//焦点,世界坐标系,控制相机方向;//camera->ComputeViewPlaneNormal();//重置视平面方向,基于当前的位置和焦点;vtkSmartPointer<vtkOutlineFilter> outfilterline = vtkSmartPointer<vtkOutlineFilter>::New();outfilterline->SetInputConnection(reader->GetOutputPort());vtkSmartPointer<vtkPolyDataMapper> outmapper = vtkSmartPointer<vtkPolyDataMapper>::New();outmapper->SetInputConnection(outfilterline->GetOutputPort());vtkSmartPointer<vtkActor> OutlineActor = vtkSmartPointer<vtkActor>::New();OutlineActor->SetMapper(outmapper);OutlineActor->GetProperty()->SetColor(0, 0, 0);//线框颜色ren->AddActor(actor);ren->AddActor(OutlineActor);//ren->SetActiveCamera(camera);//设置渲染器的相机;ren->ResetCamera();ren->ResetCameraClippingRange();//camera->Dolly(1.5);//使用Dolly()方法延伸着视平面法向移动相机;ren->SetBackground(1, 1, 1);//设置背景颜色;renWin->SetSize(1000, 600);vtkInteractorStyleTrackballCamera *style = vtkInteractorStyleTrackballCamera::New();iren->SetInteractorStyle(style);renWin->Render();iren->Initialize();iren->Start();vtkSmartPointer<vtkOBJExporter> porter = vtkSmartPointer<vtkOBJExporter>::New();porter->SetFilePrefix("E:/ceshi/aaa/regist_after/polywrite.obj");//重建图像输出porter->SetInput(renWin);porter->Write();return EXIT_SUCCESS;
}

33a9534cf4e61f81f4d5a1ff644aa7f5.png

上面就是 VTK 基于 Marching Cube算法实现的重建效果:

体绘制重建

体绘制时分为两部分:

1,定义 vtkVoluemRayCastMapper 对象

体绘制中最常用的方法 ;vtkVolumeRayCastMapper() 光线投影,体绘制时,首先定义一个Mapper 然后接受两个输入:

  • SetInput(vtkImageDate *) 用于设置输入图像数据;
  • SetVolumeRayCastFunction(vtkVolumeRayCastFunction *) 用于设置光线投影函数类型;
2,利用 vtkVolumeProperty 定义体绘制属性;
  • SetScalarOpacity() 设置灰度不透明函数;
  • SetColor() 颜色传输函数;
3, 定义 vtkVolume 对象接收 Mapper对象和 Property 对象
  • SetMapper()接受 Mapper 对象;
  • SetProperty() 接受 Property 对象;

vtk 中体绘制 核心就是改变 Mapper 和 vtkVolumeRayCastFunction() ,上面中vtkColumeRayCastMapper 只是 VolumeMapper 其中的一种,且投影函数类 vtkVolumeRayCastFunction 一共有三个子类:

  • vtkVolumeRayCastCompositeFunction
  • vtkVolumeRayCasMIPFunction、
  • vtkVolumeRayCastIsosurfaceFunction
  • 因此,其细分的话vtk中的体绘制也不止一种

而下面这个是最常用到的(`vtkVolumeRayCastMapper + vtkVolumeRayCastCompositeFunction

//体绘制#include<vtkRenderWindowInteractor.h>
#include<vtkDICOMImageReader.h>
#include<vtkCamera.h>
#include<vtkActor.h>
#include<vtkRenderer.h>
#include<vtkVolumeProperty.h>
#include<vtkProperty.h>
#include<vtkPolyDataNormals.h>
#include<vtkImageShiftScale.h>
#include "vtkVolumeRayCastMapper.h"
#include<vtkPiecewiseFunction.h>
#include<vtkColorTransferFunction.h>
#include<vtkVolumeRayCastCompositeFunction.h>
#include<vtkRenderWindow.h>
#include<vtkImageCast.h>
#include<vtkVolumeRayCastCompositeFunction.h>
#include<vtkOBJExporter.h>
#include<vtkOutlineFilter.h>
#include<vtkPolyDataMapper.h>#include<vtkInteractorStyleTrackballCamera.h>
#include<vtkRenderingVolumeOpenGL2ObjectFactory.h>
#include<vtkRenderingOpenGL2ObjectFactory.h>
#include<vtkMetaImageReader.h>#include<vtkLODProp3D.h>//体绘制加速//Gpu光照映射
#include<vtkGPUVolumeRayCastMapper.h>#include<iostream>int main()
{vtkObjectFactory::RegisterFactory(vtkRenderingOpenGL2ObjectFactory::New());vtkObjectFactory::RegisterFactory(vtkRenderingVolumeOpenGL2ObjectFactory::New());//定义绘制器;vtkRenderer *aRenderer = vtkRenderer::New();//指向指针;vtkSmartPointer<vtkRenderWindow> renWin = vtkSmartPointer<vtkRenderWindow>::New();renWin->AddRenderer(aRenderer);vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();iren->SetRenderWindow(renWin);//读取数据;/*vtkDICOMImageReader *reader = vtkDICOMImageReader::New();reader->SetDirectoryName("E:/Coding Pra/VTK/VTK_Examples_StandardFormats_Input_DicomTestImages/DICOM");reader->SetDataByteOrderToLittleEndian();*/vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();reader->SetDirectoryName("E:/DIcom_Data/DICOM");reader->SetDataByteOrderToLittleEndian();//图像数据预处理,类型转换:通过 vtkimageCast 将不同类型数据集转化为 vtk 可以处理的数据集;vtkImageCast *cast_file = vtkImageCast::New();cast_file->SetInputConnection(reader->GetOutputPort());cast_file->SetOutputScalarTypeToUnsignedShort();cast_file->Update();//透明度映射函数定义;vtkPiecewiseFunction *opacityTransform = vtkPiecewiseFunction::New();opacityTransform->AddPoint(0, 0.0);opacityTransform->AddPoint(20, 0.0);opacityTransform->AddPoint(200, 1.0);opacityTransform->AddPoint(300, 1.0);//颜色映射函数定义,梯度上升的vtkColorTransferFunction *colorTransformFunction = vtkColorTransferFunction::New();colorTransformFunction->AddRGBPoint(0.0, 0.0, 0.0, 0.0);colorTransformFunction->AddRGBPoint(64.0, 0.0, 0.0, 0.0);colorTransformFunction->AddRGBPoint(128.0, 1.0, 0.0, 0.0);colorTransformFunction->AddRGBPoint(192.0, 1.0, 0.0, 0.0);colorTransformFunction->AddRGBPoint(255.0, 1.0, 0.0, 0.0);vtkPiecewiseFunction *gradientTransform = vtkPiecewiseFunction::New();gradientTransform->AddPoint(0, 0.0);gradientTransform->AddPoint(20, 2.0);gradientTransform->AddPoint(200, 0.1);gradientTransform->AddPoint(300, 0.1);//体数据属性;vtkVolumeProperty *volumeProperty = vtkVolumeProperty::New();volumeProperty->SetColor(colorTransformFunction);volumeProperty->SetScalarOpacity(opacityTransform);volumeProperty->SetGradientOpacity(gradientTransform);volumeProperty->ShadeOn();//应用volumeProperty->SetInterpolationTypeToLinear();//直线间样条插值;volumeProperty->SetAmbient(0.4);//环境光系数;volumeProperty->SetDiffuse(0.6);//漫反射;volumeProperty->SetSpecular(0.2);volumeProperty->SetSpecularPower(10);//高光强度;计算光照效应;利用 vtkBolumeRayCaseMapper进行计算;//vtkVolumeRayCastMapper *volunemapper = vtkVolumeRayCastMapper::New();//vtkVolumeRayCastCompositeFunction *compositeFunction = vtkVolumeRayCastCompositeFunction::New();//光纤映射类型定义:vtkSmartPointer<vtkVolumeRayCastCompositeFunction> compositecast =vtkSmartPointer<vtkVolumeRayCastCompositeFunction>::New();//Mapper定义,vtkSmartPointer<vtkVolumeRayCastMapper> hiresMapper = vtkSmartPointer<vtkVolumeRayCastMapper>::New();hiresMapper->SetInputData(cast_file->GetOutput());hiresMapper->SetVolumeRayCastFunction(compositecast);vtkSmartPointer<vtkLODProp3D> prop = vtkSmartPointer<vtkLODProp3D>::New();prop->AddLOD(hiresMapper,volumeProperty,0.0);////volunemapper->SetVolumeRayCastFunction(compositeFunction);//载入体绘制方法;//volunemapper->SetInputConnection(cast_file->GetOutputPort());//vtkFixedPointVolumeRayCastMapper *fixedPointVolumeMapper = vtkFixedPointVolumeRayCastMapper::New()//fixedPointVolumeMapper->SetInput()vtkVolume *volume = vtkVolume::New();volume->SetMapper(hiresMapper);volume->SetProperty(volumeProperty);//设置体属性;double volumeView[4] = { 0,0,0.5,1 };vtkOutlineFilter *outlineData = vtkOutlineFilter::New();//线框;outlineData->SetInputConnection(reader->GetOutputPort());vtkPolyDataMapper *mapOutline = vtkPolyDataMapper::New();mapOutline->SetInputConnection(outlineData->GetOutputPort());vtkActor *outline = vtkActor::New();outline->SetMapper(mapOutline);outline->GetProperty()->SetColor(0, 0, 0);//背景纯黑色;aRenderer->AddVolume(volume);aRenderer->AddActor(outline);aRenderer->SetBackground(1, 1, 1);aRenderer->ResetCamera();//重设相机的剪切范围;aRenderer->ResetCameraClippingRange();renWin->SetSize(800, 800);renWin->SetWindowName("测试");vtkRenderWindowInteractor *iren2 = vtkRenderWindowInteractor::New();iren2->SetRenderWindow(renWin);//设置相机跟踪模式vtkInteractorStyleTrackballCamera *style = vtkInteractorStyleTrackballCamera::New();iren2->SetInteractorStyle(style);renWin->Render();iren2->Initialize();iren2->Start();vtkOBJExporter *porter = vtkOBJExporter::New();porter->SetFilePrefix("E:/ceshi/aaa/regist_after/esho.obj");porter->SetInput(renWin);porter->Write();porter->Update();return EXIT_SUCCESS;}

0c8404965dbab9c8abcab0cd04762657.png

上面是体绘制的结果,相对来说体绘制需要计算资源更大些, vtk 在这方面有所考虑,提供了vtKGPUVolumeRayCastMapper GUP 加速的光线投射算法。

这篇关于Marching Cubes算法再回顾的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/587517

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

dp算法练习题【8】

不同二叉搜索树 96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3输出:5 示例 2: 输入:n = 1输出:1 class Solution {public int numTrees(int n) {int[] dp = new int

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

最大公因数:欧几里得算法

简述         求两个数字 m和n 的最大公因数,假设r是m%n的余数,只要n不等于0,就一直执行 m=n,n=r 举例 以18和12为例 m n r18 % 12 = 612 % 6 = 06 0所以最大公因数为:6 代码实现 #include<iostream>using namespace std;/