VTK 椭圆圈取ROI区域
目录
环境
QT 5.14.2
VTK 8.2.0 (vs2019编译,64位 release 版本)
构建方式:Qt5.14.2 MSVC2017 64bit Release
1、实现效果图

2、实现方式
大致流程:
1、使用 vtkEllipseArcSource 类在原图像上画出椭圆进行框选。
2、使用 vtkImageEllipsoidSource 类制作椭圆的二值图像
3、使用 vtkImageMask 类 提取 ROI 椭圆区域
2.1 框选实现
2.1.1 初始化
自定义类 MyVTKOpenGLWidget 继承自 QVTKOpenGLWidget,初始化做了3件事:
1、创建 vtkImageViewer2 对象,用来显示图像,图像数据由外部传入。vtkImageViewer2 使用的 交互样式是自定义样式 MyInteractorStyle,主要是用来在椭圆绘制过程中屏蔽 vtk 自带的移动事件。
2、创建 vtkPointPicker 对象,用来在图像上拾取点坐标。
3、创建 vtkEllipseArcSource 对象,用来画 椭圆。
MyVTKOpenGLWidget::MyVTKOpenGLWidget(QWidget *parent) : QVTKOpenGLWidget(parent)
{
}
void MyVTKOpenGLWidget::init(vtkImageData *imageData)
{
initImageView(imageData);
addPicker(); // 添加 点 拾取对象
addEllipse(); // 添加椭圆 actor
}
void MyVTKOpenGLWidget::initImageView(vtkImageData* imageData)
{
m_imageView = vtkSmartPointer< vtkImageViewer2 >::New();
m_imageView->SetInputData(imageData); // 加载数据
// renderer 设置
vtkRenderer* renderer = m_imageView->GetRenderer();
renderer->ResetCamera();
// 设置交互对象
m_imageView->SetupInteractor(this->GetRenderWindow()->GetInteractor());
// 设置渲染窗口
m_imageView->SetRenderWindow(this->GetRenderWindow());
// 设置交互样式
m_style = vtkSmartPointer<MyInteractorStyle>::New();
this->GetRenderWindow()->GetInteractor()->SetInteractorStyle(m_style);
}
void MyVTKOpenGLWidget::addPicker()
{
// 设置 点 拾取对象
m_picker = vtkSmartPointer< vtkPointPicker >::New();
m_picker->PickFromListOn();
vtkImageActor* imageActor = m_imageView->GetImageActor();
m_picker->AddPickList(imageActor);
}
void MyVTKOpenGLWidget::addEllipse()
{
m_ellipseSource = vtkSmartPointer<vtkEllipseArcSource>::New();
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputData(m_ellipseSource->GetOutput());
// 创建actor
m_ellipseActor = vtkSmartPointer<vtkActor>::New();
m_ellipseActor->SetMapper(mapper);
m_ellipseActor->GetProperty()->SetColor( 1, 0.66, 0);
m_ellipseActor->GetProperty()->SetLineWidth(2);
m_imageView->GetRenderer()->AddActor( m_ellipseActor );
}
自定义样式 MyInteractorStyle
void MyInteractorStyle::setSelectAreaOn(bool v)
{
m_selectAreaOn = v;
}
void MyInteractorStyle::OnMouseMove()
{
if (m_selectAreaOn)
return;
vtkInteractorStyleImage::OnMouseMove();
}
2.1.2 框选实现
将拾取到的点坐标转换为图像的像素坐标
void MyVTKOpenGLWidget::calculateImageIndex(int image_coordinate[])
{
vtkRenderer* renderer = m_imageView->GetRenderer();
vtkImageData* image = m_imageView->GetInput();
vtkRenderWindowInteractor* interactor = m_imageView->GetRenderWindow()->GetInteractor();
int* clickPos = interactor->GetEventPosition();
m_picker->Pick(clickPos[0], clickPos[1], 0.0, renderer);
// Get the world coordinates of the pick
double *pos = m_picker->GetPickPosition();
double *spacing = image->GetSpacing();
double *origin = image->GetOrigin();
image_coordinate[0] = pos[0] - origin[0]; //origin是图像的原点
image_coordinate[1] = pos[1] - origin[1];
image_coordinate[2] = pos[2] - origin[2];
for (int i = 0; i < 3; i++)
{
int index = image_coordinate[i] / spacing[i];
double deltX = image_coordinate[i] - index * spacing[i];
if (deltX > spacing[i] / 2.0) index++;
image_coordinate[i] = index;
}
}
重载QT mousePressEvent、mouseMoveEvent、mouseReleaseEvent函数。
1、mousePressEvent: 鼠标左键按下时记录起始坐标,
2、mouseMoveEvent: 移动过程中计算当前的坐标,绘制椭圆
3、mouseReleaseEvent: 鼠标抬起表示本次绘制结束,向外发送结束绘制信号
void MyVTKOpenGLWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
m_bPressed = true;
m_style->setSelectAreaOn(m_bPressed);
calculateImageIndex(m_pressIndex);
}
QVTKOpenGLWidget::mousePressEvent(event);
}
void MyVTKOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
if (m_bPressed) // 左键按下的情况下 画椭圆
{
calculateImageIndex(m_moveIndex);
drawEllipse();
}
QVTKOpenGLWidget::mouseMoveEvent(event);
}
void MyVTKOpenGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
m_bPressed = false;
m_style->setSelectAreaOn(m_bPressed);
emit signalEndDrawllipse();
}
QVTKOpenGLWidget::mouseReleaseEvent(event);
}
椭圆绘制:根据起始坐标和当前坐标,计算椭圆的中心坐标,X轴半径,Y轴半径,这里给了一个限制,X轴坐标相差不大的情况下,不进行绘制,否则 VTK 会报错。
void MyVTKOpenGLWidget::drawEllipse()
{
if (qAbs(m_moveIndex[0]-m_pressIndex[0]) < 2)
return;
double xr = qAbs(m_moveIndex[0]-m_pressIndex[0])/2; // 椭圆横向半径
double yr = qAbs(m_moveIndex[1]-m_pressIndex[1])/2; // 椭圆垂直半径
double centerX = (m_moveIndex[0]+m_pressIndex[0])/2; // 椭圆中心 X 轴坐标
double centerY = (m_moveIndex[1]+m_pressIndex[1])/2; // 椭圆中心 Y 轴坐标
double *spacing = m_imageView->GetInput()->GetSpacing();
m_ellipseSource->SetCenter(centerX*spacing[0], centerY*spacing[1], 1);
m_ellipseSource->SetSegmentAngle(360);
m_ellipseSource->SetRatio(yr/xr);
m_ellipseSource->SetStartAngle(0);
m_ellipseSource->SetNormal(0, 0, 1);
m_ellipseSource->SetMajorRadiusVector(xr*spacing[0], 0, 0); // 只需设置 x 轴半径
m_ellipseSource->Update();
this->GetRenderWindow()->Modified();
this->GetRenderWindow()->Render();
}
2.2 提取椭圆 ROI 区域
主窗口区域设置两个widget,

