Mercurial > hg > orthanc-stone
comparison Framework/Loaders/DicomStructureSetLoader.cpp @ 1416:d959bc8f6c1b loader-injection-feature
Instance lookup is now performed in a separate class through an interface. Another implementation can be injected (SetInstanceLookupHandler)
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Mon, 11 May 2020 17:37:30 +0200 |
parents | ffe9beb7c5d3 |
children | 30deba7bc8e2 |
comparison
equal
deleted
inserted
replaced
1415:998697c5ec74 | 1416:d959bc8f6c1b |
---|---|
46 o << val; | 46 o << val; |
47 //return o; | 47 //return o; |
48 } | 48 } |
49 #endif | 49 #endif |
50 | 50 |
51 | 51 // implementation of IInstanceLookupHandler that uses Orthanc REST API calls to retrive the |
52 class DicomStructureSetLoader::AddReferencedInstance : public LoaderStateMachine::State | 52 // geometry of referenced instances |
53 class DicomStructureSetLoader::RestInstanceLookupHandler : public DicomStructureSetLoader::IInstanceLookupHandler, | |
54 public LoaderStateMachine | |
55 { | |
56 public: | |
57 static boost::shared_ptr<RestInstanceLookupHandler > Create(DicomStructureSetLoader& loader) | |
58 { | |
59 boost::shared_ptr<RestInstanceLookupHandler> obj(new RestInstanceLookupHandler(loader)); | |
60 obj->LoaderStateMachine::PostConstructor(); | |
61 return obj; | |
62 } | |
63 | |
64 protected: | |
65 RestInstanceLookupHandler(DicomStructureSetLoader& loader) | |
66 : LoaderStateMachine(loader.loadersContext_) | |
67 , loader_(loader) | |
68 { | |
69 } | |
70 | |
71 virtual void RetrieveReferencedSlices(const std::set<std::string>& nonEmptyInstances) ORTHANC_OVERRIDE; | |
72 | |
73 private: | |
74 // these subclasses hold the loading state | |
75 class AddReferencedInstance; // 2nd state | |
76 class LookupInstance; // 1st state | |
77 | |
78 DicomStructureSetLoader& loader_; | |
79 }; | |
80 | |
81 class DicomStructureSetLoader::RestInstanceLookupHandler::AddReferencedInstance : public LoaderStateMachine::State | |
53 { | 82 { |
54 private: | 83 private: |
55 std::string instanceId_; | 84 std::string instanceId_; |
56 | 85 |
57 public: | 86 public: |
69 | 98 |
70 Orthanc::DicomMap dicom; | 99 Orthanc::DicomMap dicom; |
71 dicom.FromDicomAsJson(tags); | 100 dicom.FromDicomAsJson(tags); |
72 | 101 |
73 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); | 102 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); |
74 | 103 |
75 loader.content_->AddReferencedSlice(dicom); | 104 loader.AddReferencedSlice(dicom); |
76 loader.countProcessedInstances_ ++; | |
77 assert(loader.countProcessedInstances_ <= loader.countReferencedInstances_); | |
78 | |
79 loader.revision_++; | |
80 loader.SetStructuresUpdated(); | |
81 | |
82 if (loader.countProcessedInstances_ == loader.countReferencedInstances_) | |
83 { | |
84 // All the referenced instances have been loaded, finalize the RT-STRUCT | |
85 loader.content_->CheckReferencedSlices(); | |
86 loader.revision_++; | |
87 loader.SetStructuresReady(); | |
88 } | |
89 } | 105 } |
90 }; | 106 }; |
91 | 107 |
92 | 108 |
93 // State that converts a "SOP Instance UID" to an Orthanc identifier | 109 // State that converts a "SOP Instance UID" to an Orthanc identifier |
94 class DicomStructureSetLoader::LookupInstance : public LoaderStateMachine::State | 110 class DicomStructureSetLoader::RestInstanceLookupHandler::LookupInstance : public LoaderStateMachine::State |
95 { | 111 { |
96 private: | 112 private: |
97 std::string sopInstanceUid_; | 113 std::string sopInstanceUid_; |
98 | 114 |
99 public: | 115 public: |
104 { | 120 { |
105 } | 121 } |
106 | 122 |
107 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) | 123 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) |
108 { | 124 { |
109 #if 0 | |
110 LOG(TRACE) << "DicomStructureSetLoader::LookupInstance::Handle() (SUCCESS)"; | |
111 #endif | |
112 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); | 125 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); |
113 | 126 |
114 Json::Value lookup; | 127 Json::Value lookup; |
115 message.ParseJsonBody(lookup); | 128 message.ParseJsonBody(lookup); |
116 | 129 |
145 Schedule(command.release()); | 158 Schedule(command.release()); |
146 } | 159 } |
147 } | 160 } |
148 }; | 161 }; |
149 | 162 |
163 void DicomStructureSetLoader::RestInstanceLookupHandler::RetrieveReferencedSlices( | |
164 const std::set<std::string>& nonEmptyInstances) | |
165 { | |
166 for (std::set<std::string>::const_iterator it = nonEmptyInstances.begin(); | |
167 it != nonEmptyInstances.end(); | |
168 ++it) | |
169 { | |
170 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); | |
171 command->SetUri("/tools/lookup"); | |
172 command->SetMethod(Orthanc::HttpMethod_Post); | |
173 command->SetBody(*it); | |
174 command->AcquirePayload(new LookupInstance(loader_, *it)); | |
175 Schedule(command.release()); | |
176 } | |
177 } | |
150 | 178 |
151 class DicomStructureSetLoader::LoadStructure : public LoaderStateMachine::State | 179 class DicomStructureSetLoader::LoadStructure : public LoaderStateMachine::State |
152 { | 180 { |
153 public: | 181 public: |
154 LoadStructure(DicomStructureSetLoader& that) : | 182 LoadStructure(DicomStructureSetLoader& that) : |
157 } | 185 } |
158 | 186 |
159 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) | 187 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) |
160 { | 188 { |
161 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); | 189 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); |
162 | 190 |
191 // Set the actual structure set content | |
163 { | 192 { |
164 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer()); | 193 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer()); |
194 | |
165 loader.content_.reset(new OrthancStone::DicomStructureSet(dicom)); | 195 loader.content_.reset(new OrthancStone::DicomStructureSet(dicom)); |
166 size_t structureCount = loader.content_->GetStructuresCount(); | 196 } |
167 loader.structureVisibility_.resize(structureCount); | 197 |
168 bool everythingVisible = false; | 198 // initialize visibility flags |
169 if ((loader.initiallyVisibleStructures_.size() == 1) | 199 SetDefaultStructureVisibility(); |
170 && (loader.initiallyVisibleStructures_[0].size() == 1) | 200 |
171 && (loader.initiallyVisibleStructures_[0][0] == '*')) | 201 // retrieve the (non-empty) referenced instances (the CT slices containing the corresponding structures) |
172 { | |
173 everythingVisible = true; | |
174 } | |
175 | |
176 for (size_t i = 0; i < structureCount; ++i) | |
177 { | |
178 // if a single "*" string is supplied, this means we want everything | |
179 // to be visible... | |
180 if(everythingVisible) | |
181 { | |
182 loader.structureVisibility_.at(i) = true; | |
183 } | |
184 else | |
185 { | |
186 // otherwise, we only enable visibility for those structures whose | |
187 // names are mentioned in the initiallyVisibleStructures_ array | |
188 const std::string& structureName = loader.content_->GetStructureName(i); | |
189 | |
190 std::vector<std::string>::iterator foundIt = | |
191 std::find( | |
192 loader.initiallyVisibleStructures_.begin(), | |
193 loader.initiallyVisibleStructures_.end(), | |
194 structureName); | |
195 std::vector<std::string>::iterator endIt = loader.initiallyVisibleStructures_.end(); | |
196 if (foundIt != endIt) | |
197 loader.structureVisibility_.at(i) = true; | |
198 else | |
199 loader.structureVisibility_.at(i) = false; | |
200 } | |
201 } | |
202 } | |
203 | |
204 // Some (admittedly invalid) Dicom files have empty values in the | 202 // Some (admittedly invalid) Dicom files have empty values in the |
205 // 0008,1155 tag. We try our best to cope with this. | 203 // 0008,1155 tag. We try our best to cope with this. |
204 // this is why we use `nonEmptyInstances` and not `instances` | |
206 std::set<std::string> instances; | 205 std::set<std::string> instances; |
207 std::set<std::string> nonEmptyInstances; | 206 std::set<std::string> nonEmptyInstances; |
207 | |
208 // this traverses the polygon collection for all structures and retrieve the SOPInstanceUID of | |
209 // the referenced instances | |
208 loader.content_->GetReferencedInstances(instances); | 210 loader.content_->GetReferencedInstances(instances); |
211 | |
209 for (std::set<std::string>::const_iterator | 212 for (std::set<std::string>::const_iterator |
210 it = instances.begin(); it != instances.end(); ++it) | 213 it = instances.begin(); it != instances.end(); ++it) |
211 { | 214 { |
212 std::string instance = Orthanc::Toolbox::StripSpaces(*it); | 215 std::string instance = Orthanc::Toolbox::StripSpaces(*it); |
213 if(instance != "") | 216 if(instance != "") |
214 nonEmptyInstances.insert(instance); | 217 nonEmptyInstances.insert(instance); |
215 } | 218 } |
216 | 219 |
217 loader.countReferencedInstances_ = | 220 loader.RetrieveReferencedSlices(nonEmptyInstances); |
218 static_cast<unsigned int>(nonEmptyInstances.size()); | 221 } |
219 | 222 |
220 for (std::set<std::string>::const_iterator | 223 void SetDefaultStructureVisibility() |
221 it = nonEmptyInstances.begin(); it != nonEmptyInstances.end(); ++it) | 224 { |
222 { | 225 DicomStructureSetLoader& loader = GetLoader<DicomStructureSetLoader>(); |
223 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); | 226 |
224 command->SetUri("/tools/lookup"); | 227 size_t structureCount = loader.content_->GetStructuresCount(); |
225 command->SetMethod(Orthanc::HttpMethod_Post); | 228 |
226 command->SetBody(*it); | 229 loader.structureVisibility_.resize(structureCount); |
227 command->AcquirePayload(new LookupInstance(loader, *it)); | 230 bool everythingVisible = false; |
228 Schedule(command.release()); | 231 if ((loader.initiallyVisibleStructures_.size() == 1) |
229 } | 232 && (loader.initiallyVisibleStructures_[0].size() == 1) |
230 } | 233 && (loader.initiallyVisibleStructures_[0][0] == '*')) |
234 { | |
235 everythingVisible = true; | |
236 } | |
237 | |
238 for (size_t i = 0; i < structureCount; ++i) | |
239 { | |
240 // if a single "*" string is supplied, this means we want everything | |
241 // to be visible... | |
242 if (everythingVisible) | |
243 { | |
244 loader.structureVisibility_.at(i) = true; | |
245 } | |
246 else | |
247 { | |
248 // otherwise, we only enable visibility for those structures whose | |
249 // names are mentioned in the initiallyVisibleStructures_ array | |
250 const std::string& structureName = loader.content_->GetStructureName(i); | |
251 | |
252 std::vector<std::string>::iterator foundIt = | |
253 std::find( | |
254 loader.initiallyVisibleStructures_.begin(), | |
255 loader.initiallyVisibleStructures_.end(), | |
256 structureName); | |
257 std::vector<std::string>::iterator endIt = loader.initiallyVisibleStructures_.end(); | |
258 if (foundIt != endIt) | |
259 loader.structureVisibility_.at(i) = true; | |
260 else | |
261 loader.structureVisibility_.at(i) = false; | |
262 } | |
263 } | |
264 } | |
265 | |
266 private: | |
267 | |
268 | |
231 }; | 269 }; |
232 | 270 |
233 | 271 |
234 class DicomStructureSetLoader::Slice : public IExtractedSlice | 272 class DicomStructureSetLoader::Slice : public IExtractedSlice |
235 { | 273 { |
345 , revision_(0) | 383 , revision_(0) |
346 , countProcessedInstances_(0) | 384 , countProcessedInstances_(0) |
347 , countReferencedInstances_(0) | 385 , countReferencedInstances_(0) |
348 , structuresReady_(false) | 386 , structuresReady_(false) |
349 { | 387 { |
350 } | 388 // the default handler to retrieve slice geometry is RestInstanceLookupHandler |
351 | 389 instanceLookupHandler_ = RestInstanceLookupHandler::Create(*this); |
390 } | |
352 | 391 |
353 boost::shared_ptr<OrthancStone::DicomStructureSetLoader> DicomStructureSetLoader::Create(OrthancStone::ILoadersContext& loadersContext) | 392 boost::shared_ptr<OrthancStone::DicomStructureSetLoader> DicomStructureSetLoader::Create(OrthancStone::ILoadersContext& loadersContext) |
354 { | 393 { |
355 boost::shared_ptr<DicomStructureSetLoader> obj( | 394 boost::shared_ptr<DicomStructureSetLoader> obj( |
356 new DicomStructureSetLoader( | 395 new DicomStructureSetLoader( |
357 loadersContext)); | 396 loadersContext)); |
358 obj->LoaderStateMachine::PostConstructor(); | 397 obj->LoaderStateMachine::PostConstructor(); |
359 return obj; | 398 return obj; |
360 | 399 } |
400 | |
401 void DicomStructureSetLoader::AddReferencedSlice(const Orthanc::DicomMap& dicom) | |
402 { | |
403 content_->AddReferencedSlice(dicom); | |
404 countProcessedInstances_ ++; | |
405 assert(countProcessedInstances_ <= countReferencedInstances_); | |
406 | |
407 revision_++; | |
408 SetStructuresUpdated(); | |
409 | |
410 if (countProcessedInstances_ == countReferencedInstances_) | |
411 { | |
412 // All the referenced instances have been loaded, finalize the RT-STRUCT | |
413 content_->CheckReferencedSlices(); | |
414 revision_++; | |
415 SetStructuresReady(); | |
416 } | |
417 } | |
418 | |
419 void DicomStructureSetLoader::RetrieveReferencedSlices(const std::set<std::string>& nonEmptyInstances) | |
420 { | |
421 // we set the number of referenced instances. This allows to know, in the method above, when we're done | |
422 countReferencedInstances_ = static_cast<unsigned int>(nonEmptyInstances.size()); | |
423 instanceLookupHandler_->RetrieveReferencedSlices(nonEmptyInstances); | |
361 } | 424 } |
362 | 425 |
363 void DicomStructureSetLoader::SetStructureDisplayState(size_t structureIndex, bool display) | 426 void DicomStructureSetLoader::SetStructureDisplayState(size_t structureIndex, bool display) |
364 { | 427 { |
365 structureVisibility_.at(structureIndex) = display; | 428 structureVisibility_.at(structureIndex) = display; |