diff --git a/Modules/CEST/autoload/IO/mitkCESTDICOMReaderService.cpp b/Modules/CEST/autoload/IO/mitkCESTDICOMReaderService.cpp index 720ac30a05..292adf7a97 100644 --- a/Modules/CEST/autoload/IO/mitkCESTDICOMReaderService.cpp +++ b/Modules/CEST/autoload/IO/mitkCESTDICOMReaderService.cpp @@ -1,86 +1,101 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCESTDICOMReaderService.h" #include "mitkCESTIOMimeTypes.h" #include #include #include #include namespace mitk { CESTDICOMReaderService::CESTDICOMReaderService() : BaseDICOMReaderService(CustomMimeType(MitkCESTIOMimeTypes::CEST_DICOM_MIMETYPE_NAME()), "MITK CEST DICOM Reader") { + Options defaultOptions; + + std::vector parseStrategy; + parseStrategy.push_back("Automatic"); + parseStrategy.push_back("CEST/WASABI"); + parseStrategy.push_back("T1"); + defaultOptions["Force type"] = parseStrategy; + + this->SetDefaultOptions(defaultOptions); + this->RegisterService(); } DICOMFileReader::Pointer CESTDICOMReaderService::GetReader(const mitk::StringList &relevantFiles) const { mitk::DICOMFileReaderSelector::Pointer selector = mitk::DICOMFileReaderSelector::New(); selector->LoadBuiltIn3DConfigs(); selector->LoadBuiltIn3DnTConfigs(); selector->SetInputFiles(relevantFiles); mitk::DICOMFileReader::Pointer reader = selector->GetFirstReaderWithMinimumNumberOfOutputImages(); if (reader.IsNotNull()) { // reset tag cache to ensure that additional tags of interest // will be regarded by the reader if set later on. reader->SetTagCache(nullptr); } return reader; } std::vector> CESTDICOMReaderService::Read() { std::vector result = BaseDICOMReaderService::Read(); + const Options options = this->GetOptions(); + + const std::string parseStrategy = options.find("Force type")->second.ToString(); + mitk::StringList relevantFiles = this->GetRelevantFiles(); mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); DICOMTag siemensCESTprivateTag(0x0029, 0x1020); scanner->AddTag(siemensCESTprivateTag); scanner->SetInputFiles(relevantFiles); scanner->Scan(); mitk::DICOMTagCache::Pointer tagCache = scanner->GetScanCache(); DICOMImageFrameList imageFrameList = mitk::ConvertToDICOMImageFrameList(tagCache->GetFrameInfoList()); DICOMImageFrameInfo *firstFrame = imageFrameList.begin()->GetPointer(); std::string byteString = tagCache->GetTagValue(firstFrame, siemensCESTprivateTag).value; mitk::CustomTagParser tagParser(relevantFiles[0]); + tagParser.SetParseStrategy(parseStrategy); auto parsedPropertyList = tagParser.ParseDicomPropertyString(byteString); for (auto &item : result) { item->GetPropertyList()->ConcatenatePropertyList(parsedPropertyList); } return result; } CESTDICOMReaderService *CESTDICOMReaderService::Clone() const { return new CESTDICOMReaderService(*this); } } diff --git a/Modules/CEST/include/mitkCustomTagParser.h b/Modules/CEST/include/mitkCustomTagParser.h index 56191284c8..5f23c13595 100644 --- a/Modules/CEST/include/mitkCustomTagParser.h +++ b/Modules/CEST/include/mitkCustomTagParser.h @@ -1,110 +1,114 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKCUSTOMTAGPARSER_H #define MITKCUSTOMTAGPARSER_H #include #include #include namespace mitk { /** The custom tag parser can be used to parse the custom dicom tag of the siemens private tag (0x0029, 0x1020) to extract relevant CEST data. An initial parsing determines whether the provided string belongs to CEST data at all. If the "tSequenceFileName" is of the format "{WHATEVER}CEST_Rev####" it is assumed that the data is indeed CEST data and was taken with revision #### (not limited to four digits). Which custom parameters to save and to which property name can be controlled by a json file. This file can be either provided as a resource for the MitkCEST module during compilation or placed next to the MitkCEST library in your binary folder. The expected format for the file "REVISIONNUMBER.json":
{
"REVISIONNUMBER" : "revision_json",
"sWiPMemBlock.alFree[1]" : "AdvancedMode",
"sWiPMemBlock.alFree[2]" : "RetreatMode"
}
where :
  • REVISIONNUMBER is the revision number of this json parameter mapping (files with non digit characters in their name will be ignored)
  • sWiPMemBlock.alFree[1] is the name of one parameter in the private dicom tag
  • AdvancedMode is the name of the property the content of sWiPMemBlock.alFree[1] should be saved to
