Medical Imaging Interaction Toolkit  2018.4.99-87d68d9f
Medical Imaging Interaction Toolkit
mitkCustomTagParser.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 #include "mitkCustomTagParser.h"
14 
15 #include <mitkProperties.h>
16 #include <mitkStringProperty.h>
17 #include <mitkLocaleSwitch.h>
18 
20 #include "usGetModuleContext.h"
21 #include "usModule.h"
22 #include "usModuleContext.h"
23 #include "usModuleResource.h"
24 #include "usModuleResourceStream.h"
25 
26 #include <itksys/SystemTools.hxx>
27 
28 #include <Poco/Glob.h>
29 
30 #include <boost/algorithm/string.hpp>
31 #include <boost/property_tree/json_parser.hpp>
32 #include <boost/property_tree/ptree.hpp>
33 #include <boost/tokenizer.hpp>
34 
35 #include <algorithm>
36 #include <cstdint>
37 #include <map>
38 #include <string>
39 #include <vector>
40 
41 namespace
42 {
44  {
45  mitk::IPropertyPersistence *result = nullptr;
46 
47  std::vector<us::ServiceReference<mitk::IPropertyPersistence>> persRegisters =
49  if (!persRegisters.empty())
50  {
51  if (persRegisters.size() > 1)
52  {
53  MITK_WARN << "Multiple property description services found. Using just one.";
54  }
55  result = us::GetModuleContext()->GetService<mitk::IPropertyPersistence>(persRegisters.front());
56  }
57 
58  return result;
59  };
60 }
61 
62 const std::string mitk::CustomTagParser::m_CESTPropertyPrefix = "CEST.";
63 const std::string mitk::CustomTagParser::m_OffsetsPropertyName = m_CESTPropertyPrefix + "Offsets";
64 const std::string mitk::CustomTagParser::m_RevisionPropertyName = m_CESTPropertyPrefix + "Revision";
65 const std::string mitk::CustomTagParser::m_JSONRevisionPropertyName = m_CESTPropertyPrefix + "revision_json";
66 
68 "\n"
69 " \"sProtConsistencyInfo.tSystemType\" : \"SysType\",\n"
70 " \"sProtConsistencyInfo.flNominalB0\" : \"NominalB0\",\n"
71 " \"sTXSPEC.asNucleusInfo[0].lFrequency\" : \"FREQ\",\n"
72 " \"sTXSPEC.asNucleusInfo[0].flReferenceAmplitude\" : \"RefAmp\",\n"
73 " \"alTR[0]\" : \"TR\",\n"
74 " \"alTE[0]\" : \"TE\",\n"
75 " \"lAverages\" : \"averages\",\n"
76 " \"lRepetitions\" : \"repetitions\",\n"
77 " \"adFlipAngleDegree[0]\" : \"ImageFlipAngle\",\n"
78 " \"lTotalScanTimeSec\" : \"TotalScanTime\",";
80 "{\n"
81 " \"default mapping, corresponds to revision 1416\" : \"revision_json\",\n"
82 " \"sWiPMemBlock.alFree[1]\" : \"AdvancedMode\",\n"
83 " \"sWiPMemBlock.alFree[2]\" : \"RecoveryMode\",\n"
84 " \"sWiPMemBlock.alFree[3]\" : \"DoubleIrrMode\",\n"
85 " \"sWiPMemBlock.alFree[4]\" : \"BinomMode\",\n"
86 " \"sWiPMemBlock.alFree[5]\" : \"MtMode\",\n"
87 " \"sWiPMemBlock.alFree[6]\" : \"PreparationType\",\n"
88 " \"sWiPMemBlock.alFree[7]\" : \"PulseType\",\n"
89 " \"sWiPMemBlock.alFree[8]\" : \"SamplingType\",\n"
90 " \"sWiPMemBlock.alFree[9]\" : \"SpoilingType\",\n"
91 " \"sWiPMemBlock.alFree[10]\" : \"measurements\",\n"
92 " \"sWiPMemBlock.alFree[11]\" : \"NumberOfPulses\",\n"
93 " \"sWiPMemBlock.alFree[12]\" : \"NumberOfLockingPulses\",\n"
94 " \"sWiPMemBlock.alFree[13]\" : \"PulseDuration\",\n"
95 " \"sWiPMemBlock.alFree[14]\" : \"DutyCycle\",\n"
96 " \"sWiPMemBlock.alFree[15]\" : \"RecoveryTime\",\n"
97 " \"sWiPMemBlock.alFree[16]\" : \"RecoveryTimeM0\",\n"
98 " \"sWiPMemBlock.alFree[17]\" : \"ReadoutDelay\",\n"
99 " \"sWiPMemBlock.alFree[18]\" : \"BinomDuration\",\n"
100 " \"sWiPMemBlock.alFree[19]\" : \"BinomDistance\",\n"
101 " \"sWiPMemBlock.alFree[20]\" : \"BinomNumberofPulses\",\n"
102 " \"sWiPMemBlock.alFree[21]\" : \"BinomPreRepetions\",\n"
103 " \"sWiPMemBlock.alFree[22]\" : \"BinomType\",\n"
104 " \"sWiPMemBlock.adFree[1]\" : \"Offset\",\n"
105 " \"sWiPMemBlock.adFree[2]\" : \"B1Amplitude\",\n"
106 " \"sWiPMemBlock.adFree[3]\" : \"AdiabaticPulseMu\",\n"
107 " \"sWiPMemBlock.adFree[4]\" : \"AdiabaticPulseBW\",\n"
108 " \"sWiPMemBlock.adFree[5]\" : \"AdiabaticPulseLength\",\n"
109 " \"sWiPMemBlock.adFree[6]\" : \"AdiabaticPulseAmp\",\n"
110 " \"sWiPMemBlock.adFree[7]\" : \"FermiSlope\",\n"
111 " \"sWiPMemBlock.adFree[8]\" : \"FermiFWHM\",\n"
112 " \"sWiPMemBlock.adFree[9]\" : \"DoubleIrrDuration\",\n"
113 " \"sWiPMemBlock.adFree[10]\" : \"DoubleIrrAmplitude\",\n"
114 " \"sWiPMemBlock.adFree[11]\" : \"DoubleIrrRepetitions\",\n"
115 " \"sWiPMemBlock.adFree[12]\" : \"DoubleIrrPreRepetitions\"\n"
116 "}";
117 
118 mitk::CustomTagParser::CustomTagParser(std::string relevantFile) : m_ClosestInternalRevision(""), m_ClosestExternalRevision("")
119 {
120  std::string pathToDirectory;
121  std::string fileName;
122  itksys::SystemTools::SplitProgramPath(relevantFile, pathToDirectory, fileName);
123  m_DicomDataPath = pathToDirectory;
124  m_ParseStrategy = "Automatic";
125  m_RevisionMappingStrategy = "Fuzzy";
126 }
127 
128 std::string mitk::CustomTagParser::ExtractRevision(std::string sequenceFileName)
129 {
130  //all rules are case insesitive. Thus we convert everything to lower case
131  //in order to check everything only once.
132  std::string cestPrefix = "cest";
133  std::string cestPrefix2 = "_cest";
134  std::string cestPrefix3 = "\\cest"; //this version covers the fact that the strings extracted
135  //from the SIEMENS tag has an additional prefix that is seperated by backslash.
136  std::string revisionPrefix = "_rev";
137  std::transform(sequenceFileName.begin(), sequenceFileName.end(), sequenceFileName.begin(), ::tolower);
138 
139  bool isCEST = sequenceFileName.compare(0, cestPrefix.length(), cestPrefix) == 0;
140  std::size_t foundPosition = 0;
141 
142  if (!isCEST)
143  {
144  foundPosition = sequenceFileName.find(cestPrefix2);
145  isCEST = foundPosition != std::string::npos;
146  }
147 
148  if (!isCEST)
149  {
150  foundPosition = sequenceFileName.find(cestPrefix3);
151  isCEST = foundPosition != std::string::npos;
152  }
153 
154  if (!isCEST)
155  {
156  mitkThrow() << "Invalid CEST sequence file name. No CEST prefix found. Could not extract revision.";
157  }
158 
159  foundPosition = sequenceFileName.find(revisionPrefix, foundPosition);
160  if (foundPosition == std::string::npos)
161  {
162  mitkThrow() << "Invalid CEST sequence file name. No revision prefix was found in CEST sequence file name. Could not extract revision.";
163  }
164 
165  std::string revisionString = sequenceFileName.substr(foundPosition + revisionPrefix.length(), std::string::npos);
166  std::size_t firstNoneNumber = revisionString.find_first_not_of("0123456789");
167  if (firstNoneNumber != std::string::npos)
168  {
169  revisionString.erase(firstNoneNumber, std::string::npos);
170  }
171 
172  return revisionString;
173 }
174 
175 bool mitk::CustomTagParser::IsT1Sequence(std::string preparationType,
176  std::string recoveryMode,
177  std::string spoilingType,
178  std::string revisionString)
179 {
180  bool isT1 = false;
181 
182  // if a forced parse strategy is set, use that one
183  if ("T1" == m_ParseStrategy)
184  {
185  return true;
186  }
187  if ("CEST/WASABI" == m_ParseStrategy)
188  {
189  return false;
190  }
191 
192  if (("T1Recovery" == preparationType) || ("T1Inversion" == preparationType))
193  {
194  isT1 = true;
195  }
196 
197  // How to interpret the recoveryMode depends on the age of the sequence
198  // older sequences use 0 = false and 1 = true, newer ones 1 = false and 2 = true.
199  // A rough rule of thumb is to assume that if the SpoilingType is 0, then the first
200  // convention is chosen, if it is 1, then the second applies. Otherwise
201  // we assume revision 1485 and newer to follow the new convention.
202  // This unfortunate heuristic is due to somewhat arbitrary CEST sequence implementations.
203  if (!isT1)
204  {
205  std::string thisIsTrue = "1";
206  std::string thisIsFalse = "0";
207  if ("0" == spoilingType)
208  {
209  thisIsFalse = "0";
210  thisIsTrue = "1";
211  }
212  else if ("1" == spoilingType)
213  {
214  thisIsFalse = "1";
215  thisIsTrue = "2";
216  }
217  else
218  {
219  int revisionNrWeAssumeToBeDifferenciating = 1485;
220  if (std::stoi(revisionString) - revisionNrWeAssumeToBeDifferenciating < 0)
221  {
222  thisIsFalse = "0";
223  thisIsTrue = "1";
224  }
225  else
226  {
227  thisIsFalse = "1";
228  thisIsTrue = "2";
229  }
230  }
231 
232  if (thisIsFalse == recoveryMode)
233  {
234  isT1 = false;
235  }
236  else if (thisIsTrue == recoveryMode)
237  {
238  isT1 = true;
239  }
240 
241  }
242 
243  return isT1;
244 }
245 
247 {
248  auto results = mitk::PropertyList::New();
249  if ("" == dicomPropertyString)
250  {
251  //MITK_ERROR << "Could not parse empty custom dicom string";
252  return results;
253  }
254 
255  std::map<std::string, std::string> privateParameters;
256 
257  // The Siemens private tag contains information like "43\52\23\34".
258  // We jump over each "\" and convert the number;
259  std::string bytes;
260 
261  {
262  const std::size_t SUBSTR_LENGTH = 2;
263  const std::size_t INPUT_LENGTH = dicomPropertyString.length();
264 
265  if (INPUT_LENGTH < SUBSTR_LENGTH)
266  return results;
267 
268  const std::size_t MAX_INPUT_OFFSET = INPUT_LENGTH - SUBSTR_LENGTH;
269  bytes.reserve(INPUT_LENGTH / 3 + 1);
270 
271  try
272  {
273  for (std::size_t i = 0; i <= MAX_INPUT_OFFSET; i += 3)
274  {
275  std::string byte_string = dicomPropertyString.substr(i, SUBSTR_LENGTH);
276  int byte = static_cast<std::string::value_type>(std::stoi(byte_string.c_str(), nullptr, 16));
277  bytes.push_back(byte);
278  }
279  }
280  catch (const std::invalid_argument&) // std::stoi() could not perform conversion
281  {
282  return results;
283  }
284  }
285 
286  // extract parameter list
287  std::string parameterListString;
288 
289  {
290  const std::string ASCCONV_BEGIN = "### ASCCONV BEGIN ###";
291  const std::string ASCCONV_END = "### ASCCONV END ###";
292 
293  auto offset = bytes.find(ASCCONV_BEGIN);
294 
295  if (std::string::npos == offset)
296  return results;
297 
298  offset += ASCCONV_BEGIN.length();
299 
300  auto count = bytes.find(ASCCONV_END, offset);
301 
302  if (std::string::npos == count)
303  return results;
304 
305  count -= offset;
306 
307  parameterListString = bytes.substr(offset, count);
308  }
309 
310  boost::replace_all(parameterListString, "\r\n", "\n");
311  boost::char_separator<char> newlineSeparator("\n");
312  boost::tokenizer<boost::char_separator<char>> parameters(parameterListString, newlineSeparator);
313  for (const auto &parameter : parameters)
314  {
315  std::vector<std::string> parts;
316  boost::split(parts, parameter, boost::is_any_of("="));
317 
318  if (parts.size() == 2)
319  {
320  parts[0].erase(std::remove(parts[0].begin(), parts[0].end(), ' '), parts[0].end());
321  parts[1].erase(parts[1].begin(), parts[1].begin() + 1); // first character is a space
322  privateParameters[parts[0]] = parts[1];
323  }
324  }
325 
326  std::string revisionString = "";
327 
328  try
329  {
330  revisionString = ExtractRevision(privateParameters["tSequenceFileName"]);
331  }
332  catch (const std::exception &e)
333  {
334  MITK_ERROR << "Cannot deduce revision information. Reason: "<< e.what();
335  return results;
336  }
337 
338  results->SetProperty(m_RevisionPropertyName, mitk::StringProperty::New(revisionString));
339 
340  std::string jsonString = GetRevisionAppropriateJSONString(revisionString);
341 
342  boost::property_tree::ptree root;
343  std::istringstream jsonStream(jsonString);
344  try
345  {
346  boost::property_tree::read_json(jsonStream, root);
347  }
348  catch (const boost::property_tree::json_parser_error &e)
349  {
350  mitkThrow() << "Could not parse json file. Error was:\n" << e.what();
351  }
352 
353  for (auto it : root)
354  {
355  if (it.second.empty())
356  {
357  std::string propertyName = m_CESTPropertyPrefix + it.second.data();
358  if (m_JSONRevisionPropertyName == propertyName)
359  {
360  results->SetProperty(propertyName, mitk::StringProperty::New(it.first));
361  }
362  else
363  {
364  results->SetProperty(propertyName, mitk::StringProperty::New(privateParameters[it.first]));
365  }
366  }
367  else
368  {
369  MITK_ERROR << "Currently no support for nested dicom tag descriptors in json file.";
370  }
371  }
372 
373  std::string offset = "";
374  std::string measurements = "";
375  results->GetStringProperty("CEST.Offset", offset);
376  results->GetStringProperty("CEST.measurements", measurements);
377 
378  if (measurements.empty())
379  {
380  std::string stringRepetitions = "";
381  results->GetStringProperty("CEST.repetitions", stringRepetitions);
382 
383  std::string stringAverages = "";
384  results->GetStringProperty("CEST.averages", stringAverages);
385 
386  const auto ERROR_STRING = "Could not find measurements, fallback assumption of repetitions + averages could not be determined either.";
387 
388  if (!stringRepetitions.empty() && !stringAverages.empty())
389  {
390  std::stringstream measurementStream;
391 
392  try
393  {
394  measurementStream << std::stoi(stringRepetitions) + std::stoi(stringAverages);
395  measurements = measurementStream.str();
396  MITK_INFO << "Could not find measurements, assuming repetitions + averages. That is: " << measurements;
397  }
398  catch (const std::invalid_argument&)
399  {
400  MITK_ERROR << ERROR_STRING;
401  }
402  }
403  else
404  {
405  MITK_WARN << ERROR_STRING;
406  }
407  }
408 
409  std::string preparationType = "";
410  std::string recoveryMode = "";
411  std::string spoilingType = "";
412  results->GetStringProperty(CEST_PROPERTY_NAME_PREPERATIONTYPE().c_str(), preparationType);
413  results->GetStringProperty(CEST_PROPERTY_NAME_RECOVERYMODE().c_str(), recoveryMode);
414  results->GetStringProperty(CEST_PROPERTY_NAME_SPOILINGTYPE().c_str(), spoilingType);
415 
416  if (this->IsT1Sequence(preparationType, recoveryMode, spoilingType, revisionString))
417  {
418  MITK_INFO << "Parsed as T1 image";
419 
420  mitk::LocaleSwitch localeSwitch("C");
421 
422  std::stringstream trecStream;
423 
424  std::string trecPath = m_DicomDataPath + "/TREC.txt";
425  std::ifstream list(trecPath.c_str());
426 
427  if (list.good())
428  {
429  std::string currentTime;
430  while (std::getline(list, currentTime))
431  {
432  trecStream << currentTime << " ";
433  }
434  }
435  else
436  {
437  MITK_WARN << "Assumed T1, but could not load TREC at " << trecPath;
438  }
439 
440  results->SetStringProperty(CEST_PROPERTY_NAME_TREC().c_str(), trecStream.str().c_str());
441  }
442  else
443  {
444  MITK_INFO << "Parsed as CEST or WASABI image";
445  std::string sampling = "";
446  bool hasSamplingInformation = results->GetStringProperty("CEST.SamplingType", sampling);
447  if (hasSamplingInformation)
448  {
449  std::string offsets = GetOffsetString(sampling, offset, measurements);
450  results->SetStringProperty(m_OffsetsPropertyName.c_str(), offsets.c_str());
451  }
452  else
453  {
454  MITK_WARN << "Could not determine sampling type.";
455  }
456  }
457 
458 
459  //persist all properties
461  if (persSrv)
462  {
463  auto propertyMap = results->GetMap();
464  for (auto const &prop : *propertyMap)
465  {
467  std::string key = prop.first;
468  std::replace(key.begin(), key.end(), '.', '_');
469  info->SetNameAndKey(prop.first, key);
470 
471  persSrv->AddInfo(info);
472  }
473  }
474 
475  return results;
476 }
477 
479 {
480  if (!dicomProperty)
481  {
482  MITK_ERROR << "DICOM property empty";
483  }
484 
485  auto results = mitk::PropertyList::New();
486 
487  if (dicomProperty)
488  {
489  results = ParseDicomPropertyString(dicomProperty->GetValue());
490  }
491 
492  return results;
493 }
494 
496 {
497  const std::vector<us::ModuleResource> configs =
498  us::GetModuleContext()->GetModule()->FindResources("/", "*.json", false);
499 
500  std::vector<int> availableRevisionsVector;
501 
502  for (auto const resource : configs)
503  {
504  availableRevisionsVector.push_back(std::stoi(resource.GetBaseName()));
505  }
506 
507  return availableRevisionsVector;
508 }
509 
511 {
512  std::string stringToJSONDirectory = GetExternalJSONDirectory();
513 
514  std::string prospectiveJsonsPath = stringToJSONDirectory + "/*.json";
515 
516  std::set<std::string> JsonFiles;
517  Poco::Glob::glob(prospectiveJsonsPath, JsonFiles, Poco::Glob::GLOB_CASELESS);
518 
519  std::vector<int> availableRevisionsVector;
520 
521  for (auto const jsonpath : JsonFiles)
522  {
523  std::string jsonDir;
524  std::string jsonName;
525  itksys::SystemTools::SplitProgramPath(jsonpath, jsonDir, jsonName);
526  std::string revision = itksys::SystemTools::GetFilenameWithoutExtension(jsonName);
527 
528  // disregard jsons which contain letters in their name
529  bool onlyNumbers = (revision.find_first_not_of("0123456789") == std::string::npos);
530 
531  if(onlyNumbers)
532  {
533  availableRevisionsVector.push_back(std::stoi(revision));
534  }
535  }
536 
537  return availableRevisionsVector;
538 }
539 
540 std::string mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString, std::vector<int> availableRevisionsVector)
541 {
542 
543  // descending order
544  std::sort(availableRevisionsVector.begin(), availableRevisionsVector.end(), std::greater<>());
545 
546  int revision = std::stoi(revisionString);
547 
548  int index = 0;
549  int numberOfRevisions = availableRevisionsVector.size();
550 
551  while (index < numberOfRevisions)
552  {
553  // current mapping still has a higher revision number
554  if ((availableRevisionsVector[index] - revision) > 0)
555  {
556  ++index;
557  }
558  else
559  {
560  break;
561  }
562  }
563 
564  if (index < numberOfRevisions)
565  {
566  std::stringstream foundRevisionStream;
567  foundRevisionStream << availableRevisionsVector[index];
568 
569  return foundRevisionStream.str();
570  }
571 
572  return "";
573 }
574 
575 void mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString)
576 {
579 
580  if ("Strict" == m_RevisionMappingStrategy && !((0 == m_ClosestInternalRevision.compare(revisionString)) ||
581  (0 == m_ClosestExternalRevision.compare(revisionString))))
582  { // strict revision mapping and neither revision does match the dicom meta data
583  std::stringstream errorMessageStream;
584  errorMessageStream << "\nCould not parse dicom data in strict mode, data revision " << revisionString
585  << " has no known matching parameter mapping. To use the closest known older parameter mapping select the "
586  << "\"Fuzzy\" revision mapping option when loading the data.\n"
587  << "\nCurrently known revision mappings are:\n Precompiled:";
588  for (const auto revision : GetInternalRevisions())
589  {
590  errorMessageStream << " " << revision;
591  }
592  errorMessageStream << "\n External:";
593  for (const auto revision : GetExternalRevisions())
594  {
595  errorMessageStream << " " << revision;
596  }
597  errorMessageStream << "\n\nExternal revision mapping descriptions should be located at\n\n";
598  std::string stringToJSONDirectory = GetExternalJSONDirectory();
599  errorMessageStream << stringToJSONDirectory;
600 
601  errorMessageStream << "\n\nTo provide an external mapping for this revision create a " << revisionString
602  << ".json there. You might need to create the directory first.";
603 
604  mitkThrow() << errorMessageStream.str();
605  }
606 }
607 
608 std::string mitk::CustomTagParser::GetRevisionAppropriateJSONString(std::string revisionString)
609 {
610  std::string returnValue = "";
611 
612  if ("" == revisionString)
613  {
614  MITK_WARN << "Could not extract revision";
615  }
616  else
617  {
618  GetClosestLowerRevision(revisionString);
619 
620  bool useExternal = false;
621  bool useInternal = false;
622 
623  if ("" != m_ClosestExternalRevision)
624  {
625  useExternal = true;
626  }
627  if ("" != m_ClosestInternalRevision)
628  {
629  useInternal = true;
630  }
631 
632  if (useExternal && useInternal)
633  {
634  if (std::stoi(m_ClosestInternalRevision) > std::stoi(m_ClosestExternalRevision))
635  {
636  useExternal = false;
637  }
638  }
639 
640  if (useExternal)
641  {
642  std::string stringToJSONDirectory = GetExternalJSONDirectory();
643 
644  std::string prospectiveJsonPath = stringToJSONDirectory + "/" + m_ClosestExternalRevision + ".json";
645 
646  std::ifstream externalJSON(prospectiveJsonPath.c_str());
647 
648  if (externalJSON.good())
649  {
650  MITK_INFO << "Found external json for CEST parameters at " << prospectiveJsonPath;
651 
652  std::stringstream buffer;
653  buffer << externalJSON.rdbuf();
654 
655  returnValue = buffer.str();
656 
657  useInternal = false;
658  }
659  }
660 
661  if (useInternal)
662  {
663  std::string filename = m_ClosestInternalRevision + ".json";
664  us::ModuleResource jsonResource = us::GetModuleContext()->GetModule()->GetResource(filename);
665 
666  if (jsonResource.IsValid() && jsonResource.IsFile())
667  {
668  MITK_INFO << "Found no external json for CEST parameters. Closest internal mapping is for revision "
670  us::ModuleResourceStream jsonStream(jsonResource);
671  std::stringstream buffer;
672  buffer << jsonStream.rdbuf();
673  returnValue = buffer.str();
674  }
675  }
676  }
677 
678  if ("" == returnValue)
679  {
680  MITK_WARN << "Could not identify parameter mapping for the given revision " << revisionString
681  << ", using default mapping.";
682  returnValue = m_DefaultJsonString;
683  }
684 
685  // inject the revision independent mapping before the first newline
686  {
687  returnValue.insert(returnValue.find("\n"), m_RevisionIndependentMapping);
688  }
689 
690  return returnValue;
691 }
692 
693 std::string mitk::CustomTagParser::GetOffsetString(std::string samplingType, std::string offset, std::string measurements)
694 {
695  mitk::LocaleSwitch localeSwitch("C");
696  std::stringstream results;
697 
698  std::string normalizationIndicatingOffset = "-300";
699 
700  double offsetDouble = 0.0;
701  int measurementsInt = 0;
702 
703  bool validOffset = false;
704  bool validMeasurements = false;
705 
706  if ("" != offset)
707  {
708  validOffset = true;
709  offsetDouble = std::stod(offset);
710  }
711  if ("" != measurements)
712  {
713  validMeasurements = true;
714  measurementsInt = std::stoi(measurements);
715  }
716 
717  std::vector<double> offsetVector;
718 
719  if (validOffset && validMeasurements)
720  {
721  for (int step = 0; step < measurementsInt -1; ++step)
722  {
723  double currentOffset = -offsetDouble + 2 * step * offsetDouble / (measurementsInt - 2.0);
724  offsetVector.push_back(currentOffset);
725  }
726  }
727  else
728  {
729  MITK_WARN << "Invalid offset or measurements, offset calculation will only work for list sampling type.";
730  }
731 
732  if (samplingType == "1" || samplingType == "Regular")
733  {
734  if (validOffset && validMeasurements)
735  {
736  results << normalizationIndicatingOffset << " ";
737  for (const auto& entry : offsetVector)
738  {
739  results << entry << " ";
740  }
741  }
742  }
743  else if (samplingType == "2" || samplingType == "Alternating")
744  {
745  if (validOffset && validMeasurements)
746  {
747  results << normalizationIndicatingOffset << " ";
748  for (auto& entry : offsetVector)
749  {
750  entry = std::abs(entry);
751  }
752 
753  std::sort(offsetVector.begin(), offsetVector.end(), std::greater<>());
754 
755  for (unsigned int index = 0; index < offsetVector.size(); ++index)
756  {
757  offsetVector[index] = std::pow(-1, index) * offsetVector[index];
758  }
759 
760  for (auto& entry : offsetVector)
761  {
762  results << entry << " ";
763  }
764  }
765  }
766  else if (samplingType == "3" || samplingType == "List")
767  {
768  std::string listPath = m_DicomDataPath + "/LIST.txt";
769  std::ifstream list(listPath.c_str());
770 
771  if (list.good())
772  {
773  std::string currentOffset;
774  while (std::getline(list, currentOffset))
775  {
776  results << currentOffset << " ";
777  }
778  }
779  else
780  {
781  MITK_ERROR << "Could not load list at " << listPath;
782  }
783  }
784  else if (samplingType == "4" || samplingType == "SingleOffset")
785  {
786  if (validOffset && validMeasurements)
787  {
788  results << normalizationIndicatingOffset << " ";
789  for (int step = 0; step < measurementsInt - 1; ++step)
790  {
791  results << offsetDouble << " ";
792  }
793  }
794  }
795  else
796  {
797  MITK_WARN << "Encountered unknown sampling type.";
798  }
799 
800  std::string resultString = results.str();
801  // replace multiple spaces by a single space
802  std::string::iterator newEnditerator =
803  std::unique(resultString.begin(), resultString.end(),
804  [=](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); }
805  );
806  resultString.erase(newEnditerator, resultString.end());
807 
808  if ((resultString.length() > 0) && (resultString.at(resultString.length() - 1) == ' '))
809  {
810  resultString.erase(resultString.end() - 1, resultString.end());
811  }
812 
813  if ((resultString.length() > 0) && (resultString.at(0) == ' '))
814  {
815  resultString.erase(resultString.begin(), ++(resultString.begin()));
816  }
817 
818  return resultString;
819 }
820 
821 void mitk::CustomTagParser::SetParseStrategy(std::string parseStrategy)
822 {
823  m_ParseStrategy = parseStrategy;
824 }
825 
826 void mitk::CustomTagParser::SetRevisionMappingStrategy(std::string revisionMappingStrategy)
827 {
828  m_RevisionMappingStrategy = revisionMappingStrategy;
829 }
830 
832 {
833  std::string moduleLocation = us::GetModuleContext()->GetModule()->GetLocation();
834  std::string stringToModule;
835  std::string libraryName;
836  itksys::SystemTools::SplitProgramPath(moduleLocation, stringToModule, libraryName);
837 
838  std::stringstream jsonDirectory;
839  jsonDirectory << stringToModule << "/CESTRevisionMapping";
840 
841  return jsonDirectory.str();
842 }
843 
845 {
846  return "CEST.TotalScanTime";
847 };
848 
850 {
851  return "CEST.PreparationType";
852 };
853 
855 {
856  return "CEST.RecoveryMode";
857 };
858 
860 {
861  return "CEST.SpoilingType";
862 };
863 
865 {
866  return "CEST.Offsets";
867 };
868 
869 const std::string mitk::CEST_PROPERTY_NAME_TREC()
870 {
871  return std::string("CEST.TREC");
872 }
static std::string ExtractRevision(std::string sequenceFileName)
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_PREPERATIONTYPE()
mitk::PropertyList::Pointer ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty)
parse the provided dicom property and return a property list based on the closest revision parameter ...
static Pointer New()
bool IsT1Sequence(std::string preparationType, std::string recoveryMode, std::string spoilingType, std::string revisionString)
Decides whether or not the image is likely to be a T1Map, if not it is assumed to be a CEST sequence...
#define MITK_INFO
Definition: mitkLogMacros.h:18
#define MITK_ERROR
Definition: mitkLogMacros.h:20
void GetClosestLowerRevision(std::string revisionString)
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_OFFSETS()
std::vector< int > GetExternalRevisions()
returns a vector revision numbers of all REVISIONNUMBER.json found beside the MitkCEST library ...
static const std::string m_CESTPropertyPrefix
prefix for all CEST related property names
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_TOTALSCANTIME()
mitk::PropertyList::Pointer ParseDicomPropertyString(std::string dicomPropertyString)
parse the provided string and return a property list based on the closest revision parameter mapping ...
static const std::string m_RevisionPropertyName
name of the property for the data acquisition revision
void * GetService(const ServiceReferenceBase &reference)
static void info(const char *fmt,...)
Definition: svm.cpp:86
static Vector3D offset
std::string GetRevisionAppropriateJSONString(std::string revisionString)
#define MITK_WARN
Definition: mitkLogMacros.h:19
Convenience class to temporarily change the current locale.
std::string GetExternalJSONDirectory()
returns the path where external jsons are expected to be located
void SetParseStrategy(std::string parseStrategy)
#define mitkThrow()
Module * GetModule() const
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_TREC()
std::vector< int > GetInternalRevisions()
returns a vector revision numbers of all REVISIONNUMBER.json provided as resources during the compile...
std::string m_RevisionMappingStrategy
How to handle parameter mapping based on absent revision jsons.
std::vector< ServiceReferenceU > GetServiceReferences(const std::string &clazz, const std::string &filter=std::string())
std::string GetOffsetString(std::string samplingType, std::string offset, std::string measurements)
Get a string filled with the properly formated offsets based on the sampling type and offset...
std::string m_DicomDataPath
path to the dicom data
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_RECOVERYMODE()
std::string m_ClosestExternalRevision
the closest lower revision provided as a json beside the library, empty if none found ...
Interface of property persistence service.
static const std::string m_DefaultJsonString
default revision dependent json string if none is found
CustomTagParser(std::string relevantFile)
the constructor expects a path to one of the files to be loaded or the directory of the dicom files ...
std::string m_ClosestInternalRevision
the closest lower revision provided as resource, empty if none found
std::string m_ParseStrategy
Should the kind of data be automatically determined or should it be parsed as a specific one...
static const char * replace[]
This is a dictionary to replace long names of classes, modules, etc. to shorter versions in the conso...
static const std::string m_RevisionIndependentMapping
revision independent mapping to inject into the revision dependent json string
mitk::IPropertyPersistence * GetPersistenceService()
virtual bool AddInfo(const PropertyPersistenceInfo *info, bool overwrite=false)=0
Add persistence info for a specific base data property. If there is already a property info instance ...
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_SPOILINGTYPE()
static const std::string m_JSONRevisionPropertyName
name of the property for the json parameter mapping revision
static ModuleContext * GetModuleContext()
Returns the module context of the calling module.
static const std::string m_OffsetsPropertyName
name of the property for the offsets, including normalization offsets
static Pointer New()
Property for time and space resolved string values.
void SetRevisionMappingStrategy(std::string revisionMappingStrategy)