简述

  VTK中体(vtkVolume)的裁剪可以通过体数据映射器(vtkVolumeMapper)设置裁剪平面(vtkPlane)进行裁剪(AddClippingPlane )。该裁剪平面可通过参数设置其属性,也可使用平面窗口(vtkPlaneWidget)通过鼠标平移缩放旋转至目标位置后,获取该窗口包含的平面(GetPlane),并将其设置给提数据映射器(vtkVolumeMapper)进行裁剪。本文实现了通过鼠标操作平面窗口(vtkPlaneWidget)后进行体数据裁剪。

代码

main.cpp

#include 
#include 
#include 
#include 
#include "CVtkPlaneCut.h"

int main( int argc, char *argv[] )
{
    QApplication a( argc, argv );

    QWidget* pWidget = new QWidget;

    CVtkPlaneCut* pPlaneCut = new CVtkPlaneCut( pWidget );
    QPushButton* pBtnClip = new QPushButton( "Clip" );
    pBtnClip->setFixedHeight( 30 );
    QObject::connect( pBtnClip, SIGNAL( clicked() ), pPlaneCut, SLOT( slotClipVolume() ) );

    QVBoxLayout* pMainLayout = new QVBoxLayout;
    pMainLayout->addWidget( pPlaneCut );
    pMainLayout->addWidget( pBtnClip );
    pWidget->setLayout( pMainLayout );
    pWidget->show();

    return a.exec();
}

CVtkPlaneCut .h

#ifndef CVTKPLANECUT_H
#define CVTKPLANECUT_H

#include "QVTKWidget.h"

#include "vtkSmartPointer.h"

class vtkVolume;
class vtkRenderWindow;
class vtkPlaneWidget;

class CVtkPlaneCut : public QVTKWidget
{
    Q_OBJECT

public:
    CVtkPlaneCut( QWidget *parent = 0 );

protected slots:
    void slotClipVolume();

private:
    vtkSmartPointer m_pVolume;
    vtkSmartPointer m_pRenderWindow;
    vtkSmartPointer m_pPlaneWidget;
};

#endif // CVTKPLANECUT_H

CVtkPlaneCut .cpp

#include  
VTK_MODULE_INIT( vtkRenderingOpenGL );
VTK_MODULE_INIT( vtkInteractionStyle );
#define vtkRenderingCore_AUTOINIT 4(vtkInteractionStyle,vtkRenderingFreeType,vtkRenderingFreeTypeOpenGL,vtkRenderingOpenGL)
#define vtkRenderingVolume_AUTOINIT 1(vtkRenderingVolumeOpenGL)  

#include "CVtkPlaneCut.h"

#include 

#include "vtkImageData.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkVolume.h"
#include "vtkVolumeProperty.h"
#include "vtkPiecewiseFunction.h"
#include "vtkSmartVolumeMapper.h"
#include "vtkColorTransferFunction.h"
#include "vtkCamera.h"
#include "vtkPlane.h"
#include "vtkPlaneWidget.h"
#include "vtkImageShiftScale.h"
#include "vtkProperty.h"

