VTK implements DICOM simple reading

60d98f006fa7e3ad2984de8c13d9bf94.png

The image can be scrolled up and down by the mouse wheel, and the coordinate position and pixel value can be obtained in real time when the mouse moves.

#include <vtkActor.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkObjectFactory.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkActor2D.h>
#include <vtkDICOMImageReader.h>
#include <vtkInteractorStyleImage.h>    //为自定义交互式类准备
#include <vtkTextMapper.h>
#include <vtkTextProperty.h>
#include <vtkImageData.h>    
#include <vtkImageActor.h>
#include <vtkAutoInit.h>
#include <vtkCamera.h>
#include <vtkImageProperty.h>
#include <vtkImageReslice.h>
#include <vtkImageMapper3D.h>
#include <vtkMatrix4x4.h>
#include <vtkLookupTable.h>
#include <vtkImageMapToColors.h>  
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkRenderingContextOpenGL2);
#include <sstream>


namespace {


  // helper class to format slice status message
  class StatusMessage                                   
  {
  public:
    static std::string Format(int slice, int maxSlice,short pixels,int xx,int yy)
{
      std::stringstream tmp;
      tmp << "X " << xx << "  Y " << yy << "  Pixel " << pixels << "\n" << "Slice Number  " << slice + 1 << "/" << maxSlice + 1;
      return tmp.str();
    }
  };


  // Define own interaction style
  class myVtkInteractorStyleImage2 : public vtkInteractorStyleImage            //自定义交互式类
  {
  public:
    static myVtkInteractorStyleImage2* New();
    vtkTypeMacro(myVtkInteractorStyleImage2, vtkInteractorStyleImage);


  protected:
    vtkImageReslice* _ImageReslice;
    vtkImageMapToColors* _MapToColors;
    vtkTextMapper* _StatusMapper;
    vtkRenderWindowInteractor* _Interactor;
    vtkRenderer* _Renderer;
    vtkImageData* _imageData;
    int _Slice;
    int _MinSlice;
    int _MaxSlice;
    short PixelAndPosition[3] = { 0,-100,-100 };


  public:
    void SetImageReslice(vtkImageReslice* ImageReslice,int MinSlice,int MaxSlice, vtkImageMapToColors* MapToColors, vtkRenderWindowInteractor* Interactor, vtkRenderer* Renderer)
{
      _ImageReslice = ImageReslice;
      _MapToColors = MapToColors;
      _Interactor = Interactor;
      _Renderer = Renderer;
      _MinSlice = MinSlice;
      _MaxSlice = MaxSlice;
      _Slice = _MinSlice;
      cout << _Slice << endl;
      _imageData = ImageReslice->GetOutput();


    }


    void SetStatusMapper(vtkTextMapper* statusMapper)
{
      _StatusMapper = statusMapper;
    }


  protected:
    void MoveSliceForward()
{
      if (_Slice < _MaxSlice)
      {
        _Slice += 1;
        _ImageReslice->Update();
        double spacing = _ImageReslice->GetOutput()->GetSpacing()[2];
        vtkMatrix4x4* matrix = _ImageReslice->GetResliceAxes();
        double centz = matrix->GetElement(2, 3);
        centz += spacing;
        matrix->SetElement(2, 3, centz);
        _MapToColors->Update();
        _Interactor->Render();
        _imageData = _ImageReslice->GetOutput();
        MoveMouse();
        std::string msg = StatusMessage::Format(_Slice, _MaxSlice, PixelAndPosition[0], PixelAndPosition[1], PixelAndPosition[2]);
        _StatusMapper->SetInput(msg.c_str());
        _MapToColors->Update();
        _Interactor->Render();
      }
    }


