/*========================================================================= * * 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 #include #include "itkByteSwapper.h" #include "itkNiftiImageIO.h" #include "itkImageFileReader.h" #include "itkImageRegionConstIterator.h" #include "itkMath.h" #include "itkSpatialOrientationAdapter.h" // debug #include namespace { // // Analyze 7.5 header -- this describes the data below, // as an 6 x 6 x 8 image of float pixels const unsigned char LittleEndian_hdr[] = { 0x5c, 0x01, 0x00, 0x00, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x04, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; // // float data, represented as a char stream, in little-endian // order const unsigned char LittleEndian_img[] = { 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, }; // Map between axis string labels and spatial orientation std::map codeToString = { { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIP, "RIP" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LIP, "LIP" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP, "RSP" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LSP, "LSP" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIA, "RIA" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LIA, "LIA" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSA, "RSA" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LSA, "LSA" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IRP, "IRP" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ILP, "ILP" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SRP, "SRP" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SLP, "SLP" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IRA, "IRA" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ILA, "ILA" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SRA, "SRA" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SLA, "SLA" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPI, "RPI" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LPI, "LPI" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, "RAI" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LAI, "LAI" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPS, "RPS" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LPS, "LPS" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAS, "RAS" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LAS, "LAS" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PRI, "PRI" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PLI, "PLI" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ARI, "ARI" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ALI, "ALI" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PRS, "PRS" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PLS, "PLS" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ARS, "ARS" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ALS, "ALS" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IPR, "IPR" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SPR, "SPR" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IAR, "IAR" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SAR, "SAR" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IPL, "IPL" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SPL, "SPL" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IAL, "IAL" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SAL, "SAL" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIR, "PIR" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PSR, "PSR" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR, "AIR" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ASR, "ASR" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIL, "PIL" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PSL, "PSL" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIL, "AIL" }, { itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ASL, "ASL" } }; /** WriteFile * Write out a char array as binary */ int WriteFile(const std::string & name, const unsigned char * buf, size_t buflen) { std::ofstream f(name.c_str(), std::ios::binary | std::ios::out); if (!f.is_open()) { return EXIT_FAILURE; } f.write(reinterpret_cast(buf), buflen); f.close(); return EXIT_SUCCESS; } /** ReadFile * read an image from disk */ template typename TImage::Pointer ReadImage(const std::string & fileName, itk::NiftiImageIOEnums::Analyze75Flavor analyze_mode = itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) { using ReaderType = itk::ImageFileReader; auto reader = ReaderType::New(); typename itk::NiftiImageIO::Pointer imageIO = itk::NiftiImageIO::New(); { imageIO->SetLegacyAnalyze75Mode(analyze_mode); reader->SetImageIO(imageIO); reader->SetFileName(fileName.c_str()); try { reader->Update(); } catch (const itk::ExceptionObject & err) { std::cout << "Caught an exception: " << std::endl; std::cout << err << ' ' << __FILE__ << ' ' << __LINE__ << std::endl; throw; } catch (...) { std::cout << "Error while reading in image " << fileName << std::endl; throw; } } typename TImage::Pointer image = reader->GetOutput(); return image; } } // namespace int itkNiftiAnalyzeContentsAndCoordinatesTest(char * argv[], unsigned char hist_orient_code, itk::SpatialOrientationEnums::ValidCoordinateOrientations expected_code, itk::NiftiImageIOEnums::Analyze75Flavor analyze_mode, bool flip_x = false) { std::string hdrName(argv[1]); hdrName += "/littleEndian_"; hdrName += codeToString[expected_code]; hdrName += ".hdr"; std::string imgName(argv[1]); imgName += "/littleEndian_"; imgName += codeToString[expected_code]; imgName += ".img"; // hack the header to have proper orientation code unsigned char tweaked_hdr[sizeof(LittleEndian_hdr)]; memcpy(tweaked_hdr, LittleEndian_hdr, sizeof(LittleEndian_hdr)); tweaked_hdr[252] = hist_orient_code; // hack the header to flip X step if (flip_x) { float negative_x = -1.f; memcpy(tweaked_hdr + 80, &negative_x, sizeof(float)); } if (WriteFile(hdrName, tweaked_hdr, sizeof(LittleEndian_hdr)) != EXIT_SUCCESS) { std::cerr << "itkNiftiAnalyzeContentsAndCoordinatesTest: failed to write " << hdrName << std::endl; return EXIT_FAILURE; } if (WriteFile(imgName, LittleEndian_img, sizeof(LittleEndian_img)) != EXIT_SUCCESS) { std::cerr << "itkNiftiAnalyzeContentsAndCoordinatesTest: failed to write " << imgName << std::endl; return EXIT_FAILURE; } // // read the image just written back in. using ImageType = itk::Image; ImageType::Pointer img; try { img = ReadImage(hdrName, analyze_mode); } catch (...) { if (analyze_mode == itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeReject) { std::cerr << "Failure is expected" << std::endl << std::endl; } return EXIT_FAILURE; } const auto * fPtr = reinterpret_cast(LittleEndian_img); itk::ImageRegionConstIterator it(img, img->GetLargestPossibleRegion()); it.GoToBegin(); for (; !it.IsAtEnd(); ++it, ++fPtr) { // // in the unlikely event we're testing on a big-endian machine, do // byte swapping on floats pulled from the little-endian array. float cur = *fPtr; itk::ByteSwapper::SwapFromSystemToLittleEndian(&cur); if (itk::Math::NotExactlyEquals(it.Get(), cur)) { std::cerr << "itkNiftiAnalyzeContentsAndCoordinatesTest: expected pixel value " << cur << " but found " << it.Get() << std::endl; return EXIT_FAILURE; } } itk::SpatialOrientationEnums::ValidCoordinateOrientations orientation_code = itk::SpatialOrientationAdapter().FromDirectionCosines(img->GetDirection()); // verify the correct orientation : if (orientation_code != expected_code) { std::cerr << "Analyze orientation " << static_cast(hist_orient_code) << std::endl; std::cerr << "expected orientation " << codeToString[expected_code] << " but found " << codeToString[orientation_code] << std::endl; return EXIT_FAILURE; } // TODO: check origin and spacing too std::cout << "Analyze orientation :" << static_cast(hist_orient_code) << std::endl << "Analyze flavor :" << analyze_mode << std::endl << "negative x step:" << (flip_x ? "true" : "false") << std::endl << "Origin :" << img->GetOrigin() << std::endl << "Spacing :" << img->GetSpacing() << std::endl << "Code :" << codeToString[orientation_code] << std::endl << "Direction:" << img->GetDirection() << std::endl; return EXIT_SUCCESS; } int itkNiftiReadAnalyzeTest(int argc, char * argv[]) { if (argc < 2) { std::cerr << "itkNiftiReadAnalyzeTest: Missing test directory argument" << std::endl; return EXIT_FAILURE; } // check that when we reject Analyze, it can't be read // NOTE: according to the information from // https://web.archive.org/web/20121116093304/http://wideman-one.com/gw/brain/analyze/formatdoc.htm Analyze code 5 // should have been PSR but it was revised in NIFTI somehow to PIL return itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeReject) != EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 1, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIP, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 2, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIR, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 3, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 4, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 5, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIL, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE || // ITK4 default behaviour: reader should ignore orientation code and always produce RAI , // there should be a warning on console itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4Warning) == EXIT_FAILURE || // ITK4 reader should ignore orientation code and always produce RAI itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 1, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 2, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 3, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 5, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 5, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE || // flip X axis , SPM reader should respect this itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LPI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM, true) == EXIT_FAILURE || // flip X axis , ITK4 reader should respect this itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LAI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4, true) == EXIT_FAILURE || // flip X axis , FSL reader should ignore this itkNiftiAnalyzeContentsAndCoordinatesTest( argv, 0, itk::SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPI, itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeFSL, true) == EXIT_FAILURE ? EXIT_FAILURE : EXIT_SUCCESS; }