VTK 实现 曲面重建(CPR)

原理

CPR±+Curved+Planar+Reformation
下载: https://www.jianguoyun.com/p/DXY5HOoQgYiuCRia-PED (访问密码 : r0ga0d)

曲面重建CPR步骤

  1. 输入点拟合样条曲线(利用vtkSplineFilter
  2. 计算样条曲线各点的法向量(弗莱纳公式)
  3. 根据点和法向量截取图片(vtkProbeFilter)
  4. 每张图片拼接起来(vtkImageAppend)

VTK实现

常用功能vtk肯定提供现成办法

  • 期刊 地址
    https://www.vtkjournal.org/browse/publication/838
  • 期刊 Github 地址(只包含了计算曲线法相和样条曲线切割的实现和测试)
    https://github.com/midas-journal/midas-journal-838
  • 作者项目 Github 地址(还带了一堆其他的算法和测试以及paraview的插件)
    https://github.com/djelouze/vtkKinship

使用

作者案例写的很完整。

  • 方法一:增加远程模块: Module_SplineDrivenImageSlicer:BOOL(vtk9要自己改一下cmakelists)

作者写的我看起来比较吃力,改了下,剔除一些没用的,并添加注释。下边2和3是自己完整改完之后的

  • 方法二:把FrenetSerretFrameSplineDrivenImageSlicer直接添加到自己工程

两个都试了下,第二个更好,自己可以随时改:

1. 输入点拟合样条曲线(利用vtkSplineFilter

捕获样条曲线直接用 vtkContourWidget即可,他的rep可以得到世界坐标。结合背景ImageReslicers的4*4变换矩阵可以得到点的空间位置。

利用vtkContourWidget捕获点

vtkContourWidget *wid = vtkContourWidget::New();
wid->SetInteractor(m_renderWindows[2]->GetInteractor());
wid->CreateDefaultRepresentation();
wid->On();
m_scrollConnection->Connect(wid, vtkCommand::EndInteractionEvent, this, SLOT(SlotContourEndInteractionEvent(vtkObject *, unsigned long, void *, void *)), nullptr, 0.0, Qt::UniqueConnection);

计算点的空间位置

auto widget = dynamic_cast<vtkContourWidget *>(t_obj);
auto rep = dynamic_cast<vtkOrientedGlyphContourRepresentation *>(widget->GetContourRepresentation());
if(rep) {vtkMatrix4x4 *sourceMatrix = m_resliceWidget->getImageReslicers()[2]->GetResliceAxes();qint32 n = rep->GetNumberOfNodes();for (qint32 i = 0; i < n; ++i) {double p[3];rep->GetNthNodeWorldPosition(i, p);vtkNew<vtkTransform> transform1;transform1->SetMatrix(sourceMatrix);transform1->Translate(p[0], p[1], 0);qDebug() << i<< transform1->GetMatrix()->GetElement(0, 3)<< transform1->GetMatrix()->GetElement(1, 3)<< transform1->GetMatrix()->GetElement(2, 3);}
}

拟合曲线

vtkNew<vtkSplineFilter> spline_filter;
spline_filter->SetSubdivideToLength();
spline_filter->SetLength(0.2);
spline_filter->SetInputData(poly_data);
spline_filter->Update();

2. 计算样条曲线各点的法向量(弗莱纳公式)

根据弗莱纳公式计算曲线向量

#include"vtkPoints.h"
#include"vtkPolyDataAlgorithm.h"
#include"vtkPolyData.h"
#include"vtkCellArray.h"/*** @brief The CBCTFrenetSerretFrame class* 弗莱纳公式(Frenet–Serret formulas)*/
class CBCTFrenetSerretFrame : public vtkPolyDataAlgorithm {public:vtkTypeMacro(CBCTFrenetSerretFrame, vtkPolyDataAlgorithm)static CBCTFrenetSerretFrame *New();vtkSetMacro(view_up_, double)vtkGetMacro(view_up_, double)static void RotateVector( double *vector, const double *axis, double angle );protected:CBCTFrenetSerretFrame();~CBCTFrenetSerretFrame() override;virtual int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *)override;virtual int FillInputPortInformation(int port, vtkInformation *info)override;// 计算导数void ComputeTangentVectors(vtkIdType pointIdNext,vtkIdType pointIdLast,double *tangent);// 计算二阶导数void ComputeNormalVectors(double *tgNext,double *tgLast,double *normal );// 由切线定义的平面上最后法线的投影void ComputeConsistentNormalVectors( double *tangent,double *lastNormal,double *normal);private:CBCTFrenetSerretFrame(const CBCTFrenetSerretFrame &);void operator=(const CBCTFrenetSerretFrame &);double view_up_;int consistent_normals_;
};

#include "vtkDoubleArray.h"
#include "vtkPointData.h"
#include "vtkMath.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"vtkStandardNewMacro(CBCTFrenetSerretFrame);CBCTFrenetSerretFrame::CBCTFrenetSerretFrame( ) {this->consistent_normals_ = 1;this->view_up_ = 0;
}CBCTFrenetSerretFrame::~CBCTFrenetSerretFrame( ) {}int CBCTFrenetSerretFrame::FillInputPortInformation(int port, vtkInformation *info) {if( port == 0 ) {info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");}return 1;
}int CBCTFrenetSerretFrame::RequestData(vtkInformation *vtkNotUsed(request),vtkInformationVector **inputVector,vtkInformationVector *outputVector) {vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);vtkInformation *outInfo = outputVector->GetInformationObject(0);vtkPolyData *input = vtkPolyData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));vtkPolyData *output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));output->DeepCopy( input );vtkDoubleArray *tangents = vtkDoubleArray::New( );tangents->SetNumberOfComponents( 3 );tangents->SetNumberOfTuples( input->GetNumberOfPoints( ) );tangents->SetName("FSTangents");vtkDoubleArray *normals = vtkDoubleArray::New( );normals->SetNumberOfComponents( 3 );normals->SetNumberOfTuples( input->GetNumberOfPoints( ) );normals->SetName("FSNormals");vtkCellArray *lines = output->GetLines( );lines->InitTraversal();vtkIdType nbPoints;vtkIdType *points;int cellIdx;for( cellIdx = 0; cellIdx < lines->GetNumberOfCells( ); cellIdx++ ) {lines->GetNextCell( nbPoints, points);for( int i = 0 ; i < nbPoints; i++) {double tangent[3];if( i == 0 ) {this->ComputeTangentVectors( points[0], points[1], tangent );} else if( i == nbPoints - 1 ) {this->ComputeTangentVectors( points[nbPoints - 2], points[nbPoints - 1], tangent );} else {this->ComputeTangentVectors( points[i - 1], points[i + 1], tangent );}vtkMath::Normalize( tangent );tangents->SetTuple(points[i], tangent);}for( int i = 0 ; i < nbPoints; i++) {if( !this->consistent_normals_ || i == 0) {double tangentLast[3], tangentNext[3], normal[3];if( i == 0 ) {tangents->GetTuple( points[i], tangentLast);} else {tangents->GetTuple( points[i - 1], tangentLast);}if( i == nbPoints - 1 ) {tangents->GetTuple( points[i], tangentNext);} else {tangents->GetTuple( points[i + 1], tangentNext);}this->ComputeNormalVectors( tangentLast, tangentNext, normal );if( this->consistent_normals_ ) {this->RotateVector( normal, tangentLast, this->view_up_ );}vtkMath::Normalize( normal );normals->SetTuple(points[i], normal);}if( this->consistent_normals_ && i != 0) {double tangent[3], lastNormal[3], normal[3];normals->GetTuple(points[i - 1], lastNormal);tangents->GetTuple(points[i], tangent);this->ComputeConsistentNormalVectors( tangent,lastNormal,normal );vtkMath::Normalize( normal );normals->SetTuple(points[i], normal);}}}output->GetPointData( )->AddArray( normals );output->GetPointData( )->AddArray( tangents );normals->Delete( );tangents->Delete( );return 1;
}void CBCTFrenetSerretFrame::ComputeTangentVectors(vtkIdType pointIdNext, vtkIdType pointIdLast, double *tangent ) {vtkPolyData *input = static_cast<vtkPolyData *>(this->GetInput( 0 ));double ptNext[3];double ptLast[3];input->GetPoint( pointIdNext, ptNext);input->GetPoint( pointIdLast, ptLast);int comp;for( comp = 0; comp < 3; comp++ ) {tangent[comp] = ( ptLast[comp] - ptNext[comp] ) / 2;}
}void CBCTFrenetSerretFrame::ComputeConsistentNormalVectors( double *tangent,double *normalLast,double *normal ) {double temp[3];vtkMath::Cross( normalLast, tangent, temp);vtkMath::Cross( tangent, temp, normal );
}void CBCTFrenetSerretFrame::ComputeNormalVectors( double *tgNext,double *tgLast,double *normal ) {int comp;for( comp = 0; comp < 3; comp++ ) {normal[comp] = ( tgNext[comp] - tgLast[comp] );}if( vtkMath::Norm(normal) == 0 ) {double unit[3] = {1, 0, 0};vtkMath::Cross( tgLast, unit, normal );}
}void CBCTFrenetSerretFrame::RotateVector( double *vector, const double *axis, double angle ) {double UdotN = vtkMath::Dot(vector, axis);double NvectU[3];vtkMath::Cross(axis, vector, NvectU);for( int comp = 0; comp < 3 ; comp++) {vector[comp] =  cos( angle ) * vector[comp]+ (1 - cos( angle )) * UdotN * axis[comp]+ sin( angle ) * NvectU[comp];}
}