\note It is assumed that the entire content of tag (0x0029, 0x1020) is provided and that it es hex encoded (12\23\04...). If the sampling type is list it will try to access LIST.txt at the location provided in the constructor to read the offsets. */ class MITKCEST_EXPORT CustomTagParser { public: /// the constructor expects a path to one of the files to be loaded or the directory of the dicom files CustomTagParser(std::string relevantFile); /// parse the provided dicom property and return a property list based on the closest revision parameter mapping mitk::PropertyList::Pointer ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty); /// parse the provided string and return a property list based on the closest revision parameter mapping mitk::PropertyList::Pointer ParseDicomPropertyString(std::string dicomPropertyString); + void SetParseStrategy(std::string parseStrategy); + /// name of the property for the offsets, including normalization offsets static const std::string m_OffsetsPropertyName; /// name of the property for the data acquisition revision static const std::string m_RevisionPropertyName; /// name of the property for the json parameter mapping revision static const std::string m_JSONRevisionPropertyName; /// prefix for all CEST related property names static const std::string m_CESTPropertyPrefix; protected: std::string GetRevisionAppropriateJSONString(std::string revisionString); void GetClosestLowerRevision(std::string revisionString); std::string GetClosestLowerRevision(std::string revisionString, std::vector availableRevisionsVector); /// Get a string filled with the properly formated offsets based on the sampling type and offset std::string GetOffsetString(std::string samplingType, std::string offset, std::string measurements); /// returns a vector revision numbers of all REVISIONNUMBER.json found beside the MitkCEST library std::vector GetExternalRevisions(); /// returns a vector revision numbers of all REVISIONNUMBER.json provided as resources during the compile std::vector GetInternalRevisions(); /// the closest lower revision provided as resource, empty if none found std::string m_ClosestInternalRevision; /// the closest lower revision provided as a json beside the library, empty if none found std::string m_ClosestExternalRevision; /// revision independent mapping to inject into the revision dependent json string static const std::string m_RevisionIndependentMapping; /// default revision dependent json string if none is found static const std::string m_DefaultJsonString; /// path to the dicom data std::string m_DicomDataPath; + /// Should the kind of data be automatically determined or should it be parsed as a specific one + std::string m_ParseStrategy; }; } #endif // MITKCUSTOMTAGPARSER_H diff --git a/Modules/CEST/src/mitkCustomTagParser.cpp b/Modules/CEST/src/mitkCustomTagParser.cpp index ebfb1a6fe2..53deb5ed72 100644 --- a/Modules/CEST/src/mitkCustomTagParser.cpp +++ b/Modules/CEST/src/mitkCustomTagParser.cpp @@ -1,630 +1,635 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCustomTagParser.h" #include #include #include #include "mitkIPropertyPersistence.h" #include "usGetModuleContext.h" #include "usModule.h" #include "usModuleContext.h" #include "usModuleResource.h" #include "usModuleResourceStream.h" #include #include #include #include #include #include #include #include namespace { mitk::IPropertyPersistence *GetPersistenceService() { mitk::IPropertyPersistence *result = nullptr; std::vector> persRegisters = us::GetModuleContext()->GetServiceReferences(); if (!persRegisters.empty()) { if (persRegisters.size() > 1) { MITK_WARN << "Multiple property description services found. Using just one."; } result = us::GetModuleContext()->GetService(persRegisters.front()); } return result; }; } const std::string mitk::CustomTagParser::m_CESTPropertyPrefix = "CEST."; const std::string mitk::CustomTagParser::m_OffsetsPropertyName = m_CESTPropertyPrefix + "Offsets"; const std::string mitk::CustomTagParser::m_RevisionPropertyName = m_CESTPropertyPrefix + "Revision"; const std::string mitk::CustomTagParser::m_JSONRevisionPropertyName = m_CESTPropertyPrefix + "revision_json"; const std::string mitk::CustomTagParser::m_RevisionIndependentMapping = "\n" " \"sProtConsistencyInfo.tSystemType\" : \"SysType\",\n" " \"sProtConsistencyInfo.flNominalB0\" : \"NominalB0\",\n" " \"sTXSPEC.asNucleusInfo[0].lFrequency\" : \"FREQ\",\n" " \"sTXSPEC.asNucleusInfo[0].flReferenceAmplitude\" : \"RefAmp\",\n" " \"alTR[0]\" : \"TR\",\n" " \"alTE[0]\" : \"TE\",\n" " \"lAverages\" : \"averages\",\n" " \"lRepetitions\" : \"repetitions\",\n" " \"adFlipAngleDegree[0]\" : \"ImageFlipAngle\",\n" " \"lTotalScanTimeSec\" : \"TotalScanTime\","; const std::string mitk::CustomTagParser::m_DefaultJsonString = "{\n" " \"default mapping, corresponds to revision 1416\" : \"revision_json\",\n" " \"sWiPMemBlock.alFree[1]\" : \"AdvancedMode\",\n" " \"sWiPMemBlock.alFree[2]\" : \"RecoveryMode\",\n" " \"sWiPMemBlock.alFree[3]\" : \"DoubleIrrMode\",\n" " \"sWiPMemBlock.alFree[4]\" : \"BinomMode\",\n" " \"sWiPMemBlock.alFree[5]\" : \"MtMode\",\n" " \"sWiPMemBlock.alFree[6]\" : \"PreparationType\",\n" " \"sWiPMemBlock.alFree[7]\" : \"PulseType\",\n" " \"sWiPMemBlock.alFree[8]\" : \"SamplingType\",\n" " \"sWiPMemBlock.alFree[9]\" : \"SpoilingType\",\n" " \"sWiPMemBlock.alFree[10]\" : \"measurements\",\n" " \"sWiPMemBlock.alFree[11]\" : \"NumberOfPulses\",\n" " \"sWiPMemBlock.alFree[12]\" : \"NumberOfLockingPulses\",\n" " \"sWiPMemBlock.alFree[13]\" : \"PulseDuration\",\n" " \"sWiPMemBlock.alFree[14]\" : \"DutyCycle\",\n" " \"sWiPMemBlock.alFree[15]\" : \"RecoveryTime\",\n" " \"sWiPMemBlock.alFree[16]\" : \"RecoveryTimeM0\",\n" " \"sWiPMemBlock.alFree[17]\" : \"ReadoutDelay\",\n" " \"sWiPMemBlock.alFree[18]\" : \"BinomDuration\",\n" " \"sWiPMemBlock.alFree[19]\" : \"BinomDistance\",\n" " \"sWiPMemBlock.alFree[20]\" : \"BinomNumberofPulses\",\n" " \"sWiPMemBlock.alFree[21]\" : \"BinomPreRepetions\",\n" " \"sWiPMemBlock.alFree[22]\" : \"BinomType\",\n" " \"sWiPMemBlock.adFree[1]\" : \"Offset\",\n" " \"sWiPMemBlock.adFree[2]\" : \"B1Amplitude\",\n" " \"sWiPMemBlock.adFree[3]\" : \"AdiabaticPulseMu\",\n" " \"sWiPMemBlock.adFree[4]\" : \"AdiabaticPulseBW\",\n" " \"sWiPMemBlock.adFree[5]\" : \"AdiabaticPulseLength\",\n" " \"sWiPMemBlock.adFree[6]\" : \"AdiabaticPulseAmp\",\n" " \"sWiPMemBlock.adFree[7]\" : \"FermiSlope\",\n" " \"sWiPMemBlock.adFree[8]\" : \"FermiFWHM\",\n" " \"sWiPMemBlock.adFree[9]\" : \"DoubleIrrDuration\",\n" " \"sWiPMemBlock.adFree[10]\" : \"DoubleIrrAmplitude\",\n" " \"sWiPMemBlock.adFree[11]\" : \"DoubleIrrRepetitions\",\n" " \"sWiPMemBlock.adFree[12]\" : \"DoubleIrrPreRepetitions\"\n" "}"; mitk::CustomTagParser::CustomTagParser(std::string relevantFile) : m_ClosestInternalRevision(""), m_ClosestExternalRevision("") { std::string pathToDirectory; std::string fileName; itksys::SystemTools::SplitProgramPath(relevantFile, pathToDirectory, fileName); m_DicomDataPath = pathToDirectory; + m_ParseStrategy = "Automatic"; } mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomPropertyString(std::string dicomPropertyString) { auto results = mitk::PropertyList::New(); if ("" == dicomPropertyString) { MITK_ERROR << "Could not parse empty custom dicom string"; return results; } std::map privateParameters; // convert hex to ascii // the Siemens private tag contains the information like this // "43\52\23\34" we jump over each \ and convert the number int len = dicomPropertyString.length(); std::string asciiString; for (int i = 0; i < len; i += 3) { std::string byte = dicomPropertyString.substr(i, 2); char chr = (char)(int)strtol(byte.c_str(), nullptr, 16); asciiString.push_back(chr); } // extract parameter list std::size_t beginning = asciiString.find("### ASCCONV BEGIN ###") + 21; std::size_t ending = asciiString.find("### ASCCONV END ###"); std::string parameterListString = asciiString.substr(beginning, ending - beginning); boost::replace_all(parameterListString, "\r\n", "\n"); boost::char_separator newlineSeparator("\n"); boost::tokenizer> parameters(parameterListString, newlineSeparator); for (const auto ¶meter : parameters) { std::vector parts; boost::split(parts, parameter, boost::is_any_of("=")); if (parts.size() == 2) { parts[0].erase(std::remove(parts[0].begin(), parts[0].end(), ' '), parts[0].end()); parts[1].erase(parts[1].begin(), parts[1].begin() + 1); // first character is a space privateParameters[parts[0]] = parts[1]; } } // determine what revision we are using to handle parameters appropriately std::string revisionPrefix = "CEST_Rev"; std::string lowerRevisionPrefix = revisionPrefix; std::string lowerParameter = privateParameters["tSequenceFileName"]; std::transform(lowerRevisionPrefix.begin(), lowerRevisionPrefix.end(), lowerRevisionPrefix.begin(), ::tolower ); std::transform(lowerParameter.begin(), lowerParameter.end(), lowerParameter.begin(), ::tolower); std::size_t foundPosition = lowerParameter.find(lowerRevisionPrefix); if (foundPosition == std::string::npos) { MITK_ERROR << "Could not find revision information."; return results; } std::string revisionString = privateParameters["tSequenceFileName"].substr(foundPosition + revisionPrefix.length(), std::string::npos); std::size_t firstNonNumber = revisionString.find_first_not_of("0123456789"); revisionString.erase(firstNonNumber, std::string::npos); results->SetProperty(m_RevisionPropertyName, mitk::StringProperty::New(revisionString)); std::string jsonString = GetRevisionAppropriateJSONString(revisionString); boost::property_tree::ptree root; std::istringstream jsonStream(jsonString); try { boost::property_tree::read_json(jsonStream, root); } catch (boost::property_tree::json_parser_error e) { mitkThrow() << "Could not parse json file. Error was:\n" << e.what(); } for (auto it : root) { if (it.second.empty()) { std::string propertyName = m_CESTPropertyPrefix + it.second.data(); if (m_JSONRevisionPropertyName == propertyName) { results->SetProperty(propertyName, mitk::StringProperty::New(it.first)); } else { results->SetProperty(propertyName, mitk::StringProperty::New(privateParameters[it.first])); } } else { MITK_ERROR << "Currently no support for nested dicom tag descriptors in json file."; } } std::string sampling = ""; std::string offset = ""; std::string measurements = ""; bool hasSamplingInformation = results->GetStringProperty("CEST.SamplingType", sampling); results->GetStringProperty("CEST.Offset", offset); results->GetStringProperty("CEST.measurements", measurements); if ("" == measurements) { std::string stringRepetitions = ""; std::string stringAverages = ""; results->GetStringProperty("CEST.repetitions", stringRepetitions); results->GetStringProperty("CEST.averages", stringAverages); std::stringstream measurementStream; measurementStream << std::stoi(stringRepetitions) + std::stoi(stringAverages); measurements = measurementStream.str(); MITK_INFO << "Could not find measurements, assuming repetitions + averages. Which is: " << measurements; } std::string preparationType = ""; std::string recoveryMode = ""; results->GetStringProperty("CEST.PreparationType", preparationType); results->GetStringProperty("CEST.RecoveryMode", recoveryMode); - if (("T1Recovery" == preparationType) || ("T1Inversion" == preparationType) || ("1" == recoveryMode)) + if ((("T1Recovery" == preparationType) || ("T1Inversion" == preparationType) || ("1" == recoveryMode) || ("T1" == m_ParseStrategy)) && !("CEST/WASABI" == m_ParseStrategy)) { MITK_INFO << "Parsed as T1 image"; mitk::LocaleSwitch localeSwitch("C"); std::stringstream trecStream; std::string trecPath = m_DicomDataPath + "/TREC.txt"; std::ifstream list(trecPath.c_str()); if (list.good()) { std::string currentTime; while (std::getline(list, currentTime)) { trecStream << currentTime << " "; } } else { MITK_WARN << "Assumed T1, but could not load TREC at " << trecPath; } results->SetStringProperty("CEST.TREC", trecStream.str().c_str()); } else { MITK_INFO << "Parsed as CEST or WASABI image"; } - if (hasSamplingInformation) { std::string offsets = GetOffsetString(sampling, offset, measurements); results->SetStringProperty(m_OffsetsPropertyName.c_str(), offsets.c_str()); } else { MITK_WARN << "Could not determine sampling type."; } //persist all properties mitk::IPropertyPersistence *persSrv = GetPersistenceService(); if (persSrv) { auto propertyMap = results->GetMap(); for (auto const &prop : *propertyMap) { PropertyPersistenceInfo::Pointer info = PropertyPersistenceInfo::New(); std::string key = prop.first; std::replace(key.begin(), key.end(), '.', '_'); info->SetNameAndKey(prop.first, key); persSrv->AddInfo(info); } } return results; } mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty) { if (!dicomProperty) { MITK_ERROR << "DICOM property empty"; } auto results = mitk::PropertyList::New(); if (dicomProperty) { results = ParseDicomPropertyString(dicomProperty->GetValue()); } return results; } std::vector mitk::CustomTagParser::GetInternalRevisions() { const std::vector configs = us::GetModuleContext()->GetModule()->FindResources("/", "*.json", false); std::vector availableRevisionsVector; for (auto const resource : configs) { availableRevisionsVector.push_back(std::stoi(resource.GetBaseName())); } return availableRevisionsVector; } std::vector mitk::CustomTagParser::GetExternalRevisions() { std::string moduleLocation = us::GetModuleContext()->GetModule()->GetLocation(); std::string stringToModule; std::string libraryName; itksys::SystemTools::SplitProgramPath(moduleLocation, stringToModule, libraryName); std::string prospectiveJsonsPath = stringToModule + "/*.json"; std::set JsonFiles; Poco::Glob::glob(prospectiveJsonsPath, JsonFiles, Poco::Glob::GLOB_CASELESS); std::vector availableRevisionsVector; for (auto const jsonpath : JsonFiles) { std::string jsonDir; std::string jsonName; itksys::SystemTools::SplitProgramPath(jsonpath, jsonDir, jsonName); std::string revision = itksys::SystemTools::GetFilenameWithoutExtension(jsonName); // disregard jsons which contain letters in their name bool onlyNumbers = (revision.find_first_not_of("0123456789") == std::string::npos); if(onlyNumbers) { availableRevisionsVector.push_back(std::stoi(revision)); } } return availableRevisionsVector; } std::string mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString, std::vector availableRevisionsVector) { // descending order std::sort(availableRevisionsVector.begin(), availableRevisionsVector.end(), std::greater<>()); int revision = std::stoi(revisionString); int index = 0; int numberOfRevisions = availableRevisionsVector.size(); while (index < numberOfRevisions) { // current mapping still has a higher revision number if ((availableRevisionsVector[index] - revision) > 0) { ++index; } else { break; } } if (index < numberOfRevisions) { std::stringstream foundRevisionStream; foundRevisionStream << availableRevisionsVector[index]; return foundRevisionStream.str(); } return ""; } void mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString) { m_ClosestInternalRevision = GetClosestLowerRevision(revisionString, GetInternalRevisions()); m_ClosestExternalRevision = GetClosestLowerRevision(revisionString, GetExternalRevisions()); } std::string mitk::CustomTagParser::GetRevisionAppropriateJSONString(std::string revisionString) { std::string returnValue = ""; if ("" == revisionString) { MITK_WARN << "Could not extract revision"; } else { GetClosestLowerRevision(revisionString); bool useExternal = false; bool useInternal = false; if ("" != m_ClosestExternalRevision) { useExternal = true; } if ("" != m_ClosestInternalRevision) { useInternal = true; } if (useExternal && useInternal) { if (std::stoi(m_ClosestInternalRevision) > std::stoi(m_ClosestExternalRevision)) { useExternal = false; } } if (useExternal) { std::string moduleLocation = us::GetModuleContext()->GetModule()->GetLocation(); std::string stringToModule; std::string libraryName; itksys::SystemTools::SplitProgramPath(moduleLocation, stringToModule, libraryName); std::string prospectiveJsonPath = stringToModule + "/" + m_ClosestExternalRevision + ".json"; std::ifstream externalJSON(prospectiveJsonPath.c_str()); if (externalJSON.good()) { MITK_INFO << "Found external json for CEST parameters at " << prospectiveJsonPath; std::stringstream buffer; buffer << externalJSON.rdbuf(); returnValue = buffer.str(); useInternal = false; } } if (useInternal) { std::string filename = m_ClosestInternalRevision + ".json"; us::ModuleResource jsonResource = us::GetModuleContext()->GetModule()->GetResource(filename); if (jsonResource.IsValid() && jsonResource.IsFile()) { MITK_INFO << "Found no external json for CEST parameters. Closest internal mapping is for revision " << m_ClosestInternalRevision; us::ModuleResourceStream jsonStream(jsonResource); std::stringstream buffer; buffer << jsonStream.rdbuf(); returnValue = buffer.str(); } } } if ("" == returnValue) { MITK_WARN << "Could not identify parameter mapping for the given revision " << revisionString << ", using default mapping."; returnValue = m_DefaultJsonString; } // inject the revision independent mapping before the first newline { returnValue.insert(returnValue.find("\n"), m_RevisionIndependentMapping); } return returnValue; } std::string mitk::CustomTagParser::GetOffsetString(std::string samplingType, std::string offset, std::string measurements) { mitk::LocaleSwitch localeSwitch("C"); std::stringstream results; std::string normalizationIndicatingOffset = "-300"; double offsetDouble = 0.0; int measurementsInt = 0; bool validOffset = false; bool validMeasurements = false; if ("" != offset) { validOffset = true; offsetDouble = std::stod(offset); } if ("" != measurements) { validMeasurements = true; measurementsInt = std::stoi(measurements); } std::vector offsetVector; if (validOffset && validMeasurements) { for (int step = 0; step < measurementsInt -1; ++step) { double currentOffset = -offsetDouble + 2 * step * offsetDouble / (measurementsInt - 2.0); offsetVector.push_back(currentOffset); } } else { MITK_WARN << "Invalid offset or measurements, offset calculation will only work for list sampling type."; } if (samplingType == "1" || samplingType == "Regular") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (const auto& entry : offsetVector) { results << entry << " "; } } } else if (samplingType == "2" || samplingType == "Alternating") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (auto& entry : offsetVector) { entry = std::abs(entry); } std::sort(offsetVector.begin(), offsetVector.end(), std::greater<>()); for (unsigned int index = 0; index < offsetVector.size(); ++index) { offsetVector[index] = std::pow(-1, index) * offsetVector[index]; } for (auto& entry : offsetVector) { results << entry << " "; } } } else if (samplingType == "3" || samplingType == "List") { std::string listPath = m_DicomDataPath + "/LIST.txt"; std::ifstream list(listPath.c_str()); if (list.good()) { std::string currentOffset; while (std::getline(list, currentOffset)) { results << currentOffset << " "; } } else { MITK_ERROR << "Could not load list at " << listPath; } } else if (samplingType == "4" || samplingType == "SingleOffset") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (int step = 0; step < measurementsInt - 1; ++step) { results << offsetDouble << " "; } } } else { MITK_WARN << "Encountered unknown sampling type."; } std::string resultString = results.str(); // replace multiple spaces by a single space std::string::iterator newEnditerator = std::unique(resultString.begin(), resultString.end(), [=](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); } ); resultString.erase(newEnditerator, resultString.end()); if ((resultString.length() > 0) && (resultString.at(resultString.length() - 1) == ' ')) { resultString.erase(resultString.end() - 1, resultString.end()); } if ((resultString.length() > 0) && (resultString.at(0) == ' ')) { resultString.erase(resultString.begin(), ++(resultString.begin())); } return resultString; } + +void mitk::CustomTagParser::SetParseStrategy(std::string parseStrategy) +{ + m_ParseStrategy = parseStrategy; +}