ITK, VTK read DiCOM file information and realize interactive preview

DICOM file introduction

The full name of DICOM is Digital Imaging and Communications in Medicine (Digital Imaging and Communications in Medicine), which is a standard for medical images and related information. It also defines the medical image format for clinical data;

The DICOM data format contains a large number of attributes, such as basic information such as patient name, ID, age, etc., which are stored in "header files". This storage method avoids mismatching patients and images;

A patient sample contains multiple DICOM sub-files, and a single DICOM object contains only one frame of pixel data. Frames exist in sequence, and can be switched back and forth to form a scene similar to movie screening, similar to scanning and imaging a 3D image , Divided into equal thickness along the vertical direction

Snipaste_2020-07-11_11-25-24.png

ITK reads DICOM data

ITK has two types according to the DICOM file reading method, single DICOM reading or serial DICOM reading

ITK reads a single DICOM file

  • itkImageSeriesReader;
  • itkGDCMImageIO;

After reading, you can use Get*** function in itkGCOMImageIO to obtain patient-related information and print it out. One thing to note here is that the data type corresponding to DICOM is signed short; core code:

	string DirectoryPath = "D:/Data_Training/Dicom_data/215020265/AT000002.152046493.dcm";
	

	typedef signed short InternalPixelType;
	const unsigned int  Dimension = 2;
	using InternalImageType = itk::Image<InternalPixelType, Dimension>;

	typedef itk::Image<InternalPixelType, Dimension> ImageType;
	typedef itk::ImageSeriesReader<ImageType> ReaderType;
	
	typedef itk::GDCMImageIO ImageIOType;
	
	ImageIOType::Pointer gdcmIO = ImageIOType::New();
	
	ReaderType::Pointer reader = ReaderType::New();
	reader->SetImageIO(gdcmIO);
	reader->SetFileNames(DirectoryPath);

	// Reading Data;
	try
	{
		reader->Update();
		reader->GetMetaDataDictionary();
		gdcmIO->GetMetaDataDictionary();// 读取头文件信息;

		//信息赋值
		char* name = new char[50];
		char* patientID = new char[50];
		char* time = new char[50];
		char* manufacture = new char[50];
		char* modility = new char[50];
		char* hospital = new char[50];
		char* sex = new char[50];
		char* age = new char[50];
		char* description = new char[100];
		

		
		
		int pixelType = gdcmIO->GetPixelType();
		int componentType = gdcmIO->GetComponentType();
		int fileType = gdcmIO->GetFileType();
		ImageIOType::ByteOrder byteOrder;
		byteOrder = gdcmIO->GetByteOrder();
		
		unsigned int dim = 0;
		gdcmIO->GetDimensions(dim);
		ImageIOType::SizeType imgsize;
		imgsize = gdcmIO->GetImageSizeInPixels();
		int componetSize = gdcmIO->GetComponentSize();
		int dimension = gdcmIO->GetNumberOfDimensions();
		int ori = 0;
	
		gdcmIO->GetOrigin(ori);
		int spa = 0;

		gdcmIO->GetPatientSex(sex);
		gdcmIO->GetPatientAge(age);
		gdcmIO->GetStudyDescription(description);
		gdcmIO->GetSpacing(spa);
		gdcmIO->GetPatientName(name);
		gdcmIO->GetModality(modility);
		gdcmIO->GetPatientID(patientID);
		gdcmIO->GetManufacturer(manufacture);
		gdcmIO->GetStudyDate(time);
		ImageIOType::TCompressionType compressType;
		compressType = gdcmIO->GetCompressionType();
		gdcmIO->GetInstitution(hospital);
		ImageType::SpacingType spacetype;
		spacetype = reader->GetOutput()->GetSpacing();
		ImageType::PointType origin;
		origin = reader->GetOutput()->GetOrigin();
		
		
		
		cout << "name:" << name << endl;
		cout << "age:" << age << endl;
		cout << "sex:" << sex << endl;
		cout << "description:" << description << endl;
		cout << "hospital:" << hospital << endl;
		cout << "modility:" << modility << endl;
		cout << "manfacture:" << manufacture << endl;
		cout << "dim:" << dim << endl;
		cout << "origin:" << origin << endl;
		cout << "Time:" << time << endl;
		cout << "patientID:" << patientID << endl;
		
	}
	catch (const itk::ExceptionObject& excp)
	{
		cout << " Reading Exceptaion Caught" << endl;
		cout << excp.what() << endl;
		
		return EXIT_FAILURE;
	}
	