CVtkPlaneCut::CVtkPlaneCut( QWidget *parent )
    : QVTKWidget( parent )
{
    /*Init RenderWindow*/
    vtkSmartPointer pRenderer = vtkSmartPointer::New();
    m_pRenderWindow = vtkSmartPointer::New();
    m_pVolume = vtkSmartPointer::New();
    m_pRenderWindow->AddRenderer( pRenderer );
    this->SetRenderWindow( m_pRenderWindow );

    /*Create Image Data*/
    int nDims[ 3 ] = { 1 };
    nDims[ 0 ] = 512;
    nDims[ 1 ] = 512;
    nDims[ 2 ] = 262;

    double dSpacing[ 3 ] = { 0.1 };
    dSpacing[ 0 ] = 0.78;
    dSpacing[ 1 ] = 0.78;
    dSpacing[ 2 ] = 3.27;

    double dOrigin[ 3 ] = { 0.0 };

    vtkSmartPointer pImageData = vtkSmartPointer::New();
    pImageData->SetSpacing( dSpacing );
    pImageData->SetDimensions( nDims );
    pImageData->SetOrigin( dOrigin );

    /*Read Data*/
    QString strFilePath = "../TestData/CT_Body_512_512_262_0.78_0.78_3.27_2048_short.HU";

    QFile file( strFilePath );
    if( !file.open( QIODevice::ReadOnly ) )
        return;
    file.seek( 2048 );

    int nSizeOfShot = sizeof( short );

    long lLength = nDims[ 0 ] * nDims[ 1 ] * nDims[ 2 ];
    char* pRawData = new char[ lLength * nSizeOfShot ];
    long lRead = 0;
    while( lRead < lLength * nSizeOfShot )
        lRead += file.read( pRawData, lLength * nSizeOfShot );

    pImageData->AllocateScalars( VTK_SHORT, 1 );
    short* pScalarPointer = (short*)pImageData->GetScalarPointer();
    short* pData = (short*)pRawData;
    memcpy( pScalarPointer, pData, lLength );
    file.close();

    /* Volume Property */
    //设置体绘制相关属性
    vtkSmartPointer volumeProperty = vtkSmartPointer::New();  
    volumeProperty->SetInterpolationTypeToLinear(); //设置线性插值方式  
    volumeProperty->ShadeOn();//开启阴影属性  
    volumeProperty->SetAmbient( 0.4 );//设置环境温度  
    volumeProperty->SetDiffuse( 0.6 );//设置漫反射系数 
    volumeProperty->SetSpecular( 2 );//设置镜面反射系数 
    //添加灰度不透明度属性    
    vtkSmartPointer compositeOpacity = vtkSmartPointer::New();  
    compositeOpacity->AddPoint( -15591, 0.0 );
    compositeOpacity->AddPoint( 4876, 0.001 );
    compositeOpacity->AddPoint( 7961, 1 );
    compositeOpacity->AddPoint( 11110, 0.001 );
    compositeOpacity->AddPoint( 32767, 0 );
    volumeProperty->SetScalarOpacity(compositeOpacity); 
    //添加梯度不同明度属性    
    vtkSmartPointer gradientOpacity = vtkSmartPointer::New();
    gradientOpacity->AddPoint( 0, 0.0 );
    gradientOpacity->AddPoint( 200, 0.4 );
    gradientOpacity->AddPoint( 500, 0.1 );
    gradientOpacity->AddPoint( 800, 0.5 );
    gradientOpacity->AddPoint( 1000, 0.6 );
    volumeProperty->SetGradientOpacity(gradientOpacity); 
    //添加颜色传输    
    vtkSmartPointer color = vtkSmartPointer::New(); 
    color->AddRGBPoint( 0, 0, 0, 0 );
    color->AddRGBPoint( 200, 0.5, 0.3, 0 );
    color->AddRGBPoint( 500, 0, 1.0, 0 );
    color->AddRGBPoint( 600, 0, 0.5, 0.5 );
    color->AddRGBPoint( 1000, 0.20, 0.5, 0.20 );
    volumeProperty->SetColor( color );

    /*Volume*/
    vtkSmartPointer volumeMapper = vtkSmartPointer::New();
    volumeMapper->SetInputData( pImageData );

    m_pVolume->SetMapper( volumeMapper );
    m_pVolume->SetProperty( volumeProperty );
    m_pVolume->SetOrigin( m_pVolume->GetCenter() );

    /*Render Window*/
    pRenderer->AddVolume( m_pVolume );
    pRenderer->ResetCamera();
    m_pRenderWindow->Modified();
    m_pRenderWindow->Render();

    /*Clipping Plane Widget*/
    m_pPlaneWidget = vtkSmartPointer::New();
    m_pPlaneWidget->SetInteractor( m_pRenderWindow->GetInteractor() );//与交互器关联
    m_pPlaneWidget->SetInputData( pImageData );//设置数据集,用于初始化平面,可以不设置
    m_pPlaneWidget->SetResolution( 50 );//即:设置网格数
    m_pPlaneWidget->GetPlaneProperty()->SetColor( .2, .8, 0.1 );//设置颜色
    m_pPlaneWidget->GetPlaneProperty()->SetOpacity( 0.5 );//设置透明度
    m_pPlaneWidget->GetHandleProperty()->SetColor( 0, .4, .7 );//设置平面顶点颜色
    m_pPlaneWidget->GetHandleProperty()->SetLineWidth( 1.5 );//设置平面线宽
    m_pPlaneWidget->NormalToZAxisOn();//初始法线方向平行于Z轴
    m_pPlaneWidget->SetRepresentationToWireframe();//平面显示为网格属性
    m_pPlaneWidget->SetCenter( m_pVolume->GetCenter() );//设置平面坐标
    m_pPlaneWidget->PlaceWidget();//放置平面
    m_pPlaneWidget->On();//显示平面
}

void CVtkPlaneCut::slotClipVolume()
{
  //进行裁剪
    vtkSmartPointer clippingPlane = vtkSmartPointer::New();
    m_pPlaneWidget->GetPlane( clippingPlane );

    m_pVolume->GetMapper()->AddClippingPlane( clippingPlane );
    m_pVolume->Modified();
    m_pRenderWindow->Modified();
    m_pRenderWindow->Render();
}

效果

初始界面
VTK: 三维体数据的平面裁剪_第1张图片
鼠标操作后界面
VTK: 三维体数据的平面裁剪_第2张图片
点击"剪裁"按钮后效果
VTK: 三维体数据的平面裁剪_第3张图片