3D体数据和2D序列切片集均以切片形式保存.
1 读序列图像
这部分的源码在文件Examples/IO/ImageSeriesReadWrite.cxx
中。
这个例子介绍了如何从独立的文件中读取一系列的2D
切片从而形成一个体。这里要用到itk::ImageSeriesReader
。这个类与提供需要被读的文件的一个列表的发生器联合工作。这里我们用itk::NumericSeriesFileNames
作为发生器。这个发生器是用一个字符串格式的打印风格。这里我们用一个像“file%03d.png
”的格式读取名字为file001.png
、
file002.png
、file003.png……的
PNG
文件。
//需要下面的头文件:
#include "itkImage.h"
#include "itkImageSeriesReader.h"
#include "itkImageFileWriter.h"
#include "itkNumericSeriesFileNames.h"
#include "itkPNGImageIO.h"
//我们首先定义PixelType和ImageType:
typedef unsigned char PixelType;
const unsigned int Dimension = 3;
typedef itk::Image< PixelType, Dimension > ImageType;
//图像类型用于定义reader和writer的模板参数:
typedef itk::ImageSeriesReader< ImageType > ReaderType;
typedef itk::ImageFileWriter< ImageType > WriterType;
ReaderType::Pointer reader = ReaderType::New( );
WriterType::Pointer writer = WriterType::New( );
//这时,我们声明一个文件名生成器的类型并创建一个例子:
typedef itk::NumericSeriesFileNames NameGeneratorType;
NameGeneratorType::Pointer nameGenerator = NameGeneratorType::New( );
//文件名发生器要求我们提供文件名的原文的模式,初始值的数量,最后值和被用作产生文件名的增加值。
nameGenerator->SetSeriesFormat( "vwe%03d.png" );
nameGenerator->SetStartIndex( first );
nameGenerator->SetEndIndex( last );
nameGenerator->SetIncrementIndex( 1 );
//实际执行读取任务的ImageIO对象现在被连接到ImageSeriesReader。这是确保我们用适于我们想要读取的文件类型的ImageIO对象的最安全方法。
reader->SetImageIO( itk::PNGImageIO::New( ) );
//输入文件的文件名必须提供给reader,而writer在一个单独的文件中写相同的体数据:
reader->SetFileNames( nameGenerator->GetFileNames( ) );
writer->SetFileName( outputFilename );
//我们将reader的输出连接到writer的输入上:
writer->SetInput( reader->GetOutput( ) );
//最后,我们调用writer的Updata函数触发通道的执行。问询必须放在一个try/catch模块里以防在读或写过程中异常情况的发生。
try
{
writer->Update( );
}
catch( itk::ExceptionObject & err )
{
std::cerr << "ExceptionObject caught !" << std::endl;
std::cerr << err << std::endl;
return EXIT_FAILURE;
}
实测代码:
![](https://img-blog.csdnimg.cn/9620ec8e92764ff0b9941d12cb578ba9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSs56m7,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/adf800f04a394f2e9f8fa339e8ca2988.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSs56m7,size_20,color_FFFFFF,t_70,g_se,x_16)
#include "itkImage.h"
#include "itkImageSeriesReader.h"
#include "itkImageFileWriter.h"
#include "itkNumericSeriesFileNames.h"
#include "itkGDCMImageIO.h"
int main(int argc, char** argv)
{
typedef unsigned char PixelType;
const unsigned int Dimension = 3;
typedef itk::Image< PixelType, Dimension > ImageType;
typedef itk::ImageSeriesReader< ImageType > ReaderType;
typedef itk::ImageFileWriter< ImageType > WriterType;
ReaderType::Pointer reader = ReaderType::New();
WriterType::Pointer writer = WriterType::New();
const unsigned int first = 0;
const unsigned int last = 85;
const char* outputFilename = "D:/leg.dcm";
typedef itk::NumericSeriesFileNames NameGeneratorType;
NameGeneratorType::Pointer nameGenerator = NameGeneratorType::New();
nameGenerator->SetSeriesFormat("D:/leg_dcm1/IM%02d.dcm");
nameGenerator->SetStartIndex(first);
nameGenerator->SetEndIndex(last);
nameGenerator->SetIncrementIndex(1);
reader->SetImageIO(itk::GDCMImageIO::New());
writer->SetImageIO(itk::GDCMImageIO::New());
reader->SetFileNames(nameGenerator->GetFileNames());
writer->SetFileName(outputFilename);
writer->SetInput(reader->GetOutput());
try
{
writer->Update();
}
catch (itk::ExceptionObject& err)
{
std::cerr << "ExceptionObject caught !" << std::endl;
std::cerr << err << std::endl;
std::cout << "succed " << std::endl;
system("pause");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
2 写序列图像
这部分的源码在文件Examples/IO/ImageReadImageSeriesWrite.cxx
中。
这个例子介绍了如何用itk::ImageSeriesWriter保存一幅图像。这个类能用每个文件包含一个2D切片的文件集方法保存一个3D体。
连续writer的类型必须考虑输入文件是一个3D体而输出文件是2D图像。
//这里声明输入图像的类型并用来声明reader的类型。它将是一个传统的3D图像reader。
typedef itk::Image< unsigned char, 3 > ImageType;
typedef itk::ImageFileReader< ImageType > ReaderType;
//用New( )构建reader对象并把结构赋给SmartPointer。从命令行中读出需要被读取的3D体的文件名并用SetFileName( )传给reader。
ReaderType::Pointer reader = ReaderType::New( );
reader->SetFileName( argv[1] );
//连续writer的类型必须考虑输入文件是一个3D体而输出文件是2D图像。另外,reader的输出作为输入传给writer。
typedef itk::Image< unsigned char, 2 > Image2DType;
typedef itk::ImageSeriesWriter< ImageType, Image2DType > WriterType;
WriterType::Pointer writer = WriterType::New( );
writer->SetInput( reader->GetOutput( ) );
//writer要求一个产生的文件名的清单。这个清单能够通过itk::NumericSeriesFileNames类的帮助产生。
typedef itk::NumericSeriesFileNames NameGeneratorType;
NameGeneratorType::Pointer nameGenerator = NameGeneratorType::New( );
//NumericSeriesFileNames类要求一个输入的字符串以便有一个生成所有输出切片的文件名的模板。这里我们用从命令行中读取出的一个前缀来排定字符串并添加PNG文件的扩展。
std::string format = argv[2];//文件前缀
format += "%03d.";//文件序列规则
format += argv[3]; // 文件格式
nameGenerator->SetSeriesFormat( format.c_str( ) );
//输入的字符串将通过设置第一和最后切片的值生成文件名。通过从输入图像中收集信息做这些事情。注意:在从reader读取任何信息之前,它的执行必须由Update( )触发,并且这个调用可能出现异常,所以必须放在一个try/catch模块中。
try
{
reader->Update( );
}
catch( itk::ExceptionObject & excp )
{
std::cerr << "Exception thrown while reading the image" << std::endl;
std::cerr << excp << std::endl;
}
//现在图像已经被读取,我们可以查询它的最大可能区域和恢复关于沿每个维度像素的数量:
ImageType::ConstPointer inputImage = reader->GetOutput( );
ImageType::RegionType region = inputImage->GetLargestPossibleRegion( );
ImageType::IndexType start = region.GetIndex( );
ImageType::SizeType size = region.GetSize( );
//有了这些信息,我们能找出确定第一和最后3D数据集的切片的数值。这些数值将会传递给文件名发生器对象,它会排列存储切片的文件名。
const unsigned int firstSlice = start[2];
const unsigned int lastSlice = start[2] + size[2] - 1;
nameGenerator->SetStartIndex( firstSlice );
nameGenerator->SetEndIndex( lastSlice );
nameGenerator->SetIncrementIndex( 1 );
//文件名发生器产生文件名列表并将它传给连续writer:
writer->SetFileNames( nameGenerator->GetFileNames( ) );
//最后,我们用writer的Update( )触发管道的执行。这时图像的切片保存在单独的文件中,每个文件包含一个单独的切片。用于这些切片的文件名由文件名发生器生成。
try
{
writer->Update( );
}
catch( itk::ExceptionObject & excp )
{
std::cerr << "Exception thrown while reading the image" << std::endl;
std::cerr << excp << std::endl;
}
实测代码:
![](https://img-blog.csdnimg.cn/a3a81aa77ee5442dad060c1c657b7c0a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSs56m7,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/54545c2a2f024bbaaf239c0937a73f0d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSs56m7,size_20,color_FFFFFF,t_70,g_se,x_16)
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageSeriesWriter.h"
#include "itkNumericSeriesFileNames.h"
#include "itkGDCMImageIO.h"
int main(int argc, char* argv[])
{
typedef itk::Image< unsigned char, 3 > ImageType;
typedef itk::ImageFileReader< ImageType > ReaderType;
ReaderType::Pointer reader = ReaderType::New();
reader->SetFileName("D:/leg.dcm");
typedef itk::Image< unsigned char, 2 > Image2DType;
typedef itk::ImageSeriesWriter< ImageType, Image2DType > WriterType;
WriterType::Pointer writer = WriterType::New();
writer->SetInput(reader->GetOutput());
typedef itk::NumericSeriesFileNames NameGeneratorType;
NameGeneratorType::Pointer nameGenerator = NameGeneratorType::New();
reader->SetImageIO(itk::GDCMImageIO::New());
writer->SetImageIO(itk::GDCMImageIO::New());
std::string format = "D:/aaa/";
format += "%03d.";
format += "dcm"; // filename extension
nameGenerator->SetSeriesFormat(format.c_str());
try
{
reader->Update();
}
catch (itk::ExceptionObject& excp)
{
std::cerr << "Exception thrown while reading the image" << std::endl;
std::cerr << excp << std::endl;
}
ImageType::ConstPointer inputImage = reader->GetOutput();
ImageType::RegionType region = inputImage->GetLargestPossibleRegion();
ImageType::IndexType start = region.GetIndex();
ImageType::SizeType size = region.GetSize();
const unsigned int firstSlice = start[2];
const unsigned int lastSlice = start[2] + size[2] - 1;
nameGenerator->SetStartIndex(firstSlice);
nameGenerator->SetEndIndex(lastSlice);
nameGenerator->SetIncrementIndex(1);
writer->SetFileNames(nameGenerator->GetFileNames());
try
{
writer->Update();
}
catch (itk::ExceptionObject& excp)
{
std::cerr << "Exception thrown while reading the image" << std::endl;
std::cerr << excp << std::endl;
}
return EXIT_SUCCESS;
}
注意:通过保存数据到单独的切片中,我们丢失了一些对于医学应用有用的信息,例如在毫米间距之内的信息。