VIS Exercise #01
Visualisierung Ãœbungsblatt #01
Medical 3D Data: CT vs. MR
Aufgabe 0: Präliminarien:
Wenn nicht bereits geschehen, installieren Sie DCMTK und ITK wie hier beschrieben. Laden Sie Sich weiterhin die Ãœbungsdaten herunter:
svn checkout https://svn.code.sf.net/p/vis-framework/code/trunk/data vis-data
Aufgabe 1: CT- und MR-Daten:
Blättern Sie nun mit dem Tool pvminfo und pvmplay des VVV in der Schnittbilddarstellung eines CT- und eines MR-Datensatzes! Verwenden Sie z.B. den CT-Head und den MRI-Head im PVM Format. Alternativ können auch die Datensätze der Volume Library verwendet werden. Welche generellen Unterschiede können Sie bzgl. der Darstellung der beiden Modalitäten CT und MR feststellen?
Aufgabe 2: CT- und MR-Daten im QtV3:
Laden Sie die beiden CT- und MR-Datensätze jeweils per Drag&Drop in den QtV3 Volume Renderer des VVV! Vergleichen Sie die beiden Datensätze bzgl. der Darstellungsqualität. Protokollieren Sie Ihr Ergebnis in Form von Screenshots.
Aufgabe 3: Dicom-Daten im QtV3:
Laden Sie die Dicom Serie des Angio/ Verzeichnisses per Drag&Drop in den QtV3!
Aufgabe 4: ITK und QtV3:
Implementieren Sie mit ITK einen Threshold Filter:
- Starten Sie mit dem Quelltext itk.cxx.
- Ãœbersetzen Sie den Quelltext so wie hier beschrieben.
- Der übersetzte Quelltext lädt eine Dicom Serie in eine ITK Filter-Pipeline und speichert das Ergebnis wieder als Dicom Serie im Pfad “out” ab. Dazu muss vorher der Pfad der zu ladenden Serie als zusätzliches Kommandozeilenargument angegeben werden (z.B. “./itk vis-data/Angio/”).
- Erweitern Sie die bereits vorhandene Filter-Pipeline um einen weiteren Filterknoten, der eine Threshold- bzw. Schwellwertoperation durchführt.
- Dazu gehen Sie wie folgt vor:
- Instantiieren Sie einen Threshold-Filterknoten
- Legen Sie dessen Arbeitsparameter bzw. Schwellwerte fest
- Verknüpfen jeweils den Eingang des Threshold-Filters mit dem Ausgang des Readers und den Eingang des Writers mit dem Ausgang des Threshold-Filters
- Ziehen Sie schließlich am Ende der gesamten Kette, d.h. rufen Sie die Update-Funktion am Writer auf
- Verwenden Sie drei verschiedene Schwellwerte von z.B. 50, 100 und 200 und laden Sie die Ergebnis-Serie per Drag&Drop in den QtV3. Hat sich die Darstellung dadurch generell verbessert?
- Dokumentieren Sie die Situation vorher und nachher im Protokoll.
itk.cxx:
#include "itkGDCMImageIO.h"
#include "itkGDCMSeriesFileNames.h"
#include "itkNumericSeriesFileNames.h"
#include "itkImageSeriesReader.h"
#include "itkImageSeriesWriter.h"
#include <iostream>
int main(int argc,char *argv[])
{
if (argc!=2) return(EXIT_FAILURE);
// Part #1:
// read image as dicom series
// series input directory
std::string series(argv[1]);
// remove trailing slash
if (series.size()>0)
if (series[series.size()-1]=='/') series.erase(series.size()-1, 1);
// image types
typedef unsigned short PixelType;
typedef itk::Image< PixelType, 2> Image2DType;
typedef itk::Image< PixelType, 3> Image3DType;
// create name generator
typedef itk::GDCMSeriesFileNames NamesGeneratorType;
NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New();
nameGenerator->SetDirectory(series);
// create dicom series reader
typedef itk::ImageSeriesReader<Image3DType> ReaderType;
ReaderType::Pointer reader = ReaderType::New();
typedef itk::GDCMImageIO ImageIOType;
ImageIOType::Pointer dicomIO = ImageIOType::New();
reader->SetImageIO(dicomIO);
// get series IDs
typedef std::vector<std::string> SeriesIdContainer;
const SeriesIdContainer &seriesUID = nameGenerator->GetSeriesUIDs();
// get dicom series
typedef std::vector<std::string> FileNamesContainer;
FileNamesContainer fileNames;
fileNames=nameGenerator->GetFileNames(seriesUID.begin()->c_str());
reader->SetFileNames(fileNames);
reader->Update();
// get image size
Image3DType::Pointer image = reader->GetOutput();
Image3DType::RegionType region = image->GetLargestPossibleRegion();
Image3DType::SizeType size = region.GetSize();
...
// Part #2:
// write image as dicom series
// make the output directory and generate the file names
itksys::SystemTools::MakeDirectory("out");
// generate the file names
typedef itk::NumericSeriesFileNames OutputNamesGeneratorType;
OutputNamesGeneratorType::Pointer outputNames = OutputNamesGeneratorType::New();
std::string seriesFormat = std::string("out/") + "image-%05d.dcm";
outputNames->SetSeriesFormat(seriesFormat.c_str());
outputNames->SetStartIndex(1);
outputNames->SetEndIndex(size[2]);
// create dicom series writer
typedef itk::ImageSeriesWriter< Image3DType, Image2DType > SeriesWriterType;
SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New();
seriesWriter->SetInput(reader->GetOutput());
seriesWriter->SetImageIO(dicomIO);
seriesWriter->SetFileNames(outputNames->GetFileNames());
// copy metadata dictionary from series reader
seriesWriter->SetMetaDataDictionaryArray(reader->GetMetaDataDictionaryArray());
// pull trigger
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);
}
Hausaufgaben:
- Welcher Schwellwert brachte das beste Ergebnis?
- Welcher Wert wäre der beste denkbare Schwellwert zur Maskierung von Luft, Wasser und Fett? Kann man diesen automatisch berechnen?
- Welcher Wert wäre der beste denkbare Schwellwert zur Maskierung der Arterien? Kann man diesen automatisch berechnen?
- Welcher Wert wäre der beste denkbare Schwellwert zur Maskierung einer einzelnen Niere? Kann man diesen automatisch berechnen?
- Wie würden Sie zur Maskierung von Luft vorgehen, wenn Sie ein Histogramm zur Verfügung hätten?
- Wie würden Sie vorgehen, wenn Sie ein Histogramm selber berechnen müssten?