LCOV - code coverage report
Current view: top level - elsa/io - MHDHandler.cpp (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 108 155 69.7 %
Date: 2024-05-16 04:22:26 Functions: 6 18 33.3 %

          Line data    Source code
       1             : #include "MHDHandler.h"
       2             : #include "Logger.h"
       3             : #include "VolumeDescriptor.h"
       4             : 
       5             : #include <stdexcept>
       6             : 
       7             : namespace elsa
       8             : {
       9             :     template <typename data_t>
      10             :     DataContainer<data_t> MHD::read(std::string filename)
      11           1 :     {
      12           1 :         Logger::get("MHD")->info("Reading data from {}", filename);
      13             : 
      14             :         // open the meta file
      15           1 :         std::ifstream metaFile(filename);
      16           1 :         if (!metaFile.good())
      17           0 :             throw Error("MHD::read: cannot read from '" + filename + "'");
      18             : 
      19             :         // get the meta data from the meta file
      20           1 :         auto properties = readHeader(metaFile);
      21           1 :         auto [descriptor, dataPath, dataType] = parseHeader(properties);
      22             : 
      23           1 :         std::string dataFilename = FileSystemUtils::getAbsolutePath(dataPath, filename);
      24           1 :         std::ifstream dataFile(dataFilename, std::ios::binary | std::ios::in);
      25           1 :         if (!dataFile.good())
      26           0 :             throw Error("MHD::read: can not read from '" + dataPath + "'");
      27             : 
      28             :         // read in the data
      29           1 :         DataContainer<data_t> dataContainer(*descriptor);
      30             : 
      31           1 :         if (dataType == DataUtils::DataType::UINT16)
      32           0 :             DataUtils::parseRawData<uint16_t, data_t>(dataFile, dataContainer);
      33           1 :         else if (dataType == DataUtils::DataType::FLOAT32)
      34           1 :             DataUtils::parseRawData<float, data_t>(dataFile, dataContainer);
      35           0 :         else if (dataType == DataUtils::DataType::FLOAT64)
      36           0 :             DataUtils::parseRawData<double, data_t>(dataFile, dataContainer);
      37           0 :         else
      38           0 :             throw LogicError("MHD::read: invalid/unsupported data type");
      39             : 
      40           1 :         return dataContainer;
      41           1 :     }
      42             : 
      43             :     template <typename data_t>
      44             :     void MHD::write(const DataContainer<data_t>& data, std::string metaFilename,
      45             :                     std::string rawFilename)
      46           1 :     {
      47           1 :         Logger::get("MHD")->info("Writing meta data to {} and raw data to {}", metaFilename,
      48           1 :                                  rawFilename);
      49             : 
      50             :         // open the meta file
      51           1 :         std::ofstream metaFile(metaFilename);
      52           1 :         if (!metaFile.good())
      53           0 :             throw Error("MHD::write: cannot write to '" + metaFilename + "'");
      54             : 
      55             :         // output the header to the meta file
      56           1 :         writeHeader(metaFile, data, rawFilename);
      57             : 
      58             :         // open the raw file
      59           1 :         std::ofstream rawFile(rawFilename, std::ios::binary);
      60           1 :         if (!rawFile.good())
      61           0 :             throw Error("MHD::write: cannot write to '" + rawFilename + "'");
      62             : 
      63             :         // output the raw data
      64             :         // TODO: this would be more efficient if we had a data pointer...
      65         188 :         for (index_t i = 0; i < data.getSize(); ++i)
      66         187 :             rawFile.write(reinterpret_cast<const char*>(&data[i]), sizeof(data_t));
      67           1 :     }
      68             : 
      69             :     std::map<std::string, std::string> MHD::readHeader(std::ifstream& metaFile)
      70           1 :     {
      71           1 :         std::map<std::string, std::string> properties;
      72             : 
      73             :         // read header data
      74           1 :         std::string metaLine;
      75           8 :         while (!metaFile.eof()) {
      76             :             // read next header line
      77           7 :             std::getline(metaFile, metaLine);
      78           7 :             StringUtils::trim(metaLine);
      79             : 
      80           7 :             if (metaLine.length() == 0u)
      81           1 :                 continue;
      82             : 
      83             :             // split the header line into name and value
      84           6 :             size_t delim = metaLine.find('=');
      85           6 :             if (delim == std::string::npos)
      86           0 :                 throw Error("MHD::readHeader: found non-empty line without name/value pair");
      87             : 
      88           6 :             std::string name = metaLine.substr(0, delim);
      89           6 :             StringUtils::trim(name);
      90           6 :             std::string value = metaLine.substr(delim + 1);
      91           6 :             StringUtils::trim(value);
      92             : 
      93           6 :             StringUtils::toLower(name);
      94           6 :             properties[name] = value;
      95           6 :         }
      96             : 
      97           1 :         return properties;
      98           1 :     }
      99             : 
     100             :     std::tuple<std::unique_ptr<DataDescriptor>, std::string, DataUtils::DataType>
     101             :         MHD::parseHeader(const std::map<std::string, std::string>& properties)
     102           1 :     {
     103             :         // check the dimensions
     104           1 :         auto nDimsIt = properties.find("ndims");
     105           1 :         index_t nDims;
     106           1 :         if (nDimsIt != properties.end())
     107           1 :             nDims = DataUtils::parse<index_t>(nDimsIt->second);
     108           0 :         else
     109           0 :             throw Error("MHD::parseHeader: tag 'ndims' not found");
     110             : 
     111             :         // check for a byte order tag, but fall back to the default value
     112           1 :         auto byteOrderIt = properties.find("elementbyteordermsb");
     113           1 :         if (byteOrderIt != properties.end()) {
     114           1 :             std::string byteOrderValue = byteOrderIt->second;
     115           1 :             StringUtils::toLower(byteOrderValue);
     116             : 
     117           1 :             if (byteOrderValue != "false" && byteOrderValue != "no")
     118           0 :                 throw Error("MHD::parseHeader: only supporting little endian byte order");
     119           1 :         }
     120             : 
     121             :         // check for the 'element type' value
     122           1 :         DataUtils::DataType dataType;
     123           1 :         auto elementTypeIt = properties.find("elementtype");
     124           1 :         if (elementTypeIt != properties.end()) {
     125           1 :             std::string elementTypeValue = elementTypeIt->second;
     126           1 :             StringUtils::toUpper(elementTypeValue);
     127             : 
     128           1 :             if (elementTypeValue == "MET_CHAR")
     129           0 :                 dataType = DataUtils::DataType::INT8;
     130           1 :             else if (elementTypeValue == "MET_UCHAR")
     131           0 :                 dataType = DataUtils::DataType::UINT8;
     132           1 :             else if (elementTypeValue == "MET_SHORT")
     133           0 :                 dataType = DataUtils::DataType::INT16;
     134           1 :             else if (elementTypeValue == "MET_USHORT")
     135           0 :                 dataType = DataUtils::DataType::UINT16;
     136           1 :             else if (elementTypeValue == "MET_FLOAT")
     137           1 :                 dataType = DataUtils::DataType::FLOAT32;
     138           0 :             else if (elementTypeValue == "MET_DOUBLE")
     139           0 :                 dataType = DataUtils::DataType::FLOAT64;
     140           0 :             else
     141           0 :                 throw Error("MHD::parseHeader: tag 'element type' of unsupported value");
     142           0 :         } else
     143           0 :             throw Error("MHD::parseHeader: tag 'element type' not found");
     144             : 
     145             :         // extract the data path
     146           1 :         std::string rawDataPath;
     147           1 :         auto elementDataFileIt = properties.find("elementdatafile");
     148           1 :         if (elementDataFileIt != properties.end())
     149           1 :             rawDataPath = elementDataFileIt->second;
     150           0 :         else
     151           0 :             throw Error("MHD::parseHeader: tag 'element data file' not found");
     152             : 
     153             :         // parse the extents
     154           1 :         std::vector<index_t> dimSizeVec;
     155           1 :         auto dimSizeIt = properties.find("dimsize");
     156           1 :         if (dimSizeIt != properties.end()) {
     157           1 :             dimSizeVec = DataUtils::parseVector<index_t>(dimSizeIt->second);
     158           1 :             if (static_cast<index_t>(dimSizeVec.size()) != nDims)
     159           0 :                 throw Error("MHD::parseHeader: dimension size mismatch");
     160           0 :         } else
     161           0 :             throw Error("MHD::parseHeader: tag 'dim size' not found");
     162             : 
     163             :         // check for spacing
     164           1 :         std::vector<real_t> dimSpacingVec;
     165           1 :         auto dimSpacingIt = properties.find("elementspacing");
     166           1 :         if (dimSpacingIt != properties.end()) {
     167           1 :             dimSpacingVec = DataUtils::parseVector<real_t>(dimSpacingIt->second);
     168           1 :             if (static_cast<index_t>(dimSpacingVec.size()) != nDims)
     169           0 :                 throw Error("MHD::parseHeader: spacing size mismatch");
     170           1 :         }
     171             : 
     172             :         // convert size
     173           1 :         IndexVector_t dimSizes(nDims);
     174           3 :         for (index_t i = 0; i < nDims; ++i)
     175           2 :             dimSizes[i] = dimSizeVec[static_cast<std::size_t>(i)];
     176             : 
     177             :         // convert spacing
     178           1 :         RealVector_t dimSpacing(RealVector_t::Ones(nDims));
     179           1 :         if (!dimSpacingVec.empty()) {
     180           3 :             for (index_t i = 0; i < nDims; ++i)
     181           2 :                 dimSpacing[i] = dimSpacingVec[static_cast<std::size_t>(i)];
     182           1 :         }
     183             : 
     184           1 :         return std::make_tuple(std::make_unique<VolumeDescriptor>(dimSizes, dimSpacing),
     185           1 :                                rawDataPath, dataType);
     186           1 :     }
     187             : 
     188             :     template <typename data_t>
     189             :     void MHD::writeHeader(std::ofstream& metaFile, const DataContainer<data_t>& data,
     190             :                           std::string rawFilename)
     191           1 :     {
     192           1 :         auto& descriptor = data.getDataDescriptor();
     193             : 
     194             :         // write dimension, size and spacing
     195           1 :         metaFile << "NDims = " << descriptor.getNumberOfDimensions() << "\n";
     196           1 :         metaFile << "DimSize = " << (descriptor.getNumberOfCoefficientsPerDimension()).transpose()
     197           1 :                  << "\n";
     198           1 :         metaFile << "ElementSpacing = " << (descriptor.getSpacingPerDimension()).transpose()
     199           1 :                  << "\n";
     200             : 
     201             :         // write the data type and the byte swapping flag
     202           1 :         metaFile << "ElementType = " << getDataTypeName(data) << "\n";
     203             : 
     204           1 :         metaFile << "ElementByteOrderMSB = False"
     205           1 :                  << "\n";
     206             : 
     207             :         // write the data path and close
     208           1 :         metaFile << "ElementDataFile = " << rawFilename << std::endl;
     209           1 :     }
     210             : 
     211             :     template <typename data_t>
     212             :     std::string MHD::getDataTypeName([[maybe_unused]] const DataContainer<data_t>& data)
     213           0 :     {
     214           0 :         throw InvalidArgumentError("MHD::getDataTypeName: invalid/unsupported data type");
     215           0 :     }
     216             : 
     217             :     template <>
     218             :     std::string MHD::getDataTypeName([[maybe_unused]] const DataContainer<int8_t>& data)
     219           0 :     {
     220           0 :         return "MET_CHAR";
     221           0 :     }
     222             :     template <>
     223             :     std::string MHD::getDataTypeName([[maybe_unused]] const DataContainer<uint8_t>& data)
     224           0 :     {
     225           0 :         return "MET_UCHAR";
     226           0 :     }
     227             :     template <>
     228             :     std::string MHD::getDataTypeName([[maybe_unused]] const DataContainer<int16_t>& data)
     229           0 :     {
     230           0 :         return "MET_SHORT";
     231           0 :     }
     232             :     template <>
     233             :     std::string MHD::getDataTypeName([[maybe_unused]] const DataContainer<uint16_t>& data)
     234           0 :     {
     235           0 :         return "MET_USHORT";
     236           0 :     }
     237             :     template <>
     238             :     std::string MHD::getDataTypeName([[maybe_unused]] const DataContainer<float>& data)
     239           1 :     {
     240           1 :         return "MET_FLOAT";
     241           1 :     }
     242             :     template <>
     243             :     std::string MHD::getDataTypeName([[maybe_unused]] const DataContainer<double>& data)
     244           0 :     {
     245           0 :         return "MET_DOUBLE";
     246           0 :     }
     247             : 
     248             :     // ------------------------------------------
     249             :     // explicit template instantiation
     250             :     template DataContainer<float> MHD::read(std::string filename);
     251             :     template DataContainer<double> MHD::read(std::string filename);
     252             :     template DataContainer<index_t> MHD::read(std::string filename);
     253             : 
     254             :     template void MHD::write(const DataContainer<float>& data, std::string metaFilename,
     255             :                              std::string rawFilename);
     256             :     template void MHD::write(const DataContainer<double>& data, std::string metaFilename,
     257             :                              std::string rawFilename);
     258             :     template void MHD::write(const DataContainer<index_t>& data, std::string metaFilename,
     259             :                              std::string rawFilename);
     260             : 
     261             :     template void MHD::writeHeader(std::ofstream& metaFile, const DataContainer<float>& data,
     262             :                                    std::string rawFilename);
     263             :     template void MHD::writeHeader(std::ofstream& metaFile, const DataContainer<double>& data,
     264             :                                    std::string rawFilename);
     265             :     template void MHD::writeHeader(std::ofstream& metaFile, const DataContainer<index_t>& data,
     266             :                                    std::string rawFilename);
     267             : 
     268             : } // namespace elsa

Generated by: LCOV version 1.14