Writing a bunch of 3D Images (Nifti Format) into one whole 4D Image.

Hello everyone,

I was wondering if anyone could help point me in the right direction.
I have a bunch of itk::Image<type, 3> 3D images that I would like to combine into a
single 4D file written out using itk::NiftiImageIO. I have looked around and
tried several approaches such as using JoinSeriesImageFilter but I seem not
to be getting the results I’m looking for, perhaps the output I have gotten thus far is wrong.

the general Idea I have tried thus far is as follow

// bear in mind --> template <class RType>

typedef itk::Image<RType, 3> InputImageType;
typedef itk::Image<RType, 4> OutputImageType;
typedef itk::ImageFileWriter<OutputImageType> WriterType;

// pseudo code, assume there are at least 1 or more valid images in the vector.
std::vector<typename InputImageType::Pointer> itkImages;
size_t sizeOfImages = itkImages.size();

// Combine all 3D images to one single 4D.
//----------------------------------------------------
typedef itk::JoinSeriesImageFilter<InputImageType, OutputImageType> JoinSeriesImageType;

auto jointSeriesImage = JoinSeriesImageType::New();
jointSeriesImage->SetOrigin(0.0);        // this isn't necessary as it's the default anyway
jointSeriesImage->SetSpacing(1.0);    // this isn't necessary as it's the default anyway

for (unsigned int idx = 0; idx < sizeOfImages; idx++)
{
	typename InputImageType::Pointer image = itkImages[idx];
	jointSeriesImage->SetInput(idx, image);
}

jointSeriesImage->Update();
typename OutputImageType::Pointer outputImage = jointSeriesImage->GetOutput();

// Write the image to a file using ITK's NIfTI writer.
//----------------------------------------------------
typename WriterType::Pointer writer = WriterType::New();
writer->SetFileName(pathBuffer);
writer->SetInput(outputImage);
itk::NiftiImageIO::Pointer imageIO = itk::NiftiImageIO::New();
imageIO->SetLegacyAnalyze75Mode(itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeReject);
writer->SetImageIO(imageIO);
writer->Update();

Is there anything wrong / off in the above code, if so could anyone give me some pointers. Thanks.

Could you give a bit more detail about what’s going wrong?

I have slices represented as 3D images (NifTi) as per the above code

typedef itk::Image<RType, 3> InputImageType

that I wish to combine into one whole 4D image and saved as one file

typedef itk::Image<RType, 4> OutputImageType

as opposed to saving them individually.

The issue is that when I run the code I had posted earlier, a single file is indeed generated, however when I try to view the file, it looks nothing like what I would expect from the individual 3D images.

here is the image of the top most slice (3D image)

and then after reloading the newly saved 4D Nifti file, here’s what I see.

I guess, I just want to know if the approach in the code presented earlier is the way to do, if so this might mean the issue is somewhere in my codebase.

Thanks - are you writing with an RGB pixel type? That can get tricky especially for 4D images.

Could you kindly elaborate what you mean by RGB pixel type? I have the function templates whereby RType could be int8_t, int16_t, float, double etc. Thanks.

Do you want to write segmentation that contains overlapping segments into a NIFTI file? As far as I know, no such convention exists for this format. Due to rigidity of NIFTI file header, it would not be possible to create a standard NIFTI file that contains metadata describing how to properly interpret you 4D volume.

In contrast, NRRD files (with 3D Slicer’s segmentation metadata convention) has been used widely for this purpose for a long time. This format can use a single shared 3D volume between non-overlapping segments, so most segmentations fit in a few 3D volumes in the 4D file. For example, if you have 300 non-overlapping brain structures segmented and you define an additional region that overlaps with some of those structures, then you don’t need to store 301 but only 2 volumes in the 4D file. This file format can also store standard coded terms to specify the meaning of each segment. You can find the file specification here. You can read/write segments in Python using slicerio package. There is also a topic about how to create such files using ITK.

4 Likes

Could you kindly elaborate what you mean by RGB pixel type ?

The first screenshot you showed was in color. I wasn’t sure if you were trying to write an RGB(A) image where each pixel has three or four values.

If you can use another file format other than NIFTI, there might be more flexibility as pointed out above.

Hi Philip,
Not at all, I’m not writing as an RGBA, it’s simply single values for each voxels (grayscale).
I’m starting to think the issue may well be due to the rigidity / limitation of NifTi as hinted by @lassoan .

Hello Andras,

Thanks for the detailed response. I’m leaning towards the issue being the way NifTi headers are, I’m not that conversant with the whole idea of overlapping segments in all honesty, however I perhaps had the naive idea that I could do just for lack of a better word concatenate the 3D images all into one single 4D based on the example in itkJoinSeriesImageFilterTest.
I guess I would need to research a bit more into this whole issue as well as look into 3D Slicer’s segmentation metadata convention for whatever it’s worth and then take it from there.
Thanks

You should be able to “concatenate” the images like that, assuming they all have the same size. What happens if you try concatenating MRIs instead of label maps?

If segments don’t overlap (each image voxel can only belong to one segment) then you don’t need a 4D volume, but you can store the labels in a single 3D volume. It is very simple and efficient, this is how most software store image segmentations.