Skip to content

Commit

Permalink
BUG: Fix TransformMINC
Browse files Browse the repository at this point in the history
convert MINC PositiveCoordinateOrientation RAS coordinate system to ITK PositiveCoordinateOrientation LPS
  • Loading branch information
gdevenyi committed Dec 6, 2024
1 parent ef1ae91 commit a8811a0
Show file tree
Hide file tree
Showing 14 changed files with 452 additions and 122 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,10 @@ mark_as_advanced(ITK_NIFTI_IO_ANALYZE_FLAVOR)
option(ITK_NIFTI_IO_SFORM_PERMISSIVE_DEFAULT "Allow non-orthogonal rotation matrix in NIFTI sform by default" OFF)
mark_as_advanced(ITK_NIFTI_IO_SFORM_PERMISSIVE_DEFAULT)

# Automatically convert MINC coordinate system from PositiveCoordinateOrientation RAS to PositiveCoordinateOrientation LPS
option(ITK_MINC_IO_RAS_TO_LPS "Convert MINC coordinate system from PositiveCoordinateOrientation RAS to PositiveCoordinateOrientation LPS by default" ON)

mark_as_advanced(ITK_MINC_IO_RAS_TO_LPS)
#-----------------------------------------------------------------------------
# ITK build classes that are in the review process
# ITK_USE_REVIEW is deprecated, only kept for backward compatibility
Expand Down
6 changes: 6 additions & 0 deletions Modules/IO/MINC/include/itkMINCImageIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ class ITKIOMINC_EXPORT MINCImageIO : public ImageIOBase
void
Write(const void * buffer) override;

/** Set to automatically convert from PositiveCoordinateOrientation RAS to PositiveCoordinateOrientation LPS*/
itkSetMacro(RAStoLPS, bool);
itkGetConstMacro(RAStoLPS, bool);
itkBooleanMacro(RAStoLPS);

protected:
MINCImageIO();
~MINCImageIO() override;
Expand Down Expand Up @@ -153,6 +158,7 @@ class ITKIOMINC_EXPORT MINCImageIO : public ImageIOBase

// complex type images, composed of complex numbers
// int m_Complex;
bool m_RAStoLPS;
};
} // end namespace itk

Expand Down
3 changes: 3 additions & 0 deletions Modules/IO/MINC/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
configure_file(itkMINCImageIOConfigurePrivate.h.in itkMINCImageIOConfigurePrivate.h @ONLY)
include_directories("${CMAKE_CURRENT_BINARY_DIR}")

set(ITKIOMINC_SRC itkMINCImageIO.cxx itkMINCImageIOFactory.cxx)

add_library(ITKIOMINC ${ITK_LIBRARY_BUILD_TYPE} ${ITKIOMINC_SRC})
Expand Down
34 changes: 23 additions & 11 deletions Modules/IO/MINC/src/itkMINCImageIO.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@

#include <memory> // For unique_ptr.

#include "itkMINCImageIOConfigurePrivate.h"


extern "C"
{
void
Expand Down Expand Up @@ -235,6 +238,7 @@ MINCImageIO::CloseVolume()

MINCImageIO::MINCImageIO()
: m_MINCPImpl(std::make_unique<MINCImageIOPImpl>())
, m_RAStoLPS(ITK_MINC_IO_RAS_TO_LPS)
{
for (auto & dimensionIndex : m_MINCPImpl->m_DimensionIndices)
{
Expand Down Expand Up @@ -283,6 +287,7 @@ MINCImageIO::PrintSelf(std::ostream & os, Indent indent) const

os << indent << "MINCPImpl: " << m_MINCPImpl.get() << std::endl;
os << indent << "DirectionCosines: " << m_DirectionCosines << std::endl;
os << indent << "RAStoLPS: " << m_RAStoLPS << std::endl;
}

void
Expand Down Expand Up @@ -511,9 +516,11 @@ MINCImageIO::ReadImageInformation()
}
}


// Transform MINC PositiveCoordinateOrientation RAS coordinates to
// internal ITK PositiveCoordinateOrientation LPS Coordinates
dir_cos = RAS_tofrom_LPS * dir_cos;
if (this->m_RAStoLPS)
dir_cos = RAS_tofrom_LPS * dir_cos;

