Label Map Overlay Problem

I am having some difficulty overlaying boolean masks onto Nifti images. The code below is basically what is shown here (which does work for me when I use the images on the page).

The differences that I can see are that I am using 3 dimensions and Nifti float input rather than 2D images of the same pixel and file types (8-bit PNG) in the example.

Can someone please tell me what I have done wrong?  The output image is garbled beyond recognition in ITK Snap

Thanks,
Jared

https://itk.org/ITKExamples/src/Filtering/ImageFusion/OverlayLabelMapOnTopOfAnImage/Documentation.html

             const unsigned int Dimension = 3;

             typedef float PixelType;
             typedef unsigned char LabelType;

             typedef itk::Image< PixelType, Dimension > ImageVolumeType;
             typedef itk::Image< LabelType, Dimension > ContourVolumeType;

             typedef itk::ImageFileReader<ContourVolumeType> ContourReaderType;
             typedef itk::ImageFileReader< ImageVolumeType >  ReaderType;

             ReaderType::Pointer reader = ReaderType::New();
             reader->SetFileName( infile1 );

             ContourReaderType::Pointer labelReader = ContourReaderType::New();
             labelReader->SetFileName(infile2);

             typedef itk::LabelObject< LabelType, Dimension > LabelObjectType;
             typedef itk::LabelMap< LabelObjectType > LabelMapType;

             typedef itk::LabelImageToLabelMapFilter< ContourVolumeType, LabelMapType > ConverterType;
             ConverterType::Pointer converter = ConverterType::New();
             converter->SetInput( labelReader->GetOutput() );

             typedef itk::LabelMapOverlayImageFilter< LabelMapType, ImageVolumeType > FilterType;
             FilterType::Pointer filter = FilterType::New();
             filter->SetInput( converter->GetOutput() );
             filter->SetFeatureImage( reader->GetOutput() );
             filter->SetOpacity(0.5);


             typedef itk::ImageFileWriter< FilterType::OutputImageType > WriterType;
             WriterType::Pointer writer = WriterType::New();
             writer->SetFileName(outfile);
             writer->SetInput( filter->GetOutput() );
             writer->Update();

Have you tried visualizing your output using Slicer? I don’t remember whether ITK-SNAP supports RGB pixel types.

What is the range of your input image? I suspect the filter is doing a poor or unexpected job of mapping the ranges. I’d have to look at the filters source code to determine what is expects could me numeric traits min/max or 0-255. If you want the output to be 0-255 I’d scale the input of the overlay filter to that range.

One more thing, I see you start with a “Label Image”. Have you looked at LabelOverlayImageFilter, it should do a similar operation with out having to convert to a “LabelMap” first.

Thank you both for the help so far. The type of my input image is float. I am rebuilding ITK to the latest version and will try your latest suggestions.

Slicer does not show it any better than ITK SNAP. I have attached an ITK SNAP screenshot showing that it is still a little messed up. This is probably very close, but I wonder if I have done something wrong with the RGB conversion.

Here is the code I used to generate it:

#include “itkBinaryImageToLabelMapFilter.h”
#include “itkImage.h”
#include “itkImageFileReader.h”
#include “itkImageFileWriter.h”
#include “itkImageRegionIterator.h”
#include “itkLabelMapToLabelImageFilter.h”
#include “itkLabelOverlayImageFilter.h”
#include “itkRGBPixel.h”

using namespace std;

typedef itk::Image<float, 3> MriImageType;
typedef itk::Image<unsigned char, 3> MaskImageType;

typedef itk::ImageFileReader MriReaderType;
typedef itk::ImageFileReader MaskReaderType;

