Medical Imaging Interaction Toolkit  2018.4.99-f51274ea
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 #include "mitkIdentifiable.h"
22 
23 std::string mitk::SourceImageRelationRule::GenerateRuleID(const std::string& purpose) const
24 {
25  return "SourceImageRelation " + purpose;
26 }
27 
29 {
30  return m_PurposeTag.empty();
31 };
32 
34 {
35  return ruleID == this->GetRuleID() || (IsAbstract() && ruleID.find("SourceImageRelation ") == 0);
36 };
37 
39 {
40  return this->GenerateRuleID(m_PurposeTag);
41 };
42 
44 {
45  return m_DisplayName;
46 };
47 
49 {
50  return m_SourceRole;
51 };
52 
54 {
55  return m_DestinationRole;
56 };
57 
59 {
60  auto node = dynamic_cast<const DataNode*>(owner);
61 
62  auto image = nullptr != node
63  ? dynamic_cast<const Image*>(node->GetData())
64  : dynamic_cast<const Image*>(owner);
65 
66  return image != nullptr;
67 }
68 
70 {
71  return Superclass::Connect(source, destination);
72 };
73 
75  : m_PurposeTag(""), m_DisplayName("Abstract image to image relation"), m_SourceRole("derived data"), m_DestinationRole("source image")
76 {};
77 
79  : SourceImageRelationRule(purposeTag, purposeTag + " relation"){};
80 
81 mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName)
83  purposeTag, displayName, "derived data", "source image"){};
84 
86  const std::string &displayName,
87  const std::string &sourceRole,
88  const std::string &destinationRole)
89  : m_PurposeTag(purposeTag), m_DisplayName(displayName), m_SourceRole(sourceRole), m_DestinationRole(destinationRole){};
90 
93  const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const
94 {
96 
97  auto relevantIndicesAndRuleIDs = GetReferenceSequenceIndices(source, destination, instances_IDLayer);
98 
99  auto itemRIIRegExStr = this->GetRIIPropertyRegEx("SourceImageSequenceItem");
100  auto regEx = std::regex(itemRIIRegExStr);
101 
102  //workaround until T24729 is done. Please remove if T24728 is done
103  auto keys = PropertyRelationRuleBase::GetPropertyKeys(source);
104  //end workaround for T24729
105 
106  for (const auto &indexNRule : relevantIndicesAndRuleIDs)
107  {
108  bool relationCoveredByRII = false;
109  for (const auto& key : keys)
110  {
111  if (std::regex_match(key, regEx))
112  {
113  auto sequItemProp = source->GetConstProperty(key);
114  if (sequItemProp.IsNotNull() && sequItemProp->GetValueAsString() == std::to_string(indexNRule.first))
115  {
116  relationCoveredByRII = true;
117  auto instanceID = GetInstanceIDByPropertyName(key);
118  auto ruleID = GetRuleIDByInstanceID(source, instanceID);
119  if (this->IsSupportedRuleID(ruleID))
120  {
121  result.emplace_back(this->GetRelationUIDByInstanceID(source, instanceID), ruleID);
122  }
123  }
124  }
125  }
126 
127  if (!relationCoveredByRII)
128  {
129  //the relation is not covered on the RII level, so we generate the property path to the DICOM source reference (this is done via
130  //the SOP Instance UIDs which uniquely identify an DICOM IOD). We use this property path as relation UID because it is identifying
131  //on the data level (even so not long term stable if relations with a lower index are removed).
132  PropertyKeyPath referencedInstanceUIDs;
133  referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddSelection("2112", indexNRule.first).AddElement("0008").AddElement("1155");
134 
135  std::string ruleID = "";
136  if (!indexNRule.second.empty())
137  {
138  ruleID = this->GenerateRuleID(indexNRule.second);
139  }
140 
141  result.emplace_back(PropertyKeyPathToPropertyName(referencedInstanceUIDs), ruleID);
142  }
143  }
144 
145  return result;
146 };
147 
148 std::vector<std::pair<size_t,std::string> > mitk::SourceImageRelationRule::GetReferenceSequenceIndices(const IPropertyProvider * source,
149  const IPropertyProvider * destination, InstanceIDVectorType ignoreInstances) const
150 {
151  std::vector<std::pair<size_t, std::string> > result;
152 
153  BaseProperty::ConstPointer destInstanceUIDProp;
154  std::string destinationUID = "";
155 
156  if (destination)
157  {
158  destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0018));
159 
160  if (destInstanceUIDProp.IsNull())
161  {
162  return result;
163  }
164 
165  auto identifiable = this->CastProviderAsIdentifiable(destination);
166 
167  if (identifiable)
168  {
169  destinationUID= identifiable->GetUID();
170  }
171  }
172 
173  std::vector<std::string> ignoreItemIndices;
174  for (const auto& iID : ignoreInstances)
175  {
176  auto sourceImageRefPath = GetRootKeyPath().AddElement(iID).AddElement("SourceImageSequenceItem");
177  auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath));
178 
179  if (imageRefProp.IsNotNull())
180  {
181  ignoreItemIndices.emplace_back(imageRefProp->GetValueAsString());
182  }
183  }
184 
185  PropertyKeyPath referencedInstanceUIDs;
186  referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155");
187 
188  auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);
189  auto regEx = std::regex(sourceRegExStr);
190 
191  std::vector<std::string> keys;
192  //workaround until T24729 is done. Please remove if T24728 is done
194  //end workaround for T24729
195 
196  for (const auto &key : keys)
197  {
198  if (std::regex_match(key, regEx))
199  {
200  auto refUIDProp = source->GetConstProperty(key);
201  if (destination==nullptr || *refUIDProp == *destInstanceUIDProp)
202  {
203  auto currentKeyPath = PropertyNameToPropertyKeyPath(key);
204  auto currentKeyPathSelection = currentKeyPath.GetNode(2).selection;
205 
206  auto finding = std::find(ignoreItemIndices.begin(), ignoreItemIndices.end(), std::to_string(currentKeyPathSelection));
207  if (finding == ignoreItemIndices.end())
208  {
209  PropertyKeyPath purposePath;
210  purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", currentKeyPathSelection).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104");
211  auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath));
212  std::string currentPurpose = "";
213  if (purposeProp.IsNotNull())
214  {
215  currentPurpose = purposeProp->GetValueAsString();
216  }
217  if (this->IsAbstract() || (purposeProp.IsNotNull() && currentPurpose == this->m_PurposeTag))
218  {
219  result.emplace_back(currentKeyPathSelection, currentPurpose);
220  }
221  }
222  }
223  }
224  }
225 
226  return result;
227 };
228 
233 namespace
234 {
235  std::mutex sequenceItemCreationLock;
236 }
237 
239  IPropertyOwner *source) const
240 {
241  std::lock_guard<std::mutex> guard(sequenceItemCreationLock);
242 
244  // Get all existing sequence items
245 
246  std::vector<PropertyKeyPath::ItemSelectionIndex> instanceIDs;
248 
249  PropertyKeyPath referencedInstanceUIDs;
250  referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155");
251  auto regExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);
252  auto regEx = std::regex(regExStr);
253  std::smatch instance_matches;
254 
255  //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed.
256  const auto keys = GetPropertyKeys(source);
257  //end workaround for T24729
258 
259  for (const auto &key : keys)
260  {
261  if (std::regex_search(key, instance_matches, regEx))
262  {
263  if (instance_matches.size()>1)
264  {
265  instanceIDs.push_back(std::stoi(instance_matches[1]));
266  }
267  }
268  }
269 
271  // Get new ID
272 
273  std::sort(instanceIDs.begin(), instanceIDs.end());
274  if (!instanceIDs.empty())
275  {
276  newID = instanceIDs.back()+1;
277  }
278 
280  // reserve new ID
281 
282  PropertyKeyPath newSourceImageSequencePath;
283  newSourceImageSequencePath.AddElement("DICOM").AddElement("0008").AddSelection("2112",newID).AddElement("0008").AddElement("1155");
284 
285  auto newKey =
286  PropertyKeyPathToPropertyName(newSourceImageSequencePath);
287  source->SetProperty(newKey, mitk::TemporoSpatialStringProperty::New("reserved slot for source image sequence"));
288 
289  return newID;
290 };
291 
292 
294  const IPropertyProvider * destination,
295  const InstanceIDType & instanceID) const
296 {
297  auto destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008,0x0018));
298  auto destClassUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0016));
299 
300  if (destInstanceUIDProp.IsNotNull() && destClassUIDProp.IsNotNull())
301  {
302  auto existingRefs = this->GetReferenceSequenceIndices(source, destination);
303  std::string newSelectionIndexStr;
304  if (!existingRefs.empty())
305  {
306  newSelectionIndexStr = std::to_string(existingRefs[0].first);
307  }
308  else
309  {
311 
312  PropertyKeyPath refInstanceUIDPath;
313  refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1155");
314  source->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), destInstanceUIDProp->Clone());
315 
316  PropertyKeyPath refClassUIDPath;
317  refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1150");
318  source->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), destClassUIDProp->Clone());
319 
320  PropertyKeyPath purposePath;
321  purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104");
322  source->SetProperty(PropertyKeyPathToPropertyName(purposePath), StringProperty::New(m_PurposeTag));
323 
324  newSelectionIndexStr = std::to_string(newSelectionIndex);
325  }
326 
327  auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem");
328  source->SetProperty(PropertyKeyPathToPropertyName(sourceImageRefPath), StringProperty::New(newSelectionIndexStr).GetPointer());
329  }
330  else
331  {
332  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)";
333  }
334 };
335 
337 {
338  std::string deletedImageRefSequenceIndexStr = "";
339 
340  if (relationUID.find("DICOM") == 0)
341  { //relation that is purly data based.
342  auto currentKeyPath = PropertyNameToPropertyKeyPath(relationUID);
343  deletedImageRefSequenceIndexStr = std::to_string(currentKeyPath.GetNode(2).selection);
344  }
345  else
346  {
347  auto sourceImageRefPath = GetRootKeyPath().AddElement(this->GetInstanceIDByRelationUID(source,relationUID)).AddElement("SourceImageSequenceItem");
348  auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath));
349  if (imageRefProp.IsNotNull())
350  {
351  deletedImageRefSequenceIndexStr = imageRefProp->GetValueAsString();
352  }
353  }
354 
355  if(!deletedImageRefSequenceIndexStr.empty())
356  {
357  auto deletedImageRefSequenceIndex = std::stoull(deletedImageRefSequenceIndexStr);
358  auto refs = GetReferenceSequenceIndices(source);
359  std::sort(refs.begin(), refs.end());
360 
361  for (auto refIndex : refs)
362  {
363  if (refIndex.first >= deletedImageRefSequenceIndex)
364  {
365  PropertyKeyPath refDICOMDataPath;
366  refDICOMDataPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", refIndex.first);
367  auto prefix = PropertyKeyPathToPropertyName(refDICOMDataPath);
368 
369  PropertyKeyPath refRelDataPath = GetRootKeyPath().AddAnyElement().AddElement("SourceImageSequenceItem");;
370  auto riiRegEx = std::regex(PropertyKeyPathToPropertyRegEx(refRelDataPath));
371 
372  //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed.
373  const auto keys = GetPropertyKeys(source);
374  //end workaround for T24729
375 
376  for (const auto &key : keys)
377  {
378  if (key.find(prefix) == 0)
379  { //its a relevant DICOM property delete or update
380  if (refIndex.first != deletedImageRefSequenceIndex)
381  {
382  //reindex to close the gap in the dicom sequence.
383  auto newPath = PropertyNameToPropertyKeyPath(key);
384  newPath.GetNode(2).selection = refIndex.first - 1;
385  source->SetProperty(PropertyKeyPathToPropertyName(newPath), source->GetNonConstProperty(key));
386  }
387  //remove old/outdated data layer information
388  source->RemoveProperty(key);
389  }
390  if (std::regex_match(key, riiRegEx))
391  { //it is a relevant RII property, remove it or update it.
392  auto imageSequenceItemProp = source->GetConstProperty(key);
393  if (imageSequenceItemProp->GetValueAsString() == deletedImageRefSequenceIndexStr)
394  {
395  source->RemoveProperty(key);
396  }
397  else if (imageSequenceItemProp->GetValueAsString() == std::to_string(refIndex.first))
398  {
399  //its a relevant data property of the relation rule reindex it.
400  source->SetProperty(key, StringProperty::New(std::to_string(refIndex.first - 1)));
401  }
402  }
403  }
404  }
405  }
406  }
407 };
408 
409 itk::LightObject::Pointer mitk::SourceImageRelationRule::InternalClone() const
410 {
411  itk::LightObject::Pointer result = Self::New(this->m_PurposeTag, this->m_DisplayName, this->m_SourceRole, this->m_DestinationRole).GetPointer();
412 
413  return result;
414 };
std::vector< std::pair< vcl_size_t, std::string > > GetReferenceSequenceIndices(const IPropertyProvider *source, const IPropertyProvider *destination=nullptr, InstanceIDVectorType ignoreInstances={}) const
bool IsDestinationCandidate(const IPropertyProvider *owner) const override
const Identifiable * CastProviderAsIdentifiable(const mitk::IPropertyProvider *provider) const
MITKCORE_EXPORT std::string PropertyKeyPathToPropertyName(const PropertyKeyPath &tagPath)
PropertyRelationRuleBase::RuleIDType RuleIDType
PropertyRelationRuleBase::DataRelationUIDVectorType DataRelationUIDVectorType
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
virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider *source, const IPropertyProvider *destination, const InstanceIDVectorType &instances_IDLayer) const override
std::string GetRIIPropertyRegEx(const std::string propName="", const InstanceIDType &instanceID="") const
RelationUIDType GetRelationUIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const
InstanceIDType GetInstanceIDByRelationUID(const IPropertyProvider *source, const RelationUIDType &relationUID) const
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::string GenerateRuleID(const std::string &purpose) const
void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType &relationUID) const override
std::vcl_size_t ItemSelectionIndex
mitk::Image::Pointer image
std::vector< std::pair< RelationUIDType, RuleIDType > > DataRelationUIDVectorType
static InstanceIDType GetInstanceIDByPropertyName(const std::string propName)
RuleIDType GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const
PropertyKeyPath::ItemSelectionIndex CreateNewSourceImageSequenceItem(IPropertyOwner *source) const
PropertyKeyPath & AddAnyElement()
std::string GetDisplayName() const override
std::vector< InstanceIDType > InstanceIDVectorType
static std::vector< std::string > GetPropertyKeys(const IPropertyProvider *owner)
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)