// Transform origin coordinates
o_origin = dir_cos * origin;
Expand Down Expand Up @@ -688,6 +695,9 @@ MINCImageIO::ReadImageInformation()
std::string classname(GetNameOfClass());
// EncapsulateMetaData<std::string>(thisDic,ITK_InputFilterName,
// classname);
// preserve information if the volume was PositiveCoordinateOrientation RAS to PositiveCoordinateOrientation LPS
// converted
EncapsulateMetaData<bool>(thisDic, "RAStoLPS", m_RAStoLPS);

// store history
size_t minc_history_length = 0;
Expand Down Expand Up @@ -997,8 +1007,10 @@ MINCImageIO::WriteImageInformation()
const vnl_matrix<double> inverseDirectionCosines{ vnl_matrix_inverse<double>(dircosmatrix).as_matrix() };
origin *= inverseDirectionCosines; // transform to minc convention


// Convert ITK direction cosines from PositiveCoordinateOrientation LPS to PositiveCoordinateOrientation RAS
dircosmatrix *= RAS_tofrom_LPS;
if (this->m_RAStoLPS)
dircosmatrix *= RAS_tofrom_LPS;

for (unsigned int i = 0; i < nDims; ++i)
{
Expand Down Expand Up @@ -1028,38 +1040,32 @@ MINCImageIO::WriteImageInformation()
{
case IOComponentEnum::UCHAR:
m_MINCPImpl->m_Volume_type = MI_TYPE_UBYTE;
// m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
break;
case IOComponentEnum::CHAR:
m_MINCPImpl->m_Volume_type = MI_TYPE_BYTE;
// m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
break;
case IOComponentEnum::USHORT:
m_MINCPImpl->m_Volume_type = MI_TYPE_USHORT;
// m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
break;
case IOComponentEnum::SHORT:
m_MINCPImpl->m_Volume_type = MI_TYPE_SHORT;
// m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
break;
case IOComponentEnum::UINT:
m_MINCPImpl->m_Volume_type = MI_TYPE_UINT;
// m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
break;
case IOComponentEnum::INT:
m_MINCPImpl->m_Volume_type = MI_TYPE_INT;
// m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
break;
// case IOComponentEnum::ULONG://TODO: make sure we are cross-platform here!
// volume_data_type=MI_TYPE_ULONG;
// break;
// case IOComponentEnum::LONG://TODO: make sure we are cross-platform here!
// volume_data_type=MI_TYPE_LONG;
// break;
case IOComponentEnum::FLOAT: // TODO: make sure we are cross-platform here!
case IOComponentEnum::FLOAT:
m_MINCPImpl->m_Volume_type = MI_TYPE_FLOAT;
break;
case IOComponentEnum::DOUBLE: // TODO: make sure we are cross-platform here!
case IOComponentEnum::DOUBLE:
m_MINCPImpl->m_Volume_type = MI_TYPE_DOUBLE;
break;
default:
Expand Down Expand Up @@ -1099,7 +1105,6 @@ MINCImageIO::WriteImageInformation()
if (ExposeMetaData<std::string>(thisDic, "dimension_order", dimension_order))
{
// the format should be ((+|-)(X|Y|Z|V|T))*
// std::cout<<"Restoring original dimension order:"<<dimension_order.c_str()<<std::endl;
if (dimension_order.length() == (minc_dimensions * 2))
{
dimorder_good = true;
Expand Down Expand Up @@ -1338,6 +1343,13 @@ MINCImageIO::WriteImageInformation()
// TODO: figure out what to do with it
}
}

// preserve information of MINC PositiveCoordinateOrientation RAS to ITK PositiveCoordinateOrientation LPS conversion
{
int tmp = (int)this->m_RAStoLPS;
miset_attr_values(m_MINCPImpl->m_Volume, MI_TYPE_INT, "itk", "RAStoLPS", 1, &tmp);
}

mifree_volume_props(hprops);
}

Expand Down
27 changes: 27 additions & 0 deletions Modules/IO/MINC/src/itkMINCImageIOConfigurePrivate.h.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*=========================================================================
*
* 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.
*
*=========================================================================*/
#ifndef itkMINCImageIOConfigurePrivate_h
#define itkMINCImageIOConfigurePrivate_h

// This file is intended to hold preprocessor macros only used internally by
// IO/MINC module and should not be installed.

// Convert from PositiveCoordinateOrientation RAS to PositiveCoordinateOrientation LPS by default
#cmakedefine01 ITK_MINC_IO_RAS_TO_LPS

#endif // itkMINCImageIOConfigurePrivate_h
79 changes: 50 additions & 29 deletions Modules/IO/MINC/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
itk_module_test()

# to know system default MINC PositiveCoordinateOrientation RAS to PositiveCoordinateOrientation LPS mode
include_directories("${CMAKE_CURRENT_BINARY_DIR}/../src")


if(NOT ITK_USE_SYSTEM_HDF5)
include_directories("${ITKHDF5_SOURCE_DIR}/src/itkhdf5" "${ITKHDF5_SOURCE_DIR}/src/itkhdf5/src"
"${ITKHDF5_BINARY_DIR}/src/itkhdf5")
Expand Down Expand Up @@ -42,7 +46,9 @@ itk_add_test(
${ITK_TEST_OUTPUT_DIR}/t1_z+_byte_cor_2.mnc
itkMINCImageIOTest4
DATA{Input/t1_z+_byte_cor.mnc}
${ITK_TEST_OUTPUT_DIR}/t1_z+_byte_cor_2.mnc)
${ITK_TEST_OUTPUT_DIR}/t1_z+_byte_cor_2.mnc
0
)

