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