2929
3030#include < memory> // For unique_ptr.
3131
32+
3233extern " C"
3334{
3435 void
@@ -235,6 +236,7 @@ MINCImageIO::CloseVolume()
235236
236237MINCImageIO::MINCImageIO ()
237238 : m_MINCPImpl(std::make_unique<MINCImageIOPImpl>())
239+ , m_RAStoLPS(false )
238240{
239241 for (auto & dimensionIndex : m_MINCPImpl->m_DimensionIndices )
240242 {
@@ -280,6 +282,7 @@ MINCImageIO::PrintSelf(std::ostream & os, Indent indent) const
280282
281283 os << indent << " MINCPImpl: " << m_MINCPImpl.get () << std::endl;
282284 os << indent << " DirectionCosines: " << m_DirectionCosines << std::endl;
285+ os << indent << " RAStoLPS: " << m_RAStoLPS << std::endl;
283286}
284287
285288void
@@ -446,14 +449,20 @@ MINCImageIO::ReadImageInformation()
446449
447450 this ->SetNumberOfDimensions (spatial_dimension_count);
448451
449- int numberOfComponents = 1 ;
450- int usable_dimensions = 0 ;
452+ int numberOfComponents = 1 ;
453+ unsigned int usableDimensions = 0 ;
454+
455+ auto dir_cos = Matrix<double , 3 , 3 >::GetIdentity ();
451456
452- Matrix<double , 3 , 3 > dir_cos{};
453- dir_cos.SetIdentity ();
457+ // Conversion matrix for MINC PositiveCoordinateOrientation RAS (LeftToRight, PosteriorToAnterior, InferiorToSuperior)
458+ // to ITK PositiveCoordinateOrientation LPS (RightToLeft, AnteriorToPosterior, InferiorToSuperior)
459+ auto RAStofromLPS = Matrix<double , 3 , 3 >::GetIdentity ();
460+ RAStofromLPS (0 , 0 ) = -1.0 ;
461+ RAStofromLPS (1 , 1 ) = -1.0 ;
462+ std::vector<double > dir_cos_temp (3 );
454463
455464 Vector<double , 3 > origin{};
456- Vector<double , 3 > o_origin {};
465+ Vector<double , 3 > oOrigin {};
457466
458467 // minc api uses inverse order of dimensions , fastest varying are last
459468 Vector<double , 3 > sep;
@@ -463,19 +472,19 @@ MINCImageIO::ReadImageInformation()
463472 {
464473 // MINC2: bad design!
465474 // micopy_dimension(hdim[m_MINCPImpl->m_DimensionIndices[i]],&apparent_dimension_order[usable_dimensions]);
466- m_MINCPImpl->m_MincApparentDims [usable_dimensions ] =
475+ m_MINCPImpl->m_MincApparentDims [usableDimensions ] =
467476 m_MINCPImpl->m_MincFileDims [m_MINCPImpl->m_DimensionIndices [i]];
468477 // always use positive
469- miset_dimension_apparent_voxel_order (m_MINCPImpl->m_MincApparentDims [usable_dimensions ], MI_POSITIVE);
478+ miset_dimension_apparent_voxel_order (m_MINCPImpl->m_MincApparentDims [usableDimensions ], MI_POSITIVE);
470479 misize_t _sz;
471- miget_dimension_size (m_MINCPImpl->m_MincApparentDims [usable_dimensions ], &_sz);
480+ miget_dimension_size (m_MINCPImpl->m_MincApparentDims [usableDimensions ], &_sz);
472481
473482 double _sep;
474- miget_dimension_separation (m_MINCPImpl->m_MincApparentDims [usable_dimensions ], MI_ORDER_APPARENT, &_sep);
483+ miget_dimension_separation (m_MINCPImpl->m_MincApparentDims [usableDimensions ], MI_ORDER_APPARENT, &_sep);
475484 std::vector<double > _dir (3 );
476- miget_dimension_cosines (m_MINCPImpl->m_MincApparentDims [usable_dimensions ], &_dir[0 ]);
485+ miget_dimension_cosines (m_MINCPImpl->m_MincApparentDims [usableDimensions ], &_dir[0 ]);
477486 double _start;
478- miget_dimension_start (m_MINCPImpl->m_MincApparentDims [usable_dimensions ], MI_ORDER_APPARENT, &_start);
487+ miget_dimension_start (m_MINCPImpl->m_MincApparentDims [usableDimensions ], MI_ORDER_APPARENT, &_start);
479488
480489 for (int j = 0 ; j < 3 ; ++j)
481490 {
@@ -486,52 +495,61 @@ MINCImageIO::ReadImageInformation()
486495 sep[i - 1 ] = _sep;
487496
488497 this ->SetDimensions (i - 1 , static_cast <unsigned int >(_sz));
489- this ->SetDirection (i - 1 , _dir);
490498 this ->SetSpacing (i - 1 , _sep);
491499
492- ++usable_dimensions;
500+ ++usableDimensions;
501+ }
502+ }
503+
504+
505+ // Transform MINC PositiveCoordinateOrientation RAS coordinates to
506+ // internal ITK PositiveCoordinateOrientation LPS Coordinates
507+ if (this ->m_RAStoLPS )
508+ dir_cos = RAStofromLPS * dir_cos;
509+
510+ // Transform origin coordinates
511+ oOrigin = dir_cos * origin;
512+
513+ for (int i = 0 ; i < spatial_dimension_count; ++i)
514+ {
515+ this ->SetOrigin (i, oOrigin[i]);
516+ for (unsigned int j = 0 ; j < 3 ; j++)
517+ {
518+ dir_cos_temp[j] = dir_cos[j][i];
493519 }
520+ this ->SetDirection (i, dir_cos_temp);
494521 }
495522
496523 if (m_MINCPImpl->m_DimensionIndices [0 ] != -1 ) // have vector dimension
497524 {
498525 // micopy_dimension(m_MINCPImpl->m_MincFileDims[m_MINCPImpl->m_DimensionIndices[0]],&apparent_dimension_order[usable_dimensions]);
499- m_MINCPImpl->m_MincApparentDims [usable_dimensions] =
500- m_MINCPImpl->m_MincFileDims [m_MINCPImpl->m_DimensionIndices [0 ]];
526+ m_MINCPImpl->m_MincApparentDims [usableDimensions] = m_MINCPImpl->m_MincFileDims [m_MINCPImpl->m_DimensionIndices [0 ]];
501527 // always use positive, vector dimension does not supposed to have notion of positive step size, so leaving as is
502528 // miset_dimension_apparent_voxel_order(m_MINCPImpl->m_MincApparentDims[usable_dimensions],MI_POSITIVE);
503529 misize_t _sz;
504- miget_dimension_size (m_MINCPImpl->m_MincApparentDims [usable_dimensions ], &_sz);
530+ miget_dimension_size (m_MINCPImpl->m_MincApparentDims [usableDimensions ], &_sz);
505531 numberOfComponents = _sz;
506- ++usable_dimensions ;
532+ ++usableDimensions ;
507533 }
508534
509535 if (m_MINCPImpl->m_DimensionIndices [4 ] != -1 ) // have time dimension
510536 {
511537 // micopy_dimension(hdim[m_MINCPImpl->m_DimensionIndices[4]],&apparent_dimension_order[usable_dimensions]);
512- m_MINCPImpl->m_MincApparentDims [usable_dimensions] =
513- m_MINCPImpl->m_MincFileDims [m_MINCPImpl->m_DimensionIndices [4 ]];
538+ m_MINCPImpl->m_MincApparentDims [usableDimensions] = m_MINCPImpl->m_MincFileDims [m_MINCPImpl->m_DimensionIndices [4 ]];
514539 // always use positive
515- miset_dimension_apparent_voxel_order (m_MINCPImpl->m_MincApparentDims [usable_dimensions ], MI_POSITIVE);
540+ miset_dimension_apparent_voxel_order (m_MINCPImpl->m_MincApparentDims [usableDimensions ], MI_POSITIVE);
516541 misize_t _sz;
517- miget_dimension_size (m_MINCPImpl->m_MincApparentDims [usable_dimensions ], &_sz);
542+ miget_dimension_size (m_MINCPImpl->m_MincApparentDims [usableDimensions ], &_sz);
518543 numberOfComponents = _sz;
519- ++usable_dimensions ;
544+ ++usableDimensions ;
520545 }
521546
522547 // Set apparent dimension order to the MINC2 api
523- if (miset_apparent_dimension_order (m_MINCPImpl->m_Volume , usable_dimensions , m_MINCPImpl->m_MincApparentDims ) < 0 )
548+ if (miset_apparent_dimension_order (m_MINCPImpl->m_Volume , usableDimensions , m_MINCPImpl->m_MincApparentDims ) < 0 )
524549 {
525550 itkExceptionMacro (" Can't set apparent dimension order!" );
526551 }
527552
528- o_origin = dir_cos * origin;
529-
530- for (int i = 0 ; i < spatial_dimension_count; ++i)
531- {
532- this ->SetOrigin (i, o_origin[i]);
533- }
534-
535553 miclass_t volume_data_class;
536554
537555 if (miget_data_class (m_MINCPImpl->m_Volume , &volume_data_class) < 0 )
@@ -660,6 +678,9 @@ MINCImageIO::ReadImageInformation()
660678 const std::string classname (GetNameOfClass ());
661679 // EncapsulateMetaData<std::string>(thisDic,ITK_InputFilterName,
662680 // classname);
681+ // preserve information if the volume was PositiveCoordinateOrientation RAS to PositiveCoordinateOrientation LPS
682+ // converted
683+ EncapsulateMetaData<bool >(thisDic, " RAStoLPS" , m_RAStoLPS);
663684
664685 // store history
665686 size_t minc_history_length = 0 ;
@@ -944,22 +965,34 @@ MINCImageIO::WriteImageInformation()
944965 }
945966
946967 // allocating dimensions
947- vnl_matrix<double > dircosmatrix (nDims, nDims);
948- dircosmatrix .set_identity ();
968+ vnl_matrix<double > directionCosineMatrix (nDims, nDims);
969+ directionCosineMatrix .set_identity ();
949970 vnl_vector<double > origin (nDims);
950971
972+ // MINC stores direction cosines in PositiveCoordinateOrientation RAS
973+ // need to convert to PositiveCoordinateOrientation LPS for ITK
974+ vnl_matrix<double > RAS_tofrom_LPS (nDims, nDims);
975+ RAS_tofrom_LPS.set_identity ();
976+ RAS_tofrom_LPS (0 , 0 ) = -1.0 ;
977+ RAS_tofrom_LPS (1 , 1 ) = -1.0 ;
978+
951979 for (unsigned int i = 0 ; i < nDims; ++i)
952980 {
953981 for (unsigned int j = 0 ; j < nDims; ++j)
954982 {
955- dircosmatrix [i][j] = this ->GetDirection (i)[j];
983+ directionCosineMatrix [i][j] = this ->GetDirection (i)[j];
956984 }
957985 origin[i] = this ->GetOrigin (i);
958986 }
959987
960- const vnl_matrix<double > inverseDirectionCosines{ vnl_matrix_inverse<double >(dircosmatrix ).as_matrix () };
988+ const vnl_matrix<double > inverseDirectionCosines{ vnl_matrix_inverse<double >(directionCosineMatrix ).as_matrix () };
961989 origin *= inverseDirectionCosines; // transform to minc convention
962990
991+
992+ // Convert ITK direction cosines from PositiveCoordinateOrientation LPS to PositiveCoordinateOrientation RAS
993+ if (this ->m_RAStoLPS )
994+ directionCosineMatrix *= RAS_tofrom_LPS;
995+
963996 for (unsigned int i = 0 ; i < nDims; ++i)
964997 {
965998 const unsigned int j = i + (nComp > 1 ? 1 : 0 );
@@ -968,7 +1001,7 @@ MINCImageIO::WriteImageInformation()
9681001 {
9691002 if (k < nDims)
9701003 {
971- dir_cos[k] = dircosmatrix [i][k];
1004+ dir_cos[k] = directionCosineMatrix [i][k];
9721005 }
9731006 else
9741007 {
@@ -988,38 +1021,32 @@ MINCImageIO::WriteImageInformation()
9881021 {
9891022 case IOComponentEnum::UCHAR:
9901023 m_MINCPImpl->m_Volume_type = MI_TYPE_UBYTE;
991- // m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
9921024 break ;
9931025 case IOComponentEnum::CHAR:
9941026 m_MINCPImpl->m_Volume_type = MI_TYPE_BYTE;
995- // m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
9961027 break ;
9971028 case IOComponentEnum::USHORT:
9981029 m_MINCPImpl->m_Volume_type = MI_TYPE_USHORT;
999- // m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
10001030 break ;
10011031 case IOComponentEnum::SHORT:
10021032 m_MINCPImpl->m_Volume_type = MI_TYPE_SHORT;
1003- // m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
10041033 break ;
10051034 case IOComponentEnum::UINT:
10061035 m_MINCPImpl->m_Volume_type = MI_TYPE_UINT;
1007- // m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
10081036 break ;
10091037 case IOComponentEnum::INT:
10101038 m_MINCPImpl->m_Volume_type = MI_TYPE_INT;
1011- // m_MINCPImpl->m_Volume_class=MI_CLASS_INT;
10121039 break ;
10131040 // case IOComponentEnum::ULONG://TODO: make sure we are cross-platform here!
10141041 // volume_data_type=MI_TYPE_ULONG;
10151042 // break;
10161043 // case IOComponentEnum::LONG://TODO: make sure we are cross-platform here!
10171044 // volume_data_type=MI_TYPE_LONG;
10181045 // break;
1019- case IOComponentEnum::FLOAT: // TODO: make sure we are cross-platform here!
1046+ case IOComponentEnum::FLOAT:
10201047 m_MINCPImpl->m_Volume_type = MI_TYPE_FLOAT;
10211048 break ;
1022- case IOComponentEnum::DOUBLE: // TODO: make sure we are cross-platform here!
1049+ case IOComponentEnum::DOUBLE:
10231050 m_MINCPImpl->m_Volume_type = MI_TYPE_DOUBLE;
10241051 break ;
10251052 default :
@@ -1059,7 +1086,6 @@ MINCImageIO::WriteImageInformation()
10591086 if (ExposeMetaData<std::string>(thisDic, " dimension_order" , dimension_order))
10601087 {
10611088 // the format should be ((+|-)(X|Y|Z|V|T))*
1062- // std::cout<<"Restoring original dimension order:"<<dimension_order<<std::endl;
10631089 if (dimension_order.length () == (minc_dimensions * 2 ))
10641090 {
10651091 dimorder_good = true ;
@@ -1296,6 +1322,13 @@ MINCImageIO::WriteImageInformation()
12961322 // TODO: figure out what to do with it
12971323 }
12981324 }
1325+
1326+ // preserve information of MINC PositiveCoordinateOrientation RAS to ITK PositiveCoordinateOrientation LPS conversion
1327+ {
1328+ int tmp = (int )this ->m_RAStoLPS ;
1329+ miset_attr_values (m_MINCPImpl->m_Volume , MI_TYPE_INT, " itk" , " RAStoLPS" , 1 , &tmp);
1330+ }
1331+
12991332 mifree_volume_props (hprops);
13001333}
13011334
0 commit comments