ITK read serial DICOM file

  • itkImageSeriesReader;
  • itkGDCMImageIO;
  • itkGDCMSeriesFileNames;

Different from single-sheet reading, there is an additional itkGDCMSeriesFileNames class, which is used to package DICOM serial files, and then pass in GDCMImageIO for reading

	string DirectoryPath = "D:/Data_Training/Dicom_data/215020265";

	typedef signed short InternalPixelType;
	const unsigned int  Dimension = 3;
	using InternalImageType = itk::Image<InternalPixelType, Dimension>;

	typedef itk::Image<InternalPixelType, Dimension> ImageType;
	typedef itk::ImageSeriesReader<ImageType> ReaderType;
	
	typedef itk::GDCMImageIO ImageIOType;
	typedef itk::GDCMSeriesFileNames NamesGeneratorType;
	
	ImageIOType::Pointer gdcmIO = ImageIOType::New();
	NamesGeneratorType::Pointer namesGenerator = NamesGeneratorType::New();
	
	namesGenerator->SetInputDirectory(DirectoryPath);
	
	const ReaderType::FileNamesContainer & filenames =
		namesGenerator->GetInputFileNames();

	std::size_t numberOfFileNames = filenames.size();
	std::cout << numberOfFileNames << std::endl;
	
	
	ReaderType::Pointer reader = ReaderType::New();
	reader->SetImageIO(gdcmIO);
	reader->SetFileNames(filenames);
	

	// Reading Data;
	try
	{
		reader->Update();
		reader->GetMetaDataDictionary();
		gdcmIO->GetMetaDataDictionary();// 读取头文件信息;

		//信息赋值
		char* name = new char[50];
		char* patientID = new char[50];
		char* time = new char[50];
		char* manufacture = new char[50];
		char* modility = new char[50];
		char* hospital = new char[50];
		char* sex = new char[50];
		char* age = new char[50];
		char* description = new char[100];
		

		
		
		int pixelType = gdcmIO->GetPixelType();
		int componentType = gdcmIO->GetComponentType();
		int fileType = gdcmIO->GetFileType();
		ImageIOType::ByteOrder byteOrder;
		byteOrder = gdcmIO->GetByteOrder();
		
		unsigned int dim = 0;
		gdcmIO->GetDimensions(dim);
		ImageIOType::SizeType imgsize;
		imgsize = gdcmIO->GetImageSizeInPixels();
		int componetSize = gdcmIO->GetComponentSize();
		int dimension = gdcmIO->GetNumberOfDimensions();
		int ori = 0;
	
		gdcmIO->GetOrigin(ori);
		int spa = 0;

		gdcmIO->GetPatientSex(sex);
		gdcmIO->GetPatientAge(age);
		gdcmIO->GetStudyDescription(description);
		gdcmIO->GetSpacing(spa);
		gdcmIO->GetPatientName(name);
		gdcmIO->GetModality(modility);
		gdcmIO->GetPatientID(patientID);
		gdcmIO->GetManufacturer(manufacture);
		gdcmIO->GetStudyDate(time);
		ImageIOType::TCompressionType compressType;
		compressType = gdcmIO->GetCompressionType();
		gdcmIO->GetInstitution(hospital);
		ImageType::SpacingType spacetype;
		spacetype = reader->GetOutput()->GetSpacing();
		ImageType::PointType origin;
		origin = reader->GetOutput()->GetOrigin();
		
		
		
		cout << "name:" << name << endl;
		cout << "age:" << age << endl;
		cout << "sex:" << sex << endl;
		cout << "description:" << description << endl;
		cout << "hospital:" << hospital << endl;
		cout << "modility:" << modility << endl;
		cout << "manfacture:" << manufacture << endl;
		cout << "dim:" << dim << endl;
		cout << "origin:" << origin << endl;
		cout << "Time:" << time << endl;
		cout << "patientID:" << patientID << endl;
		
	}
	catch (const itk::ExceptionObject& excp)
	{
		cout << " Reading Exceptaion Caught" << endl;
		cout << excp.what() << endl;
		
		return EXIT_FAILURE;
	}
	

The results are as follows:

Snipaste_2020-07-11_10-42-43.png

In addition to the basic information of the patient’s age, gender, and ID above, use itkGDCMImageIO to obtain more patient-related information. Here , the following functions are obtained from the source code of itkGDCMImageIO.h . You can refer to it:

Snipaste_2020-07-11_11-54-48.png

