0
votes

I’m new to both ITK and C++, and am currently in the process of setting up a testing program for an image registration algorithm. In my message I will tell the specifics of what I'm doing in using ITK-terminology. However, I expect that any experienced C++-programmer should be able to tell what's wrong with my code without understanding the specifics of ITK, since I think I'm just doing something wrong in my referencing/dereferencing. The thing to keep track of here (explained in more detail below) is the MetaDataContainer-type variable, called metaData in all functions. If any of the ITK-specifics confuse you, please ask for more details, but I didn't want to make this original message too long. Anyway, here goes:

I will have to read and write dicom-images quite a few times within the program execution, so instead of doing all the steps of the read/write processes every time, I decided to write separate functions for them. Since I have to use some data from the reading process, such as the MetaDataDictionaryArray in the output as well, the readDicom-function (as well as another function, preregistrationOperations) returns a tuple:

typedef std::tuple< ImageType::Pointer, MetaDataContainer, FileNamesContainer > ImageMetaOutputTuple;

The other important typedef to be aware of is the MetaDataContainer, which is itself a pointer to a DictionaryRawPointer-vector. From ItkImageSeriesReader.h:

typedef MetaDataDictionary                  DictionaryType;
typedef MetaDataDictionary *                DictionaryRawPointer;
typedef std::vector< DictionaryRawPointer > DictionaryArrayType;
typedef const DictionaryArrayType *         DictionaryArrayRawPointer;

In my own header-file, feir.h:

typedef itk::ImageSeriesWriter< ImageType, Image2DType >::DictionaryArrayRawPointer  MetaDataContainer;

The other types are the same as in the ITK-examples for Dicom-handling. The rough layout of my program is seen below.

This program crashes in the writeDicom-function however, with the error “…vector subscript out of range”. I have narrowed the error down to having to do with the MetaDataDictionaryArray. Its size when the dicoms are first read in reaDicom is e.g. 64 (the number of files in the series), but when it is returned to the preRegistrationOperations, its size is suddenly 0, and it is this zero-sized container that is then passed to writeDicom which crashes.

EDIT: the crash itself happens on the seriesWriter->Update()-line.

I have tried to get around this problem by using MetaDataContainer-pointers instead, but the problem persists. I might add that I’m quite new to C++ too (more of a physicist than a programmer). Can anybody help me in this? It should be simple enough to return a MetaDataContainer within a tuple from readDicom, unpack it in preRegistrationOperations, and pass it to writeDicom, but no matter how I try I can’t get it to work.

Best regards, Mikael

ImageMetaOutputTuple preRegistrationOperations( std::string inputDir,  std::string outputDir, std::string seriesNumber, bool preparationsDone = false, bool verbose = true )
{ 
   // No output verbose if operations have already been done
   if (preparationsDone) verbose = false;

   // Read input-Dicoms
   ImageType::Pointer image;
   MetaDataContainer metaData;
   FileNamesContainer outputFilenames; 
   ImageMetaOutputTuple returnTuple = readDicom( inputDir, outputDir, seriesNumber, verbose );
   std::tie (image, metaData, outputFilenames) = returnTuple;    

   // Pass image and directory and filename information to writeDicom and write into outputDir
   if (!preparationsDone) {                
          try {
                 int resultCode = writeDicom( image, outputDir, outputFilenames, metaData );
          }
          catch (itk::ExceptionObject &ex) {
                 std::cout << "Exception caught in writeDICOM:" << std::endl;
                 std::cout << ex << std::endl;            
          }      
   }      
   return returnTuple;
}

-

int writeDicom ( ImageType::Pointer image, std::string inputDir, FileNamesContainer filenames, MetaDataContainer metaData )
{ 
            …
            seriesWriter->SetMetaDataDictionaryArray( metaData );  
            try {       
                 seriesWriter->Update();        
                 return 0;
               }
            catch {
                   ...
                  }
   …
}

-

ImageMetaOutputTuple readDicom ( std::string inputDir, std::string outputDir = "", std::string seriesNumber = "", bool verbose = true)
{      
   …
   image = reader->GetOutput();
   files = nameGenerator->GetOutputFileNames();
   MetaDataContainer metaData = reader->GetMetaDataDictionaryArray();
          …
ImageMetaOutputTuple returnTuple (image, metaData, files);
return returnTuple;
}

-

int main( int argc, char* argv[] )
{
   …
   ImageType::Pointer moving; 
   ImageType::Pointer target;
   std::tie(moving, std::ignore, std::ignore) = preRegistrationOperations( inputDir, movingDir, movingSeriesNumber, preparationsDone, verbose );       
   std::tie(target, std::ignore, std::ignore) = preRegistrationOperations( inputDir, targetDir, targetSeriesNumber, preparationsDone, verbose );       
   …
}
1
You say that the program crashes in the writeDicom-function with message “…vector subscript out of range”, but the shown line of writeDicom only seems to pass the pointer to another function. Are you sure that the program crashes on that line?eerorika
@user2079303, it crashes in a try-catch block immediately after the line shown, containing the command seriesWriter->Update();When I print metaData-Zsize() to the standard output inside readDicom it is 64, which is correct since I have 64 DICOM-images, but when I print its size in the other functions its 0, so something goes wrong when I return it from readDicom.mkerikss
I removed my answer since it seems to be wrong. I'll add here though that typedeffing pointers is a bad idea. The framework seems to do that, so it's ok to keep the convention, but you should at least name your typedef MetaDataContainerPtr or similar.eerorika
@user2079303 Point taken, but as you said, the framework does that, and I have used the exact same typedeffing-conventions as in the official examples. That is, they used MetaDataContainer as the exact same typedef in their Dicom-examples as an additional typedef to the ones included in the standard itk-class header files.mkerikss
Well, that's a bit strange choice since they seem to use ...RawPointer for all other pointer typedefs, but the choice is their fault.eerorika

1 Answers

0
votes

It could be that GetMetaDataDictionaryArray just returns the raw pointer of a member of the series reader, but when you exit from readDicom the reader goes out scope. I would expect more that you had a segfault in this case, but maybe it's a direction to look into. Instead of returning a tuple, you could add 4 extra arguments in your function where to store the result (for an easier debugging).

Touching the dictionary it's always nasty, here is an example http://www.itk.org/Wiki/ITK/Examples/DICOM/ResampleDICOM (but here they read the dictionary slice by slice). Are you sure that you need to update it? Some fields will be automatically updated by the dicom writer (for example orientation), you can not force them anyway.