ImageSeriesReader type pointer crashes in debug mode


(Ritesh) #1

hen i am creating a smart pointer globally and allocating the memory to it.
In debug mode “it crashes”. While when the same code is run in release mode
it seems to work fine. Can some one help me out, why is it so ?

Please find the below code:

typedef signed short PixelType;
typedef itk::Image< PixelType, 3>ImageType;
typedef itk::ImageSeriesReader< ImageType >ReaderType;
ReaderType:: Pointer reader= ReaderType::New();

void ImageRead()
{
// using pointer reader here.
// which is allocated memory at global level
}

The above code crashes in debug mode


(Dženan Zukić) #2

Ritesh provided this on the mailing list:

This is simple cpp file containing function to read dicom. In which the dicomReader pointer is made global. This code runs in release mode and crashes in debug mode. While debugging i found that i crashes in itkImageSource class under template< typename TOutputImage > ImageSource< TOutputImage >::ImageSource().

// itk headers
#include “itkGDCMImageIO.h”
#include “itkGDCMSeriesFileNames.h”
#include “itkImageSeriesReader.h”
#include “itkImageSeriesWriter.h”
#include “itksys/SystemTools.hxx”
#include “itkVersion.h”
#include “itkImage.h”
#include “gdcmUIDGenerator.h”
#include “itkFileOutputWindow.h”
#include “DicomReader.hpp”

using namespace std;

typedef signed short PixelType;
typedef itk::Image< PixelType, 3> ImageType;
typedef itk::ImageSeriesReader< ImageType > ReaderType;
typedef itk::GDCMImageIO ImageIOType;
typedef itk::GDCMSeriesFileNames NamesGeneratorType;

ReaderType::Pointer dicomReader= ReaderType::New();

void ParseDicom(const string &dicomDirectory)
{
NamesGeneratorType::Pointer apNamesGenerator = NamesGeneratorType::New();
ImageIOType::Pointer apGdcmIO = ImageIOType::New();

apNamesGenerator->SetInputDirectory(dicomDirectory);

const ReaderType::FileNamesContainer & filenames =
apNamesGenerator->GetInputFileNames();

dicomReader->SetImageIO(apGdcmIO);
dicomReader->SetFileNames(filenames);
try
{
dicomReader->Update();
}
catch (itk::ExceptionObject &excp)
{
std::cerr << "exception in file reader " << std::endl;
throw (excp);
}

}

(Dženan Zukić) #3

The program you provided crashed in both Debug and Release mode for me. The problem seems to be having the reader as a global variable. With global variables, the order of initialization between different translation units is not predetermined, so it might get to be initialized before ITK’s global variables. Here is the code which does work for me:

// itk headers
#include "itkGDCMImageIO.h"
#include "itkGDCMSeriesFileNames.h"
#include "itkImageSeriesReader.h"
#include "itkImageSeriesWriter.h"
#include "itksys/SystemTools.hxx"
#include "itkVersion.h"
#include "itkImage.h"
#include "gdcmUIDGenerator.h"
#include "itkFileOutputWindow.h"
//#include "DicomReader.hpp"

using namespace std;

typedef signed short PixelType;
typedef itk::Image< PixelType, 3> ImageType;
typedef itk::ImageSeriesReader< ImageType > ReaderType;
typedef itk::GDCMImageIO ImageIOType;
typedef itk::GDCMSeriesFileNames NamesGeneratorType;

//ReaderType::Pointer dicomReader = ReaderType::New(); //crashes on construction of this global variable

void ParseDicom(const string &dicomDirectory)
{
    NamesGeneratorType::Pointer apNamesGenerator = NamesGeneratorType::New();
    ImageIOType::Pointer apGdcmIO = ImageIOType::New();

    apNamesGenerator->SetInputDirectory(dicomDirectory);

    const ReaderType::FileNamesContainer & filenames =
        apNamesGenerator->GetInputFileNames();

    ReaderType::Pointer dicomReader = ReaderType::New();
    dicomReader->SetImageIO(apGdcmIO);
    dicomReader->SetFileNames(filenames);
    try
    {
        dicomReader->Update();
        //now check whether the image was read correctly
        std::cout << *(dicomReader->GetOutput()) << std::endl;
    }
    catch (itk::ExceptionObject &excp)
    {
        std::cerr << "exception in file reader " << std::endl;
        throw (excp);
    }

}