itk_add_test(
NAME
Expand All @@ -54,7 +60,9 @@ itk_add_test(
${ITK_TEST_OUTPUT_DIR}/HeadMRVolume.mnc
itkMINCImageIOTest4
DATA{${ITK_DATA_ROOT}/Input/HeadMRVolume.mhd,HeadMRVolume.raw}
${ITK_TEST_OUTPUT_DIR}/HeadMRVolume.mnc)
${ITK_TEST_OUTPUT_DIR}/HeadMRVolume.mnc
-1
)

# Test to make sure that inter-slice normalization was properly dealt with
itk_add_test(
Expand All @@ -67,7 +75,8 @@ itk_add_test(
${ITK_TEST_OUTPUT_DIR}/t1_z+_ubyte_yxz_nonorm_noParams.mnc
itkMINCImageIOTest4
DATA{Input/t1_z+_ubyte_yxz_nonorm.mnc}
${ITK_TEST_OUTPUT_DIR}/t1_z+_ubyte_yxz_nonorm_noParams.mnc)
${ITK_TEST_OUTPUT_DIR}/t1_z+_ubyte_yxz_nonorm_noParams.mnc
0)

itk_add_test(
NAME
Expand All @@ -79,7 +88,8 @@ itk_add_test(
${ITK_TEST_OUTPUT_DIR}/t1_z+_float_yxz_nonorm_noParams.mnc
itkMINCImageIOTest4
DATA{Input/t1_z+_float_yxz_nonorm.mnc}
${ITK_TEST_OUTPUT_DIR}/t1_z+_float_yxz_nonorm_noParams.mnc)
${ITK_TEST_OUTPUT_DIR}/t1_z+_float_yxz_nonorm_noParams.mnc
0)