3. 根据点和法向量截取图片(vtkProbeFilter)

沿着样条曲线切割图片

class CBCTFrenetSerretFrame;
class vtkImageReslice;/*** @brief The CBCTSplineDrivenImageSlicer class* 沿着样条曲线切割图片*/
class CBCTSplineDrivenImageSlicer : public vtkImageAlgorithm {public:vtkTypeMacro(CBCTSplineDrivenImageSlicer, vtkImageAlgorithm)static CBCTSplineDrivenImageSlicer *New();void SetPathConnection(int id, vtkAlgorithmOutput *algOutput);void SetPathConnection(vtkAlgorithmOutput *algOutput);vtkAlgorithmOutput *GetPathConnection( );vtkSetMacro(offset_point_, vtkIdType)protected:CBCTSplineDrivenImageSlicer();~CBCTSplineDrivenImageSlicer()override;virtual int RequestData(vtkInformation *, vtkInformationVector **,vtkInformationVector *)override;virtual int FillInputPortInformation(int port, vtkInformation *info)override;virtual int FillOutputPortInformation( int, vtkInformation *)override;virtual int RequestInformation(vtkInformation *, vtkInformationVector **,vtkInformationVector *)override;private:CBCTSplineDrivenImageSlicer(const CBCTSplineDrivenImageSlicer &);void operator=(const CBCTSplineDrivenImageSlicer &);CBCTFrenetSerretFrame *local_frenetFrames_;vtkImageReslice *reslicer_;int slice_extent_[2]; // 输出image的xy像素数量double slice_spacing_[2]; // 输出image的xy间隔double slice_thickness_; // 输出image的z轴间隔double incidence_;// 初始法向量的旋转vtkIdType offset_point_;vtkIdType offset_line_;vtkIdType probe_input_;
};
#include"vtkPoints.h"
#include"vtkPolyData.h"
#include"vtkCellArray.h"
#include "vtkImageReslice.h"#include "vtkFrenetSerretFrame.h"
#include "vtkPlaneSource.h"
#include "vtkImageData.h"
#include "vtkProbeFilter.h"
#include "vtkMatrix4x4.h"
#include "vtkImageAppend.h"
#include "vtkDoubleArray.h"
#include "vtkPointData.h"
#include "vtkMath.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkSmartPointer.h"
#include "vtkStreamingDemandDrivenPipeline.h"vtkStandardNewMacro(CBCTSplineDrivenImageSlicer);CBCTSplineDrivenImageSlicer::CBCTSplineDrivenImageSlicer( ) {this->local_frenetFrames_ = CBCTFrenetSerretFrame::New( );this->reslicer_ = vtkImageReslice::New();this->slice_extent_[0] = 256;this->slice_extent_[1] = 256;this->slice_spacing_[0] = 0.2;this->slice_spacing_[1] = 0.2;this->slice_thickness_ = 0.2;this->offset_point_ = 0;this->offset_line_ = 0;this->incidence_ = 0;this->probe_input_ = 0;this->SetNumberOfInputPorts( 2 );this->SetNumberOfOutputPorts( 2 );this->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS,vtkDataSetAttributes::SCALARS);
}CBCTSplineDrivenImageSlicer::~CBCTSplineDrivenImageSlicer( ) {this->local_frenetFrames_->Delete( );this->reslicer_->Delete( );
}void CBCTSplineDrivenImageSlicer::SetPathConnection(int id, vtkAlgorithmOutput *algOutput) {if (id < 0) {vtkErrorMacro("Bad index " << id << " for source.");return;}int numConnections = this->GetNumberOfInputConnections(1);if (id < numConnections) {this->SetNthInputConnection(1, id, algOutput);} else if (id == numConnections && algOutput) {this->AddInputConnection(1, algOutput);} else if (algOutput) {vtkWarningMacro("The source id provided is larger than the maximum ""source id, using " << numConnections << " instead.");this->AddInputConnection(1, algOutput);}
}void CBCTSplineDrivenImageSlicer::SetPathConnection(vtkAlgorithmOutput *algOutput) {this->SetPathConnection(0, algOutput);
}vtkAlgorithmOutput *CBCTSplineDrivenImageSlicer::GetPathConnection() {return( this->GetInputConnection( 1, 0 ) );
}int CBCTSplineDrivenImageSlicer::FillInputPortInformation(int port, vtkInformation *info) {if( port == 0 ) {info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");} else {info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");}return 1;
}int CBCTSplineDrivenImageSlicer::FillOutputPortInformation(int port, vtkInformation *info) {if (port == 0) {info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkImageData");}if (port == 1) {info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkPolyData");}return 1;
}int CBCTSplineDrivenImageSlicer::RequestInformation (vtkInformation *vtkNotUsed(request),vtkInformationVector **inputVector,vtkInformationVector *outputVector) {vtkInformation *outInfo = outputVector->GetInformationObject(0);int extent[6] = {0, this->slice_extent_[0] - 1,0, this->slice_extent_[1] - 1,0, 1};double spacing[3] = {this->slice_spacing_[0], this->slice_spacing_[1], this->slice_thickness_};outInfo->Set(vtkDataObject::SPACING(), spacing, 3);outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent, 6);return 1;
}int CBCTSplineDrivenImageSlicer::RequestData(vtkInformation *vtkNotUsed(request),vtkInformationVector **inputVector,vtkInformationVector *outputVector) {// 获取信息对象vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);vtkInformation *pathInfo = inputVector[1]->GetInformationObject(0);vtkInformation *outImageInfo = outputVector->GetInformationObject(0);vtkInformation *outPlaneInfo = outputVector->GetInformationObject(1);// 获取输入和输出vtkImageData *input = vtkImageData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));vtkImageData *inputCopy = vtkImageData::New( );inputCopy->ShallowCopy( input );vtkPolyData *inputPath = vtkPolyData::SafeDownCast(pathInfo->Get(vtkDataObject::DATA_OBJECT()));vtkImageData *outputImage = vtkImageData::SafeDownCast(outImageInfo->Get(vtkDataObject::DATA_OBJECT()));vtkPolyData *outputPlane = vtkPolyData::SafeDownCast(outPlaneInfo->Get(vtkDataObject::DATA_OBJECT()));vtkSmartPointer<vtkPolyData> pathCopy = vtkSmartPointer<vtkPolyData>::New( );pathCopy->ShallowCopy( inputPath );// 计算路径的局部法线和切线this->local_frenetFrames_->SetInputData( pathCopy );this->local_frenetFrames_->Setview_up_(this->incidence_ );this->local_frenetFrames_->Update( );// 路径将包含点数据数组“切线”和“向量”vtkPolyData *path = static_cast<vtkPolyData *>(this->local_frenetFrames_->GetOutputDataObject( 0 ));// 计算单元格中使用了多少个点。如果循环,点可以使用多次// 不使用NumberOfPoints,因为我们只需要线条和点vtkCellArray *lines = path->GetLines( );lines->InitTraversal( );vtkIdType nbCellPoints;vtkIdType *points;vtkIdType cellId = -1;do {lines->GetNextCell( nbCellPoints, points);cellId++;} while( cellId != this->offset_line_ );vtkIdType ptId = this->offset_point_;if( ptId >= nbCellPoints ) {ptId = nbCellPoints - 1;}// 建立一个新的reslicer与图像输入作为输入。this->reslicer_->SetInputData( inputCopy );// 获取ptId点的Frenet-Serret图表:// - position (center)// - tangent T// - normal Ndouble center[3];path->GetPoints( )->GetPoint( ptId, center );vtkDoubleArray *pathTangents = static_cast<vtkDoubleArray *>(path->GetPointData( )->GetArray( "FSTangents" ));double tangent[3];pathTangents->GetTuple( ptId, tangent );vtkDoubleArray *pathNormals = static_cast<vtkDoubleArray *>(path->GetPointData( )->GetArray( "FSNormals" ));double normal[3];pathNormals->GetTuple( ptId, normal );// Frenet-Serret 图表由 T, N and B = T ^ Ndouble crossProduct[3];vtkMath::Cross( tangent, normal, crossProduct );// 构建平面输出,该输出将表示三维视图中的切片位置vtkSmartPointer<vtkPlaneSource> plane= vtkSmartPointer<vtkPlaneSource>::New( );double planeOrigin[3];double planePoint1[3];double planePoint2[3];for( int comp = 0; comp < 3; comp ++) {planeOrigin[comp] = center[comp] - normal[comp] * this->slice_extent_[1] * this->slice_spacing_[1] / 2.0- crossProduct[comp] * this->slice_extent_[0] * this->slice_spacing_[0] / 2.0;planePoint1[comp] = planeOrigin[comp] + crossProduct[comp] * this->slice_extent_[0] * this->slice_spacing_[0];planePoint2[comp] = planeOrigin[comp] + normal[comp] * this->slice_extent_[1] * this->slice_spacing_[1];}plane->SetOrigin(planeOrigin);plane->SetPoint1(planePoint1);plane->SetPoint2(planePoint2);plane->SetResolution(this->slice_extent_[0],this->slice_extent_[1]);plane->Update();if( this->probe_input_ == 1 ) {vtkSmartPointer<vtkProbeFilter> probe = vtkSmartPointer<vtkProbeFilter>::New( );probe->SetInputConnection( plane->GetOutputPort( ) );probe->SetSourceData( inputCopy );probe->Update( );outputPlane->DeepCopy(probe->GetOutputDataObject(0));} else {outputPlane->DeepCopy(plane->GetOutputDataObject(0));}// 构建转换矩阵vtkMatrix4x4 *resliceAxes = vtkMatrix4x4::New( );resliceAxes->Identity();double origin[4];// 仿照 vtkImageReslice:// - 1st column contains the resliced image x-axis// - 2nd column contains the resliced image y-axis// - 3rd column contains the normal of the resliced image plane// -> 1st column is normal to the path// -> 3nd column is tangent to the path// -> 2nd column is B = T^Nfor ( int comp = 0; comp < 3; comp++ ) {resliceAxes->SetElement(0, comp, crossProduct[comp]);resliceAxes->SetElement(1, comp, normal[comp]);resliceAxes->SetElement(2, comp, tangent[comp]);origin[comp] = center[comp] -normal[comp] * this->slice_extent_[1] * this->slice_spacing_[1] / 2.0 -crossProduct[comp] * this->slice_extent_[0] * this->slice_spacing_[0] / 2.0;}origin[3] = 1.0;double originXYZW[4];resliceAxes->MultiplyPoint(origin, originXYZW);resliceAxes->Transpose();double neworiginXYZW[4];resliceAxes->MultiplyPoint(originXYZW, neworiginXYZW);resliceAxes->SetElement(0, 3, neworiginXYZW[0]);resliceAxes->SetElement(1, 3, neworiginXYZW[1]);resliceAxes->SetElement(2, 3, neworiginXYZW[2]);this->reslicer_->SetResliceAxes( resliceAxes );this->reslicer_->SetInformationInput( input );this->reslicer_->SetInterpolationModeToCubic( );this->reslicer_->SetOutputDimensionality( 2 );this->reslicer_->SetOutputOrigin(0, 0, 0);this->reslicer_->SetOutputExtent(0, this->slice_extent_[0] - 1,0, this->slice_extent_[1] - 1,0, 1);this->reslicer_->SetOutputSpacing(this->slice_spacing_[0],this->slice_spacing_[1],this->slice_thickness_);this->reslicer_->Update();resliceAxes->Delete();outputImage->DeepCopy(this->reslicer_->GetOutputDataObject(0));outputImage->GetPointData()->GetScalars()->SetName("ReslicedImage");return 1;
}

