Medical Imaging Interaction Toolkit  2018.4.99-9c019225
Medical Imaging Interaction Toolkit
mitkDICOMTagPath.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 <algorithm>
14 #include <utility>
15 
16 #include <mitkDICOMTagPath.h>
17 #include <mitkExceptionMacro.h>
18 
19 #include <regex>
20 
21 namespace
22 {
23  std::string GenerateRegExForNumber(unsigned int tagNumber)
24  {
25  std::ostringstream resultRegEx;
26 
27  std::ostringstream hexNumber;
28  hexNumber << std::hex << tagNumber; // default std::hex output is lowercase
29  std::regex reg_character("([a-f]+)"); // so only check for lowercase characters here
30  if (std::regex_search(hexNumber.str(), reg_character))
31  {
32  // hexNumber contains a characters from a-f
33  // needs to be generated with lowercase and uppercase characters
34  resultRegEx << "("
35  << std::setw(4) << std::setfill('0') << std::hex << tagNumber
36  << "|"
37  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << tagNumber << std::nouppercase
38  << ")";
39  }
40  else
41  {
42  // only decimal values (0-9) contained in hexNumber - no need to modify the result
43  resultRegEx << std::setw(4) << std::setfill('0') << std::hex << tagNumber;
44  }
45 
46  return resultRegEx.str();
47  }
48 }
49 
50 namespace mitk
51 {
52 
54  NodeInfo() : type(NodeType::Invalid), tag(0, 0), selection(0)
55  {
56  ;
57  };
58 
60  NodeInfo(const DICOMTag& aTag, NodeType aType, ItemSelectionIndex index) : type(aType), tag(aTag), selection(index)
61  {};
62 
64  {
65  if (!(this->tag == right.tag)) return false;
66  if (this->type != right.type) return false;
67  if (this->selection != right.selection) return false;
68 
69  return true;
70  };
71 
73  Matches(const NodeInfo& right) const
74  {
76  {
77  return true;
78  }
79  else if (tag == right.tag && type != NodeType::Invalid && right.type != NodeType::Invalid)
80  {
81  if (type == NodeType::Element && right.type == NodeType::Element)
82  {
83  return true;
84  }
85  else if(selection == right.selection || type == NodeType::AnySelection || right.type == NodeType::AnySelection)
86  {
87  return true;
88  }
89  }
90  return false;
91  };
92 
93  bool DICOMTagPath::IsEmpty() const
94  {
95  return m_NodeInfos.empty();
96  };
97 
98  bool
100  IsExplicit() const
101  {
102  for (const auto & pos : m_NodeInfos)
103  {
104  if ((pos.type == NodeInfo::NodeType::AnySelection) || (pos.type == NodeInfo::NodeType::AnyElement)) return false;
105  }
106 
107  return true;
108  };
109 
110  bool
113  {
114  bool result = false;
115  for (const auto & pos : m_NodeInfos)
116  {
117  if (pos.type == NodeInfo::NodeType::AnyElement) return false;
118  result = result || pos.type == NodeInfo::NodeType::AnySelection;
119  }
120 
121  return result;
122  };
123 
125  {
126  return m_NodeInfos.size();
127  }
128 
131  AddNode(const NodeInfo& newNode)
132  {
133  m_NodeInfos.push_back(newNode);
134  return m_NodeInfos.size() - 1;
135  };
136 
139  GetNode(const PathIndexType& index) const
140  {
141  if (index >= Size())
142  {
143  mitkThrow() << "Error. Cannot return info of path node. Node index is out of bound. Index: " << index << "; Path: " << this->ToStr();
144  }
145 
146  return m_NodeInfos[index];
147  };
148 
151  GetNode(const PathIndexType& index)
152  {
153  if (index >= Size())
154  {
155  mitkThrow() << "Error. Cannot return info of path node. Node index is out of bound. Index: " << index << "; Path: " << this->ToStr();
156  }
157 
158  return m_NodeInfos[index];
159  };
160 
164  {
165  return GetNode(0);
166  };
167 
171  {
172  return GetNode(Size() - 1);
173  };
174 
178  {
179  return GetNode(Size() - 1);
180  };
181 
184  GetNodes() const
185  {
186  return m_NodeInfos;
187  };
188 
189  std::string
191  ToStr() const
192  {
193  std::ostringstream nameStream;
194 
195  if (this->Size() == 0) return nameStream.str();
196 
197  PathIndexType i = 0;
198  for (const auto& node : m_NodeInfos)
199  {
200  if (i)
201  {
202  nameStream << ".";
203  }
204  ++i;
205 
206  if (node.type == NodeInfo::NodeType::AnyElement)
207  {
208  nameStream << "*";
209  }
210  else if (node.type != NodeInfo::NodeType::Invalid)
211  {
212  nameStream << "("
213  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase
214  << ","
215  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement() << std::nouppercase
216  << ")";
217 
218  if (node.type == NodeInfo::NodeType::SequenceSelection)
219  {
220  nameStream << "[" << node.selection << "]";
221  }
222  else if (node.type == NodeInfo::NodeType::AnySelection)
223  {
224  nameStream << "[*]";
225  }
226  }
227  else
228  {
229  nameStream << "INVALID_NODE";
230  }
231  }
232 
233  return nameStream.str();
234  };
235 
236  bool
238  operator == (const DICOMTagPath& path) const
239  {
240  return this->m_NodeInfos == path.m_NodeInfos;
241  };
242 
243  bool
245  operator < (const DICOMTagPath& right) const
246  {
247  auto rightIter = right.m_NodeInfos.cbegin();
248  const auto rightEnd = right.m_NodeInfos.cend();
249  for (const auto& leftPos : m_NodeInfos)
250  {
251  if (rightIter == rightEnd) return false;
252 
253  if (leftPos.tag.GetElement() < rightIter->tag.GetElement()) return true;
254  if (rightIter->tag.GetElement() < leftPos.tag.GetElement()) return false;
255 
256  if (leftPos.tag.GetGroup() < rightIter->tag.GetGroup()) return true;
257  if (rightIter->tag.GetGroup()< leftPos.tag.GetGroup()) return false;
258 
259  if (leftPos.type < rightIter->type) return true;
260  if (rightIter->type< leftPos.type) return false;
261 
262  if (leftPos.selection < rightIter->selection) return true;
263  if (rightIter->selection< leftPos.selection) return false;
264  ++rightIter;
265  }
266  return rightIter != rightEnd;
267  }
268 
269  bool
271  Equals(const DICOMTagPath& path) const
272  {
273  return DICOMTagPathesMatch(*this, path);
274  };
275 
276  DICOMTagPath&
279  {
280  if (&path == this) return *this;
281 
282  this->m_NodeInfos = path.m_NodeInfos;
283 
284  return *this;
285  };
286 
287  DICOMTagPath&
289  {
291  return *this;
292  };
293 
294  DICOMTagPath&
295  DICOMTagPath::AddElement(unsigned int group, unsigned int element)
296  {
297  m_NodeInfos.emplace_back(DICOMTag(group, element), NodeInfo::NodeType::Element);
298  return *this;
299  };
300 
301  DICOMTagPath&
302  DICOMTagPath::AddAnySelection(unsigned int group, unsigned int element)
303  {
304  m_NodeInfos.emplace_back(DICOMTag(group, element), NodeInfo::NodeType::AnySelection);
305  return *this;
306  };
307 
308  DICOMTagPath&
309  DICOMTagPath::AddSelection(unsigned int group, unsigned int element, ItemSelectionIndex index)
310  {
311  m_NodeInfos.emplace_back(DICOMTag(group, element), NodeInfo::NodeType::SequenceSelection, index);
312  return *this;
313  };
314 
315  DICOMTagPath&
317  FromStr(const std::string& pathStr)
318  {
319  NodeInfoVectorType result;
320  std::istringstream f(pathStr);
321  std::string subStr;
322 
323  while (getline(f, subStr, '.'))
324  {
325  NodeInfo info;
326 
327  if (subStr == "*")
328  {
330  }
331  else
332  {
333  std::regex reg_element("\\(([A - Fa - f\\d]{4}),([A - Fa - f\\d]{4})\\)");
334  std::regex reg_anySelection("\\(([A - Fa - f\\d]{4}),([A - Fa - f\\d]{4})\\)\\[\\*\\]");
335  std::regex reg_Selection("\\(([A - Fa - f\\d]{4}),([A - Fa - f\\d]{4})\\)\\[(\\d+)\\]");
336  std::smatch sm;
337  if (std::regex_match(subStr, sm, reg_anySelection))
338  {
340  info.tag = DICOMTag(std::stoul(sm[1], nullptr, 16), std::stoul(sm[2], nullptr, 16));
341  }
342  else if (std::regex_match(subStr, sm, reg_Selection))
343  {
345  info.tag = DICOMTag(std::stoul(sm[1], nullptr, 16), std::stoul(sm[2], nullptr, 16));
346  info.selection = std::stoi(sm[3]);
347  }
348  else if (std::regex_match(subStr, sm, reg_element))
349  {
351  info.tag = DICOMTag(std::stoul(sm[1], nullptr, 16), std::stoul(sm[2], nullptr, 16));
352  }
353  }
354  result.push_back(info);
355  }
356 
357  this->m_NodeInfos.swap(result);
358  return *this;
359  };
360 
362  {
363  Reset();
364  };
365 
368  {
369  *this = path;
370  };
371 
374  {
375  m_NodeInfos.emplace_back(tag, NodeInfo::NodeType::Element);
376  };
377 
378  DICOMTagPath::DICOMTagPath(unsigned int group, unsigned int element)
379  {
380  m_NodeInfos.emplace_back(DICOMTag(group,element));
381  };
382 
385 
386  void
389  {
390  this->m_NodeInfos.clear();
391  };
392 
393  bool
396  {
397  auto leftPos = left.GetNodes().cbegin();
398  auto rightPos = right.GetNodes().cbegin();
399  auto leftEnd = left.GetNodes().cend();
400  auto rightEnd = right.GetNodes().cend();
401 
402  while (leftPos != leftEnd && rightPos != rightEnd)
403  {
404  if (!leftPos->Matches(*rightPos)) break;
405  ++leftPos;
406  ++rightPos;
407  }
408 
409  if (leftPos == leftEnd && rightPos == rightEnd) return true;
410  else return false;
411  };
412 
413  std::ostream & operator<<(std::ostream &os, const DICOMTagPath &value)
414  {
415  os << value.ToStr();
416  return os;
417  };
418 
419  std::string DICOMTagPathToPropertyRegEx(const DICOMTagPath& tagPath)
420  {
421  std::ostringstream nameStream;
422 
423  nameStream << "DICOM";
424 
425  for (const auto& node : tagPath.GetNodes())
426  {
427  nameStream << "\\.";
428 
430  {
431  nameStream << "([A-Fa-f\\d]{4})\\.([A-Fa-f\\d]{4})";
432  }
433  else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid)
434  {
435  nameStream << GenerateRegExForNumber(node.tag.GetGroup())
436  << "\\."
437  << GenerateRegExForNumber(node.tag.GetElement());
438 
440  {
441  nameStream << "\\.\\[" << node.selection << "\\]";
442  }
444  {
445  nameStream << "\\.\\[(\\d*)\\]";
446  }
447  }
448  else
449  {
450  nameStream << "INVALIDNODE";
451  }
452  }
453 
454  return nameStream.str();
455  };
456 
458  {
459  std::ostringstream nameStream;
460 
461  nameStream << "DICOM";
462 
463  for (const auto& node : tagPath.GetNodes())
464  {
465  nameStream << "_";
466 
468  {
469  nameStream << "([A-Fa-f\\d]{4})_([A-Fa-f\\d]{4})";
470  }
471  else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid)
472  {
473  nameStream << GenerateRegExForNumber(node.tag.GetGroup())
474  << "_"
475  << GenerateRegExForNumber(node.tag.GetElement());
476 
478  {
479  nameStream << "_\\[" << node.selection << "\\]";
480  }
482  {
483  nameStream << "_\\[(\\d*)\\]";
484  }
485  }
486  else
487  {
488  nameStream << "INVALIDNODE";
489  }
490  }
491 
492  return nameStream.str();
493  };
494 
496  {
497  std::ostringstream nameStream;
498 
499  nameStream << "DICOM";
500 
501  int captureGroup = 1;
502 
503  for (const auto& node : tagPath.GetNodes())
504  {
505  nameStream << "_";
506 
508  {
509  nameStream << "$" << captureGroup++;
510  nameStream << "_$" << captureGroup++;
511  }
512  else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid)
513  {
514  nameStream << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase << "_"
515  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement();
516 
518  {
519  nameStream << "_[" << node.selection << "]";
520  }
522  {
523  nameStream << "_[$" << captureGroup++ << "]";
524  }
525  }
526  else
527  {
528  nameStream << "INVALID_NODE";
529  }
530  }
531 
532  return nameStream.str();
533  };
534 
536  {
537  std::ostringstream nameStream;
538 
539  nameStream << "DICOM";
540 
541  int captureGroup = 1;
542 
543  for (const auto& node : tagPath.GetNodes())
544  {
545  nameStream << ".";
546 
548  {
549  nameStream << "$" << captureGroup++;
550  nameStream << ".$" << captureGroup++;
551  }
552  else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid)
553  {
554  nameStream << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase << "."
555  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement();
556 
558  {
559  nameStream << ".[" << node.selection << "]";
560  }
562  {
563  nameStream << ".[$"<<captureGroup++<<"]";
564  }
565  }
566  else
567  {
568  nameStream << "INVALID_NODE";
569  }
570  }
571 
572  return nameStream.str();
573  };
574 
575 
576  std::string DICOMTagPathToDCMTKSearchPath(const DICOMTagPath& tagPath)
577  {
578  if (!tagPath.IsExplicit() && !tagPath.HasItemSelectionWildcardsOnly())
579  {
580  mitkThrow() << "Cannot convert DICOMTagPath into DCMTK search path. Path has element wild cards. Path: " << tagPath.ToStr();
581  }
582 
583  return tagPath.ToStr();
584  };
585 
586 
588  PropertyNameToDICOMTagPath(const std::string& propertyName)
589  {
590  DICOMTagPath result;
591 
592  std::istringstream f(propertyName);
593  std::string subStr;
594 
595  if (getline(f, subStr, '.'))
596  {
597  if (subStr != "DICOM")
598  {
599  return DICOMTagPath();
600  }
601 
602  unsigned int nrCount = 0;
603  unsigned long group = 0;
604  unsigned long element = 0;
605 
606  while (getline(f, subStr, '.'))
607  {
608  if (subStr == "*")
609  {
610  if (nrCount == 2)
611  { //add last element
612  result.AddElement(group, element);
613  nrCount = 0;
614  }
615  else if (nrCount != 0)
616  { //invalid path
617  return DICOMTagPath();
618  }
619 
620  result.AddAnyElement();
621  }
622  else
623  {
624  std::regex reg_element("([A-Fa-f\\d]{4})");
625  std::regex reg_anySelection("\\[\\*\\]");
626  std::regex reg_Selection("\\[(\\d+)\\]");
627  std::smatch sm;
628  if (std::regex_match(subStr, sm, reg_anySelection))
629  {
630  if (nrCount == 2)
631  {
632  result.AddAnySelection(group, element);
633  nrCount = 0;
634  }
635  else
636  { //invalid path
637  return DICOMTagPath();
638  }
639  }
640  else if (std::regex_match(subStr, sm, reg_Selection))
641  {
642  if (nrCount == 2)
643  {
644  result.AddSelection(group, element, std::stoi(sm[1]));
645  nrCount = 0;
646  }
647  else
648  { //invalid path
649  return DICOMTagPath();
650  }
651  }
652  else if (std::regex_match(subStr, sm, reg_element))
653  {
654  if (nrCount == 0)
655  {
656  group = std::stoul(sm[1], nullptr, 16);
657  ++nrCount;
658  }
659  else if (nrCount == 1)
660  {
661  element = std::stoul(sm[1], nullptr, 16);
662  ++nrCount;
663  }
664  else if (nrCount == 2)
665  { //store the last element and start the next
666  result.AddElement(group, element);
667  group = std::stoul(sm[1], nullptr, 16);
668  nrCount = 1;
669  }
670  else
671  { //invalid path
672  return DICOMTagPath();
673  }
674  }
675  }
676  }
677 
678  if (nrCount == 2)
679  { //add last element
680  result.AddElement(group, element);
681  nrCount = 0;
682  }
683  }
684 
685  return result;
686  };
687 
688  std::string
690  {
691  std::ostringstream nameStream;
692 
693  nameStream << "DICOM";
694 
695  for (const auto& node : tagPath.GetNodes())
696  {
697  nameStream << ".";
698 
700  {
701  nameStream << "*";
702  }
703  else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid)
704  {
705  nameStream << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase << "."
706  << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement();
707 
709  {
710  nameStream << ".[" << node.selection << "]";
711  }
713  {
714  nameStream << ".[*]";
715  }
716  }
717  else
718  {
719  nameStream << "INVALID_NODE";
720  }
721  }
722 
723  return nameStream.str();
724  };
725 }
DICOMTagPath & AddElement(unsigned int group, unsigned int element)
bool operator==(const NodeInfo &right) const
DICOMTagPath & AddAnySelection(unsigned int group, unsigned int element)
bool HasItemSelectionWildcardsOnly() const
Class is used to identify (nested) attributes in a DICOM dataset. In contrast to the class DICOMTag...
bool Equals(const DICOMTagPath &path) const
NodeInfoVectorType m_NodeInfos
const NodeInfoVectorType & GetNodes() const
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPersistenceKeyRegEx(const DICOMTagPath &tagPath)
bool operator==(const DICOMTagPath &path) const
DICOMTagPath & AddAnyElement()
Representation of a DICOM tag.
Definition: mitkDICOMTag.h:32
bool operator<(const DICOMTagPath &right) const
std::vector< NodeInfo > NodeInfoVectorType
DataCollection - Class to facilitate loading/accessing structured data.
PathIndexType AddNode(const NodeInfo &newNode)
static void info(const char *fmt,...)
Definition: svm.cpp:86
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPersistenceNameTemplate(const DICOMTagPath &tagPath)
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPropertyName(const DICOMTagPath &tagPath)
PathIndexType Size() const
static bool DICOMTagPathesMatch(const DICOMTagPath &left, const DICOMTagPath &right)
NodeInfoVectorType::size_type PathIndexType
DICOMTagPath & FromStr(const std::string &pathStr)
#define mitkThrow()
bool Matches(const NodeInfo &right) const
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPersistenceKeyTemplate(const DICOMTagPath &tagPath)
std::string ToStr() const
DICOMTagPath & operator=(const DICOMTagPath &path)
MITKDICOMREADER_EXPORT std::string DICOMTagPathToPropertyRegEx(const DICOMTagPath &tagPath)
const NodeInfo & GetNode(const PathIndexType &index) const
DICOMTagPath & AddSelection(unsigned int group, unsigned int element, ItemSelectionIndex index)
ItemSelectionIndex selection
MITKDICOMREADER_EXPORT std::string DICOMTagPathToDCMTKSearchPath(const DICOMTagPath &tagPath)
MITKDICOMREADER_EXPORT DICOMTagPath PropertyNameToDICOMTagPath(const std::string &propertyName)
MITKCORE_EXPORT std::ostream & operator<<(std::ostream &o, DataNode::Pointer &dtn)
NodeInfo & GetFirstNode()