VisExercises

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:

  1. Starten Sie mit dem Quelltext itk.cxx.
  2. Ãœbersetzen Sie den Quelltext so wie hier beschrieben.
  3. 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/”).
  4. Erweitern Sie die bereits vorhandene Filter-Pipeline um einen weiteren Filterknoten, der eine Threshold- bzw. Schwellwertoperation durchführt.
  5. Dazu gehen Sie wie folgt vor:
    1. Instantiieren Sie einen Threshold-Filterknoten
    2. Legen Sie dessen Arbeitsparameter bzw. Schwellwerte fest
    3. Verknüpfen jeweils den Eingang des Threshold-Filters mit dem Ausgang des Readers und den Eingang des Writers mit dem Ausgang des Threshold-Filters
    4. Ziehen Sie schließlich am Ende der gesamten Kette, d.h. rufen Sie die Update-Funktion am Writer auf
  1. 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?
  2. Dokumentieren Sie die Situation vorher und nachher im Protokoll.

itk.cxx:

#include "itkImage.h"
#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:

  1. Welcher Schwellwert brachte das beste Ergebnis?
  2. Welcher Wert wäre der beste denkbare Schwellwert zur Maskierung von Luft, Wasser und Fett? Kann man diesen automatisch berechnen?
  3. Welcher Wert wäre der beste denkbare Schwellwert zur Maskierung der Arterien? Kann man diesen automatisch berechnen?
  4. Welcher Wert wäre der beste denkbare Schwellwert zur Maskierung einer einzelnen Niere? Kann man diesen automatisch berechnen?
  5. Wie würden Sie zur Maskierung von Luft vorgehen, wenn Sie ein Histogramm zur Verfügung hätten?
  6. Wie würden Sie vorgehen, wenn Sie ein Histogramm selber berechnen müssten?

Options: