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