Medical Imaging Interaction Toolkit  2018.4.99-07c45cb1
Medical Imaging Interaction Toolkit
mitkSourceImageRelationRule.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 <regex>
14 #include <mutex>
15 
17 #include "mitkPropertyNameHelper.h"
18 #include "mitkStringProperty.h"
20 #include "mitkDataNode.h"
21 
22 
24 {
25  return m_PurposeTag.empty();
26 };
27 
29 {
30  return ruleID == this->GetRuleID() || (IsAbstract() && ruleID.find("SourceImageRelation ") == 0);
31 };
32 
34 {
35  return "SourceImageRelation " + m_PurposeTag;
36 };
37 
39 {
40  return m_DisplayName;
41 };
42 
44 {
45  return m_SourceRole;
46 };
47 
49 {
50  return m_DestinationRole;
51 };
52 
54 {
55  auto node = dynamic_cast<const DataNode*>(owner);
56 
57  auto image = nullptr != node
58  ? dynamic_cast<const Image*>(node->GetData())
59  : dynamic_cast<const Image*>(owner);
60 
61  return image != nullptr;
62 }
63 
65 {
66  return Superclass::Connect(source, destination);
67 };
68 
70  : m_PurposeTag(""), m_DisplayName("Abstract image to image relation"), m_SourceRole("derived data"), m_DestinationRole("source image")
71 {};
72 
74  : SourceImageRelationRule(purposeTag, purposeTag + " relation"){};
75 
76 mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName)
78  purposeTag, displayName, "derived data", "source image"){};
79 
81  const std::string &displayName,
82  const std::string &sourceRole,
83  const std::string &destinationRole)
84  : m_PurposeTag(purposeTag), m_DisplayName(displayName), m_SourceRole(sourceRole), m_DestinationRole(destinationRole){};
85 
87  const IPropertyProvider * source, const IPropertyProvider * destination) const
88 {
89  InstanceIDVectorType result;
90 
91  auto relevantReferenceIndices = GetReferenceSequenceIndices(source, destination);
92 
93  auto itemRegExStr = this->GetRIIPropertyRegEx("SourceImageSequenceItem");
94  auto regEx = std::regex(itemRegExStr);
95 
96  //workaround until T24729 is done. Please remove if T24728 is done
98  //end workaround for T24729
99 
100  for (const auto &key : keys)
101  {
102  if (std::regex_match(key, regEx))
103  {
104  auto sequItemProp = source->GetConstProperty(key);
105  if (sequItemProp.IsNotNull())
106  {
107  auto finding = std::find(std::cbegin(relevantReferenceIndices), std::cend(relevantReferenceIndices), sequItemProp->GetValueAsString());
108  if (finding != std::cend(relevantReferenceIndices))
109  {
110  auto instanceID = GetInstanceIDByPropertyName(key);
111  auto ruleID = GetRuleIDByInstanceID(source, instanceID);
112  if (this->IsSupportedRuleID(ruleID))
113  {
114  result.push_back(instanceID);
115  }
116  }
117  }
118  }
119  }
120 
121  return result;
122 };
123 
125  const IPropertyProvider * destination) const
126 {
127  std::vector<std::string> result;
128 
129  BaseProperty::ConstPointer destInstanceUIDProp;
130 
131  if (destination)
132  {
133  destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0018));
134 
135  if (destInstanceUIDProp.IsNull())
136  {
137  return result;
138  }
139  }
140 
141  PropertyKeyPath referencedInstanceUIDs;
142  referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155");
143 
144  auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);;
145  auto regEx = std::regex(sourceRegExStr);
146 
147  std::vector<std::string> keys;
148  //workaround until T24729 is done. Please remove if T24728 is done
150  //end workaround for T24729
151 
152  for (const auto &key : keys)
153  {
154  if (std::regex_match(key, regEx))
155  {
156  auto refUIDProp = source->GetConstProperty(key);
157  if (destination==nullptr || *refUIDProp == *destInstanceUIDProp)
158  {
159  auto currentKeyPath = PropertyNameToPropertyKeyPath(key);
160  auto currentKeyPathSelection = currentKeyPath.GetNode(2).selection;
161  PropertyKeyPath purposePath;
162  purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", currentKeyPathSelection).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104");
163 
164  auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath));
165  if (this->IsAbstract() || (purposeProp.IsNotNull() && purposeProp->GetValueAsString() == this->m_PurposeTag))
166  {
167  result.push_back(std::to_string(currentKeyPathSelection));
168  }
169  }
170  }
171  }
172 
173  return result;
174 };
175 
177  const IPropertyProvider * destination) const
178 {
179  auto relevantReferences = GetReferenceSequenceIndices(source, destination);
180 
181  if (this->IsAbstract())
182  {
183  return !relevantReferences.empty();
184  }
185  else
186  {
187  for (auto referenceIndex : relevantReferences)
188  {
189  PropertyKeyPath purposePath;
190  purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", std::stoi(referenceIndex)).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104");
191  auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath));
192 
193  if (purposeProp.IsNotNull() && purposeProp->GetValueAsString() == this->m_PurposeTag)
194  {
195  return true;
196  }
197  }
198  }
199 
200  return false;
201 };
202 
203 
208 namespace
209 {
210  std::mutex sequenceItemCreationLock;
211 }
212 
214  IPropertyOwner *source) const
215 {
216  std::lock_guard<std::mutex> guard(sequenceItemCreationLock);
217 
219  // Get all existing sequence items
220 
221  std::vector<PropertyKeyPath::ItemSelectionIndex> instanceIDs;
223 
224  PropertyKeyPath referencedInstanceUIDs;
225  referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155");
226  auto regExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);
227  auto regEx = std::regex(regExStr);
228  std::smatch instance_matches;
229 
230  //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed.
231  const auto keys = GetPropertyKeys(source);
232  //end workaround for T24729
233 
234  for (const auto &key : keys)
235  {
236  if (std::regex_search(key, instance_matches, regEx))
237  {
238  if (instance_matches.size()>1)
239  {
240  instanceIDs.push_back(std::stoi(instance_matches[1]));
241  }
242  }
243  }
244 
246  // Get new ID
247 
248  std::sort(instanceIDs.begin(), instanceIDs.end());
249  if (!instanceIDs.empty())
250  {
251  newID = instanceIDs.back()+1;
252  }
253 
255  // reserve new ID
256 
257  PropertyKeyPath newSourceImageSequencePath;
258  newSourceImageSequencePath.AddElement("DICOM").AddElement("0008").AddSelection("2112",newID).AddElement("0008").AddElement("1155");
259 
260  auto newKey =
261  PropertyKeyPathToPropertyName(newSourceImageSequencePath);
262  source->SetProperty(newKey, mitk::TemporoSpatialStringProperty::New("reserved slot for source image sequence"));
263 
264  return newID;
265 };
266 
267 
269  const IPropertyProvider * destination,
270  const InstanceIDType & instanceID) const
271 {
272  auto destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008,0x0018));
273  auto destClassUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0016));
274 
275  if (destInstanceUIDProp.IsNotNull() && destClassUIDProp.IsNotNull())
276  {
277  auto existingRefs = this->GetReferenceSequenceIndices(source, destination);
278  std::string newSelectionIndexStr;
279  if (!existingRefs.empty())
280  {
281  newSelectionIndexStr = existingRefs[0];
282  }
283  else
284  {
286 
287  PropertyKeyPath refInstanceUIDPath;
288  refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1155");
289  source->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), destInstanceUIDProp->Clone());
290 
291  PropertyKeyPath refClassUIDPath;
292  refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1150");
293  source->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), destClassUIDProp->Clone());
294 
295  PropertyKeyPath purposePath;
296  purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104");
297  source->SetProperty(PropertyKeyPathToPropertyName(purposePath), StringProperty::New(m_PurposeTag));
298 
299  newSelectionIndexStr = std::to_string(newSelectionIndex);
300  }
301 
302  auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem");
303  source->SetProperty(PropertyKeyPathToPropertyName(sourceImageRefPath), StringProperty::New(newSelectionIndexStr).GetPointer());
304  }
305  else
306  {
307  MITK_DEBUG << "Cannot connect SourceImageRelationRule on data layer. Passed destination does not have properties for DICOM SOP Instance UIDs(0x0008, 0x0018) and DICOM SOP Class UID(0x0008, 0x0016)";
308  }
309 };
310 
312 {
313  auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem");
314  auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath));
315 
316  if (imageRefProp.IsNotNull())
317  {
318  auto deletedImageRefSequenceIndex = imageRefProp->GetValueAsString();
319 
320  auto refs = GetReferenceSequenceIndices(source);
321  std::sort(refs.begin(), refs.end());
322 
323  for (auto refIndexStr : refs)
324  {
325  auto refIndex = std::stoi(refIndexStr);
326 
327  if (refIndex >= std::stoi(deletedImageRefSequenceIndex))
328  {
329  PropertyKeyPath refDICOMDataPath;
330  refDICOMDataPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", refIndex);
331  auto prefix = PropertyKeyPathToPropertyName(refDICOMDataPath);
332 
333  PropertyKeyPath refRelDataPath = GetRootKeyPath().AddAnyElement().AddElement("SourceImageSequenceItem");;
334  auto regEx = std::regex(PropertyKeyPathToPropertyRegEx(refRelDataPath));
335 
336  //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed.
337  const auto keys = GetPropertyKeys(source);
338  //end workaround for T24729
339 
340  for (const auto &key : keys)
341  {
342  if (key.find(prefix) == 0)
343  { //its a relevant DICOM property delete or update
344  if (refIndexStr != deletedImageRefSequenceIndex)
345  {
346  //reindex to close the gap in the dicom sequence.
347  auto newPath = PropertyNameToPropertyKeyPath(key);
348  newPath.GetNode(2).selection = refIndex - 1;
349  source->SetProperty(PropertyKeyPathToPropertyName(newPath), source->GetNonConstProperty(key));
350  }
351  //remove old/outdated data layer information
352  source->RemoveProperty(key);
353 
354  auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem");
355  }
356  if (std::regex_match(key, regEx))
357  {
358  auto imageSequenceItemProp = source->GetConstProperty(key);
359  if (imageSequenceItemProp->GetValueAsString() == std::to_string(refIndex))
360  {
361  //its a relevant data property of the relation rule.
362  source->SetProperty(key, StringProperty::New(std::to_string(refIndex - 1)));
363  }
364  }
365  }
366  }
367  }
368  }
369 };
370 
371 itk::LightObject::Pointer mitk::SourceImageRelationRule::InternalClone() const
372 {
373  itk::LightObject::Pointer result = Self::New(this->m_PurposeTag, this->m_DisplayName, this->m_SourceRole, this->m_DestinationRole).GetPointer();
374 
375  return result;
376 };
bool HasImplicitDataRelation(const IPropertyProvider *source, const IPropertyProvider *destination) const override
bool IsDestinationCandidate(const IPropertyProvider *owner) const override
MITKCORE_EXPORT std::string PropertyKeyPathToPropertyName(const PropertyKeyPath &tagPath)
PropertyRelationRuleBase::RuleIDType RuleIDType
std::string MITKCORE_EXPORT GeneratePropertyNameForDICOMTag(unsigned int group, unsigned int element)
std::string GetSourceRoleName() const override
#define MITK_DEBUG
Definition: mitkLogMacros.h:22
itk::LightObject::Pointer InternalClone() const override
PropertyKeyPath & AddElement(const ElementNameType &name)
virtual BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName="", bool fallBackOnDefaultContext=true) const =0
Get property by its key.
PropertyRelationRuleBase::RelationUIDType RelationUIDType
PropertyKeyPath & AddSelection(const ElementNameType &name, ItemSelectionIndex index)
RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const
std::string GetRIIPropertyRegEx(const std::string propName="", const InstanceIDType &instanceID="") const
InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, const IPropertyProvider *destination) const override
virtual void RemoveProperty(const std::string &propertyKey, const std::string &contextName="", bool fallBackOnDefaultContext=false)=0
Removes a property. If the property does not exist, nothing will be done.
std::string GetDestinationRoleName() const override
Image class for storing images.
Definition: mitkImage.h:72
MITKCORE_EXPORT PropertyKeyPath PropertyNameToPropertyKeyPath(const std::string &propertyName)
RelationUIDType Connect(Image *source, const Image *destination) const
std::vcl_size_t ItemSelectionIndex
mitk::Image::Pointer image
static std::vector< std::string > GetPropertyKeys(const mitk::IPropertyProvider *owner)
void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType &instanceID) const override
static InstanceIDType GetInstanceIDByPropertyName(const std::string propName)
RuleIDType GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const
PropertyKeyPath::ItemSelectionIndex CreateNewSourceImageSequenceItem(IPropertyOwner *source) const
PropertyKeyPath & AddAnyElement()
PropertyRelationRuleBase::InstanceIDVectorType InstanceIDVectorType
std::string GetDisplayName() const override
std::vector< InstanceIDType > InstanceIDVectorType
std::vector< std::string > GetReferenceSequenceIndices(const IPropertyProvider *source, const IPropertyProvider *destination=nullptr) const
Class that can be used to specify nested or wild carded property keys. E.g. for the use in context of...
void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override
virtual BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName="", bool fallBackOnDefaultContext=true)=0
Get property by its key.
static Pointer New()
MITKCORE_EXPORT std::string PropertyKeyPathToPropertyRegEx(const PropertyKeyPath &tagPath)
bool IsSupportedRuleID(const RuleIDType &ruleID) const override
Class for nodes of the DataTree.
Definition: mitkDataNode.h:57
virtual void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName="", bool fallBackOnDefaultContext=false)=0
Add new or change existent property.
PropertyKeyPath & AddAnySelection(const ElementNameType &name)