VTK read DICOM

vtk reading DICOM is relatively simple, because VTK encapsulates related DICOM reading class: vtkDICOMImageReader, which can be operated directly;

VTK reads a single DICOM file and previews it

  • vtkDICOMImageReader
#include<vtkSmartPointer.h>
#include<vtkImageViewer2.h>
#include<vtkDICOMImageReader.h>
#include<vtkRenderWindow.h>
#include<vtkRenderWindowInteractor.h>
#include<vtkRenderer.h>
#include<iostream>
#include<string.h>
#include<vtkAutoInit.h>


VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);


int main()
{
	using namespace std;
	
	string OpenPath = "D:/Data_Training/Dicom_data/215020265/AT000001.151955287.dcm";
	
	vtkSmartPointer<vtkDICOMImageReader> reader =
		vtkSmartPointer<vtkDICOMImageReader>::New();
	reader->SetFileName(OpenPath.c_str());
	reader->Update();


	vtkSmartPointer<vtkImageViewer2> imageViewer =
		vtkSmartPointer<vtkImageViewer2>::New();
	imageViewer->SetInputConnection(reader->GetOutputPort());

	vtkSmartPointer<vtkRenderWindowInteractor> renWin =
		vtkSmartPointer<vtkRenderWindowInteractor>::New();
	imageViewer->SetupInteractor(renWin);
	imageViewer->Render();
	imageViewer->GetRenderer()->ResetCamera();
	imageViewer->Render();

	renWin->Start();

	return EXIT_SUCCESS;
}

Because an interactive window is added, zoom in and out can be used

VTKiMAge1.gif

VTK reads serial DICOM files and previews them

  • vtkDICOMImageReader

The usage is similar to single sheet reading, the difference is that the folder read in the sequence diagram, and the single sheet read is a single DICOM;

#include<vtkSmartPointer.h>
#include<vtkImageViewer2.h>
#include<vtkDICOMImageReader.h>
#include<vtkRenderWindow.h>
#include<vtkRenderWindowInteractor.h>
#include<vtkRenderer.h>
#include<vtkTextProperty.h>
#include<vtkTextMapper.h>
#include<vtkActor2D.h>
#include<vtkInteractorStyleImage.h>
#include<vtkObjectFactory.h>
#include<iostream>
#include<string.h>
#include<vtkAutoInit.h>
#include<sstream>


VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType)

using namespace std;
class StatusMessage{
    
    
public:
	static std::string Format(int slice, int maxSlice)
	{
    
    
		std::stringstream tmp;
		tmp << "Slice Number  " << slice + 1 << "/" << maxSlice + 1;
		
		return tmp.str();
	}

};

// Define own interation style;
class myVtkInteractorStyleImage : public vtkInteractorStyleImage
{
    
    
public:
	static myVtkInteractorStyleImage* New();
	vtkTypeMacro(myVtkInteractorStyleImage, vtkInteractorStyleImage);
	
protected:
	vtkImageViewer2* _ImageViewer;
	vtkTextMapper* _StatusMapper;
	int _Slice;
	int _MinSlice;
	int _MaxSlice;
	
public:
	void SetImageViewer(vtkImageViewer2* imageViwer)
	{
    
    
		_ImageViewer = imageViwer;
		_MinSlice = imageViwer->GetSliceMin();
		_MaxSlice = imageViwer->GetSliceMax();
		_Slice = _MinSlice;
		
		cout << "Slice : Min = " << _MinSlice << ", Max = " << _MaxSlice << endl;

	}
	
	void SetStatusMapper(vtkTextMapper* statusMapper)
	{
    
    
		_StatusMapper = statusMapper;
	}
	
protected:
	void MoveSliceForward()
	{
    
    
		if (_Slice<_MaxSlice)
		{
    
    
			_Slice += 1;
			cout << " MoveSliceForward::Slice = " << _Slice << endl;
			_ImageViewer->SetSlice(_Slice);
			std::string msg = StatusMessage::Format(_Slice, _MaxSlice);
			_StatusMapper->SetInput(msg.c_str());
			_ImageViewer->Render();

		}
	}

	void MoveSliceBackward()
	{
    
    
		if(_Slice>_MinSlice)
		{
    
    
			_Slice -= 1;
			cout << "MoveSliceBackward::Slice = " << _Slice << endl;
			_ImageViewer->SetSlice(_Slice);
			std::string msg = StatusMessage::Format(_Slice, _MaxSlice);
			_StatusMapper->SetInput(msg.c_str());
			_ImageViewer->Render();

		}
	}

