这篇博文是接着VS2022联合Qt5开发学习5(QT5.12.3联合VTK在VS2022上开发医学图像项目)-CSDN博客这篇博文延伸开发医学图像的显示渲染相关项目。三视图这个有点繁琐,还得先放放(可能是下一篇博文?),这一篇主要介绍的是在之前显示的图像上增加中心十字叉。(虽然看上去挺简单的,但是其实还是综合了很多我前面博文介绍过的内容的。)
用到的内容有:
11.VTK图形处理_vtkPolyData-CSDN博客
3.VTK坐标系统以及VTK管线_vtk时间管线-CSDN博客
VS2022联合Qt5开发学习5(QT5.12.3联合VTK在VS2022上开发医学图像项目)-CSDN博客
大家可以先提前瞅一眼,或者直接读这一篇博文,到了要用到的地方,我会都标上的。
这个项目我是接着前面那个博客的STLshowtest_vtk7项目,用的VTK版本是VTK7,也就是说,显示图像我用的是QVTKWidget这个控件。如果是用的VTK9或者其他更高版本的VTK,解决方案参考VS2022联合Qt5开发学习5(QT5.12.3联合VTK在VS2022上开发医学图像项目)-CSDN博客,里面我有详细写怎么用哈,这里就不赘述了。
我们可以看到,在那种比较成熟的医学图像显示项目里,图像上都有这种十字叉,有的是为了显示图像中心,有的是为了方便在图像中取点,以便进行之后的一些应用。(下图来源:Medical Imaging Interaction Toolkit: The MITK Workbench)
我这里就简单构造一个专门画十字叉的函数了,具体怎么应用看大家的需求了。
首先,画线条这里我用到了vtkLine,所以记得在项目开头加上
#include
然后构造十字叉的函数,放在.cpp文件里。
vtkSmartPointer STLshowtest_vtk7::DrawCross(vtkSmartPointer _Actor, double _Pos[3])
{
if (_Actor == nullptr)
{
_Actor = vtkSmartPointer::New();
}
vtkSmartPointer _Points = vtkSmartPointer::New();
_Points->InsertNextPoint(_Pos[0] - 3, _Pos[1], _Pos[2]);
_Points->InsertNextPoint(_Pos[0] + 3, _Pos[1], _Pos[2]);
_Points->InsertNextPoint(_Pos[0], _Pos[1] - 3, _Pos[2]);
_Points->InsertNextPoint(_Pos[0], _Pos[1] + 3, _Pos[2]);
vtkSmartPointer _Line0 = vtkSmartPointer::New();
_Line0->GetPointIds()->SetId(0, 0);
_Line0->GetPointIds()->SetId(1, 1);
vtkSmartPointer _Line1 = vtkSmartPointer::New();
_Line1->GetPointIds()->SetId(0, 2);
_Line1->GetPointIds()->SetId(1, 3);
vtkSmartPointer _Lines = vtkSmartPointer::New();
_Lines->InsertNextCell(_Line0);
_Lines->InsertNextCell(_Line1);
vtkSmartPointer _PolyData = vtkSmartPointer::New();
_PolyData->SetPoints(_Points);
_PolyData->SetLines(_Lines);
vtkSmartPointer _Mapper = vtkSmartPointer::New();
_Mapper->SetInputData(_PolyData);
vtkSmartPointer lineProperty = vtkSmartPointer::New();
lineProperty->SetLineWidth(1.0); // 设置线宽为 1,可以根据需要调整
_Actor->SetMapper(_Mapper);
_Actor->SetProperty(lineProperty);
_Actor->GetProperty()->SetColor(1.0, 0.0, 0.0);
return _Actor;
}
在.h文件加入函数和变量的声明
private:
vtkSmartPointer m_vtkRenderer;
vtkSmartPointer m_vtkRenderWindow;
private:
vtkSmartPointer DrawCross(vtkSmartPointer _Actor, double _Pos[3]);
最后把位置参数设一下,然后把十字叉作为Actor推到渲染器Render里,然后再丢到窗口里就大功告成了。
STLshowtest_vtk7::STLshowtest_vtk7(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
//test DrawCross
static vtkSmartPointer _CrossCenterActor = vtkSmartPointer::New();
double _Pos[3];
_Pos[0] = 0;
_Pos[1] = 0;
_Pos[2] = 0;
_CrossCenterActor = DrawCross(_CrossCenterActor, _Pos);
_CrossCenterActor->SetPosition(0, 0, 1);
m_vtkRenderer = vtkSmartPointer::New();
m_vtkRenderer->AddActor(_CrossCenterActor);
m_vtkRenderer->SetBackground(.0, .0, .0);
vtkSmartPointer window = vtkSmartPointer::New();
ui.qvtkWidget->SetRenderWindow(window);
ui.qvtkWidget->GetRenderWindow()->AddRenderer(m_vtkRenderer);
}
。。。我写的这么详细,应该不需要再把完整代码贴上了吧。
运行结果
进行到这一步会出现一个问题。一旦我们点击选择图像,导入.stl文件后,这个十字叉就会消失了,这显然也不是我们想要的结果。这时候就要祭出我之前学VTK的一篇博文了3.VTK坐标系统以及VTK管线_vtk时间管线-CSDN博客 (嘎嘎嘎所以说多记笔记多写心得是有好处的,这不就用上了)。这个博文里的例子就是一个窗口里加了好多个Actor和Render,可以参考参考。
修改openFileSlot()函数,把_CrossCenterActor加进去,一起和.stl文件的图像的Actor推到渲染器里。
void STLshowtest_vtk7::openFileSlot()
{
QString selectFilePath = QFileDialog::getOpenFileName(this, QString("choose STL file"), QString(""), QString("file(*.stl)"));
if (selectFilePath.isEmpty())
{
ui.textBrowser->append("The address of the STL file you choose is null!");
return;
}
// 原始图像
vtkSmartPointer reader = vtkSmartPointer::New();
reader->SetFileName(selectFilePath.toStdString().c_str());
reader->Update();
//将source转换成mapper
vtkSmartPointer mapper = vtkSmartPointer::New();
mapper->SetInputConnection(reader->GetOutputPort());
//送入渲染引擎进行显示
vtkSmartPointer actor = vtkSmartPointer::New();
actor->SetMapper(mapper);
static vtkSmartPointer _CrossCenterActor = vtkSmartPointer::New();
double _Pos[3];
_Pos[0] = 0;
_Pos[1] = 0;
_Pos[2] = 0;
_CrossCenterActor = DrawCross(_CrossCenterActor, _Pos);
_CrossCenterActor->SetPosition(0, 0, 1);
//渲染
vtkSmartPointer renderer = vtkSmartPointer::New();
renderer->AddActor(actor);
renderer->SetBackground(.0, .0, .0);
renderer->AddActor(_CrossCenterActor);
renderer->SetBackground(.0, .0, .0);
//设置渲染窗口
vtkSmartPointer window = vtkSmartPointer::New();
ui.qvtkWidget->SetRenderWindow(window);
ui.qvtkWidget->GetRenderWindow()->AddRenderer(renderer); //等价于window->AddRenderer(renderer);
//ok
ui.textBrowser->append(QString("upload the file:") + selectFilePath + QString(" succeed !"));
}
运行结果
红叉叉不大明显,让我放大一点
所以可以看到现在又出来了新的问题:红叉不在图像中心、红叉太小。
先解决十字叉太小的问题。修改一下这里的坐标就可以了
vtkSmartPointer _Points = vtkSmartPointer::New();
_Points->InsertNextPoint(_Pos[0] - 10, _Pos[1], _Pos[2]);
_Points->InsertNextPoint(_Pos[0] + 10, _Pos[1], _Pos[2]);
_Points->InsertNextPoint(_Pos[0], _Pos[1] - 10, _Pos[2]);
_Points->InsertNextPoint(_Pos[0], _Pos[1] + 10, _Pos[2]);
我这里只改了XY轴的坐标,这对于二维图是够用了。但我这里读取的是三维图,先再加一个轴。
vtkSmartPointer _Points = vtkSmartPointer::New();
_Points->InsertNextPoint(_Pos[0] - 10, _Pos[1], _Pos[2]);
_Points->InsertNextPoint(_Pos[0] + 10, _Pos[1], _Pos[2]);
_Points->InsertNextPoint(_Pos[0], _Pos[1] - 10, _Pos[2]);
_Points->InsertNextPoint(_Pos[0], _Pos[1] + 10, _Pos[2]);
_Points->InsertNextPoint(_Pos[0], _Pos[1], _Pos[2] - 10);
_Points->InsertNextPoint(_Pos[0], _Pos[1], _Pos[2] + 10);
vtkSmartPointer _Line0 = vtkSmartPointer::New();
_Line0->GetPointIds()->SetId(0, 0);
_Line0->GetPointIds()->SetId(1, 1);
vtkSmartPointer _Line1 = vtkSmartPointer::New();
_Line1->GetPointIds()->SetId(0, 2);
_Line1->GetPointIds()->SetId(1, 3);
vtkSmartPointer _Line2 = vtkSmartPointer::New();
_Line2->GetPointIds()->SetId(0, 4);
_Line2->GetPointIds()->SetId(1, 5);
vtkSmartPointer _Lines = vtkSmartPointer::New();
_Lines->InsertNextCell(_Line0);
_Lines->InsertNextCell(_Line1);
_Lines->InsertNextCell(_Line2);
vtkSmartPointer _PolyData = vtkSmartPointer::New();
_PolyData->SetPoints(_Points);
_PolyData->SetLines(_Lines);
运行结果
如果有对这些画线还有坐标不清楚的,可以看一下我这个博客11.VTK图形处理_vtkPolyData-CSDN博客,这里我还举了其他的例子。
接下来是要确定图形的中心点,确保十字叉正中中心,不管怎么放大缩小旋转都在中心位置。这里可以参考一下这个博文(7.VTK图像基本操作-CSDN博客)。
// 获取图像信息
vtkSmartPointer polyData = reader->GetOutput();
vtkSmartPointer points = polyData->GetPoints();
// 计算中心点坐标
double center[3] = { 0.0, 0.0, 0.0 };
for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i) {
double point[3];
points->GetPoint(i, point);
for (int j = 0; j < 3; ++j) {
center[j] += point[j];
}
}
for (int j = 0; j < 3; ++j) {
center[j] /= points->GetNumberOfPoints();
}
cout << "The Center of the Picture:" << center[0] << " " << center[1] << " " << center[2] << endl;
计算好中心点,将中心点坐标带入到十字叉函数
_CrossCenterActor = DrawCross(_CrossCenterActor, center);
就完工啦!
运行结果