<commit_before>/*
 * Medical Image Registration ToolKit (MIRTK)
 *
 * Copyright 2008-2015 Imperial College London
 * Copyright 2008-2015 <NAME>, <NAME>
 *
 * 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
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 "NiftiImage.h"

#include "mirtk/NiftiImageReader.h"
#include "mirtk/ImageReaderFactory.h"

#include "mirtk/Array.h"
#include "mirtk/Voxel.h"
#include "mirtk/Path.h"
#include "mirtk/Matrix.h"

#include <sys/types.h>
#include <sys/stat.h>


namespace mirtk {


// Register image reader with object factory during static initialization
mirtkAutoRegisterImageReaderMacro(NiftiImageReader);


// =============================================================================
// Auxiliaries
// =============================================================================

// -----------------------------------------------------------------------------
static inline nifti_dmat33 nifti_dmat44_to_dmat33(const nifti_dmat44 &x)
{
  nifti_dmat33 y;
  for (int i = 0; i < 3; ++i)
  for (int j = 0; j < 3; ++j) {
    y.m[i][j] = x.m[i][j];
  }
  return y;
}

// =============================================================================
// Construction/Destruction
// =============================================================================

// -----------------------------------------------------------------------------
NiftiImageReader::NiftiImageReader()
:
  _Nifti(new NiftiImage)
{
}

// -----------------------------------------------------------------------------
NiftiImageReader::~NiftiImageReader()
{
  delete _Nifti;
}

// =============================================================================
// Execution
// =============================================================================

// -----------------------------------------------------------------------------
bool NiftiImageReader::CheckHeader(const char *fname)
{
  char magic_number[4];
  Cifstream from(fname);
  from.Open(fname);
  from.ReadAsChar(magic_number, 4, 344);
  from.Close();
  return (strcmp(magic_number, "n+1") == 0 || strcmp(magic_number, "ni1") == 0);
}

// -----------------------------------------------------------------------------
bool NiftiImageReader::CanRead(const char *fname) const
{
  return CheckHeader(fname);
}

// -----------------------------------------------------------------------------
void NiftiImageReader::Initialize()
{
  // Read magic no
  char magic_number[4];
  Cifstream from(_FileName.c_str());
  from.ReadAsChar(magic_number, 4, 344);
  from.Close();

  // Determine corresponding image data file
  _ImageName     = _FileName;
  const size_t l = _FileName.size();
  if (l <= 4) {
    cerr << this->NameOfClass() << ": Input file name is missing proper extension: " << _FileName << endl;
    exit(1);
  }

  if (strcmp(magic_number, "ni1") == 0) {
    string ext = Extension(_FileName, EXT_LastWithGz);
    if (ext == ".hdr.gz") {
      _ImageName[l-6] = 'i';
      _ImageName[l-5] = 'm';
      _ImageName[l-4] = 'g';
    } else {
      struct stat buf;
      _ImageName[l-3] = 'i';
      _ImageName[l-2] = 'm';
      _ImageName[l-1] = 'g';
      if (stat(_ImageName.c_str(), &buf) != 0) {
        string fname;
        Array<string> exts;
        exts.push_back(".gz");
        exts.push_back(".GZ");
        exts.push_back(".z");
        exts.push_back(".Z");
        for (size_t i = 0; i < exts.size(); ++i) {
          fname = _ImageName + exts[i];
          if (stat(fname.c_str(), &buf) == 0) break;
          fname.clear();
        }
        if (!fname.empty()) _ImageName = fname;
      }
    }
  } else if (strcmp(magic_number, "n+1") != 0) {
    cerr << this->NameOfClass() << "::Read: File format is not NIFTI" << endl;
    exit(1);
  }

  // Read header
  this->ReadHeader();

  // Open image data file
  this->Open(_ImageName.c_str());
}

// -----------------------------------------------------------------------------
void NiftiImageReader::ReadHeader()
{
  int          order;
  float        det;
  nifti_dmat44 mat_44, mat_inv_44;
  nifti_dmat33 mat_33;

  // Read header
  _Nifti->Read(_FileName.c_str());

  // Check dimension
  if (_Nifti->nim->dim[0] > 5) {
    cerr << this->NameOfClass() << "::ReadHeader: Number of dimensions > 5 (Number of dimensions = " << _Nifti->nim->dim[0] << ") \n";
    exit(1);
  }
  if ((_Nifti->nim->dim[0] == 5) && (_Nifti->nim->dim[4] != 1)) {
    cerr << this->NameOfClass() << "::ReadHeader: Number of dimensions == 5 (Number of dimensions = " << _Nifti->nim->dim[0] << ") \n";
    exit(1);
  }

  // Check for swapping
#if MIRTK_BIG_ENDIAN
  if (_Nifti->nim->byteorder == MSB_FIRST) {
    _Swapped = !_Swapped;
  }
#else
  if (_Nifti->nim->byteorder == LSB_FIRST) {
    _Swapped = !_Swapped;
  }
#endif

  // Check data scaling
  if (_Nifti->nim->scl_slope != 0) {
    _Slope = _Nifti->nim->scl_slope;
  } else {
    _Slope = 1.0;<commit_msg>fix: NIfTI-2 support of NiftiImageReader [IO]<commit_after>/*
 * Medical Image Registration ToolKit (MIRTK)
 *
 * Copyright 2008-2015 Imperial College London
 * Copyright 2008-2015 <NAME>, <NAME>
 *
 * 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
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 */

