LCOV - code coverage report
Current view: top level - io - MHDHandler.cpp (source / functions) Hit Total Coverage
Test: test_coverage.info.cleaned Lines: 96 131 73.3 %
Date: 2022-05-27 02:48:28 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           1 :     DataContainer<data_t> MHD::read(std::string filename)
      11             :     {
      12           1 :         Logger::get("MHD")->info("Reading data from {}", filename);
      13             : 
      14             :         // open the meta file
      15           2 :         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           2 :         auto properties = readHeader(metaFile);
      21           2 :         auto [descriptor, dataPath, dataType] = parseHeader(properties);
      22             : 
      23           3 :         std::string dataFilename = FileSystemUtils::getAbsolutePath(dataPath, filename);
      24           2 :         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             :         else
      38           0 :             throw LogicError("MHD::read: invalid/unsupported data type");
      39             : 
      40           2 :         return dataContainer;
      41             :     }
      42             : 
      43             :     template <typename data_t>
      44           1 :     void MHD::write(const DataContainer<data_t>& data, std::string metaFilename,
      45             :                     std::string rawFilename)
      46             :     {
      47           1 :         Logger::get("MHD")->info("Writing meta data to {} and raw data to {}", metaFilename,
      48             :                                  rawFilename);
      49             : 
      50             :         // open the meta file
      51           2 :         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           2 :         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           1 :     std::map<std::string, std::string> MHD::readHeader(std::ifstream& metaFile)
      70             :     {
      71           1 :         std::map<std::string, std::string> properties;
      72             : 
      73             :         // read header data
      74           2 :         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          12 :             std::string name = metaLine.substr(0, delim);
      89           6 :             StringUtils::trim(name);
      90          12 :             std::string value = metaLine.substr(delim + 1);
      91           6 :             StringUtils::trim(value);
      92             : 
      93           6 :             StringUtils::toLower(name);
      94           6 :             properties[name] = value;
      95             :         }
      96             : 
      97           2 :         return properties;
      98             :     }
      99             : 
     100             :     std::tuple<std::unique_ptr<DataDescriptor>, std::string, DataUtils::DataType>
     101           1 :         MHD::parseHeader(const std::map<std::string, std::string>& properties)
     102             :     {
     103             :         // check the dimensions
     104           1 :         auto nDimsIt = properties.find("ndims");
     105             :         index_t nDims;
     106           1 :         if (nDimsIt != properties.end())
     107           1 :             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           1 :         auto byteOrderIt = properties.find("elementbyteordermsb");
     113           1 :         if (byteOrderIt != properties.end()) {
     114           2 :             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             :         }
     120             : 
     121             :         // check for the 'element type' value
     122             :         DataUtils::DataType dataType;
     123           1 :         auto elementTypeIt = properties.find("elementtype");
     124           1 :         if (elementTypeIt != properties.end()) {
     125           2 :             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             :             else
     141           0 :                 throw Error("MHD::parseHeader: tag 'element type' of unsupported value");
     142             :         } else
     143           0 :             throw Error("MHD::parseHeader: tag 'element type' not found");
     144             : 
     145             :         // extract the data path
     146           2 :         std::string rawDataPath;
     147           1 :         auto elementDataFileIt = properties.find("elementdatafile");
     148           1 :         if (elementDataFileIt != properties.end())
     149           1 :             rawDataPath = elementDataFileIt->second;
     150             :         else
     151           0 :             throw Error("MHD::parseHeader: tag 'element data file' not found");
     152             : 
     153             :         // parse the extents
     154           2 :         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             :         } else
     161           0 :             throw Error("MHD::parseHeader: tag 'dim size' not found");
     162             : 
     163             :         // check for spacing
     164           2 :         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             :         }
     171             : 
     172             :         // convert size
     173           2 :         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             :         }
     183             : 
     184           2 :         return std::make_tuple(std::make_unique<VolumeDescriptor>(dimSizes, dimSpacing),
     185           2 :                                rawDataPath, dataType);
     186             :     }
     187             : 
     188             :     template <typename data_t>
     189           1 :     void MHD::writeHeader(std::ofstream& metaFile, const DataContainer<data_t>& data,
     190             :                           std::string rawFilename)
     191             :     {
     192           1 :         auto& descriptor = data.getDataDescriptor();
     193             : 
     194             :         // write dimension, size and spacing
     195           1 :         metaFile << "NDims = " << descriptor.getNumberOfDimensions() << "\n";
     196           2 :         metaFile << "DimSize = " << (descriptor.getNumberOfCoefficientsPerDimension()).transpose()
     197           1 :                  << "\n";
     198           2 :         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             :         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           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           1 :     std::string MHD::getDataTypeName([[maybe_unused]] const DataContainer<float>& data)
     239             :     {
     240           1 :         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.15