依赖:
ITK/4.13.2
VTK/8.2.0
代码:
#include <itkGDCMImageIO.h>
#include <itkGDCMSeriesFileNames.h>
#include <itkImageSeriesReader.h>
#include <itkImageToVTKImageFilter.h>
#include <vtkSmartPointer.h>
#include <vtkObjectFactory.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkActor.h>
#include <vtkImageViewer2.h>
#include <vtkDICOMImageReader.h>
#include <vtkImageData.h>
#include <vtkInteractorStyleImage.h>
#include <vtkActor2D.h>
#include <vtkTextProperty.h>
#include <vtkTextMapper.h>
#include <vtkImageFlip.h>
#include <vtkAutoInit.h>
#include <vector>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
class StatusMessage {
public:
static std::string Format(int slice, int maxSlice) {
std::stringstream tmp;
tmp << "Slice Number " << slice + 1 << "/" << maxSlice + 1;
return tmp.str();
}
};
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 *imageViewer) {
_ImageViewer = imageViewer;
_MinSlice = imageViewer->GetSliceMin();
_MaxSlice = imageViewer->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() {
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();
}
}
};
vtkStandardNewMacro(myvtkInteractorStyleImage);
std::vector<std::string> Split(const std::string& str, const std::string& pattern)
{
//const char* convert to char*
char* strc = new char[strlen(str.c_str()) + 1];
strcpy(strc, str.c_str());
std::vector<std::string> resultVec;
char* tmpStr = strtok(strc, pattern.c_str());
while (tmpStr != NULL)
{
resultVec.push_back(std::string(tmpStr));
tmpStr = strtok(NULL, pattern.c_str());
}
delete[] strc;
return resultVec;
}
std::string GetValuesByTag(itk::GDCMImageIO::Pointer dicomIO, const std::string& entryId)
{
using Dictionary = itk::MetaDataDictionary;
using MetaDataStringType = itk::MetaDataObject<std::string>;
const Dictionary& dic = dicomIO->GetMetaDataDictionary();
auto itr = dic.Begin();
auto end = dic.End();
//std::string entryId = "0010|0010";
auto tagItr = dic.Find(entryId);
std::string tagvalue("");
if (tagItr != end)
{
MetaDataStringType::ConstPointer entryvalue =
dynamic_cast<const MetaDataStringType*> (tagItr->second.GetPointer());
if (entryvalue)
{
tagvalue = entryvalue->GetMetaDataObjectValue();
std::cout << "tag: " << entryId << " value:" << tagvalue << std::endl;
}
}
return tagvalue;
}
int main(int argc, char* argv[])
{
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " DicomDirectory" << std::endl;
return EXIT_FAILURE;
}
using PixelType = signed short;
constexpr unsigned int Dimension = 3; // The dimension is 3, not 2
using ImageType = itk::Image<PixelType, Dimension>;
using ImageIOType = itk::GDCMImageIO;
ImageIOType::Pointer dicomIO = ImageIOType::New();
using NamesGeneratorType = itk::GDCMSeriesFileNames;
NamesGeneratorType::Pointer nameGenarator = NamesGeneratorType::New();
nameGenarator->SetDirectory(argv[1]);
using FilenamesContainer = std::vector<std::string>;
FilenamesContainer filenames = nameGenarator->GetInputFileNames();
using ReaderType = itk::ImageSeriesReader<ImageType>;
ReaderType::Pointer reader = ReaderType::New();
reader->SetImageIO(dicomIO);
reader->SetFileNames(filenames);
reader->SetReverseOrder(true);
try
{
reader->Update();
}
catch (itk::ExceptionObject& ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}
char name[512];
dicomIO->GetPatientName(name);
std::cout << "patient: " << name << std::endl;
//(0028, 1050) DS 40 # 1, 2 Window Center
//(0028, 1051) DS 200 # 1, 4 Window Width
std::string tagWindowCenter = "0028|1050";
std::string valueWindowCenter = GetValuesByTag(dicomIO, tagWindowCenter);
std::vector<std::string> WindowCenters = Split(valueWindowCenter, "\\");
valueWindowCenter = WindowCenters.empty() ? "40" : WindowCenters[0];
std::string tagWindowWidth = "0028|1051";
std::string valueWindowWidth = GetValuesByTag(dicomIO, tagWindowWidth);
std::vector<std::string> WindowWidths = Split(valueWindowWidth, "\\");
valueWindowWidth = WindowWidths.empty() ? "400" : WindowWidths[0];
using ImageConnector = itk::ImageToVTKImageFilter<ImageType>;
ImageConnector::Pointer imageConnector = ImageConnector::New();
imageConnector->SetInput(reader->GetOutput());
imageConnector->Update();
//===================================
//flip image
vtkSmartPointer<vtkImageFlip> flip = vtkSmartPointer<vtkImageFlip>::New();
flip->SetInputData(imageConnector->GetOutput());
flip->SetFilteredAxis(1);//y轴为1,x轴为0,z轴为2;
flip->Update();
vtkSmartPointer<vtkImageViewer2> imageViewer = vtkSmartPointer<vtkImageViewer2>::New();
//imageViewer->SetInputConnection(reader->GetOutputPort());
//imageViewer->SetInputData(imageConnector->GetOutput());
imageViewer->SetInputData(flip->GetOutput());
vtkSmartPointer<vtkTextProperty> sliceTextProp = vtkSmartPointer<vtkTextProperty>::New();
sliceTextProp->SetFontFamilyToCourier();
sliceTextProp->SetFontSize(20);
sliceTextProp->SetVerticalJustificationToBottom();
sliceTextProp->SetJustificationToLeft();
vtkSmartPointer<vtkTextMapper> sliceTextMapper = vtkSmartPointer<vtkTextMapper>::New();
std::string msg = StatusMessage::Format(imageViewer->GetSliceMin(), imageViewer->GetSliceMax());
sliceTextMapper->SetInput(msg.c_str());
sliceTextMapper->SetTextProperty(sliceTextProp);
vtkSmartPointer<vtkActor2D> sliceTextActor = vtkSmartPointer<vtkActor2D>::New();
sliceTextActor->SetMapper(sliceTextMapper);
sliceTextActor->SetPosition(15, 10);
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 with 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<vtkRenderWindowInteractor> renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<myvtkInteractorStyleImage> myInteractorStyle =
vtkSmartPointer<myvtkInteractorStyleImage>::New();
myInteractorStyle->SetImageViewer(imageViewer);
myInteractorStyle->SetStatusMapper(sliceTextMapper);
imageViewer->SetupInteractor(renderWindowInteractor);
renderWindowInteractor->SetInteractorStyle(myInteractorStyle);
imageViewer->GetRenderer()->AddActor2D(sliceTextActor);
imageViewer->GetRenderer()->AddActor2D(usageTextActor);
imageViewer->GetRenderWindow()->SetSize(800, 600);
imageViewer->SetColorLevel(atoi(valueWindowCenter.c_str()));//窗位 40 400
imageViewer->SetColorWindow(atoi(valueWindowWidth.c_str()));//窗宽
imageViewer->Render();
imageViewer->GetRenderer()->ResetCamera();
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}