#define _NIFTI2_IO_C_
#include "NiftiImage.h"

#include "mirtk/NiftiImageReader.h"
#include "mirtk/ImageReaderFactory.h"

#include "mirtk/Array.h"
#include "mirtk/Voxel.h"
#include "mirtk/Path.h"
#include "mirtk/Matrix.h"

#include <sys/types.h>
#include <sys/stat.h>


namespace mirtk {


// Register image reader with object factory during static initialization
mirtkAutoRegisterImageReaderMacro(NiftiImageReader);


// =============================================================================
// Auxiliaries
// =============================================================================

// -----------------------------------------------------------------------------
static inline nifti_dmat33 nifti_dmat44_to_dmat33(const nifti_dmat44 &x)
{
  nifti_dmat33 y;
  for (int i = 0; i < 3; ++i)
  for (int j = 0; j < 3; ++j) {
    y.m[i][j] = x.m[i][j];
  }
  return y;
}

// =============================================================================
// Construction/Destruction
// =============================================================================

// -----------------------------------------------------------------------------
NiftiImageReader::NiftiImageReader()
:
  _Nifti(new NiftiImage)
{
}

// -----------------------------------------------------------------------------
NiftiImageReader::~NiftiImageReader()
{
  delete _Nifti;
}

// =============================================================================
// Execution
// =============================================================================

// -----------------------------------------------------------------------------
bool NiftiImageReader::CheckHeader(const char *fname)
{
  // Get name of corresponding image header file
  const char *hname = nifti_findhdrname(fname);
  if (hname == nullptr) return false;
  // Open header file stream
  Cifstream from(hname);
  // Check if it as a NIfTI image in ASCII format
  // (cf. has_ascii_header function defined in nifti2_io.cc)
  char nia_magic[16];
  if (from.ReadAsChar(nia_magic, 12)) {
    if (strncmp(nia_magic, "<nifti_image", 12) == 0) return true;
    from.Seek(0);
  }
  // Try to read NIfTI-1 header
  nifti_1_header n1hdr;
  const size_t h1size = sizeof(nifti_1_header);
  from.ReadAsChar((char *)&n1hdr, h1size);
  // Check if it is a NIfTI header and which version
  const int ni_ver = nifti_header_version((char *)&n1hdr, h1size);
  if (ni_ver == -1) return false;
  // In case of NIfTI-2, the file may be a CIFTI file instead
  if (ni_ver == 2) {
    // Fill remaining bytes of NIfTI-2 header
    nifti_2_header n2hdr;
    const size_t h2size = sizeof(nifti_2_header);
    memcpy(&n2hdr, &n1hdr, h1size);
    if (!from.ReadAsChar((char *)&n2hdr + h1size, static_cast<int>(h2size - h1size))) {
      return false;
    }
    // Check dimension sizes if it could be a CIFTI before checking the extensions
    nifti_image *nim = nifti_convert_n2hdr2nim(n2hdr, hname);
    const bool needs_swap = (nim->byteorder != nifti_short_order());
    bool maybe_cifti = (nim->nx < 2 && nim->ny < 2 && nim->nz < 2 && nim->nt < 2);
    if (nim->nu < 2 && nim->nt < 2) maybe_cifti = false;
    nifti_image_free(nim);
    // Look for CIFTI extension
    if (maybe_cifti) {
      int size, code;
      while (from.ReadAsChar((char *)&size, 4) &&
             from.ReadAsChar((char *)&code, 4)) {
        if (needs_swap) {
          nifti_swap_4bytes(1, &size);
          nifti_swap_4bytes(1, &code);
        }
        if (size < 16 || size & 0xf) break;          // size must be multiple of 16
        if (code == NIFTI_ECODE_CIFTI) return false; // found CIFTI extension
        if (!nifti_is_valid_ecode(code)) break;      // extension code must be valid
      }
    }
  }
  return true;
}

// -----------------------------------------------------------------------------
bool NiftiImageReader::CanRead(const char *fname) const
{
  return CheckHeader(fname);
}

// -----------------------------------------------------------------------------
void NiftiImageReader::Initialize()
{
  // Read header
  this->ReadHeader();

  // Open image data file
  const char *iname = nifti_findimgname(_FileName.c_str(), _Nifti->nim->nifti_type);
  if (iname == nullptr) {
    cerr << this->NameOfClass() << "::ReadHeader: Could not find image data file for " << _FileName << endl;
    exit(1);
  }
  this->Open(iname);
  _ImageName = iname;
}

// -----------------------------------------------------------------------------
void NiftiImageReader::ReadHeader()
{
  int          order;
  float        det;
  nifti_dmat44 mat_44, mat_inv_44;
  nifti_dmat33 mat_33;

  // Read header
  const char *hname = nifti_findhdrname(_FileName.c_str());
  if (hname == nullptr) {
    cerr << this->NameOfClass() << "::ReadHeader: Could not find header file for " << _FileName << endl;
    exit(1);
  }
  _Nifti->Read(hname);

  // Check dimension
  if (_Nifti->nim->dim[0] > 5) {
    cerr << this->NameOfClass() << "::ReadHeader: Number of dimensions > 5 (Number of dimensions = " << _Nifti->nim->dim[0] << ") \n";
    exit(1);
  }
  if ((_Nifti->nim->dim[0] == 5) && (_Nifti->nim->dim[4] != 1)) {
    cerr << this->NameOfClass() << "::ReadHeader: Number of dimensions == 5 (Number of dimensions = " << _Nifti->nim->dim[0] << ") \n";
    exit(1);
  }

  // Check for swapping
  _Swapped = (_Nifti->nim->byteorder != nifti_short_order());

  // Check data scaling
  if (_Nifti->nim->scl_slope != 0) {
    _Slope = _Nifti->nim->scl_slope;
  } else {
    _Slope = 1.0;
{'fraction_non_alphanumeric': 0.29583297385902857, 'fraction_numerical': 0.017686135795013373, 'mean_word_length': 2.4044052863436125, 'pattern_counts': {'":': 8, '<': 89, '<?xml version=': 0, '>': 74, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5304904', 'n_tokens_mistral': 3768, 'n_tokens_neox': 3601, 'n_words': 894}