Skip to content

Commit

Permalink
BUG: Set NIFTI header xyzt_units field correctly
Browse files Browse the repository at this point in the history
Previously, the correct xyzt code was assigned to the 'xyz_units' variable,
but time gets masked out because it's only for space units, resulting in
xyzt_units=2.

Fix by setting 'time_units' to NIFTI_UNITS_SEC.

This produces images with xyzt_units == 10, which is correct for ITK output
(NIFTI_UNITS_MM | NIFTI_UNITS_SEC)
  • Loading branch information
cookpa committed Apr 18, 2024
1 parent 318139a commit 52a5719
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 5 deletions.
11 changes: 6 additions & 5 deletions Modules/IO/NIFTI/src/itkNiftiImageIO.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1503,13 +1503,14 @@ NiftiImageIO::WriteImageInformation()
// dim[2] is required for 2-D volumes,
// dim[3] for 3-D volumes, etc.
this->m_NiftiImage->nvox = 1;
// Spacial dims in ITK are given in mm.
// Spatial dims in ITK are given in mm.
// If 4D assume 4thD is in SECONDS, for all of ITK.
// NOTE: Due to an ambiguity in the nifti specification, some developers
// external tools believe that the time units must be set, even if there
// is only one dataset. Having the time specified for a purly spatial
// is only one dataset. Having the time specified for a purely spatial
// image has no consequence, so go ahead and set it to seconds.
this->m_NiftiImage->xyz_units = static_cast<int>(NIFTI_UNITS_MM | NIFTI_UNITS_SEC);
this->m_NiftiImage->xyz_units = static_cast<int>(NIFTI_UNITS_MM);
this->m_NiftiImage->time_units = static_cast<int>(NIFTI_UNITS_SEC);
this->m_NiftiImage->dim[7] = this->m_NiftiImage->nw = 1;
this->m_NiftiImage->dim[6] = this->m_NiftiImage->nv = 1;
this->m_NiftiImage->dim[5] = this->m_NiftiImage->nu = 1;
Expand Down Expand Up @@ -2364,7 +2365,7 @@ NiftiImageIO::Write(const void * buffer)
// so will destructor of the image that really owns it.
if (nifti_write_status)
{
itkExceptionMacro("ERROR: nifti library failed to write image" << this->GetFileName());
itkExceptionMacro("ERROR: nifti library failed to write image: " << this->GetFileName());
}
}
else /// Image intent is vector image
Expand Down Expand Up @@ -2469,7 +2470,7 @@ NiftiImageIO::Write(const void * buffer)
this->m_NiftiImage->data = nullptr; // if left pointing to data buffer
if (nifti_write_status)
{
itkExceptionMacro("ERROR: nifti library failed to write image" << this->GetFileName());
itkExceptionMacro("ERROR: nifti library failed to write image: " << this->GetFileName());
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions Modules/IO/NIFTI/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ set(ITKIONIFTITests
itkNiftiImageIOTest11.cxx
itkNiftiImageIOTest12.cxx
itkNiftiImageIOTest13.cxx
itkNiftiImageIOTest14.cxx
itkNiftiLargeImageRegionReadTest.cxx
itkNiftiReadAnalyzeTest.cxx
itkNiftiReadWriteDirectionTest.cxx
Expand Down Expand Up @@ -245,6 +246,14 @@ itk_add_test(
itkNiftiImageIOTest13
DATA{Input/SmallVoxels_AffinePrecision.nii.gz})

itk_add_test(
NAME
itkNiftiSpatialTemporalUnitsTest
COMMAND
ITKIONIFTITestDriver
itkNiftiImageIOTest14
${ITK_TEST_OUTPUT_DIR})

itk_add_test(
NAME
itkNiftiQFormSFormDifferentSpacingTest
Expand Down
109 changes: 109 additions & 0 deletions Modules/IO/NIFTI/test/itkNiftiImageIOTest14.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*=========================================================================
*
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/

#include "itkNiftiImageIOTest.h"
#include "itkTestingMacros.h"

// Test writing NIFTI xyzt_units field - should be 10 (mm, sec)
int
itkNiftiImageIOTest14(int argc, char * argv[])
{

if (argc > 1)
{
char * testdir = argv[1];
itksys::SystemTools::ChangeDirectory(testdir);
}
else
{
return EXIT_FAILURE;
}

constexpr unsigned int Dimension = 3;

using PixelType = float;
using ImageType = itk::Image<PixelType, Dimension>;

ImageType::Pointer image;

// fill out an image and write it as nifti.
const char * filename = "xyzt_unit_check.nii";
using OutputImageType = itk::Image<PixelType, Dimension>;
typename OutputImageType::RegionType region;
typename OutputImageType::IndexType start;
typename OutputImageType::SizeType size;
size[0] = 8;
size[1] = 8;
size[2] = 4;
region.SetSize(size);
region.SetIndex(start);
auto outputimage = OutputImageType::New();
outputimage->SetRegions(region);
outputimage->Allocate();

auto writer = itk::ImageFileWriter<OutputImageType>::New();
auto niftiImageIO = itk::NiftiImageIO::New();
writer->SetImageIO(niftiImageIO);
writer->SetFileName(filename);
writer->SetInput(outputimage);
try
{
writer->Update();
}
catch (const itk::ExceptionObject & err)
{
std::cerr << "Exception Object caught: " << std::endl << err << std::endl;
throw;
}

bool fileHasCorrectXYZTTUnits = false;

try
{
// read the image back in
ImageType::Pointer image_from_disk = itk::IOTestHelper::ReadImage<ImageType>(std::string(filename));
// check the metadata for xyzt_units
itk::MetaDataDictionary & dictionary = image_from_disk->GetMetaDataDictionary();
std::string xyzt_units;
if (!itk::ExposeMetaData<std::string>(dictionary, "xyzt_units", xyzt_units))
{
std::cerr << "xyzt_units not found in metadata" << std::endl;
}
if (xyzt_units == "10")
{
fileHasCorrectXYZTTUnits = true;
}
else
{
std::cerr << "xyzt_units not set correctly in metadata" << std::endl;
fileHasCorrectXYZTTUnits = false;
}
}
catch (...)
{
std::cerr << "Exception caught while reading image back in" << std::endl;
}

itk::IOTestHelper::Remove(filename);

if (fileHasCorrectXYZTTUnits)
{
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}

0 comments on commit 52a5719

Please sign in to comment.