2.2.1 初始化界面
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 读取数据
vtkSmartPointer< vtkDICOMImageReader > reader = vtkSmartPointer< vtkDICOMImageReader >::New();
reader->SetDirectoryName("E:\\DicomDir\\Angio Sample-1409241311-000006-002");
reader->Update();
m_imageData = reader->GetOutput();
ui->sourceWidget->init(m_imageData);
initROIWidget(m_imageData);
connect(ui->sourceWidget, SIGNAL(signalEndDrawllipse()), this, SLOT(slotDrawEllipseROI()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::initROIWidget(vtkImageData* imageData)
{
m_imageView = vtkSmartPointer<vtkImageViewer2>::New();
m_imageView->SetInputData(imageData);
// 设置交互对象
m_imageView->SetupInteractor(ui->roiWidget->GetRenderWindow()->GetInteractor());
// 设置渲染窗口actor
m_imageView->SetRenderWindow(ui->roiWidget->GetRenderWindow());
// 设置交互样式
vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New();
ui->roiWidget->GetRenderWindow()->GetInteractor()->SetInteractorStyle(style);
m_imageView->Render();
}
2.2.2 ROI 提取
MyVTKOpenGLWidget 椭圆绘制结束会向外发送 signalEndDrawllipse 信号,主窗口接收该信号,在槽函数中提取椭圆区域
void Widget::slotDrawEllipseROI()
{
int * pressIndex = ui->sourceWidget->getPressIndex();
int * moveIndex = ui->sourceWidget->getMoveIndex();
double xr = qAbs(moveIndex[0]-pressIndex[0])/2; // 椭圆横向半径
double yr = qAbs(moveIndex[1]-pressIndex[1])/2; // 椭圆垂直半径
double centerX = (moveIndex[0]+pressIndex[0])/2; // 椭圆中心 X 轴坐标
double centerY = (moveIndex[1]+pressIndex[1])/2; // 椭圆中心 Y 轴坐标
// 制作 椭圆 二值图像
vtkNew<vtkImageEllipsoidSource> source;
source->SetWholeExtent(m_imageData->GetExtent());
source->SetOutputScalarTypeToUnsignedChar();
source->SetInValue(255);
source->SetOutValue(0);
source->SetCenter(centerX, centerY, 0);
source->SetRadius(xr, yr, 1);
source->Update();
// 提取 ROI 区域
vtkNew<vtkImageMask> maskFilter;
maskFilter->SetInputData(0, m_imageData);
maskFilter->SetInputData(1, source->GetOutput());
maskFilter->SetMaskedOutputValue(0,0,0);
maskFilter->Update();
m_imageView->SetInputData(maskFilter->GetOutput());
m_imageView->Render();
}
完整工程:VtkEllipseROI.zip-C++文档类资源-CSDN下载
VTK8.2.0编译好的库:VTK8.2.0编译好的库-其它文档类资源-CSDN下载