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