int main(int argc, char *argv[])
{

if (argc < 4){
cerr << “mri, mask, output_image” << endl;
return 1;
}

string mri_filename = argv[1];
string mask_filename = argv[2];
string output_filename = argv[3];

MriReaderType::Pointer mri_reader = MriReaderType::New();
mri_reader->SetFileName(mri_filename);
MriImageType::Pointer mri_image = mri_reader->GetOutput(); // MriImageType::New();

MaskReaderType::Pointer mask_reader = MaskReaderType::New();
mask_reader->SetFileName(mask_filename);
MaskImageType::Pointer mask_image = mask_reader->GetOutput(); // MaskImageType::New();

typedef itk::BinaryImageToLabelMapFilter BinaryImageToLabelMapFilterType;
BinaryImageToLabelMapFilterType::Pointer binaryImageToLabelMapFilter = BinaryImageToLabelMapFilterType::New();
binaryImageToLabelMapFilter->SetInput(mask_image);
binaryImageToLabelMapFilter->Update();

typedef itk::LabelMapToLabelImageFilter<BinaryImageToLabelMapFilterType::OutputImageType, MaskImageType> LabelMapToLabelImageFilterType;
LabelMapToLabelImageFilterType::Pointer labelMapToLabelImageFilter = LabelMapToLabelImageFilterType::New();
labelMapToLabelImageFilter->SetInput(binaryImageToLabelMapFilter->GetOutput());
labelMapToLabelImageFilter->Update();
typedef itk::RGBPixel RGBPixelType;
typedef itk::Image<RGBPixelType, 3> RGBImageType;

typedef itk::LabelOverlayImageFilter<MriImageType, MaskImageType, RGBImageType>
LabelOverlayImageFilterType;
LabelOverlayImageFilterType::Pointer labelOverlayImageFilter = LabelOverlayImageFilterType::New();
labelOverlayImageFilter->SetInput(mri_image);
labelOverlayImageFilter->SetLabelImage(labelMapToLabelImageFilter->GetOutput());
labelOverlayImageFilter->SetOpacity(.5);
labelOverlayImageFilter->Update();

typedef itk::ImageFileWriter< RGBImageType > WriterType;
WriterType::Pointer writer = WriterType::New();
writer->SetFileName(output_filename);
writer->SetInput(labelOverlayImageFilter->GetOutput());
writer->Update();

return EXIT_SUCCESS;
}

Thank you for the help. It steered me in the right direction. I got it working with this code, which probably has some redundant lines (Updates and GetOutputs and so on) but helps make it clear to me at least:

typedef itk::RGBPixel<unsigned char> RGBPixelType;
typedef itk::Image<RGBPixelType, 3> RGBImageType;

RGBImageType::Pointer overlayContour(ImageType::Pointer patient_image, BinaryImageType::Pointer segmented_image, const float opacity){

        // rescale patient volume
        typedef itk::RescaleIntensityImageFilter<ImageType, ImageType> RescaleFilterType;
        RescaleFilterType::Pointer rescale_filter= RescaleFilterType::New();
        rescale_filter->SetInput(patient_image);
        rescale_filter->SetOutputMinimum(0);
        rescale_filter->SetOutputMaximum(itk::NumericTraits<BinaryPixelType>::max());
        rescale_filter->Update();
        ImageType::Pointer rescaled = rescale_filter->GetOutput();

        // cast patient volume
        typedef itk::CastImageFilter<ImageType, BinaryImageType> CastFilterType;
        CastFilterType::Pointer cast_filter = CastFilterType::New();
        cast_filter->SetInput(rescaled);
        cast_filter->Update();
        BinaryImageType::Pointer casted = cast_filter->GetOutput();

        // convert label image to label map
        typedef itk::LabelObject<BinaryPixelType, 3> LabelObjectType;
        typedef itk::LabelMap<LabelObjectType> LabelMapType;

        typedef itk::LabelImageToLabelMapFilter<BinaryImageType, LabelMapType> ConverterType;
        ConverterType::Pointer converter = ConverterType::New();
        converter->SetInput(segmented_image);
        converter->Update();
        LabelMapType::Pointer converted = converter->GetOutput();

        // overlay contour on volume
        typedef itk::LabelMapOverlayImageFilter<LabelMapType, BinaryImageType> OverlayFilterType;
        OverlayFilterType::Pointer overlay_filter = OverlayFilterType::New();
        overlay_filter->SetInput(converted);
        overlay_filter->SetFeatureImage(casted);
        overlay_filter->SetOpacity(opacity);
        overlay_filter->Update();
        OverlayFilterType::OutputImageType::Pointer overlay_image = overlay_filter->GetOutput();

        return overlay_image;
}
2 Likes

Thanks for sharing your solution!