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 }