4. 每张图片拼接起来(vtkImageAppend)

就是作者的案例,加了个Permute和Flip用来摆正

vtkNew<vtkImageAppend> append;
append->SetAppendAxis(2);
vtkNew<CBCTSplineDrivenImageSlicer> reslicer;
reslicer->SetInputData(img_reader->GetOutput());
reslicer->SetPathConnection(spline_filter->GetOutputPort());
long long nb_points = spline_filter->GetOutput()->GetNumberOfPoints();
for(int pt_id = 0; pt_id < nb_points; pt_id++) {reslicer->Setoffset_point_(pt_id);reslicer->Update();vtkNew<vtkImageData> tempSlice;tempSlice->DeepCopy(reslicer->GetOutput());append->AddInputData(tempSlice);
}
append->Update();
vtkNew<vtkImagePermute> permute_filter;
permute_filter->SetInputData(append->GetOutput());
permute_filter->SetFilteredAxes(2, 0, 1);
permute_filter->Update();
vtkNew<vtkImageFlip> flip_filter;
flip_filter->SetInputData(permute_filter->GetOutput());
flip_filter->SetFilteredAxes(1);
flip_filter->Update();

VTK 实现 曲面重建(CPR)相关推荐

  1. vtk相机_C#开发PACS医学影像三维重建(一)使用VTK重建3D影像

    VTK简介: VTK是一个开源的免费软件系统,主要用于三维计算机图形学.图像处理和可视化.Vtk是在面向对象原理的基础上设计和实现的,它的内核是用C++构建的. 因为使用C#语言开发,而VTK是C++ ...

  2. 医学影像后处理技术超详细收藏版(上)

    转自:医学影像后处理技术超详细收藏版(上) 导语: 医学图像后处理是通过综合运用计算机图像处理技术,医学知识,将由各种数字化成像技术所获得的人体信息按照一定的需要在计算机上表现出来,使之满足医疗需要的 ...

  3. 泌尿系统CT成像-常识

    https://wenku.baidu.com/view/28ac29c951e79b896902262f.html 泌尿系统疾病是多发病,泌尿系结石是泌尿系统常见病之一,泌尿系统位于腹膜后,与周围组 ...

  4. VTK修炼之道56:图形基本操作进阶_表面重建技术(三维点云曲面重建)

    1.点云重建 虽然Delaunay三角剖分算法可以实现网格曲面重建,但是其应用主要在二维剖分,在三维空间网格生成中遇到了问题.因为在三维点云曲面重建中,Delaunay条件不在满足,不仅基于最大最小角 ...

  5. VTK修炼之道49:图形基本操作进阶_网格平滑(点云的曲面重建技术)

    1.网格平滑 现代扫描技术的发展使得获取点云数据不再困难,通过曲线重建技术可以获取表面网格来表示各种复杂的实体.但是点云数据中往往存在噪声,这样得到的重建网格通常都需要进行平滑处理. 拉普拉斯平滑是一 ...

  6. CT三维重建及三维可视化资料:opengl+VTK+PCL点云库

    自己做过边缘轮廓三维重建方面的工作,今天看到一篇CT三维重建的介绍性质的文章挺感兴趣的,附录一下: CT三维重建主要有六种基本后处理方法 多层面重建(MPR) 最大密度投影(MIP) 表面阴影遮盖(S ...

  7. VTK CT重建(一) MPR 多层面重建 四视图

    目录 概述: MPR,全称是multi-planar reformation / reconstruction,是常用的医学图像后处理技术 优化后的视频: vtkImageReslice 参考资料 概 ...

  8. C#开发PACS医学影像三维重建(一):使用VTK重建3D影像

    VTK简介: VTK是一个开源的免费软件系统,主要用于三维计算机图形学.图像处理和可视化.Vtk是在面向对象原理的基础上设计和实现的,它的内核是用C++构建的. 因为使用C#语言开发,而VTK是C++ ...

  9. VTK 曲面构建+颜色映射

    Vtk,(visualization toolkit)是一个开源的免费软件系统,主要用于三维计算机图形学.图像处理和可视化. VTK编程中主要用到的几个对象 vtkRenderer ,vtkRende ...

最新文章

  1. Python Day26:多态、封装、内置函数:__str__、__del__、反射(反省)、动态导入模块...
  2. 利用prototxt文件绘制网络的结构图
  3. DRV8301的使用
  4. 两个奇技淫巧,将 Docker 镜像体积减小 99%
  5. 安装zookeeper时候,可以查看进程启动,但是状态显示报错:Error contacting service. It is probably not running
  6. 深度学习之基于卷积神经网络(VGG16)实现性别判别
  7. A+B问题(信息学奥赛一本通-T1006)
  8. 编译OpenJDK8:CreateJars.gmk/BuildJdk.gmk/Main.gmk/错误
  9. Java入门到精通全教程
  10. Win10重装系统后的lockdir解密失败
  11. u盘启动盘 联想服务器TS250 Win Server 2016 下 安装Linux(ubuntu-18.04.1-desktop-amd64)双系统 各种问题总结(2018-11-13)
  12. 0.1 番外篇: UML 类图
  13. 关于支付接口回调地址参数字段是“notify_url”,签名过后的特殊字符url编码以后再解码后出现错误(¬ , ¢, ¤, £)
  14. Spring包下Pageable的使用 2
  15. iOS逆向开发(2):获取APP的类声明 | class-dump | dumpdecrypted
  16. 第一次做鸭子的感受!
  17. Android Material Design 之 CardView卡片式布局
  18. 【设计模式】Java设计模式 - 享元模式
  19. java8 日期范围内 日/周/月/季度/年 的日期结果集
  20. ER图连接线上加个圆圈什么意思_没有声母的音节叫什么?零声母音节

热门文章

  1. WireShark抓包 图解探索网络请求过程(五层网络模型、三次握手、滑动窗口协议)
  2. Win7的任务计划怎么是后台运行的,怎么设置能让任务前台运行?
  3. think-cell高阶教程(2)——多图设置同比例数轴
  4. Kalman滤波器从原理到实现 github收藏
  5. padstack editor制作表贴焊盘
  6. 求root(N,k)
  7. 2021版1:100万基础地理信息数据更新数据(整理GDB批量合并)
  8. zencart免费模板下载
  9. Cubietruck-街机移植
  10. 谷歌搜索区分优质内容与不良内容SEO优化