DICOM 图像
20 世纪 70 年代,在 CT 引入之后,数字诊断图像模式例如 MRI 和电脑在临床应用的增加,美国放射医学学会 (ACR) 和国家电子制造学会 (NEMA) 认为有设置一个标准的需要,以便为不同的厂商制造的装置之间传递信息和图像带来方便。大多数医学领域的图像存储和传递都用的是 DICOM 标准。DICOM 文件由一个头文件和一个图像数据体构成。头文件包括标准和自由形成域。
这个例子介绍了如何读一个单独的 DICOM 切片以及把它写作另一个 DICOM 切片。在处理过程中应用亮度变化。
为了读和写切片,我们这里使用 itk::GDCMImageIO 类, itk::GDCMImageIO 类压缩了一个优先的 GDCM 库的连接。用这种方法我们就可以进行从 ITK 到 GDCM 提供的 DICOM 的范函性的存取。 GDCMImageIO 对象被作为 itk::ImageFileWriter 使用的 ImageIO 的对象连接。
基于ITK的读、写一幅 2D 的DICOM图像
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkGDCMImageIO.h"
#include <list>
#include <fstream>
//reader:读取DICOM格式图像 IM3
//writer1:将读取的DICOM图像再另存为DICOM图像 writer1.dcm
//writer2:读取的DICOM图像进行像素亮度改变调节后写出保存 writer2.png
//writer3:将亮度调节后的数据写成DICOM格式图像输出 writer3.dcm
int main( int argc, char* argv[] )
{
/* if( argc < 5 )
{
std::cerr << "Usage: " << std::endl;
std::cerr << argv[0] << " DicomImage OutputDicomImage ";
std::cerr << " OutputImage RescaleDicomImage\n";
return EXIT_FAILURE;
}*/
//声明像素类型和图像维数,并用它们实例化被读的图像类型
typedef signed short InputPixelType;
const unsigned int InputDimension = 2;
typedef itk::Image< InputPixelType, InputDimension > InputImageType;
//用图像类型实例化 reader 的类型,创建它并设置被读的图像的文件名:
typedef itk::ImageFileReader< InputImageType > ReaderType;
ReaderType::Pointer reader = ReaderType::New();
reader->SetFileName( "IM3" );
//GDCMImageIO 是一个读取和写入 DICOM V3 和 ACR/NEMA 图像的 ImageIO 类。在这里
//GDCMImageIO 对象被创建并与 ImageFileReader 相连。
typedef itk::GDCMImageIO ImageIOType;
ImageIOType::Pointer gdcmImageIO = ImageIOType::New();
reader->SetImageIO( gdcmImageIO );
/*调用 Update() 来触发读取过程。因为这个读取过程可能会导致异常,我们放置一个
问询在 try / catch 模块中*/
try
{
reader->Update();//reader:读取DICOM格式图像
}
catch (itk::ExceptionObject & e)
{
std::cerr << "exception in file reader " << std::endl;
std::cerr << e << std::endl;
return EXIT_FAILURE;
}
/*现在图像在内存中,通过 GetOutput() 我们可以对它进行存取。在当前例子的维护中,
焦点放在我们如何再一次将图像在新文件中保存成 DICOM 格式。
首先,我们必须实例化一个 ImageFileWriter 类型。这时,我们创建它并设置用于写的文
件名,连接被写的输入图像。在这个例子中,我们用不同的方法写图像,每种情况下我们用
不同的 writer ,我们列举 writer 对象的变量名及其类型*/
typedef itk::ImageFileWriter< InputImageType > Writer1Type;
Writer1Type::Pointer writer1 = Writer1Type::New();
writer1->SetFileName( "writer1.dcm" );
writer1->SetInput( reader->GetOutput() );
//我们需要明确地设置对于 writer 滤波器的合适的图像IO ,因为输入的 DICOM 是沿着写入
//过程被传递的。这个名称包含所有有效的 DICOM 文件应该包含的所有信息,像病人名字、
//病人ID 、机构名等等
writer1->SetImageIO( gdcmImageIO );
/*通过调用 Update() 来触发写入程序。因为执行会导致异常情况出现,我们放 Update() 问
询在一个 try / catch 模块里*/
try
{
writer1->Update();//writer1:将读取的DICOM图像再另存为DICOM图像
}
catch (itk::ExceptionObject & e)
{
std::cerr << "exception in file writer " << std::endl;
std::cerr << e << std::endl;
return EXIT_FAILURE;
}
/*现在我们使用重新调节亮度图像滤波器对图像进行重新调节。为了这个目的,我们使用
一个更适合的像素类型:无符字符型代替有符短型。输出图像的最小值和最大值在缩放滤波
器中明确定义*/
typedef unsigned char WritePixelType;
typedef itk::Image< WritePixelType, 2 > WriteImageType;
typedef itk::RescaleIntensityImageFilter<
InputImageType, WriteImageType > RescaleFilterType;
RescaleFilterType::Pointer rescaler = RescaleFilterType::New();
//改变图像的像素范围(亮度调节)
rescaler->SetOutputMinimum( 0 );
rescaler->SetOutputMaximum( 255 );
/*我们现在创建第二个 writer 对象,保存图像到一个文件中。这时不是 DICOM 格式。做这
个只是为了校验图像,对照在这个例子里以后被保存成 DICOM 的格式*/
typedef itk::ImageFileWriter< WriteImageType > Writer2Type;
Writer2Type::Pointer writer2 = Writer2Type::New();
writer2->SetFileName("writer2.png");
rescaler->SetInput(reader->GetOutput());
writer2->SetInput(rescaler->GetOutput());
//writer 能够通过调用 try/catch 模块里的 Update( ) 来执行
try
{
writer2->Update();//writer2:读取的DICOM图像进行像素亮度改变调节后写出保存
}
catch (itk::ExceptionObject & e)
{
std::cerr << "exception in file writer " << std::endl;
std::cerr << e << std::endl;
return EXIT_FAILURE;
}
/*我们现在保存同一个重新调节的图像到一个 DICOM 格式的文件中。为此我们仅仅需要
设置 itk::ImageFileWriter ,并传递给它重新调节的图像作为输入*/
typedef itk::ImageFileWriter< WriteImageType > Writer3Type;
Writer3Type::Pointer writer3 = Writer3Type::New();
writer3->SetFileName( "writer3.dcm" );
writer3->SetInput( rescaler->GetOutput() );
/*我们现在需要明确地设置合适的图像 IO(GDCMImageIO) ,所以我们必须告诉
ImageFileWriter 不要从输入中用 MetaDataDictionary 而是从 GDCMImageIO 中用,因为 IO 包含
DICOM 的精确信息。GDCMImageIO对象将会自动地探测像素类型,这种情况下无符字符串和GDCMImageIO 对象
将会更新 DICOM 头文件信息。*/
writer3->UseInputMetaDataDictionaryOff ();
writer3->SetImageIO( gdcmImageIO );//写成DICOM 格式
//最后我们调用一个 try/catch 模块的 Update( ) 来触发 DICOM writer 的执行
try
{
writer3->Update();//writer3将亮度调节后的数据写成DICOM格式图像输出
}
catch (itk::ExceptionObject & e)
{
std::cerr << "Exception in file writer " << std::endl;
std::cerr << e << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
//IM3:读取的输入DICOM格式图像
//writer1:将读取的DICOM图像再另存为DICOM图像 writer1.dcm
//writer2:读取的DICOM图像进行像素亮度改变调节后写出保存 writer2.png
//writer3:将亮度调节后的数据写成DICOM格式图像输出 writer3.dcm
输入MR图像:IM3 输出DCM图像:writer1.dcm
输出png图像:writer2.png
输出DCM图像:writer3.dcm