    void MoveSliceBackward()
{
      if (_Slice > _MinSlice)
      {
        _Slice -= 1;
        _ImageReslice->Update();
        double spacing = _ImageReslice->GetOutput()->GetSpacing()[2];
        vtkMatrix4x4* matrix = _ImageReslice->GetResliceAxes();
        double centz = matrix->GetElement(2, 3);
        centz -= spacing;
        matrix->SetElement(2, 3, centz);


        _MapToColors->Update();
        _Interactor->Render();


        _imageData = _ImageReslice->GetOutput();
        MoveMouse();
        std::string msg = StatusMessage::Format(_Slice, _MaxSlice, PixelAndPosition[0], PixelAndPosition[1], PixelAndPosition[2]);
        _StatusMapper->SetInput(msg.c_str());
        _MapToColors->Update();
        _Interactor->Render();
      }
    }
    void MoveMouse()
{
      int* pos = this->GetInteractor()->GetEventPosition();   
      int* DataSize = _ImageReslice->GetOutput()->GetDimensions();
      int* rendsize = _Renderer->GetSize();
      int MinSize = 9999;
      for (int i = 0; i < 2; i++)
      {
        if (rendsize[i] < MinSize)
        {
          MinSize = rendsize[i];
        }
      }
      int* windowsize = _Interactor->GetRenderWindow()->GetSize();


      int posmin_x = (windowsize[0] - MinSize) / 2;
      int posmax_x = posmin_x + MinSize;
      int posmin_y = (windowsize[1] - MinSize) / 2;
      int posmax_y = posmin_y + MinSize;
      float scales = float(DataSize[0]) / float(MinSize);


      if (pos[0] >= posmin_x && pos[0] < posmax_x && pos[1] >= posmin_y && pos[1] < posmax_y)
      {
        int index_x = pos[0] - posmin_x;
        int index_y = pos[1] - posmin_y;
        int posxx = int(index_x * scales);
        int posyy = int(index_y * scales);
        if (posxx > DataSize[0] - 1)
        {
          posxx = DataSize[0] - 1;
        }
        if (posyy > DataSize[1] - 1)
        {
          posyy = DataSize[1] - 1;
        }


        short pixelvalue = (short)_imageData->GetScalarComponentAsDouble(posxx, posyy, 0, 0);
        PixelAndPosition[0] = pixelvalue;
        PixelAndPosition[1] = posxx;
        PixelAndPosition[2] = posyy;
      }
      else
      {
        PixelAndPosition[0] = 0;
        PixelAndPosition[1] = -100;
        PixelAndPosition[2] = -100;
      }
    }


    virtual void OnKeyDown()              
{
      std::string key = this->GetInteractor()->GetKeySym();
      if (key.compare("Up") == 0)
      {
        MoveSliceForward();
      }
      else if (key.compare("Down") == 0)
      {
        MoveSliceBackward();
      }
      vtkInteractorStyleImage::OnKeyDown();
    }


    virtual void OnMouseWheelForward()    
{
      MoveSliceForward();
    }


    virtual void OnMouseWheelBackward()    
{
      if (_Slice > _MinSlice)
      {
        MoveSliceBackward();
      }
    }


    virtual void OnMouseMove()   
{
      MoveMouse();


      _MapToColors->Update();
      _Interactor->Render();


      std::string msg = StatusMessage::Format(_Slice, _MaxSlice, PixelAndPosition[0],PixelAndPosition[1], PixelAndPosition[2]);
      _StatusMapper->SetInput(msg.c_str());


      _MapToColors->Update();
      _Interactor->Render();


      vtkInteractorStyleImage::OnMouseMove();   
    }
  };


  vtkStandardNewMacro(myVtkInteractorStyleImage2);   // 宏定义
} // namespace