	virtual void OnKeyDown()
	{
    
    
		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();
		}
	}
};


vtkStandardNewMacro(myVtkInteractorStyleImage);



int main()
{
    
    
	using namespace std;
	
	string OpenPath = "D:/Data_Training/Dicom_data/215020265/";
	
	vtkSmartPointer<vtkDICOMImageReader> reader =
		vtkSmartPointer<vtkDICOMImageReader>::New();
	reader->SetDirectoryName(OpenPath.c_str());
	reader->Update();
	

	vtkSmartPointer<vtkImageViewer2> imageViewer =
		vtkSmartPointer<vtkImageViewer2>::New();
	imageViewer->SetInputConnection(reader->GetOutputPort());



	//Slice Status
	vtkSmartPointer<vtkTextProperty> sliceTextProp =
		vtkSmartPointer<vtkTextProperty>::New();
	sliceTextProp->SetFontFamilyToCourier();
	sliceTextProp->SetFontSize(20);
	sliceTextProp->SetVerticalJustificationToBottom();
	sliceTextProp->SetJustificationToLeft();

	vtkSmartPointer<vtkTextMapper> sliceTextMapper =
		vtkSmartPointer<vtkTextMapper>::New();
	string msg = StatusMessage::Format(imageViewer->GetSliceMin(), imageViewer->GetSliceMax());
	sliceTextMapper->SetInput(msg.c_str());
	cout << "msg" << msg.c_str() << endl;

	sliceTextMapper->SetTextProperty(sliceTextProp);
	
	vtkSmartPointer<vtkActor2D> sliceTextActor =
		vtkSmartPointer<vtkActor2D>::New();
	sliceTextActor->SetMapper(sliceTextMapper);
	sliceTextActor->SetPosition(15, 10);
	

	//usage hint message;
	vtkSmartPointer<vtkTextProperty> usageTextProp =
		vtkSmartPointer<vtkTextProperty>::New();
	usageTextProp->SetFontFamilyToCourier();
	usageTextProp->SetFontSize(14);
	usageTextProp->SetVerticalJustificationToTop();
	usageTextProp->SetJustificationToLeft();
	
	vtkSmartPointer<vtkTextMapper> usageTextMapper =
		vtkSmartPointer<vtkTextMapper>::New();
	usageTextMapper->SetInput("Slice with mouse wheel\n or Up/Down-Key\n =Zoom weh pressed right\n mouse button while dragging");
	usageTextMapper->SetTextProperty(usageTextProp);
	
	vtkSmartPointer<vtkActor2D> usageTextActor =
		vtkSmartPointer <vtkActor2D>::New();
	usageTextActor->SetMapper(usageTextMapper);
	usageTextActor->GetPositionCoordinate()->SetCoordinateSystemToNormalizedDisplay();
	usageTextActor->GetPositionCoordinate()->SetValue(0.05, 0.95);


	vtkSmartPointer<myVtkInteractorStyleImage> myInteractorStyle =
		vtkSmartPointer<myVtkInteractorStyleImage>::New();
	
	myInteractorStyle->SetImageViewer(imageViewer);
	myInteractorStyle->SetStatusMapper(sliceTextMapper);


	vtkSmartPointer<vtkRenderWindowInteractor> renWin =
		vtkSmartPointer<vtkRenderWindowInteractor>::New();
	imageViewer->SetupInteractor(renWin);
	renWin->SetInteractorStyle(myInteractorStyle);

	imageViewer->GetRenderer()->AddActor2D(sliceTextActor);
	imageViewer->GetRenderer()->AddActor2D(usageTextActor);


	imageViewer->Render();
	imageViewer->GetRenderer()->ResetCamera();
	imageViewer->Render();

	renWin->Start();

	return EXIT_SUCCESS;
}

The code is excerpted from the official, where a custom interaction class is defined, which can be replaced by sliding the mouse or pressing the button. And two text comments are added to the interface through the class vtkTextMapper, which is convenient for guiding users

The preview effect is as follows:

VTKimage2.gif
(The article was first published on the public account : Mr. Z's notes )

Reference:

1,https://itk.org/Doxygen/html/classitk_1_1ImageSeriesReader.html

2,https://lorensen.github.io/VTKExamples/site/Cxx/IO/ReadDICOMSeries/`

Guess you like

Origin blog.csdn.net/weixin_42512684/article/details/107461223