itk_add_test(
NAME
Expand Down Expand Up @@ -130,6 +140,7 @@ itk_add_test(
itkMINCImageIOTest4
DATA{Input/t1_z+_float_yxz_nonorm.mnc}
${ITK_TEST_OUTPUT_DIR}/t1_z+_float_yxz_nonorm.mnc
0 # RAStoLPS
427621.7839
-8.195741583
72.45998819
Expand All @@ -146,6 +157,7 @@ itk_add_test(
itkMINCImageIOTest4
DATA{Input/t1_z+_float_yxz_norm.mnc}
${ITK_TEST_OUTPUT_DIR}/t1_z+_float_yxz_norm.mnc
0 # RAStoLPS
427621.7839
-8.195741583
72.45998819
Expand All @@ -162,47 +174,51 @@ itk_add_test(
itkMINCImageIOTest4
DATA{Input/t1_z+_ubyte_yxz_nonorm.mnc}
${ITK_TEST_OUTPUT_DIR}/t1_z+_ubyte_yxz_nonorm.mnc
0 # RAStoLPS
427621.7838
-8.195741583
72.45998819
-3.148534512)

# multiple loops because of different numerical parameters
foreach(rastoeps 0;1)
foreach(type byte;short;ubyte)
foreach(axis cor;sag;trans)
foreach(plusMinus -;+)
set(imageName t1_z${plusMinus}_${type}_${axis})
set(outImage ${ITK_TEST_OUTPUT_DIR}/${imageName}-${rastoeps}.mnc)

foreach(type byte;short;ubyte)
foreach(axis cor;sag;trans)
foreach(plusMinus -;+)
set(imageName t1_z${plusMinus}_${type}_${axis})
set(outImage ${ITK_TEST_OUTPUT_DIR}/${imageName}.mnc)

itk_add_test(
NAME
itkMINCImageIOTest-COM-${imageName}
COMMAND
ITKIOMINCTestDriver
--compare
DATA{Input/${imageName}.mnc}
${outImage}
itkMINCImageIOTest4
DATA{Input/${imageName}.mnc}
${outImage}
427620.3115
-8.195582241
72.46002233
-3.148594157) # this line is different
itk_add_test(
NAME
itkMINCImageIOTest-COM-${imageName}-${rastoeps}
COMMAND
ITKIOMINCTestDriver
--compare
DATA{Input/${imageName}.mnc}
${outImage}
itkMINCImageIOTest4
DATA{Input/${imageName}.mnc}
${outImage}
${rastoeps} # RAStoLPS
427620.3115
-8.195582241
72.46002233
-3.148594157) # this line is different
endforeach()
endforeach()
endforeach()
endforeach()

foreach(rastoeps 0;1)
foreach(type double;float;long;ulong)
foreach(axis cor;sag;trans)
foreach(plusMinus -;+)
set(imageName t1_z${plusMinus}_${type}_${axis})
set(outImage ${ITK_TEST_OUTPUT_DIR}/${imageName}.mnc)
set(outImage ${ITK_TEST_OUTPUT_DIR}/${imageName}_${rastoeps}.mnc)

itk_add_test(
NAME
itkMINCImageIOTest-COM-${imageName}
itkMINCImageIOTest-COM-${imageName}-${rastoeps}
COMMAND
ITKIOMINCTestDriver
--compare
Expand All @@ -211,23 +227,26 @@ foreach(type double;float;long;ulong)
itkMINCImageIOTest4
DATA{Input/${imageName}.mnc}
${outImage}
${rastoeps} # RAStoLPS
427590.7631
-8.195995507
72.45943584
-3.148635493) # this line is different
endforeach()
endforeach()
endforeach()
endforeach()

foreach(rastoeps 0;1)
foreach(type ushort)
foreach(axis cor;sag;trans)
foreach(plusMinus -;+)
set(imageName t1_z${plusMinus}_${type}_${axis})
set(outImage ${ITK_TEST_OUTPUT_DIR}/${imageName}.mnc)
set(outImage ${ITK_TEST_OUTPUT_DIR}/${imageName}_${rastoeps}.mnc)

itk_add_test(
NAME
itkMINCImageIOTest-COM-${imageName}
itkMINCImageIOTest-COM-${imageName}-${rastoeps}
COMMAND
ITKIOMINCTestDriver
--compare
Expand All @@ -236,10 +255,12 @@ foreach(type ushort)
itkMINCImageIOTest4
DATA{Input/${imageName}.mnc}
${outImage}
${rastoeps} # RAStoLPS
427590.7957
-8.195997123
72.45943721
-3.148635961) # this line is different
endforeach()
endforeach()
endforeach()
endforeach()
Loading

0 comments on commit a8811a0

Please sign in to comment.