LCOV - code coverage report
Current view: top level - io - MHDHandler.cpp (source / functions) Hit Total Coverage
Test: test_coverage.info.cleaned Lines: 0 137 0.0 %
Date: 2022-08-04 03:43:28 Functions: 0 18 0.0 %

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

Generated by: LCOV version 1.14