/*========================================================================= * * 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 "itkFreeSurferBinaryMeshIO.h" #include "itksys/SystemTools.hxx" #include "itkMakeUniqueForOverwrite.h" namespace itk { FreeSurferBinaryMeshIO::FreeSurferBinaryMeshIO() { this->AddSupportedWriteExtension(".fsb"); this->AddSupportedWriteExtension(".fcv"); } FreeSurferBinaryMeshIO::~FreeSurferBinaryMeshIO() = default; bool FreeSurferBinaryMeshIO::CanReadFile(const char * fileName) { if (!itksys::SystemTools::FileExists(fileName, true)) { return false; } if (itksys::SystemTools::GetFilenameLastExtension(fileName) != ".fsb" && itksys::SystemTools::GetFilenameLastExtension(fileName) != ".fcv") { return false; } return true; } bool FreeSurferBinaryMeshIO::CanWriteFile(const char * fileName) { if (itksys::SystemTools::GetFilenameLastExtension(fileName) != ".fsb" && itksys::SystemTools::GetFilenameLastExtension(fileName) != ".fcv") { return false; } return true; } void FreeSurferBinaryMeshIO::OpenFile() { if (this->m_FileName.empty()) { itkExceptionMacro("No input FileName"); } if (!itksys::SystemTools::FileExists(m_FileName.c_str())) { itkExceptionMacro("File " << this->m_FileName << " does not exist"); } m_InputFile.open(this->m_FileName.c_str(), std::ios::binary); if (!this->m_InputFile.is_open()) { itkExceptionMacro("Unable to open file inputFile " << this->m_FileName); } } void FreeSurferBinaryMeshIO::CloseFile() { if (m_InputFile.is_open()) { m_InputFile.close(); } } void FreeSurferBinaryMeshIO::ReadMeshInformation() { // Define input file stream and attach it to input file OpenFile(); // Define required variables constexpr unsigned int fileTypeIdLength = 3; unsigned char fileTypeId[fileTypeIdLength]; this->m_FileType = IOFileEnum::BINARY; // Read file type m_InputFile.read(reinterpret_cast(fileTypeId), fileTypeIdLength); m_FileTypeIdentifier = 0; m_FileTypeIdentifier <<= 8; m_FileTypeIdentifier |= fileTypeId[0]; m_FileTypeIdentifier <<= 8; m_FileTypeIdentifier |= fileTypeId[1]; m_FileTypeIdentifier <<= 8; m_FileTypeIdentifier |= fileTypeId[2]; // If input file is freesurfer binary surface file if (m_FileTypeIdentifier == (-2 & 0x00ffffff)) { constexpr unsigned int numberOfCellPoints = 3; // Read input commend int byte; // Extract Comment, and ignore it. byte = m_InputFile.get(); std::string comment = ""; while (byte != '\n') { comment += byte; byte = m_InputFile.get(); if (byte == EOF) { itkExceptionMacro("Unexpected EOF"); } } // Try to get the second '\n', but if the '\n' is not there, we put the byte // back. byte = m_InputFile.get(); if (byte != '\n') { if (byte == EOF) { itkExceptionMacro("Unexpected EOF"); } m_InputFile.unget(); } // Read the number of points and number of cells itk::uint32_t numberOfPoints; m_InputFile.read((char *)(&numberOfPoints), sizeof(numberOfPoints)); itk::ByteSwapper::SwapFromSystemToBigEndian(&numberOfPoints); this->m_NumberOfPoints = static_cast(numberOfPoints); itk::uint32_t numberOfCells; m_InputFile.read((char *)(&numberOfCells), sizeof(numberOfCells)); itk::ByteSwapper::SwapFromSystemToBigEndian(&numberOfCells); this->m_NumberOfCells = static_cast(numberOfCells); this->m_PointDimension = 3; // If number of points is not equal zero, update points if (this->m_NumberOfPoints) { this->m_UpdatePoints = true; } // If number of cells is not equal zero, update points if (this->m_NumberOfCells) { this->m_UpdateCells = true; } // Set default point component type this->m_PointComponentType = IOComponentEnum::FLOAT; // Set default cell component type this->m_CellComponentType = IOComponentEnum::UINT; this->m_CellBufferSize = this->m_NumberOfCells * (numberOfCellPoints + 2); m_FilePosition = m_InputFile.tellg(); } // If input file is curvature file else if (m_FileTypeIdentifier == (-1 & 0x00ffffff)) { // Set corresponding flags this->m_UpdatePoints = false; this->m_UpdateCells = false; this->m_UpdatePointData = true; this->m_UpdateCellData = false; // Read numberOfValuesPerPoint and numberOfPoints and numberOfCells itk::uint32_t numberOfPoints; m_InputFile.read((char *)(&numberOfPoints), sizeof(numberOfPoints)); itk::ByteSwapper::SwapFromSystemToBigEndian(&numberOfPoints); this->m_NumberOfPoints = static_cast(numberOfPoints); this->m_NumberOfPointPixels = this->m_NumberOfPoints; itk::uint32_t numberOfCells; m_InputFile.read((char *)(&numberOfCells), sizeof(numberOfCells)); itk::ByteSwapper::SwapFromSystemToBigEndian(&numberOfCells); this->m_NumberOfCells = static_cast(numberOfCells); itk::uint32_t numberOfValuesPerPoint; m_InputFile.read((char *)(&numberOfValuesPerPoint), sizeof(numberOfValuesPerPoint)); itk::ByteSwapper::SwapFromSystemToBigEndian(&numberOfValuesPerPoint); m_FilePosition = m_InputFile.tellg(); } else { itkExceptionMacro("Unvalid file type " << m_FileTypeIdentifier); } // Set default point pixel component and point pixel type this->m_PointPixelComponentType = IOComponentEnum::FLOAT; this->m_NumberOfPointPixelComponents = 1; this->m_PointPixelType = IOPixelEnum::SCALAR; // Set default cell pixel component and point pixel type this->m_CellPixelComponentType = IOComponentEnum::FLOAT; this->m_NumberOfCellPixelComponents = 1; this->m_CellPixelType = IOPixelEnum::SCALAR; CloseFile(); } void FreeSurferBinaryMeshIO::ReadPoints(void * buffer) { OpenFile(); m_InputFile.seekg(m_FilePosition, std::ios::beg); // Number of data array auto * data = static_cast(buffer); m_InputFile.read(static_cast(buffer), this->m_NumberOfPoints * this->m_PointDimension * sizeof(float)); itk::ByteSwapper::SwapRangeFromSystemToBigEndian(data, this->m_NumberOfPoints * this->m_PointDimension); } void FreeSurferBinaryMeshIO::ReadCells(void * buffer) { constexpr unsigned int numberOfCellPoints = 3; const auto data = make_unique_for_overwrite(this->m_NumberOfCells * numberOfCellPoints); m_InputFile.read(reinterpret_cast(data.get()), this->m_NumberOfCells * numberOfCellPoints * sizeof(itk::uint32_t)); itk::ByteSwapper::SwapRangeFromSystemToBigEndian(data.get(), this->m_NumberOfCells * numberOfCellPoints); this->WriteCellsBuffer( data.get(), static_cast(buffer), CellGeometryEnum::TRIANGLE_CELL, 3, this->m_NumberOfCells); CloseFile(); } void FreeSurferBinaryMeshIO::ReadPointData(void * buffer) { OpenFile(); m_InputFile.seekg(m_FilePosition, std::ios::beg); auto * data = static_cast(buffer); m_InputFile.read(reinterpret_cast(data), this->m_NumberOfPointPixels * sizeof(float)); itk::ByteSwapper::SwapRangeFromSystemToBigEndian(data, this->m_NumberOfPointPixels); CloseFile(); } void FreeSurferBinaryMeshIO::ReadCellData(void * itkNotUsed(buffer)) {} void FreeSurferBinaryMeshIO::WriteMeshInformation() { // Check file name if (this->m_FileName.empty()) { itkExceptionMacro("No Input FileName"); } // Write to output file std::ofstream outputFile(this->m_FileName.c_str(), std::ios::out | std::ios::binary); // Whether output stream opened successfully if (!outputFile.is_open()) { itkExceptionMacro("Unable to open file\n" "outputFilename= " << this->m_FileName); } if (this->m_UpdatePoints && this->m_UpdateCells) { // MAGIC_NUMBER = 16777214 ( little endian ) const char buffer[3] = { static_cast(255), static_cast(255), static_cast(254) }; outputFile.write(buffer, 3); std::string creator = "Created by ITK \n\n"; outputFile.write(const_cast(creator.c_str()), creator.size()); auto numberOfPoints = static_cast(this->m_NumberOfPoints); auto numberOfCells = static_cast(this->m_NumberOfCells); itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian(&numberOfPoints, 1, &outputFile); itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian(&numberOfCells, 1, &outputFile); } else if (this->m_UpdatePointData && (!this->m_UpdatePoints && !this->m_UpdateCells)) { // MAGIC_NUMBER = 16777215 ( little endian ) const char buffer[3] = { static_cast(255), static_cast(255), static_cast(255) }; outputFile.write(buffer, 3); auto numberOfPoints = static_cast(this->m_NumberOfPointPixels); auto numberOfCells = static_cast(this->m_NumberOfCells); itk::uint32_t numberOfValuesPerPoint = 1; itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian(&numberOfPoints, 1, &outputFile); itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian(&numberOfCells, 1, &outputFile); itk::ByteSwapper::SwapWriteRangeFromSystemToBigEndian(&numberOfValuesPerPoint, 1, &outputFile); } outputFile.close(); } void FreeSurferBinaryMeshIO::WritePoints(void * buffer) { // check file name if (this->m_FileName.empty()) { itkExceptionMacro("No Input FileName"); } // Write to output file std::ofstream outputFile(this->m_FileName.c_str(), std::ios_base::app | std::ios::binary); if (!outputFile.is_open()) { itkExceptionMacro("Unable to open file\n" "outputFilename= " << this->m_FileName); } // Write points switch (this->m_PointComponentType) { case IOComponentEnum::UCHAR: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::CHAR: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::USHORT: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::SHORT: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::UINT: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::INT: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::ULONG: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::LONG: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::ULONGLONG: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::LONGLONG: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::FLOAT: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::DOUBLE: { WritePoints(static_cast(buffer), outputFile); break; } case IOComponentEnum::LDOUBLE: { WritePoints(static_cast(buffer), outputFile); break; } default: { itkExceptionMacro("Unknown point pixel component type" << std::endl); } } outputFile.close(); } void FreeSurferBinaryMeshIO::WriteCells(void * buffer) { // Check file name if (this->m_FileName.empty()) { itkExceptionMacro("No Input FileName"); } // Write to output file std::ofstream outputFile(this->m_FileName.c_str(), std::ios_base::app | std::ios::binary); if (!outputFile.is_open()) { itkExceptionMacro("Unable to open file\n" "outputFilename= " << this->m_FileName); } // Write triangles switch (this->m_CellComponentType) { case IOComponentEnum::UCHAR: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::CHAR: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::USHORT: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::SHORT: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::UINT: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::INT: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::ULONG: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::LONG: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::ULONGLONG: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::LONGLONG: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::FLOAT: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::DOUBLE: { WriteCells(static_cast(buffer), outputFile); break; } case IOComponentEnum::LDOUBLE: { WriteCells(static_cast(buffer), outputFile); break; } default: { itkExceptionMacro("Unknown cell component type" << std::endl); } } outputFile.close(); } void FreeSurferBinaryMeshIO::WritePointData(void * buffer) { // check file name if (this->m_FileName.empty()) { itkExceptionMacro("No Input FileName"); } // Write to output file std::ofstream outputFile(this->m_FileName.c_str(), std::ios_base::app | std::ios::binary); if (!outputFile.is_open()) { itkExceptionMacro("Unable to open file\n" "outputFilename= " << this->m_FileName); } // Write point data switch (this->m_PointPixelComponentType) { case IOComponentEnum::UCHAR: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::CHAR: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::USHORT: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::SHORT: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::UINT: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::INT: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::ULONG: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::LONG: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::ULONGLONG: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::LONGLONG: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::FLOAT: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::DOUBLE: { WritePointData(static_cast(buffer), outputFile); break; } case IOComponentEnum::LDOUBLE: { WritePointData(static_cast(buffer), outputFile); break; } default: { itkExceptionMacro("Unknown point data pixel component type" << std::endl); } } outputFile.close(); } void FreeSurferBinaryMeshIO::WriteCellData(void * itkNotUsed(buffer)) {} void FreeSurferBinaryMeshIO::Write() {} void FreeSurferBinaryMeshIO::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); } } // namespace itk