int main(int, char *[])
{
    ParseDicom("C:/Temp/Ashvins/DICOM/MC138/137926/375127/1189252");
    return EXIT_SUCCESS;
}

(Bradley Lowekamp) #4

In general ITK Objects should not be allocated during global initialization, due to the order of initialization issues Dzenan mentioned.

A simple way around it is to use function scope. Something like this:

ReaderType::Pointer GetGlobalReader(void)
{
static ReaderType::Pointer globalReader = ReaderType::New();
return globalReader;
}

(Ritesh) #5

Thanks for your kind explanation. In that case reader pointer scope will be bound to ParseDicom function. If i want to use the reader pointer in another function then it will be problem. Suppose i have another function which updates the origin matrix using reader origin, as the reader pointer will not be available to me in this function.

The following code seems to work for me. But i am not sure further how this will behave inside ITK. Please let me know if it is fine to do as follows:

// itk headers
#include “itkGDCMImageIO.h”
#include “itkGDCMSeriesFileNames.h”
#include “itkImageSeriesReader.h”
#include “itkImageSeriesWriter.h”
#include “itksys/SystemTools.hxx”
#include “itkVersion.h”
#include “itkImage.h”
#include “gdcmUIDGenerator.h”
#include “itkFileOutputWindow.h”

#include “DicomReader.hpp”

using namespace std;

typedef signed short PixelType;
typedef itk::Image< PixelType, 3 > ImageType;
typedef itk::ImageSeriesReader< ImageType > ReaderType;
typedef itk::GDCMImageIO ImageIOType;
typedef itk::GDCMSeriesFileNames NamesGeneratorType;

ReaderType::Pointer dicomReader ; ////= ReaderType::New(); // this is done inside function

// in this case needs to make sure ParseDicom is called first, before UpdateOrigin
void ParseDicom(const string &dicomDirectory)
{
dicomReader = ReaderType::New();

    NamesGeneratorType::Pointer apNamesGenerator = NamesGeneratorType::New();
    ImageIOType::Pointer apGdcmIO = ImageIOType::New();

    apNamesGenerator->SetInputDirectory(dicomDirectory);
    const ReaderType::FileNamesContainer & filenames =
            apNamesGenerator->GetInputFileNames();

    if (dicomReader  != nullptr)
    {
            dicomReader ->SetImageIO(apGdcmIO);
            dicomReader ->SetFileNames(filenames);
            try
            {
                    dicomReader ->Update();
            }
            catch (itk::ExceptionObject &excp)
            {
                    std::cerr << "exception in file reader " << std::endl;
                    throw (excp);
            }

    }

}

void UpdateOrigin(double origin[3])
{
if (dicomReader != nullptr)
{
const ImageType::PointType & ImageOrigin = dicomReader ->GetOutput()->GetOrigin();
origin[0] = ImageOrigin[0];
origin[1] = ImageOrigin[1];
origin[2] = ImageOrigin[2];
}
}

Thanks,
Ritesh Mahajan


(Dženan Zukić) #6

That would work. However that would recreate the reader each time, despite having a global pointer to it. A much more common approach is to save the image, e.g.:

ImageType::Pointer image; //global variable
void ParseDicom(const string &dicomDirectory)
{
…
image = dicomReader->GetOutput();
}

This is more common than a global reader, but not correct. The “correct” way would be to avoid global variables completely, and pass parameters to and from functions:

ImageType::Pointer ParseDicom(const string &dicomDirectory)
{
…
return dicomReader->GetOutput();
}

and then from “main” function invoke ImageType::Pointer image = ParseDicom(dir);


(Ritesh) #7

That sounds good. Thanks for the help.