Mercurial > hg > orthanc-stone
comparison Applications/Samples/WebAssembly/SingleFrameViewer/SingleFrameViewerApplication.h @ 1538:d1806b4e4839
moving OrthancStone/Samples/ as Applications/Samples/
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 11 Aug 2020 13:24:38 +0200 |
parents | OrthancStone/Samples/WebAssembly/SingleFrameViewer/SingleFrameViewerApplication.h@244ad1e4e76a |
children | 4cfdaf4ef3fe |
comparison
equal
deleted
inserted
replaced
1537:de8cf5859e84 | 1538:d1806b4e4839 |
---|---|
1 /** | |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
22 #pragma once | |
23 | |
24 #include "../../../Sources/Loaders/DicomResourcesLoader.h" | |
25 #include "../../../Sources/Loaders/ILoadersContext.h" | |
26 #include "../../../Sources/Loaders/SeriesFramesLoader.h" | |
27 #include "../../../Sources/Loaders/SeriesThumbnailsLoader.h" | |
28 #include "../../../Sources/Viewport/IViewport.h" | |
29 | |
30 #include <Compatibility.h> // For std::unique_ptr<> | |
31 | |
32 #include <boost/make_shared.hpp> | |
33 | |
34 | |
35 namespace OrthancStone | |
36 { | |
37 class Application : public ObserverBase<Application> | |
38 { | |
39 private: | |
40 ILoadersContext& context_; | |
41 boost::shared_ptr<IViewport> viewport_; | |
42 boost::shared_ptr<DicomResourcesLoader> dicomLoader_; | |
43 boost::shared_ptr<SeriesFramesLoader> framesLoader_; | |
44 | |
45 Application(ILoadersContext& context, | |
46 boost::shared_ptr<IViewport> viewport) : | |
47 context_(context), | |
48 viewport_(viewport) | |
49 { | |
50 } | |
51 | |
52 void Handle(const SeriesFramesLoader::FrameLoadedMessage& message) | |
53 { | |
54 LOG(INFO) << "Frame decoded! " | |
55 << message.GetImage().GetWidth() << "x" << message.GetImage().GetHeight() | |
56 << " " << Orthanc::EnumerationToString(message.GetImage().GetFormat()); | |
57 | |
58 std::unique_ptr<TextureBaseSceneLayer> layer( | |
59 message.GetInstanceParameters().CreateTexture(message.GetImage())); | |
60 layer->SetLinearInterpolation(true); | |
61 | |
62 { | |
63 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); | |
64 lock->GetController().GetScene().SetLayer(0, layer.release()); | |
65 lock->GetCompositor().FitContent(lock->GetController().GetScene()); | |
66 lock->Invalidate(); | |
67 } | |
68 } | |
69 | |
70 void Handle(const DicomResourcesLoader::SuccessMessage& message) | |
71 { | |
72 if (message.GetResources()->GetSize() != 1) | |
73 { | |
74 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
75 } | |
76 | |
77 //message.GetResources()->GetResource(0).Print(stdout); | |
78 | |
79 { | |
80 std::unique_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
81 SeriesFramesLoader::Factory f(*message.GetResources()); | |
82 | |
83 framesLoader_ = boost::dynamic_pointer_cast<SeriesFramesLoader>(f.Create(*lock)); | |
84 Register<SeriesFramesLoader::FrameLoadedMessage>(*framesLoader_, &Application::Handle); | |
85 | |
86 assert(message.HasUserPayload()); | |
87 const Orthanc::SingleValueObject<unsigned int>& payload = | |
88 dynamic_cast<const Orthanc::SingleValueObject<unsigned int>&>(message.GetUserPayload()); | |
89 | |
90 LOG(INFO) << "Loading pixel data of frame: " << payload.GetValue(); | |
91 framesLoader_->ScheduleLoadFrame( | |
92 0, message.GetDicomSource(), payload.GetValue(), | |
93 message.GetDicomSource().GetQualityCount() - 1 /* download best quality available */, | |
94 NULL); | |
95 } | |
96 } | |
97 | |
98 public: | |
99 static boost::shared_ptr<Application> Create(ILoadersContext& context, | |
100 boost::shared_ptr<IViewport> viewport) | |
101 { | |
102 boost::shared_ptr<Application> application(new Application(context, viewport)); | |
103 | |
104 { | |
105 std::unique_ptr<ILoadersContext::ILock> lock(context.Lock()); | |
106 application->dicomLoader_ = DicomResourcesLoader::Create(*lock); | |
107 } | |
108 | |
109 application->Register<DicomResourcesLoader::SuccessMessage>(*application->dicomLoader_, &Application::Handle); | |
110 | |
111 return application; | |
112 } | |
113 | |
114 void LoadOrthancFrame(const DicomSource& source, | |
115 const std::string& instanceId, | |
116 unsigned int frame) | |
117 { | |
118 std::unique_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
119 | |
120 dicomLoader_->ScheduleLoadOrthancResource( | |
121 boost::make_shared<LoadedDicomResources>(Orthanc::DICOM_TAG_SOP_INSTANCE_UID), | |
122 0, source, Orthanc::ResourceType_Instance, instanceId, | |
123 new Orthanc::SingleValueObject<unsigned int>(frame)); | |
124 } | |
125 | |
126 void LoadDicomWebFrame(const DicomSource& source, | |
127 const std::string& studyInstanceUid, | |
128 const std::string& seriesInstanceUid, | |
129 const std::string& sopInstanceUid, | |
130 unsigned int frame) | |
131 { | |
132 std::unique_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
133 | |
134 // We first must load the "/metadata" to know the number of frames | |
135 dicomLoader_->ScheduleGetDicomWeb( | |
136 boost::make_shared<LoadedDicomResources>(Orthanc::DICOM_TAG_SOP_INSTANCE_UID), 0, source, | |
137 "/studies/" + studyInstanceUid + "/series/" + seriesInstanceUid + "/instances/" + sopInstanceUid + "/metadata", | |
138 new Orthanc::SingleValueObject<unsigned int>(frame)); | |
139 } | |
140 | |
141 void FitContent() | |
142 { | |
143 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); | |
144 lock->GetCompositor().FitContent(lock->GetController().GetScene()); | |
145 lock->Invalidate(); | |
146 } | |
147 }; | |
148 | |
149 | |
150 | |
151 class IWebViewerLoadersObserver : public boost::noncopyable | |
152 { | |
153 public: | |
154 virtual ~IWebViewerLoadersObserver() | |
155 { | |
156 } | |
157 | |
158 virtual void SignalSeriesUpdated(LoadedDicomResources& series) = 0; | |
159 | |
160 virtual void SignalThumbnailLoaded(const std::string& studyInstanceUid, | |
161 const std::string& seriesInstanceUid, | |
162 SeriesThumbnailType type) = 0; | |
163 }; | |
164 | |
165 | |
166 class WebViewerLoaders : public ObserverBase<WebViewerLoaders> | |
167 { | |
168 private: | |
169 static const int PRIORITY_ADD_RESOURCES = 0; | |
170 static const int PRIORITY_THUMBNAILS = OracleScheduler::PRIORITY_LOW + 100; | |
171 | |
172 enum Type | |
173 { | |
174 Type_Orthanc = 1, | |
175 Type_DicomWeb = 2 | |
176 }; | |
177 | |
178 ILoadersContext& context_; | |
179 std::unique_ptr<IWebViewerLoadersObserver> observer_; | |
180 bool loadThumbnails_; | |
181 DicomSource source_; | |
182 std::set<std::string> scheduledSeries_; | |
183 std::set<std::string> scheduledThumbnails_; | |
184 std::set<std::string> scheduledStudies_; | |
185 boost::shared_ptr<LoadedDicomResources> loadedSeries_; | |
186 boost::shared_ptr<LoadedDicomResources> loadedStudies_; | |
187 boost::shared_ptr<DicomResourcesLoader> resourcesLoader_; | |
188 boost::shared_ptr<SeriesThumbnailsLoader> thumbnailsLoader_; | |
189 | |
190 WebViewerLoaders(ILoadersContext& context, | |
191 IWebViewerLoadersObserver* observer) : | |
192 context_(context), | |
193 observer_(observer), | |
194 loadThumbnails_(false) | |
195 { | |
196 loadedSeries_ = boost::make_shared<LoadedDicomResources>(Orthanc::DICOM_TAG_SERIES_INSTANCE_UID); | |
197 loadedStudies_ = boost::make_shared<LoadedDicomResources>(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID); | |
198 } | |
199 | |
200 static Orthanc::IDynamicObject* CreatePayload(Type type) | |
201 { | |
202 return new Orthanc::SingleValueObject<Type>(type); | |
203 } | |
204 | |
205 void HandleThumbnail(const SeriesThumbnailsLoader::SuccessMessage& message) | |
206 { | |
207 if (observer_.get() != NULL) | |
208 { | |
209 observer_->SignalThumbnailLoaded(message.GetStudyInstanceUid(), | |
210 message.GetSeriesInstanceUid(), | |
211 message.GetType()); | |
212 } | |
213 } | |
214 | |
215 void HandleLoadedResources(const DicomResourcesLoader::SuccessMessage& message) | |
216 { | |
217 LoadedDicomResources series(Orthanc::DICOM_TAG_SERIES_INSTANCE_UID); | |
218 | |
219 switch (dynamic_cast<const Orthanc::SingleValueObject<Type>&>(message.GetUserPayload()).GetValue()) | |
220 { | |
221 case Type_DicomWeb: | |
222 { | |
223 for (size_t i = 0; i < loadedSeries_->GetSize(); i++) | |
224 { | |
225 std::string study; | |
226 if (loadedSeries_->GetResource(i).LookupStringValue( | |
227 study, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) && | |
228 loadedStudies_->HasResource(study)) | |
229 { | |
230 Orthanc::DicomMap m; | |
231 m.Assign(loadedSeries_->GetResource(i)); | |
232 loadedStudies_->MergeResource(m, study); | |
233 series.AddResource(m); | |
234 } | |
235 } | |
236 | |
237 break; | |
238 } | |
239 | |
240 case Type_Orthanc: | |
241 { | |
242 for (size_t i = 0; i < message.GetResources()->GetSize(); i++) | |
243 { | |
244 series.AddResource(message.GetResources()->GetResource(i)); | |
245 } | |
246 | |
247 break; | |
248 } | |
249 | |
250 default: | |
251 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
252 } | |
253 | |
254 if (loadThumbnails_ && | |
255 (!source_.IsDicomWeb() || | |
256 source_.HasDicomWebRendered())) | |
257 { | |
258 for (size_t i = 0; i < series.GetSize(); i++) | |
259 { | |
260 std::string patientId, studyInstanceUid, seriesInstanceUid; | |
261 if (series.GetResource(i).LookupStringValue(patientId, Orthanc::DICOM_TAG_PATIENT_ID, false) && | |
262 series.GetResource(i).LookupStringValue(studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) && | |
263 series.GetResource(i).LookupStringValue(seriesInstanceUid, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false) && | |
264 scheduledThumbnails_.find(seriesInstanceUid) == scheduledThumbnails_.end()) | |
265 { | |
266 scheduledThumbnails_.insert(seriesInstanceUid); | |
267 thumbnailsLoader_->ScheduleLoadThumbnail(source_, patientId, studyInstanceUid, seriesInstanceUid); | |
268 } | |
269 } | |
270 } | |
271 | |
272 if (observer_.get() != NULL && | |
273 series.GetSize() > 0) | |
274 { | |
275 observer_->SignalSeriesUpdated(series); | |
276 } | |
277 } | |
278 | |
279 void HandleOrthancRestApi(const OrthancRestApiCommand::SuccessMessage& message) | |
280 { | |
281 Json::Value body; | |
282 message.ParseJsonBody(body); | |
283 | |
284 if (body.type() != Json::arrayValue) | |
285 { | |
286 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
287 } | |
288 else | |
289 { | |
290 for (Json::Value::ArrayIndex i = 0; i < body.size(); i++) | |
291 { | |
292 if (body[i].type() == Json::stringValue) | |
293 { | |
294 AddOrthancSeries(body[i].asString()); | |
295 } | |
296 else | |
297 { | |
298 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
299 } | |
300 } | |
301 } | |
302 } | |
303 | |
304 public: | |
305 static boost::shared_ptr<WebViewerLoaders> Create(ILoadersContext& context, | |
306 const DicomSource& source, | |
307 bool loadThumbnails, | |
308 IWebViewerLoadersObserver* observer) | |
309 { | |
310 boost::shared_ptr<WebViewerLoaders> application(new WebViewerLoaders(context, observer)); | |
311 application->source_ = source; | |
312 application->loadThumbnails_ = loadThumbnails; | |
313 | |
314 { | |
315 std::unique_ptr<ILoadersContext::ILock> lock(context.Lock()); | |
316 | |
317 application->resourcesLoader_ = DicomResourcesLoader::Create(*lock); | |
318 | |
319 { | |
320 SeriesThumbnailsLoader::Factory f; | |
321 f.SetPriority(PRIORITY_THUMBNAILS); | |
322 application->thumbnailsLoader_ = boost::dynamic_pointer_cast<SeriesThumbnailsLoader>(f.Create(*lock)); | |
323 } | |
324 | |
325 application->Register<OrthancRestApiCommand::SuccessMessage>( | |
326 lock->GetOracleObservable(), &WebViewerLoaders::HandleOrthancRestApi); | |
327 | |
328 application->Register<DicomResourcesLoader::SuccessMessage>( | |
329 *application->resourcesLoader_, &WebViewerLoaders::HandleLoadedResources); | |
330 | |
331 application->Register<SeriesThumbnailsLoader::SuccessMessage>( | |
332 *application->thumbnailsLoader_, &WebViewerLoaders::HandleThumbnail); | |
333 | |
334 lock->AddLoader(application); | |
335 } | |
336 | |
337 return application; | |
338 } | |
339 | |
340 void AddDicomAllSeries() | |
341 { | |
342 std::unique_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
343 | |
344 if (source_.IsDicomWeb()) | |
345 { | |
346 resourcesLoader_->ScheduleGetDicomWeb(loadedSeries_, PRIORITY_ADD_RESOURCES, source_, | |
347 "/series", CreatePayload(Type_DicomWeb)); | |
348 resourcesLoader_->ScheduleGetDicomWeb(loadedStudies_, PRIORITY_ADD_RESOURCES, source_, | |
349 "/studies", CreatePayload(Type_DicomWeb)); | |
350 } | |
351 else if (source_.IsOrthanc()) | |
352 { | |
353 std::unique_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
354 command->SetMethod(Orthanc::HttpMethod_Get); | |
355 command->SetUri("/series"); | |
356 lock->Schedule(GetSharedObserver(), PRIORITY_ADD_RESOURCES, command.release()); | |
357 } | |
358 else | |
359 { | |
360 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
361 } | |
362 } | |
363 | |
364 void AddDicomStudy(const std::string& studyInstanceUid) | |
365 { | |
366 // Avoid adding twice the same study | |
367 if (scheduledStudies_.find(studyInstanceUid) == scheduledStudies_.end()) | |
368 { | |
369 scheduledStudies_.insert(studyInstanceUid); | |
370 | |
371 if (source_.IsDicomWeb()) | |
372 { | |
373 Orthanc::DicomMap filter; | |
374 filter.SetValue(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, studyInstanceUid, false); | |
375 | |
376 std::set<Orthanc::DicomTag> tags; | |
377 | |
378 { | |
379 std::unique_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
380 | |
381 resourcesLoader_->ScheduleQido(loadedStudies_, PRIORITY_ADD_RESOURCES, source_, | |
382 Orthanc::ResourceType_Study, filter, tags, CreatePayload(Type_DicomWeb)); | |
383 | |
384 resourcesLoader_->ScheduleQido(loadedSeries_, PRIORITY_ADD_RESOURCES, source_, | |
385 Orthanc::ResourceType_Series, filter, tags, CreatePayload(Type_DicomWeb)); | |
386 } | |
387 } | |
388 else if (source_.IsOrthanc()) | |
389 { | |
390 std::unique_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
391 command->SetMethod(Orthanc::HttpMethod_Post); | |
392 command->SetUri("/tools/find"); | |
393 | |
394 Json::Value body; | |
395 body["Level"] = "Series"; | |
396 body["Query"] = Json::objectValue; | |
397 body["Query"]["StudyInstanceUID"] = studyInstanceUid; | |
398 command->SetBody(body); | |
399 | |
400 { | |
401 std::unique_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
402 lock->Schedule(GetSharedObserver(), PRIORITY_ADD_RESOURCES, command.release()); | |
403 } | |
404 } | |
405 else | |
406 { | |
407 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
408 } | |
409 } | |
410 } | |
411 | |
412 void AddDicomSeries(const std::string& studyInstanceUid, | |
413 const std::string& seriesInstanceUid) | |
414 { | |
415 std::set<Orthanc::DicomTag> tags; | |
416 | |
417 std::unique_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
418 | |
419 if (scheduledStudies_.find(studyInstanceUid) == scheduledStudies_.end()) | |
420 { | |
421 scheduledStudies_.insert(studyInstanceUid); | |
422 | |
423 if (source_.IsDicomWeb()) | |
424 { | |
425 Orthanc::DicomMap filter; | |
426 filter.SetValue(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, studyInstanceUid, false); | |
427 | |
428 resourcesLoader_->ScheduleQido(loadedStudies_, PRIORITY_ADD_RESOURCES, source_, | |
429 Orthanc::ResourceType_Study, filter, tags, CreatePayload(Type_DicomWeb)); | |
430 } | |
431 } | |
432 | |
433 if (scheduledSeries_.find(seriesInstanceUid) == scheduledSeries_.end()) | |
434 { | |
435 scheduledSeries_.insert(seriesInstanceUid); | |
436 | |
437 if (source_.IsDicomWeb()) | |
438 { | |
439 Orthanc::DicomMap filter; | |
440 filter.SetValue(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, studyInstanceUid, false); | |
441 filter.SetValue(Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, seriesInstanceUid, false); | |
442 | |
443 resourcesLoader_->ScheduleQido(loadedSeries_, PRIORITY_ADD_RESOURCES, source_, | |
444 Orthanc::ResourceType_Series, filter, tags, CreatePayload(Type_DicomWeb)); | |
445 } | |
446 else if (source_.IsOrthanc()) | |
447 { | |
448 std::unique_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
449 command->SetMethod(Orthanc::HttpMethod_Post); | |
450 command->SetUri("/tools/find"); | |
451 | |
452 Json::Value body; | |
453 body["Level"] = "Series"; | |
454 body["Query"] = Json::objectValue; | |
455 body["Query"]["StudyInstanceUID"] = studyInstanceUid; | |
456 body["Query"]["SeriesInstanceUID"] = seriesInstanceUid; | |
457 command->SetBody(body); | |
458 | |
459 lock->Schedule(GetSharedObserver(), PRIORITY_ADD_RESOURCES, command.release()); | |
460 } | |
461 else | |
462 { | |
463 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
464 } | |
465 } | |
466 } | |
467 | |
468 void AddOrthancStudy(const std::string& orthancId) | |
469 { | |
470 if (source_.IsOrthanc()) | |
471 { | |
472 std::unique_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
473 resourcesLoader_->ScheduleLoadOrthancResources( | |
474 loadedSeries_, PRIORITY_ADD_RESOURCES, source_, | |
475 Orthanc::ResourceType_Study, orthancId, Orthanc::ResourceType_Series, | |
476 CreatePayload(Type_Orthanc)); | |
477 } | |
478 else | |
479 { | |
480 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType, | |
481 "Only applicable to Orthanc DICOM sources"); | |
482 } | |
483 } | |
484 | |
485 void AddOrthancSeries(const std::string& orthancId) | |
486 { | |
487 if (source_.IsOrthanc()) | |
488 { | |
489 std::unique_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
490 resourcesLoader_->ScheduleLoadOrthancResource( | |
491 loadedSeries_, PRIORITY_ADD_RESOURCES, | |
492 source_, Orthanc::ResourceType_Series, orthancId, | |
493 CreatePayload(Type_Orthanc)); | |
494 } | |
495 else | |
496 { | |
497 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType, | |
498 "Only applicable to Orthanc DICOM sources"); | |
499 } | |
500 } | |
501 }; | |
502 } |