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