zoukankan      html  css  js  c++  java
  • 5.7 区域提取

     

    5.7.1 提取感兴趣区域 

    感兴趣区域(Volum of Interest)是指图像内部的一个子区域。在VTK中vtkExtractVOI类实现由用户指定的区域范围提取图像的子图像。该Filter的输入和输出都是一个vtkImageData,因此其结果可以直接作为图像保存。

       1:      vtkSmartPointer<vtkBMPReader> reader =

       2:          vtkSmartPointer<vtkBMPReader>::New();

       3:      reader->SetFileName ( "lena.bmp" );

       4:      reader->Update();

       5:  

       6:      int dims[3];

       7:      reader->GetOutput()->GetDimensions(dims);

       8:  

       9:      vtkSmartPointer<vtkExtractVOI> extractVOI =

      10:          vtkSmartPointer<vtkExtractVOI>::New();

      11:      extractVOI->SetInputConnection(reader->GetOutputPort());

      12:      extractVOI->SetVOI(dims[0]/4.,3.*dims[0]/4.,dims[1]/4.,3.*dims[1]/4., 0, 0);

      13:      extractVOI->Update();

    上例代码实现了提取一副图像的子区域。首先读取一个图像,并获取图像的维数。然后定义vtkExtractVOI对象,该对象接收两个输入一个是图像数据,第二个是区域大小。设置区域大小的函数原型:

    void SetVOI(int _arg1, int _arg2, int _arg3, int _arg4, int _arg5, int _arg6)

    void SetVOI(int _arg[])

    其参数是提取的区域各个方向的大小,共6个参数,依次表示x方向最小值,x方向最大值,y方向最小值,y方向最大值,z方向最小值和z方向最大值。上例中由于读取的是二维图像,因此z方向的区域为[0,0],而在x方向范围为[ dims[0]/4 , 3*dims[0]/4 ],y方向范围为[ dims[1]/4 , 3*dims[1]/4 ],即提取图像原图中间1/4图像。执行结果如下:

     

    图5.18 提取感兴趣区域

    5.7.2 三维图像切片提取

    切片是指三维图像中的一个切面对应的图像。切面可以是过图像内部一点且平行于XY、YZ、XZ平面的平面,也可以是任意的过三维图像内部一点任意方向的平面。通过提取切片可以方便的浏览和分析图像内部组织结构,是医学图像浏览软件中的一个重要的功能。在VTK中vtkImageReslice类实现图像切片提取功能。下面首先看一段切片提取的代码。

    1:  vtkSmartPointer<vtkMetaImageReader> reader =

       2:     vtkSmartPointer<vtkMetaImageReader>::New();

       3:  reader->SetFileName ( " brain.mhd" );

       4:  reader->Update();

       5:   

       6:  int extent[6];

       7:  double spacing[3];

       8:  double origin[3];

       9:   

      10:  reader->GetOutput()->GetExtent(extent);

      11:  reader->GetOutput()->GetSpacing(spacing);

      12:  reader->GetOutput()->GetOrigin(origin);

      13:   

      14:  double center[3];

      15:  center[0] = origin[0] + spacing[0] * 0.5 * (extent[0] + extent[1]);

      16:  center[1] = origin[1] + spacing[1] * 0.5 * (extent[2] + extent[3]);

      17:  center[2] = origin[2] + spacing[2] * 0.5 * (extent[4] + extent[5]);

      18:   

      19:  static double axialElements[16] = {

      20:     1, 0, 0, 0,

      21:     0, 1, 0, 0,

      22:     0, 0, 1, 0,

      23:     0, 0, 0, 1 };

      24:   

      25:  vtkSmartPointer<vtkMatrix4x4> resliceAxes =

      26:     vtkSmartPointer<vtkMatrix4x4>::New();

      27:  resliceAxes->DeepCopy(axialElements);

      28:   

      29:  resliceAxes->SetElement(0, 3, center[0]);

      30:  resliceAxes->SetElement(1, 3, center[1]);

      31:  resliceAxes->SetElement(2, 3, center[2]);

      32:   

      33:   

      34:  vtkSmartPointer<vtkImageReslice> reslice =

      35:     vtkSmartPointer<vtkImageReslice>::New();

      36:  reslice->SetInputConnection(reader->GetOutputPort());

      37:  reslice->SetOutputDimensionality(2);

      38:  reslice->SetResliceAxes(resliceAxes);

      39:  reslice->SetInterpolationModeToLinear();

      40:   

      41:  vtkSmartPointer<vtkLookupTable> colorTable =

      42:     vtkSmartPointer<vtkLookupTable>::New();

      43:  colorTable->SetRange(0, 1000);

      44:  colorTable->SetValueRange(0.0, 1.0);

      45:  colorTable->SetSaturationRange(0.0, 0.0);

      46:  colorTable->SetRampToLinear();

      47:  colorTable->Build();

      48:   

      49:  vtkSmartPointer<vtkImageMapToColors> colorMap =

      50:     vtkSmartPointer<vtkImageMapToColors>::New();

      51:  colorMap->SetLookupTable(colorTable);

      52:  colorMap->SetInputConnection(reslice->GetOutputPort());

      53:   

      54:  vtkSmartPointer<vtkImageActor> imgActor =

      55:     vtkSmartPointer<vtkImageActor>::New();

      56:  imgActor->SetInput(colorMap->GetOutput());

      57:   

      58:  vtkSmartPointer<vtkRenderer> renderer =

      59:     vtkSmartPointer<vtkRenderer>::New();

      60:  renderer->AddActor(imgActor);

      61:  renderer->SetBackground(.4, .5, .6);

      62:   

      63:  vtkSmartPointer<vtkRenderWindow> renderWindow =

      64:     vtkSmartPointer<vtkRenderWindow>::New();

      65:  renderWindow->SetSize(500, 500);

      66:  renderWindow->AddRenderer(renderer);

      67:   

      68:  vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =

      69:     vtkSmartPointer<vtkRenderWindowInteractor>::New();

      70:  vtkSmartPointer<vtkInteractorStyleImage> imagestyle =

      71:     vtkSmartPointer<vtkInteractorStyleImage>::New();

      72:   

      73:  renderWindowInteractor->SetInteractorStyle(imagestyle);

      74:  renderWindowInteractor->SetRenderWindow(renderWindow);

      75:  renderWindowInteractor->Initialize();

      76:   

      77:  renderWindowInteractor->Start();

    首先通过vtkMetaImageReader读取一副医学三维图像,并获取得到图像范围(extent),原点和像素间隔;由这三个参数可以计算图像的中心位置center;接下来定义了切面的变换矩阵axialElements,该矩阵的前三列分别表示x、y和z方向向量,第四列为中心点坐标;代码中的axialElements表示切面变换矩阵与当前坐标系一致,且切面为过中心点center,并平行于XY平面的平面。当前,定义该切面时,也可以是其他平面,甚至是任意平面,但是必须要过图像内部点。下面给出了一个常用的变换矩阵:

    static double coronalElements[16] = {

     1, 0, 0, 0,

     0, 0, 1, 0,

    0,-1, 0, 0,

     0, 0, 0, 1 }; 提取平行于XZ平面的切片

    static double sagittalElements[16] = {

     0, 0,-1, 0,

     1, 0, 0, 0,

     0,-1, 0, 0,

     0, 0, 0, 1 }; 提取平行于YZ平面的切片

    static double obliqueElements[16] = {

     1, 0, 0, 0,

     0, 0.866025, -0.5, 0,

     0, 0.5, 0.866025, 0,

     0, 0, 0, 1 }; 提取斜切切片

    注意使用这些变换矩阵的时候,需要将第四列替换为切片经过图像的一个点坐标,上例中将图像的中心添加到axialElements矩阵,并通过函数SetResliceAxes设置变换矩阵,SetOutputDimensionality(2)指定输出的图像为一个二维图像;而函数SetInterpolationModeToLinear()则指定了切面提取中的差值方式为线性差值,另外该类中还提供了其他的差值方式:

    SetInterpolationModeToNearestNeighbor():最近邻方式

    SetInterpolationModeToCubic():三次线性差值

    设置完毕后,执行Update()即可完成切面计算。运行结果如下图:

     

    图5.19 切片提取

    5.7.3 扩展

    学习三维图像切面的提取后,我们在上节的程序上做一个扩展,实现一个稍微复杂的程序——通过滑动鼠标来切换三维图像切片,这也是医学图像处理软件中一个很基本的功能。实现该功能难点是怎样在VTK中控制鼠标来实时提取图像切片。在前面的章节中已经介绍观察者/命令(Observer/Command)模式,我们也采用这种机制来实现。VTK中鼠标消息是在交互类型对象(interactorstyle)中响应,因此通过为交互类型对象(interactorstyle)添加观察者(observer)来监听相应的消息,当消息触发时,由命令模式执行相应的回调函数。闲话少说,放代码。

      

    class vtkImageInteractionCallback : public vtkCommand

    {

    public:

    static vtkImageInteractionCallback *New()

    {

    return new vtkImageInteractionCallback;

    }

    vtkImageInteractionCallback()

    {

    this->Slicing = 0;

    this->ImageReslice = 0;

    this->Interactor = 0;

    }

    void SetImageReslice(vtkImageReslice *reslice)

    {

    this->ImageReslice = reslice;

    }

    vtkImageReslice *GetImageReslice()

    {

    return this->ImageReslice;

    }

    void SetInteractor(vtkRenderWindowInteractor *interactor)

    {

    this->Interactor = interactor;

    }

    vtkRenderWindowInteractor *GetInteractor()

    {

    return this->Interactor;

    }

    virtual void Execute(vtkObject *, unsigned long event, void *)

    {

    vtkRenderWindowInteractor *interactor = this->GetInteractor();

    int lastPos[2];

    interactor->GetLastEventPosition(lastPos);

    int currPos[2];

    interactor->GetEventPosition(currPos);

    if (event == vtkCommand::LeftButtonPressEvent)

    {

    this->Slicing = 1;

    }

    else if (event == vtkCommand::LeftButtonReleaseEvent)

    {

    this->Slicing = 0;

    }

    else if (event == vtkCommand::MouseMoveEvent)

    {

    if (this->Slicing)

    {

    vtkImageReslice *reslice = this->ImageReslice;

    // Increment slice position by deltaY of mouse

    int deltaY = lastPos[1] - currPos[1];

    reslice->Update();

    double sliceSpacing = reslice->GetOutput()->GetSpacing()[2];

    vtkMatrix4x4 *matrix = reslice->GetResliceAxes();

    // move the center point that we are slicing through

    double point[4];

    double center[4];

    point[0] = 0.0;

    point[1] = 0.0;

    point[2] = sliceSpacing * deltaY;

    point[3] = 1.0;

    matrix->MultiplyPoint(point, center);

    matrix->SetElement(0, 3, center[0]);

    matrix->SetElement(1, 3, center[1]);

    matrix->SetElement(2, 3, center[2]);

    interactor->Render();

    }

    else

    {

    vtkInteractorStyle *style = vtkInteractorStyle::SafeDownCast(

    interactor->GetInteractorStyle());

    if (style)

    {

    style->OnMouseMove();

    }

    }

    }

    }

    private:

    int Slicing;

    vtkImageReslice *ImageReslice;

    vtkRenderWindowInteractor *Interactor;

    };

    vtkImageInteractionCallback继承自vtkCommand类,并覆盖父类函数Execute()。该类提供了两个接口:SetImageReslice和SetInteractor。SetImageReslice用以设置vtkImageSlice对象,vtkImageSlice根据设置的变换矩阵提取三维图像切片。SetInteractor用以设置vtkRenderWindowInteractor,vtkRenderWindowInteractor类对象负责每次提取切片后刷新视图。

    下面我们重点来看一下Execute函数,该函数提供了具体的切片提取功能。在该函数里面,主要监听了三个消息:

    vtkCommand::LeftButtonPressEvent,

    vtkCommand::LeftButtonReleaseEvent,

    vtkCommand::MouseMoveEvent,

    前两个消息分别是鼠标左键的按下和弹起消息。当鼠标左键按下时,就设置切片提取标志为1,而当弹起时,将标志置为0。这样在鼠标移动时,只有在确定切片提取标志为1时,执行切片提取功能。

    vtkCommand::MouseMoveEvent即为鼠标移动消息。当检测到该消息时,首先检查切片提取标志,当为1时提取切片。提取切片时,需要为vtkImageSlice对象设置变换矩阵。这里在函数开始时,首先获取了鼠标滑动的前后两次点的位置lastPos和currPos。然后根据两点的Y坐标差deltaY,计算新的中心点center并变换至vtkImageSlice当前变换矩阵中,得到变换中心点,将其设置到原来的变换矩阵matrix中,并设置到vtkImageSlice中,最后执行interactor->Render()即可不断的根据鼠标移动刷新图像。

    Command对象定义完毕后,即可为交互对象InteractorStyle添加观察者,响应鼠标消息。这里可以在上节的程序上进行修改,前面代码一致,只需要在最后添加如下代码:

      vtkSmartPointer<vtkImageInteractionCallback> callback =

      vtkSmartPointer<vtkImageInteractionCallback>::New();

      callback->SetImageReslice(reslice);

      callback->SetInteractor(renderWindowInteractor);

      imagestyle->AddObserver(vtkCommand::MouseMoveEvent, callback);

      imagestyle->AddObserver(vtkCommand::LeftButtonPressEvent, callback);

      imagestyle->AddObserver(vtkCommand::LeftButtonReleaseEvent, callback);

    这里主要是定义了vtkImageInteractionCallback对象,并设置vtkImageSlice对象和vtkRenderWindowInteractor对象。然后为交互对象vtkInteractorStyle添加观察者来监控相应的消息,这里主要是三个消息:

    vtkCommand::LeftButtonPressEvent,

    vtkCommand::LeftButtonReleaseEvent,

    vtkCommand::MouseMoveEvent,

    当响应到这三个消息时,立即执行vtkImageInteractionCallback的Execute函数,以便实现切片的实时提取和更新。完成以后,运行程序,当鼠标在图像上移动时,会发现图像会跟着鼠标的移动而变化,神奇吧?有兴趣的话,还可以实现YZ平面、XZ平面切片提取,甚至是任意方向的切面提取。

    ==========欢迎转载,转载时请保留该声明信息==========

    版权归@东灵工作室所有,更多信息请访问东灵工作室

     

    教程系列导航:http://blog.csdn.net/www_doling_net/article/details/8763686

    ================================================

  • 相关阅读:
    Chelsio T520 T420安装iSER
    Chelsio T520 T420开启RDMA-NFS
    Chelsio T520 T420开启RDMA(服务器&&客户端)
    [JAR包] android引入JAR包,打包成JAR包,打包成Library项目,导入Library项目
    android EditText中inputType的属性列表
    JavaSwing标准对话框
    Android反编译与防止反编译
    spinner的使用
    Content Provider基础
    字体颜色添加阴影效果
  • 原文地址:https://www.cnblogs.com/h2zZhou/p/5112306.html
Copyright © 2011-2022 走看看