有位专家大佬讲的特别好,图画的也非常好,多有借鉴:https://blog.csdn.net/shenziheng1/category_9277349.html
若有错误和不同的见解,还望多多指点和交流。
1.搭建编译环境,此略;
2.下载VTK源码、data(各种格式的图片数据,在source源码同级目录进行解压)、对应版本的帮助文档包(vtkDocHtml-*.tar.gz),VTK官网下载地址:https://vtk.org/download/;
3.学会查看帮助文档,首先打开annotated.html网页,可跳转到各其他帮助页面Main Page、Related Pages、Modules、Namespaces、Classes、Files;
4.进入学习,推荐《VTK图形图像开发进阶》,先对总体只是架构有基本的认识。结合源码example代码跑出效果,对每个组件的使用有更深入的认识;
VTK数据流程:
若整个VTK是一场表演,那么下图就是各角色之间的对应关系(来源与借鉴资料);
vtkCylinderSource/vtkCubeSource/vtkSphereSource:
= 实例创建
vtkAlgorithmOutput* vtkAlgorithm::GetOutputPort()
Eg1.vtkCylinderSource,VTK-9.0.1/Examples/Rendering/Cxx/Cylinder.cpp,可以尝试做参数修改,观察图形的改变。
vtkCylinderSource* cylinder = vtkCylinderSource::New();
cylinder->SetResolution(3);//三边
cylinder->SetCapping(0);//0不封闭,1封闭
vtkCoordinate:
= 坐标系统
├── Model 笛卡尔/直角/斜角坐标系(二维、三维),是actor的实体坐标系
├── World 三维空间坐标系,light、camera、
├── View 视觉二维坐标系,view(x,y)面,camera.(z)
└── Display 视图坐标系,坐标取值来自与屏幕像素值,
reference source:
二维坐标到三维坐标之间的转换:https://blog.csdn.net/weixin_39827728/article/details/111105515
vtkPoints/vtkCellArray:
= Points定义数据几何结构;Cell Array定义数据拓扑结构;
vtkDataReader-Write/vtkDicomImageReader/vtkMetaImageReader/vtkBMPReader:
= 图像数据的处理;
Eg1:基于vtkMetaImageReader( read binary UNC meta image data)读取*.mhd文件 .
#include
#include
#include
#include
#include
#include
#include
#include
#include
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
main(int argc, char * argv [])
{
vtkSmartPointer Metareader = vtkSmartPointer::New();
Metareader->SetFileName("../data/brain.mhd");
Metareader->Update();
vtkSmartPointer reader = vtkSmartPointer::New();
reader->SetFileName("../data/VTK-logo.bmp");
reader->Update();
//vtkSmartPointer mapper = vtkSmartPointer::New();
//mapper->SetInputConnection(reader->GetOutput());
vtkSmartPointer actor = vtkSmartPointer::New();
actor->SetInputData(reader->GetOutput());
vtkSmartPointer render = vtkSmartPointer::New();
render->AddActor(actor);
vtkSmartPointer renderwin = vtkSmartPointer::New();
renderwin->AddRenderer(render);
vtkSmartPointer renderwinInter = vtkSmartPointer::New();
renderwinInter->SetRenderWindow(renderwin);
vtkSmartPointer InteStyle = vtkSmartPointer::New();
InteStyle->SetCurrentRenderer(render);
renderwinInter->SetInteractorStyle(InteStyle);
renderwinInter->Initialize();
renderwinInter->Start();
}
Eg2:基于vtkDicomImageReader进行数据读取;
vtkMarchingCubes/vtkContourFilter:
= Filter,不同的标量值代表的是人体的不同部分,因而可以分别提取出人的皮肤或骨头。SetValue(0, 1)设置第一条是值为1的等值线/GenerateValues(5.0, 0.0, 1.2)从0.0-1.2之间抽取5个抽取值。
vtkPolyDataMapper/vtkPolyDataMapper2D:
= 数据获取,SetInputConnection()/SetInputData()
vtkActor/vtkActor2D:
= 实例对象,输入数据需要为unsigned char类型;
vtkRender(vtkLight/vtkCamera):
= 用于对Actor/window渲染;G/SetActiveCamera()设置视窗相机/;SetViewport((xmin,ymin,xmax,ymax) 0~1设置view坐标系中各视窗的位置;
reader->GetOutput()->GetExtent(extent);//坐标轴尺寸[XMin,XMax,YMin,YMax,ZMin,ZMax]
reader->GetOutput()->GetSpacing(spacing);//体素大小,如一个立方体的长宽高
reader->GetOutput()->GetOrigin(origin);//3D空间中第一像素位置
center[i] = origin[i] + spacing[i] * 0.5 * (extent[2*i] + extent[2*i+1]);//计算图形中心
Eg1.renderer1->SetViewport(0.0,0.0,0.5,0.5)左下;renderer2->SetViewport(0.5,0.0,1.0,0.5)右下;renderer3->SetViewport(0.0,0.5,0.5,1.0)左上;renderer4->SetViewport(0.5,0.5,1.0,1.0)右上;
cylinderren1->AddActor(cylinderActor);
cylinderren1->SetViewport(0.0,0.0,0.5,0.5);
Eg2.vtkCamera,SetPosition(*,*,*)/SetFocusPoint()/SetClippingRange()/ComputeViewPlaneNormal()/SetViewUp()等
camera->SetViewUp(0,0,-1);//摄像机的视图向上方向
camera->SetPosition(0.1,0.1,0.1);//相机在世界坐标系中的位置
camera->SetFocalPoint(0,0,1);//摄像机在世界坐标系中的焦距
camera->ComputeViewPlaneNormal();//计算视图平面法线
Eg3.vtkLight,SetColor()/SetPosition()/SetFocalPoint()/SetIntensity()/SetSwitch()/SwitchOn()/SwitchOff()等
vtkCamera* Camera = vtkCamera::New();
Camera->SetClippingRange(50,50);
Camera->SetPosition(-6,0,0);
Camera->SetFocalPoint(0.0573,-0.2134, -0.0523);
Camera->ComputeViewPlaneNormal();
Camera->SetViewUp(0, 1, 0);
cylinderren1->SetActiveCamera(Camera);
vtkLight* light = vtkLight::New();
light->SetColor(0,1,0);
light->SetPosition(6,0,0);
light->SetFocalPoint(cylinderren1->GetActiveCamera()->GetFocalPoint());
light->SetIntensity(0.5);//0~1
cylinderren1->AddLight(light);
vtkLight* light1 = vtkLight::New();
light1->SetColor(1,0,0);
light1->SetPosition(-6,0,0);
light1->SetFocalPoint(cylinderren1->GetActiveCamera()->GetFocalPoint());
light1->SwitchOff();
cylinderren1->AddLight(light1);
vtkRenderWindow:
= 创建渲染窗口;
vtkImageViewer2:
= 图片显示与交互、切片选择、切片方向,汇集actor/render/window/interactor/等功能;
Eg1:基于vtkImageViewer2 实现的水平试图切片
#include
#include
#include
#include
#include
#include
#include
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
main(int argc, char * argv [])
{
vtkSmartPointer reader= vtkSmartPointer::New();
reader->SetFileName("../data/HeadMRVolume.mhd");
reader->Update();
vtkSmartPointer View= vtkSmartPointer::New();
View->SetInputConnection(reader->GetOutputPort());
vtkSmartPointer RenWinInter=vtkSmartPointer::New();
vtkSmartPointer Style= vtkSmartPointer::New();
//设置基本属性
View->SetSize(640,480);
View->SetColorLevel(400);
View->SetColorWindow(2000);
//默认选择第50张切片
View->SetSlice(40);
//View->SetSliceOrientationToXZ();
//View->SetSliceOrientationToXY();
View->SetSliceOrientationToYZ();
View->SetupInteractor(RenWinInter);
View->GetRenderer()->SetBackground(1,1,1);
View->GetRenderer()->ResetCamera();
View->Render();
RenWinInter->Initialize();
RenWinInter->Start();
}
Eg2:基于vtkImageViewer2实现有异常的三视图显示
#include
#include
#include
#include
#include
#include
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
main(int argc, char * argv [])
{
vtkSmartPointer reader= vtkSmartPointer::New();
reader->SetFileName("../data/brain.mhd");
reader->Update();
vtkSmartPointer View= vtkSmartPointer::New();
View->SetInputConnection(reader->GetOutputPort());
vtkSmartPointer RenWinInter=vtkSmartPointer::New();
vtkSmartPointer Style= vtkSmartPointer::New();
View->SetupInteractor(RenWinInter);
//设置基本属性
View->SetSize(500,200);
View->SetColorLevel(400);
View->SetColorWindow(2000);
//默认选择第50张切片
View->SetSlice(40);
View->SetSliceOrientationToXZ();
//View->GetRenderer()->SetBackground(0.1, 0.2, 1.4);
View->GetRenderer()->SetViewport(0.5,0.5,1.0,1.0);
//默认选择第50张切片
View->SetSlice(40);
View->SetSliceOrientationToXY();
View->GetRenderer()->SetViewport(0.5,0.0,1.0,0.5);
//默认选择第50张切片
View->SetSlice(40);
View->SetSliceOrientationToYZ();
View->GetRenderer()->SetViewport(0.0,0.0,0.5,0.5);
View->Render();
RenWinInter->Initialize();
RenWinInter->Start();
}
vtkImageBlend:
= 图片融合处理;
vtkImageReslice:
= 沿一组新轴重新切割;SetOutputDimensionality(int *)设置维数;SetInterpolationModeToLinear()切面提取中的差值方式为线性差值;SetResliceAxes(* *)设置切面矩阵;
vtkMatrix4x4:
=矩阵转换;
三维基础几何变换矩阵:https://max.book118.com/html/2018/0423/162757315.shtm
//轴状面变换矩阵
static double axialElements[16] = {
1, 0, 0, 0,//x轴向量(第四个元素必须设置为0)
0, 1, 0, 0,//y轴
0, 0, 1, 0,//z轴
0, 0, 0, 1 //第四个元素必须设置为1
};
x[1,0,0] - y[0,1,0] - z[0,0,1] - 经过变换矩阵处理之后的新的原点[0,0,0]
//冠状面变换矩阵
double Coronal [16] = {
1, 0, 0, 0,
0, 0, -1, 0,
0, 1, 0, 0,
0, 0, 0, 1 };
//矢状面变换矩阵
double Sagittal [16] = {
0, 0, 1, 0,
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0, 1 };
//斜切面变换矩阵
double Oblique[16] = {
1, 0, 0, 0,
0, 0.866025, -0.5, 0,
0, 0.5, 0.866025, 0,
0, 0, 0, 1 };
--AxialResliceMatrix,轴切面/2
--CoronalResliceMatrix,冠状面/1
--SagittalResliceMatrix,矢状面/0
CT文件数据到三维立体图形
交互
-观察值/指令模式方式
1.定义事件回调函数
1.1 定义回调函数 ,形式如下
void func(vtkObject* obj, unsigned long eid, void* clientdata, void* calldata);
1.2 创建vtkCallbackCommand对象,调用vtkCallBackCommand::SetCallback()设置的回调函数;
vtkSmartPoint
eventcallback->setCallBack(mycallbackFunc);
1.3 添加vtkCallBack对象到观察者列表
interactor->AddObserver(vtkCommand::LeftButtonParessEvent, eventcallback);
2.扩展vtkCommand,实现它的超类,实现新的excuse函数。
2.1 扩展vtkCommand子类,实现excuse函数
2.2 实例化vtkCommand子类,调用相关函数
vtkSmartPoint
callback->SetObject(coneSource);
2.3 调用AddObserver进行事件监听,监听到对应事件后调用实现子类的excuse函数;
-交互器
1.事件捕捉
1.1 用户界面事件捕捉,平台驱动事件消息;
1.2 vtkRenderwindowinteractor回调函数vtkHandleMessage接收到消息,并分发到其对应的响应函数中;
1.3 响应函数中的vtkObject::invokeEvent,将消息转换为VTK消息,函数中的被动观察者Passive/焦点观察者Focus/other做消息捕获;
-被动观察者Passive:不改变系统状态;
-焦点观察者Focus:使窗口获得焦点(如:LeftButtonParessEvent焦点观察者)
-other
1.4 消息路由至vtkInteractorOberserver或其子类(比如widget处理类),其vtkSubjectHelper::invokeEvent将消息发送观察者;
1.5 观察者调用vtkInteractorStyle::ProcessEvent处理事件,将事件发送到对应的消息响应函数;
2.render渲染实现,依据当前请求渲染库类型,进而创建对应的子类。
3.interactorstyle是事件处理样式定义,不同的样式对同一个动作会给出不同的界面响应效果。
-widget(vtk5.0及以上版本)
-作用
交互部件,交互器表达的实体化,做到操作的可视化,方便用户的操作
-类关系
-父类
vtkInteractorOberserver是widget基类的父类
-基础类
-vtk3DWidget
主要应用于控制数据可视化,如画点、线、面、立体球框等;
-vtkAbstractWidget
主要应用与交互/表达实体(interaction/representation);
-表达实体对应对象
-vtkprop
-vtkWidgetRepresentation子类
-事件转换
widgeteventtranslator将vtk事件转换为widget事件;
vtkWidgetCallBackMapper将对应widget事件 -> 与操作函数进行关联,通过vtkWidgetCallBackMapper::setCallBackMethod方法;
-widget创建步骤:
1.实例化widget。
2.指定渲染窗口 的 交互器,widget可通过interactor监听用户信息。
3.用户可以创建自己的观察者/命令模式回调函数,可监听需要的事件消息做出事件响应。
4.创建对应的几何表达实体,可以用setrepresentation将几何实体与widget关联起来;也可使用默认几何实体。
5.激活widget,使其在渲染场景中显示。
-其他一些widget类
-测量类widget
vtkDistanceWidget(测量二维平面两点之间的距离)、vtkAngleWidget(二维平面的较低测量)、vtkBiDimensionalWidget(二维平面任意正交方向轴长)等。
-标注类widget
vtkTextWidget(在渲染场景中生成一串标识文本)、vtkScalarBarWidget(生成标量条)、vtkCaptionWidget(带线框和箭头的文本信息标注对象)、vtkOrientationMarkerWidget(渲染数据的方向指示标志)、vtkBalloonWidget(鼠标在某actor停留之后弹出提示信息)等。
-分割/配准类
vtkContourWidget(绘制闭合/不不闭合轮廓线)、vtkSeedWidget(放置种子点)、vtkImageTraceWidget(绘制轨迹线)、vtkCheckerboardWidget(在二维图像上生成自定义棋盘格)、 vtkRectilinearWipeWidget(生成2x2棋盘格)等。
Q:事件处理与widget表达实体是如何同步的?
A:
.拾取
-作用
选择和控制角色
-基类
vtkAbstractPicker
-按理解析
-点拾取-vtkPointPicker
1.RenderWindowInteractor 接收到事件之后,分发到响应函数,进行处理;
2.使用SetPicker(*)函数设置 需要拾取操作的 拾取对象;
3.定制相应的鼠标处理函数,并在其中 完成拾取操作;
vtkImagePlaneWidget:
= 切片图像提取,切面三视图可视化部件;
vtkResliceCursorWidget:
=交互式图像切分,定义了“十”字坐标轴,支持坐标轴的旋转平移等鼠标操作;
一个该对象对应一个vtkResliceCursor对象,当坐标系发生改变时调用vtkResliceCursor来进行图像切分并进行更新到vtkRenderer对象中;
vtkAbstractTransform/vtkTransform:
=旋转、平移、缩放等操作;PreMultiply()/PostMultiply()
vtkImagePlaneWidget:
=三维图像的实时切割
vtkCommand:
= 指令交互模式,
Widget 使用:
1.SetEnabled(int *)可以对widget进行使能/开关控制;
Interaction/Widgets/Testing/Cxx的栗子跑出来的效果:
==================================分析伪代码
基于widget实现图片切片
//window/interaction创建
vtkSmartPointer renWin->AddRenderer(ren[i]);
vtkSmartPointer iren->SetRenderWindow(renWin);//只有一个RenderWindowInteractor
//三维切面图
vtkSmartPointer planeWidget[i]->SetInteractor(iren);
planeWidget[i]->SetDefaultRenderer(ren[3]);
//回调函数
vtkSmartPointer cbk = vtkSmartPointer::New();//我们实现的VTKcommand派生类
vtkSmartPointer< vtkResliceCursor> resliceCursor = vtkSmartPointer< vtkResliceCursor >::New();//创建vtkResliceCursor,一个vtkResliceCursor将所有的vtkResliceCursorWidget链接起来
resliceCursorRep[i] =vtkSmartPointer< vtkResliceCursorLineRepresentation >::New();//显示图像切分时的“十”字坐标轴
vtkSmartPointer< vtkResliceCursorWidget > resliceCursorWidget[i]->SetInteractor(iren);
resliceCursorWidget[i]->SetDefaultRenderer(ren[i]);
resliceCursorWidget[i]->SetRepresentation(resliceCursorRep[i]);
resliceCursorRep[i]->GetResliceCursorActor()->GetCursorAlgorithm()->SetResliceCursor(resliceCursor);//设置同一个vtkResliceCursor使得所有的resliceCursorWidget同步
//回调函数注册
cbk->IPW[i] = planeWidget[i];
cbk->RCW[i] = resliceCursorWidget[i];
resliceCursorWidget[i]->AddObserver(vtkResliceCursorWidget::ResliceAxesChangedEvent, cbk);//监听ResliceAxesChangedEvent事件,Execute实现回调功能
vtkResliceCursorWidget :
vtkAbstractWidget :
vtkWidgetCallbackMapper : SetCallbackMethod/SelectAction/RotateAction/EndSelectAction/MoveAction/ResetResliceCursorAction
vtkWidgetEventTranslator :
vtkWidgetRepresentation :
vtkInteractorObserver : SetInteractor(AddObserver)/ComputeDisplayToWorld.../SetCurrentRenderer/SetDefaultRenderer
vtkCallbackCommand : Execute() -- vtkCommand :SetCallback/event type/
vtkRenderWindowInteractor :
vtkRenderer :
-vtkResliceCursorLineRepresentation:
vtkImagePlaneWidget : SetInteractor()/SetDefaultRenderer()//三维切面图
vtkImageReslice :
debug调试
1.Linux
2.Android
基础解读:
color:
控色M1:SetColor(RGB 0~1),红,绿,蓝;
控色M2:AmbientColor, 环境光颜色;DiffuseColor, 散射光颜色;SpecularColor, 镜面光颜色;
灰度:
一般的非灰度图像unsigned char类型来表示。但是在医学图像处理中常见像素数据类型为unsigned short,灰度范围为0-65536。
4x4矩阵变换:
1.图形的伸缩、旋转、对称、错切、平移;
2.图形透视变换;
3.图形比例变换;
图形结构中的隐形层之间的疏密:层是紧密连接的(dense)/稀疏连接的(sparse)
图片文件格式:1.二进制图片数据,2.图片数据+fileinfo,读写ram(裸数据)格式数据时需要指定维度、字节顺序、存储像素值等属性;
VTK常识了解:
Q1.为什么需要了解类之间的继承?
A1.超类是父类的扩充,有些功能接口在父类中,有些在新加的子类中;开发者也可以继承的方式开发扩展自己的类。
Q2.camera和light 数量是如何规定的?
A2.camera只有一个,light可以多个,多个light的效果都将呈现到一个camera上。
Q3.vtkResliceCursorWidget与vtkImagePlaneWidget是如何实现同步更新的?
A3.这两个类,ResliceCursor是实现切面光标可视化,ImagePlane实现图像切面可视化。
Q4:what is mesa?
A4:
QA.1.实践很重要,多跑代码验证和加深了解。
2.点是属于零维。
图形学问答:
Q1.what is OpenGL ?
A1.Open Graphics Library,用于图形的绘制、渲染。
Q2.What is OpenCV?
A2.Open Source Computer Vision Library,图形视频基础处理算法库。
Q3.what is Volume rendering?
A3.
Q5.what is ITK (insight segmentation registration toolkit)?
A5:医学图像分割与配准工具包,主要提供算法技术,不支持可视化功能,可以通过相关实现与vtk的配合使用。
常用英文单词:
1.线:line
2.圆:sphere
3.三角形:triangle
4.四边形:quadri-quadru 四边多边形:four-sided polygons
5.正方形:
6.长方形:
7.多边形柱体:polygonal cylinder
8.立方体:cube
9.圆锥体:cone
10.渲染窗口交互器:render window interactor
11.图形结构:graphics structure
12.缩放:zoom
13.辨识度 :Resolution
14.经度-纬度:longitude - latitude
15.位置:Position
16.PNM图片(黑白、灰度、彩色);PPM->Portable PixMap、PGM->Portable GreyMap、PBM->Portable BitMap
17.切片方向:slice orientation -xzy
18.切面坐标矩阵
资源外接:
paraView Tutorial:https://blog.csdn.net/dsfsdffgfd/category_10102763.html?utm_source=BWXQ_bottombtn&spm=1001.2101.3001.4225
东灵出版社 :https://blog.csdn.net/www_doling_net/category_1331102.html
VTK官网 : https://vtk.org/,其中有VTK-Source/VTK-Data/VTK-Doc等重要学习资源
图像处理VTK/ITK博文列表:https://blog.csdn.net/calmreason/article/details/88075073
图像切片vtkImagePlaneWidget与vtkResliceCursorWidget的使用:https://blog.csdn.net/www_doling_net/article/details/8939115
vtkcommand使用解析:https://blog.csdn.net/webzhuce/article/details/71245134