int main()
{
  // 创建 DICOM 图像阅读器
  vtkSmartPointer<vtkDICOMImageReader> reader =
    vtkSmartPointer<vtkDICOMImageReader>::New();
  reader->SetDirectoryName("/*****");
  reader->Update();


  int dimss[3];
  reader->GetOutput()->GetDimensions(dimss);
  cout << "x:" << dimss[0] << "y:" << dimss[1] << "z:" << dimss[2] << endl;


  //文字渲染器
  // 创建图像 Actor
  //vtkTextProperty是VTK中的一个类,主要用于控制文本(文字)属性的显示,包括文字大小、颜色、字体、对齐方式等。
  vtkNew<vtkTextProperty> sliceTextProp;
  sliceTextProp->SetFontFamilyToCourier();        //设置为Courier字体
  sliceTextProp->SetFontSize(20);                 //字号为20
  sliceTextProp->SetVerticalJustificationToBottom();   //一种对齐方式,默认水平居中对齐。SetVerticalJustificationToTop顶部对齐SetVerticalJustificationToBottom垂直对齐到底部
  sliceTextProp->SetJustificationToLeft();     //左侧对齐(SetJustificationToLeft)或右侧对齐(SetJustificationToRight)。


  //vtkTextProperty类通常与vtkTextMapper一起使用。vtkTextMapper是一个用于将一段字符串渲染成二维纹理贴图的类。
  //使用vtkTextProperty,用户可以控制文本的各种属性,包括字体、颜色、大小、对齐方式等。
  vtkNew<vtkTextMapper> sliceTextMapper;
  std::string msg = StatusMessage::Format(0,
    dimss[2] - 1,0,-100,-100);
  sliceTextMapper->SetInput(msg.c_str());     // 设置要显示的文本内容
  sliceTextMapper->SetTextProperty(sliceTextProp);


  vtkNew<vtkActor2D> sliceTextActor;
  sliceTextActor->SetMapper(sliceTextMapper);
  sliceTextActor->SetPosition(15, 20);            //设置文本位置(坐标左下角为(0,0),(x,y))


  // usage hint message
  vtkNew<vtkTextProperty> usageTextProp;
  usageTextProp->SetFontFamilyToCourier();
  usageTextProp->SetFontSize(14);
  usageTextProp->SetVerticalJustificationToTop();
  usageTextProp->SetJustificationToLeft();


  vtkNew<vtkTextMapper> usageTextMapper;
  usageTextMapper->SetInput(
    "- Slice with mouse wheel\n  or Up/Down-Key\n- Zoom with pressed right\n "
    " mouse button while dragging");
  usageTextMapper->SetTextProperty(usageTextProp);


  vtkNew<vtkActor2D> usageTextActor;
  usageTextActor->SetMapper(usageTextMapper);
  usageTextActor->GetPositionCoordinate()
    ->SetCoordinateSystemToNormalizedDisplay();   //将该行文字的位置坐标系设置为规范化屏幕显示坐标系,其坐标原点位于窗口左下角,坐标范围在[0,1]之间。
  usageTextActor->GetPositionCoordinate()->SetValue(0.05, 0.95);  //根据范围设置位置


  // 创建渲染器
  vtkNew<vtkNamedColors> colors;
  vtkSmartPointer<vtkRenderer> renderertext = vtkSmartPointer<vtkRenderer>::New();
  renderertext->AddActor(usageTextActor);
  renderertext->AddActor(sliceTextActor);
  renderertext->SetBackground(colors->GetColor3d("SlateGray").GetData());


  int extent[6];
  double spacing[3];
  double origin[3];
  double center[3];


  reader->GetOutput()->GetExtent(extent);
  reader->GetOutput()->GetSpacing(spacing);
  reader->GetOutput()->GetOrigin(origin);


  center[0] = origin[0] + spacing[0] * 0.5 * (extent[0] + extent[1]);
  center[1] = origin[1] + spacing[1] * 0.5 * (extent[2] + extent[3]);
  center[2] = origin[2] ;
  static double axialElements[16] = {
    -1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, -1, 0,
    0, 0, 0, 1 };


  auto resliceAxes = vtkSmartPointer<vtkMatrix4x4>::New();  
  resliceAxes->DeepCopy(axialElements);
  resliceAxes->SetElement(0, 3, center[0]);  
  resliceAxes->SetElement(1, 3, center[1]);
  resliceAxes->SetElement(2, 3, center[2]);
  vtkSmartPointer<vtkImageReslice> ImageReslice = vtkSmartPointer<vtkImageReslice>::New();
  ImageReslice->SetInputConnection(reader->GetOutputPort());
  ImageReslice->SetOutputDimensionality(2);  
  ImageReslice->AutoCropOutputOn();


  double x[3] = { 1, 0, 0 };
  double y[3] = { 0, -1, 0 };
  double z[3] = { 0, 0, 1 };


#if 1
  ImageReslice->SetResliceAxes(resliceAxes);
#else
  ImageReslice->SetResliceAxesDirectionCosines(x, y, z);
  ImageReslice->SetResliceAxesOrigin(center);
#endif
  ImageReslice->SetInterpolationModeToLinear();
  ImageReslice->Update();


  auto lookupTable = vtkSmartPointer<vtkLookupTable>::New();
  lookupTable->SetRange(-1250, 1250);   //颜色映射的数值范围
  lookupTable->SetValueRange(0.0, 1.0);//设置颜色映射的数值范围对应的颜色值范围,
  lookupTable->SetSaturationRange(0.0, 0.0);//设置颜色映射的饱和度范围,这里将饱和度范围设置为 0.0,即无饱和度。
  lookupTable->SetRampToLinear(); //将颜色映射设置为线性渐变模式,这意味着颜色将在数值范围内均匀分布。
  lookupTable->Build();


  auto mapToColors = vtkSmartPointer<vtkImageMapToColors>::New();
  mapToColors->SetLookupTable(lookupTable);
  mapToColors->SetInputConnection(ImageReslice->GetOutputPort());
  mapToColors->Update();


  auto imageActor = vtkSmartPointer<vtkImageActor>::New();
  imageActor->GetMapper()->SetInputConnection(mapToColors->GetOutputPort());


  auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
  renderWindow->SetSize(800, 800);


  auto renderer = vtkSmartPointer<vtkRenderer>::New();
  renderer->AddActor(imageActor);
  renderer->SetBackground(colors->GetColor3d("SlateGray").GetData());


  // 设置视口大小为512x512,并使图像填充满视口
  double viewportSizeX = 512 / 800.0;
  double viewportSizeY = 512 / 800.0;
  renderer->SetViewport((1.0 - viewportSizeX) / 2.0, (1.0 - viewportSizeY) / 2.0, (1.0 + viewportSizeX) / 2.0, (1.0 + viewportSizeY) / 2.0);


  //设置相机位置,回填充满视口
  vtkCamera* camera = renderer->GetActiveCamera();
  camera->ParallelProjectionOn();     //用于将相机设置为平行投影模式。
  double* V = camera->GetViewUp();  //获取相机视角的上方向向量。默认(0, 1, 0),表示相机视角的上方向为正Y轴方向。
  V[0] = V[0];
  V[1] = -1;
  V[2] = V[2];
  camera->SetViewUp(V);
  int* windowSize = renderWindow->GetSize();


  double xc = center[0];    //计算图像中心位置x
  double yc = center[1];    //计算图像中心位置y
  double xd = (extent[1] - extent[0] + 1) * spacing[0];                  //图像物理宽
  double yd = (extent[3] - extent[2] + 1) * spacing[1];                  //图像物理高
  double d = camera->GetDistance();                                      //获得相机的距离
  double aspectwindow = static_cast<double>(windowSize[0]) / windowSize[1];  //计算窗口宽/高
  double aspectImage = xd / yd;                                              //图像宽/高 
  float RRR = 0;
  if (aspectImage < aspectwindow)           //如果窗口的宽高比较大      
  {
    RRR = xd / aspectImage;               
    camera->SetParallelScale(0.5 * RRR);   
  }
  else
  {
    RRR = xd / aspectwindow;
    camera->SetParallelScale(0.5 * RRR);
  }


  renderWindow->AddRenderer(renderertext);
  renderWindow->AddRenderer(renderer);
  auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
  vtkSmartPointer<myVtkInteractorStyleImage2> imagestyle =vtkSmartPointer<myVtkInteractorStyleImage2>::New();
  imagestyle->SetStatusMapper(sliceTextMapper);
  imagestyle->SetImageReslice(ImageReslice, extent[4], extent[5],mapToColors, interactor, renderer);
  interactor->SetInteractorStyle(imagestyle);
  interactor->SetRenderWindow(renderWindow);
  interactor->Initialize();
  renderWindow->Render();
  interactor->Start();
  return 0;
}

Summary: If anyone sees an error or has a better way to implement it, please remind me in time, "Cauchy's Pen" official account. Hope to get more guidance and learning, thank you very much.

The above is the whole content of this article, and finally thank you for reading!

Guess you like

Origin blog.csdn.net/weixin_41202834/article/details/131507762