【ITK學習筆記】2. 影象的讀取
1 簡單的讀取
簡單的讀取只需要 itkImageFileReader.h
以及 itkImageFileWriter.h
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
int main(int argc,char ** argv){
typedef itk::Image<unsigned short,2> ImageType;
typedef itk::ImageFileReader<ImageType> ReaderType;
typedef itk::ImageFileWriter<ImageType> WriterType;
ReaderType::Pointer reader=ReaderType::New();
WriterType::Pointer writer=WriterType::New();
reader->SetFileName(argv[1]); //要讀取的檔名
writer->SetFileName(argv[2]); //寫入的檔名
writer->SetInput(reader->GetOutput());
try {
writer->Update();
}catch(itk::ExceptionObject &err){
std::cerr<<"ExceptionObject Caught"<<std::endl;
std::cerr<<err<<std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
2 RGB影象的讀取
RGB影象只要設定影象的畫素為RGBPixel
即可
...//修改
#include "itkRGBPixel.h"
typedef itk::RGBPixel<unsigned short> PixelType;
typedef itk::ImageType<PixelType,2> ImageType;
....//下面的相同
3 影象的顯示 ITKtoVTK
有兩種方法
QuickView
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
cmake_policy(SET CMP0020 NEW)
project(QuickViewDemo)
find_package(ITK REQUIRED)
include(${ITK_USE_FILE})
if (ITKVtkGlue_LOADED)
find_package(VTK REQUIRED)
include(${VTK_USE_FILE})
else()
find_package(ItkVtkGlue REQUIRED)
include(${ItkVtkGlue_USE_FILE})
set(Glue ItkVtkGlue)
endif()
add_executable(QuickViewDemo QuickViewDemo.cxx)
target_link_libraries(QuickViewDemo
${Glue} ${VTK_LIBRARIES} ${ITK_LIBRARIES})
QuickViewDemo.cxx
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkRescaleIntensityImageFilter.h"
#include "QuickView.h"
int main(int argc, char *argv[])
{
typedef itk::Image<unsigned char, 2> ImageType;
typedef itk::ImageFileReader<ImageType> ReaderType;
ReaderType::Pointer reader = ReaderType::New();
reader->SetFileName(argv[1]);
typedef itk::RescaleIntensityImageFilter< ImageType, ImageType > RescaleFilterType;
RescaleFilterType::Pointer rescaleFilter = RescaleFilterType::New();
rescaleFilter->SetInput(reader->GetOutput());
rescaleFilter->SetOutputMinimum(0);
rescaleFilter->SetOutputMaximum(255);
QuickView viewer;
viewer.AddImage(reader->GetOutput());
viewer.AddImage(rescaleFilter->GetOutput());
viewer.Visualize();
return EXIT_SUCCESS;
}
itkImageToVTKImageFilter
ImageToVTKImageFilter.cxx
#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageToVTKImageFilter.h>
#include "vtkVersion.h"
#include "vtkImageViewer.h"
#include "vtkImageMapper3D.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"
#include "vtkImageActor.h"
#include "vtkInteractorStyleImage.h"
#include "vtkRenderer.h"
#include "itkRGBPixel.h"
int main(int argc, char *argv[])
{
typedef itk::Image<itk::RGBPixel<unsigned char>, 2> ImageType;
typedef itk::ImageFileReader<ImageType> ReaderType;
typedef itk::ImageToVTKImageFilter<ImageType> ConnectorType;
ReaderType::Pointer reader = ReaderType::New();
ConnectorType::Pointer connector = ConnectorType::New();
reader->SetFileName(argv[1]);
connector->SetInput(reader->GetOutput());
vtkSmartPointer<vtkImageActor> actor = vtkSmartPointer<vtkImageActor>::New();
connector->Update();
actor->GetMapper()->SetInputData(connector->GetOutput());
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(actor);
renderer->ResetCamera();
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor=
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style =
vtkSmartPointer<vtkInteractorStyleImage>::New();
renderWindowInteractor->SetInteractorStyle(style);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindowInteractor->Initialize();
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}
生成的影象是倒立的
解決方法是 增加一個影象翻轉濾波器
//新增下面程式碼
#include "itkFlipImageFilter"
...
typedef itk::FlipImageFilter<ImageType> FilterType
typedef FlipterType::FilpAxesArrayType FlipAxesArrayType;
FlipterType::Pointer filter=FilterType::New();
FlipAxesArrayType flipArray;
flipArray[0]=0;
flipArray[1]=1;//x不變,y翻轉
fliter->SetFlipAxes(flipArray);
//修改連線順序
reader->SetFileName(argv[1]);
filter->SetInput(reader->GetOutput());//將濾波其插入到這
connector->SetInput(filter->GetOutput());
結果
4 DICOM 影象的讀取
4.1. 2D Dicom 影象的讀取 以及 插拔式工廠
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkGDCMImageIO.h"
#include "itkImageIOBase.h"
#include "itkRescaleIntensityImageFilter.h"
int main(int argc, char ** argv) {
typedef itk::Image<unsigned short, 2> ImageType;
typedef itk::ImageFileReader<ImageType> ReaderType;
typedef itk::ImageFileWriter<ImageType> WriterType;
typedef itk::GDCMImageIO GDCMIOType;
typedef itk::RescaleIntensityImageFilter<ImageType,ImageType> FilterType;
ReaderType::Pointer reader = ReaderType::New();
WriterType::Pointer writer = WriterType::New();
GDCMIOType::Pointer gdcmIO = GDCMIOType::New();
FilterType::Pointer filter = FilterType::New();
filter->SetOutputMinimum(0);
filter->SetOutputMaximum(255);
reader->SetFileName(argv[1]);
reader->SetImageIO(gdcmIO);
try {
reader->Update();
}
catch (itk::ExceptionObject &err) {
std::cerr << "ExceptionObject Caught" << std::endl;
std::cerr << err << std::endl;
return EXIT_FAILURE;
}
filter->SetInput(reader->GetOutput());
writer->SetInput(filter->GetOutput());
writer->SetFileName(argv[2]);
writer->UseInputMetaDataDictionaryOff();
writer->SetImageIO(gdcmIO);
try {
writer->Update();
}
catch (itk::ExceptionObject &err) {
std::cerr << "ExceptionObject caught" << std::endl;
std::cerr << err << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
插拔式工廠
ITK中在輸入/輸出結構後面的原理叫插拔式工廠。如上圖。從使用者的觀點看,可靠的讀寫檔案的類是
itk::ImageFileReader
和 itk::ImageFileWriter
.這兩個類並不知道讀和寫特殊檔案格式如 PNG 和 DICOM的細節,他們所做的就是分派使用者的要求給知道檔案格式的類。這些類是itk::ImageIO
的.
ImageIO
的每一個必須提供一個工廠類。如上圖,比如 我們需要讀取一個 PNG檔案,那麼ImageFileReader
要求 ImageIOFactory
給一個能讀取PNG檔案的 ImageIO
類,ImageIOFactory 會問工廠列表中那個辦到這件事,結果
PNGImageIOFactory
和 MetaImageIOFactory
表示 我能行,那麼就將他們註冊,建立MetaImageIO
或 PNGImageIO
物件來讀取PNG檔案。
4.2. 2D DICOM 序列影象
讀取 序列影象寫入體資料
這是需要讀取的DICOM 序列,我們用MITK檢視
程式碼
#include "itkImage.h"
#include "itkGDCMImageIO.h"
#include "itkGDCMSeriesFileNames.h"
#include "itkImageSeriesReader.h"
#include "itkImageFileWriter.h"
int main(int argc, char* argv[])
{
if (argc < 3)
{
std::cerr << "Usage: " << std::endl;
std::cerr << argv[0] << " DicomDirectory outputFileName [seriesName]"
<< std::endl;
return EXIT_FAILURE;
}
typedef signed short PixelType;
const unsigned int Dimension = 3;
typedef itk::Image< PixelType, Dimension > ImageType;
typedef itk::ImageSeriesReader< ImageType > ReaderType;
ReaderType::Pointer reader = ReaderType::New();
typedef itk::GDCMImageIO ImageIOType;
ImageIOType::Pointer dicomIO = ImageIOType::New();
reader->SetImageIO(dicomIO);
typedef itk::GDCMSeriesFileNames NamesGeneratorType;
NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New();
nameGenerator->SetUseSeriesDetails(true);
nameGenerator->AddSeriesRestriction("0008|0021");
nameGenerator->SetDirectory(argv[1]);
try
{
std::cout << std::endl << "The directory: " << std::endl;
std::cout << std::endl << argv[1] << std::endl << std::endl;
std::cout << "Contains the following DICOM Series: ";
std::cout << std::endl << std::endl;
typedef std::vector< std::string > SeriesIdContainer;
const SeriesIdContainer & seriesUID = nameGenerator->GetSeriesUIDs();
SeriesIdContainer::const_iterator seriesItr = seriesUID.begin();
SeriesIdContainer::const_iterator seriesEnd = seriesUID.end();
while (seriesItr != seriesEnd)
{
std::cout << seriesItr->c_str() << std::endl;
++seriesItr;
}
std::string seriesIdentifier;
if (argc > 3) // If no optional series identifier
{
seriesIdentifier = argv[3];
}
else
{
seriesIdentifier = seriesUID.begin()->c_str();
}
std::cout << std::endl << std::endl;
std::cout << "Now reading series: " << std::endl << std::endl;
std::cout << seriesIdentifier << std::endl;
std::cout << std::endl << std::endl;
typedef std::vector< std::string > FileNamesContainer;
FileNamesContainer fileNames;
fileNames = nameGenerator->GetFileNames(seriesIdentifier);
reader->SetFileNames(fileNames);
try
{
reader->Update();
}
catch (itk::ExceptionObject &ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}
typedef itk::ImageFileWriter< ImageType > WriterType;
WriterType::Pointer writer = WriterType::New();
writer->SetFileName(argv[2]);
writer->SetInput(reader->GetOutput());
std::cout << "Writing the image as " << std::endl << std::endl;
std::cout << argv[2] << std::endl << std::endl;
try
{
writer->Update();
}
catch (itk::ExceptionObject &ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}
}
catch (itk::ExceptionObject &ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}
}
我們用MITK開啟
DICOM 的 UID
DICOM 的識別碼就像命令列中的那一串數字.
我們換一組DICOM序列
會發現後面部分數字不同,相同的是組織識別碼,不同的是組織內的唯一識別碼
這是他的圖片
如果我們只有一組影象不用分辨
#include "itkImage.h"
#include "itkGDCMImageIO.h"
#include "itkGDCMSeriesFileNames.h"
#include "itkImageSeriesReader.h"
#include "itkImageFileWriter.h"
int main(int argc, char* argv[])
{
typedef signed short PixelType;
const unsigned int Dimension = 3;
typedef itk::Image< PixelType, Dimension > ImageType;
typedef itk::ImageSeriesReader< ImageType > ReaderType;
ReaderType::Pointer reader = ReaderType::New();
typedef itk::GDCMImageIO ImageIOType;
ImageIOType::Pointer dicomIO = ImageIOType::New();
reader->SetImageIO(dicomIO);
typedef itk::GDCMSeriesFileNames NamesGeneratorType;
NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New();
nameGenerator->SetDirectory(argv[1]);
try
{
std::cout << std::endl << "The directory: " << std::endl;
std::cout << std::endl << argv[1] << std::endl << std::endl;
std::cout << "Contains the following DICOM Series: ";
std::cout << std::endl << std::endl;
typedef std::vector< std::string > SeriesIdContainer;
const SeriesIdContainer & seriesUID = nameGenerator->GetSeriesUIDs();
std::string seriesIdentifier= seriesUID.begin()->c_str();
std::cout << std::endl << std::endl;
std::cout << "Now reading series: " << std::endl << std::endl;
std::cout << seriesIdentifier << std::endl;
std::cout << std::endl << std::endl;
typedef std::vector< std::string > FileNamesContainer;
FileNamesContainer fileNames;
fileNames = nameGenerator->GetFileNames(seriesIdentifier);
reader->SetFileNames(fileNames);
try
{
reader->Update();
}
catch (itk::ExceptionObject &ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}
typedef itk::ImageFileWriter< ImageType > WriterType;
WriterType::Pointer writer = WriterType::New();
writer->SetFileName(argv[2]);
writer->SetInput(reader->GetOutput());
std::cout << "Writing the image as " << std::endl << std::endl;
std::cout << argv[2] << std::endl << std::endl;
try
{
writer->Update();
}
catch (itk::ExceptionObject &ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}
}
catch (itk::ExceptionObject &ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
去掉
nameGenerator->SetUseSeriesDetails(true);
後只有組織識別嗎,沒有組織內唯一識別嗎
下面的程式碼是用標籤限制
nameGenerator->AddSeriesRestriction("0008|0021");
主要過程如下
nameGenerator->SetDirectory(argv[1]);
const SeriesIdContainer & seriesUID = nameGenerator->GetSeriesUIDs();
std::string seriesIdentifier= seriesUID.begin()->c_str();
std::vector< std::string > fileNames = nameGenerator->GetFileNames(seriesIdentifier);
reader->SetFileNames(fileNames);
讀取序列影象寫出序列影象
#include "itkGDCMImageIO.h"
#include "itkGDCMSeriesFileNames.h"
#include "itkImageSeriesReader.h"
#include "itkImageSeriesWriter.h"
#include <vector>
#include "itksys/SystemTools.hxx"
int main( int argc, char* argv[] )
{
if( argc < 3 )
{
std::cerr << "Usage: " << argv[0] <<
" DicomDirectory OutputDicomDirectory" << std::endl;
return EXIT_FAILURE;
}
typedef signed short PixelType;
const unsigned int Dimension = 3;
typedef itk::Image< PixelType, 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( argv[1] );
const ReaderType::FileNamesContainer & filenames =namesGenerator->GetInputFileNames();
std::size_t numberOfFileNames = filenames.size();
std::cout << numberOfFileNames << std::endl;
for(unsigned int fni = 0; fni < numberOfFileNames; ++fni)
{
std::cout << "filename # " << fni << " = ";
std::cout << filenames[fni] << std::endl;
}
ReaderType::Pointer reader = ReaderType::New();
reader->SetImageIO( gdcmIO );
reader->SetFileNames( filenames );
try
{
reader->Update();
}
catch (itk::ExceptionObject &excp)
{
std::cerr << "Exception thrown while writing the image" << std::endl;
std::cerr << excp << std::endl;
return EXIT_FAILURE;
}
const char * outputDirectory = argv[2];
itksys::SystemTools::MakeDirectory( outputDirectory );
typedef signed short OutputPixelType;
const unsigned int OutputDimension = 2;
typedef itk::Image< OutputPixelType, OutputDimension > Image2DType;
typedef itk::ImageSeriesWriter<ImageType, Image2DType > SeriesWriterType;
SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New();
seriesWriter->SetInput( reader->GetOutput() );
seriesWriter->SetImageIO( gdcmIO );
namesGenerator->SetOutputDirectory( outputDirectory );
seriesWriter->SetFileNames( namesGenerator->GetOutputFileNames() );
seriesWriter->SetMetaDataDictionaryArray(reader->GetMetaDataDictionaryArray() );
try
{
seriesWriter->Update();
}
catch( itk::ExceptionObject & excp )
{
std::cerr << "Exception thrown while writing the series " << std::endl;
std::cerr << excp << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
關鍵程式碼
namesGenerator->SetInputDirectory( argv[1] );
const ReaderType::FileNamesContainer & filenames =namesGenerator->GetInputFileNames();
namesGenerator->SetOutputDirectory( outputDirectory );
seriesWriter->SetFileNames( namesGenerator->GetOutputFileNames() );
seriesWriter->SetMetaDataDictionaryArray(reader->GetMetaDataDictionaryArray() );