comparison Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp @ 2091:35de56fb6f89 dicom-sr

support of referenced instances
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 08 Nov 2023 10:11:21 +0100
parents c2dfc253bd04
children b7d4d288459a
comparison
equal deleted inserted replaced
2090:c2dfc253bd04 2091:35de56fb6f89
203 203
204 static const unsigned int DEFAULT_CINE_RATE = 30; 204 static const unsigned int DEFAULT_CINE_RATE = 30;
205 205
206 206
207 207
208 class IFramesCollection : public boost::noncopyable
209 {
210 public:
211 virtual ~IFramesCollection()
212 {
213 }
214
215 // TODO - MUST BE REMOVED
216 virtual std::string GetStudyInstanceUid() const = 0;
217
218 // TODO - MUST BE REMOVED
219 virtual std::string GetSeriesInstanceUid() const = 0;
220
221 virtual size_t GetFramesCount() const = 0;
222
223 virtual const OrthancStone::DicomInstanceParameters& GetInstanceOfFrame(size_t frameIndex) const = 0;
224
225 virtual unsigned int GetFrameNumberInInstance(size_t frameIndex) const = 0;
226
227 // TODO - Could be removed
228 virtual OrthancStone::CoordinateSystem3D GetFrameGeometry(size_t frameIndex) const = 0;
229
230 virtual bool LookupFrame(size_t& frameIndex,
231 const std::string& sopInstanceUid,
232 unsigned int frameNumber) const = 0;
233
234 virtual bool FindClosestFrame(size_t& frameIndex,
235 const OrthancStone::Vector& point,
236 double maximumDistance) const = 0;
237 };
238
239
240 class SortedFramesCollection : public IFramesCollection
241 {
242 private:
243 std::unique_ptr<OrthancStone::SortedFrames> frames_;
244
245 public:
246 SortedFramesCollection(OrthancStone::SortedFrames* frames)
247 {
248 if (frames == NULL)
249 {
250 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
251 }
252 else
253 {
254 frames_.reset(frames);
255 }
256 }
257
258 virtual std::string GetStudyInstanceUid() const ORTHANC_OVERRIDE
259 {
260 return frames_->GetStudyInstanceUid();
261 }
262
263 virtual std::string GetSeriesInstanceUid() const ORTHANC_OVERRIDE
264 {
265 return frames_->GetSeriesInstanceUid();
266 }
267
268 virtual size_t GetFramesCount() const ORTHANC_OVERRIDE
269 {
270 return frames_->GetFramesCount();
271 }
272
273 const OrthancStone::DicomInstanceParameters& GetInstanceOfFrame(size_t frameIndex) const ORTHANC_OVERRIDE
274 {
275 return frames_->GetInstanceOfFrame(frameIndex);
276 }
277
278 virtual unsigned int GetFrameNumberInInstance(size_t frameIndex) const ORTHANC_OVERRIDE
279 {
280 return frames_->GetFrameNumberInInstance(frameIndex);
281 }
282
283 virtual OrthancStone::CoordinateSystem3D GetFrameGeometry(size_t frameIndex) const ORTHANC_OVERRIDE
284 {
285 return frames_->GetFrameGeometry(frameIndex);
286 }
287
288 virtual bool LookupFrame(size_t& frameIndex,
289 const std::string& sopInstanceUid,
290 unsigned int frameNumber) const ORTHANC_OVERRIDE
291 {
292 return frames_->LookupFrame(frameIndex, sopInstanceUid, frameNumber);
293 }
294
295 virtual bool FindClosestFrame(size_t& frameIndex,
296 const OrthancStone::Vector& point,
297 double maximumDistance) const ORTHANC_OVERRIDE
298 {
299 return frames_->FindClosestFrame(frameIndex, point, maximumDistance);
300 };
301 };
302
303
304 class DicomStructuredReportFrames : public IFramesCollection
305 {
306 private:
307 class Frame : public boost::noncopyable
308 {
309 private:
310 OrthancStone::DicomStructuredReport::Frame info_;
311 Orthanc::DicomMap tags_;
312 std::unique_ptr<OrthancStone::DicomInstanceParameters> parameters_;
313
314 public:
315 Frame(const OrthancStone::DicomStructuredReport::Frame& info,
316 const OrthancStone::LoadedDicomResources& instances) :
317 info_(info)
318 {
319 if (!instances.LookupResource(tags_, info.GetSopInstanceUid()))
320 {
321 throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem);
322 }
323
324 parameters_.reset(new OrthancStone::DicomInstanceParameters(tags_));
325 }
326
327 const OrthancStone::DicomStructuredReport::Frame& GetInformation() const
328 {
329 return info_;
330 }
331
332 const Orthanc::DicomMap& GetTags() const
333 {
334 return tags_;
335 }
336
337 const OrthancStone::DicomInstanceParameters& GetParameters() const
338 {
339 return *parameters_;
340 }
341 };
342
343 std::string studyInstanceUid_;
344 std::string seriesInstanceUid_;
345 std::vector<Frame*> frames_;
346
347 void Finalize()
348 {
349 for (size_t i = 0; i < frames_.size(); i++)
350 {
351 if (frames_[i] != NULL)
352 {
353 delete frames_[i];
354 }
355 }
356
357 frames_.clear();
358 }
359
360 const Frame& GetFrame(size_t index) const
361 {
362 if (index >= frames_.size())
363 {
364 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
365 }
366 else
367 {
368 assert(frames_[index] != NULL);
369 return *frames_[index];
370 }
371 }
372
373 public:
374 DicomStructuredReportFrames(OrthancStone::DicomStructuredReport& sr,
375 const OrthancStone::LoadedDicomResources& instances) :
376 studyInstanceUid_(sr.GetStudyInstanceUid()),
377 seriesInstanceUid_(sr.GetSeriesInstanceUid())
378 {
379 std::list<OrthancStone::DicomStructuredReport::Frame> tmp;
380 sr.ExportOrderedFrames(tmp);
381
382 frames_.reserve(tmp.size());
383 for (std::list<OrthancStone::DicomStructuredReport::Frame>::const_iterator
384 it = tmp.begin(); it != tmp.end(); ++it)
385 {
386 try
387 {
388 frames_.push_back(new Frame(*it, instances));
389 }
390 catch (Orthanc::OrthancException&)
391 {
392 // An instance is not loaded yet
393 Finalize();
394 throw;
395 }
396 }
397 }
398
399 virtual ~DicomStructuredReportFrames()
400 {
401 Finalize();
402 }
403
404 virtual std::string GetStudyInstanceUid() const ORTHANC_OVERRIDE
405 {
406 return studyInstanceUid_;
407 }
408
409 virtual std::string GetSeriesInstanceUid() const ORTHANC_OVERRIDE
410 {
411 return seriesInstanceUid_;
412 }
413
414 virtual size_t GetFramesCount() const ORTHANC_OVERRIDE
415 {
416 return frames_.size();
417 }
418
419 virtual const OrthancStone::DicomInstanceParameters& GetInstanceOfFrame(size_t frameIndex) const ORTHANC_OVERRIDE
420 {
421 return GetFrame(frameIndex).GetParameters();
422 }
423
424 virtual unsigned int GetFrameNumberInInstance(size_t frameIndex) const ORTHANC_OVERRIDE
425 {
426 return GetFrame(frameIndex).GetInformation().GetFrameNumber();
427 }
428
429 virtual OrthancStone::CoordinateSystem3D GetFrameGeometry(size_t frameIndex) const ORTHANC_OVERRIDE
430 {
431 return GetFrame(frameIndex).GetParameters().GetFrameGeometry(GetFrameNumberInInstance(frameIndex));
432 }
433
434 virtual bool LookupFrame(size_t& frameIndex,
435 const std::string& sopInstanceUid,
436 unsigned int frameNumber) const ORTHANC_OVERRIDE
437 {
438 // TODO - Could be speeded up with an additional index
439 for (size_t i = 0; i < frames_.size(); i++)
440 {
441 if (frames_[i]->GetInformation().GetSopInstanceUid() == sopInstanceUid &&
442 frames_[i]->GetInformation().GetFrameNumber() == frameNumber)
443 {
444 frameIndex = i;
445 return true;
446 }
447 }
448
449 return false;
450 }
451
452 virtual bool FindClosestFrame(size_t& frameIndex,
453 const OrthancStone::Vector& point,
454 double maximumDistance) const ORTHANC_OVERRIDE
455 {
456 bool found = false;
457
458 for (size_t i = 0; i < GetFramesCount(); i++)
459 {
460 double distance = GetFrameGeometry(i).ComputeDistance(point);
461 if (distance <= maximumDistance)
462 {
463 found = true;
464 frameIndex = i;
465 }
466 }
467
468 return found;
469 }
470 };
471
472
208 class VirtualSeries : public boost::noncopyable 473 class VirtualSeries : public boost::noncopyable
209 { 474 {
210 private: 475 private:
211 class Item : public boost::noncopyable 476 class Item : public boost::noncopyable
212 { 477 {
331 std::unique_ptr<IObserver> observer_; 596 std::unique_ptr<IObserver> observer_;
332 OrthancStone::DicomSource source_; 597 OrthancStone::DicomSource source_;
333 size_t pending_; 598 size_t pending_;
334 boost::shared_ptr<OrthancStone::LoadedDicomResources> studies_; 599 boost::shared_ptr<OrthancStone::LoadedDicomResources> studies_;
335 boost::shared_ptr<OrthancStone::LoadedDicomResources> series_; 600 boost::shared_ptr<OrthancStone::LoadedDicomResources> series_;
601 boost::shared_ptr<OrthancStone::LoadedDicomResources> instances_;
336 boost::shared_ptr<OrthancStone::DicomResourcesLoader> resourcesLoader_; 602 boost::shared_ptr<OrthancStone::DicomResourcesLoader> resourcesLoader_;
337 boost::shared_ptr<OrthancStone::SeriesThumbnailsLoader> thumbnailsLoader_; 603 boost::shared_ptr<OrthancStone::SeriesThumbnailsLoader> thumbnailsLoader_;
338 boost::shared_ptr<OrthancStone::SeriesMetadataLoader> metadataLoader_; 604 boost::shared_ptr<OrthancStone::SeriesMetadataLoader> metadataLoader_;
339 std::set<std::string> scheduledVirtualSeriesThumbnails_; 605 std::set<std::string> scheduledVirtualSeriesThumbnails_;
340 VirtualSeries virtualSeries_; 606 VirtualSeries virtualSeries_;
341 std::vector<std::string> skipSeriesFromModalities_; 607 std::vector<std::string> skipSeriesFromModalities_;
342 608
609 typedef std::map<std::string, boost::shared_ptr<OrthancStone::DicomStructuredReport> > StructuredReports;
610 StructuredReports structuredReports_;
611
343 explicit ResourcesLoader(OrthancStone::ILoadersContext& context, 612 explicit ResourcesLoader(OrthancStone::ILoadersContext& context,
344 const OrthancStone::DicomSource& source) : 613 const OrthancStone::DicomSource& source) :
345 context_(context), 614 context_(context),
346 source_(source), 615 source_(source),
347 pending_(0), 616 pending_(0),
348 studies_(new OrthancStone::LoadedDicomResources(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID)), 617 studies_(new OrthancStone::LoadedDicomResources(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID)),
349 series_(new OrthancStone::LoadedDicomResources(Orthanc::DICOM_TAG_SERIES_INSTANCE_UID)) 618 series_(new OrthancStone::LoadedDicomResources(Orthanc::DICOM_TAG_SERIES_INSTANCE_UID)),
619 instances_(new OrthancStone::LoadedDicomResources(Orthanc::DICOM_TAG_SOP_INSTANCE_UID))
350 { 620 {
351 } 621 }
352 622
353 void Handle(const OrthancStone::DicomResourcesLoader::SuccessMessage& message) 623 void Handle(const OrthancStone::DicomResourcesLoader::SuccessMessage& message)
354 { 624 {
358 OrthancStone::LoadedDicomResources& dicom = *message.GetResources(); 628 OrthancStone::LoadedDicomResources& dicom = *message.GetResources();
359 629
360 LOG(INFO) << "resources loaded: " << dicom.GetSize() 630 LOG(INFO) << "resources loaded: " << dicom.GetSize()
361 << ", " << Orthanc::EnumerationToString(payload.GetValue()); 631 << ", " << Orthanc::EnumerationToString(payload.GetValue());
362 632
363 std::vector<std::string> seriesIdsToRemove;
364
365 if (payload.GetValue() == Orthanc::ResourceType_Series) 633 if (payload.GetValue() == Orthanc::ResourceType_Series)
366 { 634 {
367 // the 'dicom' var is actually equivalent to the 'series_' member in this case 635 // the 'dicom' var is actually equivalent to the 'series_' member in this case
636
637 std::vector<std::string> seriesIdsToRemove;
368 638
369 for (size_t i = 0; i < dicom.GetSize(); i++) 639 for (size_t i = 0; i < dicom.GetSize(); i++)
370 { 640 {
371 std::string studyInstanceUid, seriesInstanceUid, modality; 641 std::string studyInstanceUid, seriesInstanceUid, modality;
372 if (dicom.GetResource(i).LookupStringValue( 642 if (dicom.GetResource(i).LookupStringValue(
393 { 663 {
394 LOG(INFO) << "series to hide: " << seriesIdsToRemove[i]; 664 LOG(INFO) << "series to hide: " << seriesIdsToRemove[i];
395 dicom.RemoveResource(seriesIdsToRemove[i]); 665 dicom.RemoveResource(seriesIdsToRemove[i]);
396 } 666 }
397 } 667 }
398 668 else if (payload.GetValue() == Orthanc::ResourceType_Instance)
399 if (pending_ == 0) 669 {
400 { 670 // This occurs if loading DICOM-SR
401 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 671
402 } 672 // TODO - Hide/show DICOM-SR once they have been loaded
403 else 673 }
404 { 674
405 pending_ --; 675 if (payload.GetValue() == Orthanc::ResourceType_Study ||
406 if (pending_ == 0 && 676 payload.GetValue() == Orthanc::ResourceType_Series)
407 observer_.get() != NULL) 677 {
408 { 678 if (pending_ == 0)
409 observer_->SignalResourcesLoaded(); 679 {
680 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
681 }
682 else
683 {
684 pending_ --;
685 if (pending_ == 0 &&
686 observer_.get() != NULL)
687 {
688 observer_->SignalResourcesLoaded();
689 }
410 } 690 }
411 } 691 }
412 } 692 }
413 693
414 void Handle(const OrthancStone::SeriesThumbnailsLoader::SuccessMessage& message) 694 void Handle(const OrthancStone::SeriesThumbnailsLoader::SuccessMessage& message)
432 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock()); 712 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock());
433 lock->Schedule( 713 lock->Schedule(
434 GetSharedObserver(), PRIORITY_NORMAL, OrthancStone::ParseDicomFromWadoCommand::Create( 714 GetSharedObserver(), PRIORITY_NORMAL, OrthancStone::ParseDicomFromWadoCommand::Create(
435 source_, message.GetStudyInstanceUid(), message.GetSeriesInstanceUid(), sopInstanceUid, 715 source_, message.GetStudyInstanceUid(), message.GetSeriesInstanceUid(), sopInstanceUid,
436 false /* no transcoding */, Orthanc::DicomTransferSyntax_LittleEndianExplicit /* dummy value */, 716 false /* no transcoding */, Orthanc::DicomTransferSyntax_LittleEndianExplicit /* dummy value */,
437 new InstanceInfo(message.GetStudyInstanceUid(), message.GetSeriesInstanceUid(), OrthancStone::SopClassUid_ComprehensiveSR))); 717 new InstanceInfo(message.GetStudyInstanceUid(), message.GetSeriesInstanceUid(), sopInstanceUid, Action_ComprehensiveSR)));
438 return; 718 return;
439 } 719 }
440 } 720 }
441 721
442 if (observer_.get() != NULL) 722 if (observer_.get() != NULL)
482 series_, PRIORITY_HIGH, source_, Orthanc::ResourceType_Series, filter, tags, 762 series_, PRIORITY_HIGH, source_, Orthanc::ResourceType_Series, filter, tags,
483 new Orthanc::SingleValueObject<Orthanc::ResourceType>(Orthanc::ResourceType_Series)); 763 new Orthanc::SingleValueObject<Orthanc::ResourceType>(Orthanc::ResourceType_Series));
484 764
485 pending_ += 2; 765 pending_ += 2;
486 } 766 }
487 767
768
769 enum Action
770 {
771 Action_Pdf,
772 Action_ComprehensiveSR,
773 Action_ReferencedInstance
774 };
775
488 776
489 class InstanceInfo : public Orthanc::IDynamicObject 777 class InstanceInfo : public Orthanc::IDynamicObject
490 { 778 {
491 private: 779 private:
492 std::string studyInstanceUid_; 780 std::string studyInstanceUid_;
493 std::string seriesInstanceUid_; 781 std::string seriesInstanceUid_;
494 OrthancStone::SopClassUid sopClassUid_; 782 std::string sopInstanceUid_;
783 Action action_;
495 784
496 public: 785 public:
497 InstanceInfo(const std::string& studyInstanceUid, 786 InstanceInfo(const std::string& studyInstanceUid,
498 const std::string& seriesInstanceUid, 787 const std::string& seriesInstanceUid,
499 OrthancStone::SopClassUid sopClassUid) : 788 const std::string& sopInstanceUid,
789 Action action) :
500 studyInstanceUid_(studyInstanceUid), 790 studyInstanceUid_(studyInstanceUid),
501 seriesInstanceUid_(seriesInstanceUid), 791 seriesInstanceUid_(seriesInstanceUid),
502 sopClassUid_(sopClassUid) 792 sopInstanceUid_(sopInstanceUid),
793 action_(action)
503 { 794 {
504 } 795 }
505 796
506 const std::string& GetStudyInstanceUid() const 797 const std::string& GetStudyInstanceUid() const
507 { 798 {
511 const std::string& GetSeriesInstanceUid() const 802 const std::string& GetSeriesInstanceUid() const
512 { 803 {
513 return seriesInstanceUid_; 804 return seriesInstanceUid_;
514 } 805 }
515 806
516 OrthancStone::SopClassUid GetSopClassUid() const 807 const std::string& GetSopInstanceUid() const
517 { 808 {
518 return sopClassUid_; 809 return sopInstanceUid_;
810 }
811
812 Action GetAction() const
813 {
814 return action_;
519 } 815 }
520 }; 816 };
521 817
522 818
523 void Handle(const OrthancStone::ParseDicomSuccessMessage& message) 819 void Handle(const OrthancStone::ParseDicomSuccessMessage& message)
524 { 820 {
525 const InstanceInfo& info = dynamic_cast<const InstanceInfo&>(message.GetOrigin().GetPayload()); 821 const InstanceInfo& info = dynamic_cast<const InstanceInfo&>(message.GetOrigin().GetPayload());
526 822
527 if (observer_.get() != NULL) 823 if (observer_.get() != NULL)
528 { 824 {
529 switch (info.GetSopClassUid()) 825 switch (info.GetAction())
530 { 826 {
531 case OrthancStone::SopClassUid_EncapsulatedPdf: 827 case Action_Pdf:
532 { 828 {
533 std::string pdf; 829 std::string pdf;
534 if (message.GetDicom().ExtractPdf(pdf)) 830 if (message.GetDicom().ExtractPdf(pdf))
535 { 831 {
536 observer_->SignalSeriesPdfLoaded(info.GetStudyInstanceUid(), info.GetSeriesInstanceUid(), pdf); 832 observer_->SignalSeriesPdfLoaded(info.GetStudyInstanceUid(), info.GetSeriesInstanceUid(), pdf);
541 } 837 }
542 838
543 break; 839 break;
544 } 840 }
545 841
546 case OrthancStone::SopClassUid_ComprehensiveSR: 842 case Action_ComprehensiveSR:
547 { 843 {
548 try 844 try
549 { 845 {
550 OrthancStone::DicomStructuredReport sr(message.GetDicom()); 846 boost::shared_ptr<OrthancStone::DicomStructuredReport> sr(new OrthancStone::DicomStructuredReport(message.GetDicom()));
551 847
552 std::list<OrthancStone::DicomStructuredReport::Frame> frames; 848 for (size_t i = 0; i < sr->GetReferencedInstancesCount(); i++)
553 sr.ExportOrderedFrames(frames);
554
555 for (std::list<OrthancStone::DicomStructuredReport::Frame>::const_iterator
556 it = frames.begin(); it != frames.end(); ++it)
557 { 849 {
558 LOG(ERROR) << "YOU " << it->GetSopInstanceUid() << " " << it->GetFrameNumber(); 850 std::string studyInstanceUid;
851 std::string seriesInstanceUid;
852 std::string sopInstanceUid;
853 std::string sopClassUid;
854 sr->GetReferencedInstance(studyInstanceUid, seriesInstanceUid, sopInstanceUid, sopClassUid, i);
855
856 Orthanc::DicomMap filter;
857 filter.SetValue(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, studyInstanceUid, false);
858 filter.SetValue(Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, seriesInstanceUid, false);
859 filter.SetValue(Orthanc::DICOM_TAG_SOP_INSTANCE_UID, sopInstanceUid, false);
860
861 std::set<Orthanc::DicomTag> tags;
862
863 resourcesLoader_->ScheduleQido(
864 instances_, PRIORITY_NORMAL, source_, Orthanc::ResourceType_Instance, filter, tags,
865 new Orthanc::SingleValueObject<Orthanc::ResourceType>(Orthanc::ResourceType_Instance));
559 } 866 }
867
868 structuredReports_[info.GetSeriesInstanceUid()] = sr;
560 869
561 if (observer_.get() != NULL) 870 if (observer_.get() != NULL)
562 { 871 {
563 observer_->SignalSeriesMetadataLoaded( 872 observer_->SignalSeriesMetadataLoaded(
564 info.GetStudyInstanceUid(), info.GetSeriesInstanceUid()); 873 info.GetStudyInstanceUid(), info.GetSeriesInstanceUid());
803 { 1112 {
804 return false; 1113 return false;
805 } 1114 }
806 } 1115 }
807 1116
808 bool SortSeriesFrames(OrthancStone::SortedFrames& target, 1117 IFramesCollection* GetSeriesFrames(const std::string& seriesInstanceUid) const
809 const std::string& seriesInstanceUid) const
810 { 1118 {
811 OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid); 1119 OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
812 1120
813 if (accessor.IsComplete()) 1121 if (accessor.IsComplete())
814 { 1122 {
815 target.Clear(); 1123 StructuredReports::const_iterator sr = structuredReports_.find(seriesInstanceUid);
1124 if (sr != structuredReports_.end())
1125 {
1126 assert(sr->second != NULL);
1127
1128 try
1129 {
1130 return new DicomStructuredReportFrames(*sr->second, *instances_);
1131 }
1132 catch (Orthanc::OrthancException&)
1133 {
1134 // TODO
1135 LOG(ERROR) << "Instances of the DICOM-SR are not available yet";
1136 return NULL;
1137 }
1138 }
1139
1140 std::unique_ptr<OrthancStone::SortedFrames> target(new OrthancStone::SortedFrames);
1141 target->Clear();
816 1142
817 for (size_t i = 0; i < accessor.GetInstancesCount(); i++) 1143 for (size_t i = 0; i < accessor.GetInstancesCount(); i++)
818 { 1144 {
819 target.AddInstance(accessor.GetInstance(i)); 1145 target->AddInstance(accessor.GetInstance(i));
820 } 1146 }
821 1147
822 target.Sort(); 1148 target->Sort();
823 1149
824 return true; 1150 return new SortedFramesCollection(target.release());
825 } 1151 }
826 else 1152 else
827 { 1153 {
828 return false; 1154 return NULL;
829 } 1155 }
830 } 1156 }
831 1157
832 bool SortVirtualSeriesFrames(OrthancStone::SortedFrames& target, 1158 IFramesCollection* GetVirtualSeriesFrames(const std::string& virtualSeriesId) const
833 const std::string& virtualSeriesId) const
834 { 1159 {
835 const std::string& seriesInstanceUid = virtualSeries_.GetSeriesInstanceUid(virtualSeriesId); 1160 const std::string& seriesInstanceUid = virtualSeries_.GetSeriesInstanceUid(virtualSeriesId);
836 1161
837 OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid); 1162 OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
838 1163
839 if (accessor.IsComplete()) 1164 if (accessor.IsComplete())
840 { 1165 {
841 const std::list<std::string>& sopInstanceUids = virtualSeries_.GetSopInstanceUids(virtualSeriesId); 1166 const std::list<std::string>& sopInstanceUids = virtualSeries_.GetSopInstanceUids(virtualSeriesId);
842 1167
843 target.Clear(); 1168 std::unique_ptr<OrthancStone::SortedFrames> target(new OrthancStone::SortedFrames);
1169 target->Clear();
844 1170
845 for (std::list<std::string>::const_iterator 1171 for (std::list<std::string>::const_iterator
846 it = sopInstanceUids.begin(); it != sopInstanceUids.end(); ++it) 1172 it = sopInstanceUids.begin(); it != sopInstanceUids.end(); ++it)
847 { 1173 {
848 Orthanc::DicomMap instance; 1174 Orthanc::DicomMap instance;
849 if (accessor.LookupInstance(instance, *it)) 1175 if (accessor.LookupInstance(instance, *it))
850 { 1176 {
851 target.AddInstance(instance); 1177 target->AddInstance(instance);
852 } 1178 }
853 else 1179 else
854 { 1180 {
855 LOG(ERROR) << "Missing instance: " << *it; 1181 LOG(ERROR) << "Missing instance: " << *it;
856 } 1182 }
857 } 1183 }
858 1184
859 target.Sort(); 1185 target->Sort();
860 return true; 1186
1187 return new SortedFramesCollection(target.release());
861 } 1188 }
862 else 1189 else
863 { 1190 {
864 return false; 1191 return NULL;
865 } 1192 }
866 } 1193 }
867 1194
868 size_t GetSeriesNumberOfFrames(const std::string& seriesInstanceUid) const 1195 size_t GetSeriesNumberOfFrames(const std::string& seriesInstanceUid) const
869 { 1196 {
922 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock()); 1249 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock());
923 lock->Schedule( 1250 lock->Schedule(
924 GetSharedObserver(), PRIORITY_NORMAL, OrthancStone::ParseDicomFromWadoCommand::Create( 1251 GetSharedObserver(), PRIORITY_NORMAL, OrthancStone::ParseDicomFromWadoCommand::Create(
925 source_, studyInstanceUid, seriesInstanceUid, sopInstanceUid, 1252 source_, studyInstanceUid, seriesInstanceUid, sopInstanceUid,
926 false /* no transcoding */, Orthanc::DicomTransferSyntax_LittleEndianExplicit /* dummy value */, 1253 false /* no transcoding */, Orthanc::DicomTransferSyntax_LittleEndianExplicit /* dummy value */,
927 new InstanceInfo(studyInstanceUid, seriesInstanceUid, OrthancStone::SopClassUid_EncapsulatedPdf))); 1254 new InstanceInfo(studyInstanceUid, seriesInstanceUid, sopInstanceUid, Action_Pdf)));
928
929 return; 1255 return;
930 } 1256 }
931 } 1257 }
932 1258
933 LOG(WARNING) << "Series without a PDF: " << seriesInstanceUid; 1259 LOG(WARNING) << "Series without a PDF: " << seriesInstanceUid;
1619 }; 1945 };
1620 }; 1946 };
1621 1947
1622 1948
1623 1949
1624
1625 class ViewerViewport : public OrthancStone::ObserverBase<ViewerViewport> 1950 class ViewerViewport : public OrthancStone::ObserverBase<ViewerViewport>
1626 { 1951 {
1627 public: 1952 public:
1628 class IObserver : public boost::noncopyable 1953 class IObserver : public boost::noncopyable
1629 { 1954 {
1901 2226
1902 2227
1903 class SetFullDicomFrame : public ICommand 2228 class SetFullDicomFrame : public ICommand
1904 { 2229 {
1905 private: 2230 private:
2231 std::string studyInstanceUid_;
2232 std::string seriesInstanceUid_;
1906 std::string sopInstanceUid_; 2233 std::string sopInstanceUid_;
1907 unsigned int frameNumber_; 2234 unsigned int frameNumber_;
1908 int priority_; 2235 int priority_;
1909 bool isPrefetch_; 2236 bool isPrefetch_;
1910 bool serverSideTranscoding_; 2237 bool serverSideTranscoding_;
1911 2238
1912 public: 2239 public:
1913 SetFullDicomFrame(boost::shared_ptr<ViewerViewport> viewport, 2240 SetFullDicomFrame(boost::shared_ptr<ViewerViewport> viewport,
2241 const std::string& studyInstanceUid,
2242 const std::string& seriesInstanceUid,
1914 const std::string& sopInstanceUid, 2243 const std::string& sopInstanceUid,
1915 unsigned int frameNumber, 2244 unsigned int frameNumber,
1916 int priority, 2245 int priority,
1917 bool isPrefetch, 2246 bool isPrefetch,
1918 bool serverSideTranscoding) : 2247 bool serverSideTranscoding) :
1919 ICommand(viewport), 2248 ICommand(viewport),
2249 studyInstanceUid_(studyInstanceUid),
2250 seriesInstanceUid_(seriesInstanceUid),
1920 sopInstanceUid_(sopInstanceUid), 2251 sopInstanceUid_(sopInstanceUid),
1921 frameNumber_(frameNumber), 2252 frameNumber_(frameNumber),
1922 priority_(priority), 2253 priority_(priority),
1923 isPrefetch_(isPrefetch), 2254 isPrefetch_(isPrefetch),
1924 serverSideTranscoding_(serverSideTranscoding) 2255 serverSideTranscoding_(serverSideTranscoding)
1940 if (!serverSideTranscoding_) 2271 if (!serverSideTranscoding_)
1941 { 2272 {
1942 // If we haven't tried server-side rendering yet, give it a try 2273 // If we haven't tried server-side rendering yet, give it a try
1943 LOG(INFO) << "Switching to server-side transcoding"; 2274 LOG(INFO) << "Switching to server-side transcoding";
1944 GetViewport().serverSideTranscoding_ = true; 2275 GetViewport().serverSideTranscoding_ = true;
1945 GetViewport().ScheduleLoadFullDicomFrame(sopInstanceUid_, frameNumber_, priority_, isPrefetch_); 2276 GetViewport().ScheduleLoadFullDicomFrame(studyInstanceUid_, seriesInstanceUid_, sopInstanceUid_, frameNumber_, priority_, isPrefetch_);
1946 } 2277 }
1947 return; 2278 return;
1948 } 2279 }
1949 else 2280 else
1950 { 2281 {
2058 OrthancStone::WebAssemblyLoadersContext& context_; 2389 OrthancStone::WebAssemblyLoadersContext& context_;
2059 boost::shared_ptr<OrthancStone::WebAssemblyViewport> viewport_; 2390 boost::shared_ptr<OrthancStone::WebAssemblyViewport> viewport_;
2060 boost::shared_ptr<OrthancStone::DicomResourcesLoader> loader_; 2391 boost::shared_ptr<OrthancStone::DicomResourcesLoader> loader_;
2061 OrthancStone::DicomSource source_; 2392 OrthancStone::DicomSource source_;
2062 boost::shared_ptr<FramesCache> framesCache_; 2393 boost::shared_ptr<FramesCache> framesCache_;
2063 std::unique_ptr<OrthancStone::SortedFrames> frames_; 2394 std::unique_ptr<IFramesCollection> frames_;
2064 std::unique_ptr<SeriesCursor> cursor_; 2395 std::unique_ptr<SeriesCursor> cursor_;
2065 float windowingCenter_; 2396 float windowingCenter_;
2066 float windowingWidth_; 2397 float windowingWidth_;
2067 std::vector<float> windowingPresetCenters_; 2398 std::vector<float> windowingPresetCenters_;
2068 std::vector<float> windowingPresetWidths_; 2399 std::vector<float> windowingPresetWidths_;
2455 } 2786 }
2456 } 2787 }
2457 } 2788 }
2458 } 2789 }
2459 2790
2460 void ScheduleLoadFullDicomFrame(const std::string& sopInstanceUid, 2791 void ScheduleLoadFullDicomFrame(const std::string& studyInstanceUid,
2792 const std::string& seriesInstanceUid,
2793 const std::string& sopInstanceUid,
2461 unsigned int frameNumber, 2794 unsigned int frameNumber,
2462 int priority, 2795 int priority,
2463 bool isPrefetch) 2796 bool isPrefetch)
2464 { 2797 {
2465 if (frames_.get() != NULL) 2798 if (frames_.get() != NULL)
2466 { 2799 {
2467 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock()); 2800 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_.Lock());
2468 lock->Schedule( 2801 lock->Schedule(
2469 GetSharedObserver(), priority, OrthancStone::ParseDicomFromWadoCommand::Create( 2802 GetSharedObserver(), priority, OrthancStone::ParseDicomFromWadoCommand::Create(
2470 source_, frames_->GetStudyInstanceUid(), frames_->GetSeriesInstanceUid(), 2803 source_, studyInstanceUid, seriesInstanceUid, sopInstanceUid, serverSideTranscoding_,
2471 sopInstanceUid, serverSideTranscoding_,
2472 Orthanc::DicomTransferSyntax_LittleEndianExplicit, 2804 Orthanc::DicomTransferSyntax_LittleEndianExplicit,
2473 new SetFullDicomFrame(GetSharedObserver(), sopInstanceUid, frameNumber, priority, isPrefetch, serverSideTranscoding_))); 2805 new SetFullDicomFrame(GetSharedObserver(), studyInstanceUid, seriesInstanceUid,
2806 sopInstanceUid, frameNumber, priority, isPrefetch, serverSideTranscoding_)));
2474 } 2807 }
2475 } 2808 }
2476 2809
2477 void ScheduleLoadFullDicomFrame(size_t cursorIndex, 2810 void ScheduleLoadFullDicomFrame(size_t cursorIndex,
2478 int priority, 2811 int priority,
2479 bool isPrefetch) 2812 bool isPrefetch)
2480 { 2813 {
2481 if (frames_.get() != NULL) 2814 if (frames_.get() != NULL)
2482 { 2815 {
2816 std::string studyInstanceUid = frames_->GetInstanceOfFrame(cursorIndex).GetStudyInstanceUid();
2817 std::string seriesInstanceUid = frames_->GetInstanceOfFrame(cursorIndex).GetSeriesInstanceUid();
2483 std::string sopInstanceUid = frames_->GetInstanceOfFrame(cursorIndex).GetSopInstanceUid(); 2818 std::string sopInstanceUid = frames_->GetInstanceOfFrame(cursorIndex).GetSopInstanceUid();
2484 unsigned int frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); 2819 unsigned int frameNumber = frames_->GetFrameNumberInInstance(cursorIndex);
2485 ScheduleLoadFullDicomFrame(sopInstanceUid, frameNumber, priority, isPrefetch); 2820 ScheduleLoadFullDicomFrame(studyInstanceUid, seriesInstanceUid, sopInstanceUid, frameNumber, priority, isPrefetch);
2486 } 2821 }
2487 } 2822 }
2488 2823
2489 void ScheduleLoadRenderedFrame(size_t cursorIndex, 2824 void ScheduleLoadRenderedFrame(size_t cursorIndex,
2490 int priority, 2825 int priority,
2529 } 2864 }
2530 2865
2531 bool isMonochrome1 = (instance.GetImageInformation().GetPhotometricInterpretation() == 2866 bool isMonochrome1 = (instance.GetImageInformation().GetPhotometricInterpretation() ==
2532 Orthanc::PhotometricInterpretation_Monochrome1); 2867 Orthanc::PhotometricInterpretation_Monochrome1);
2533 2868
2534 const std::string uri = ("studies/" + frames_->GetStudyInstanceUid() + 2869 const std::string uri = ("studies/" + instance.GetStudyInstanceUid() +
2535 "/series/" + frames_->GetSeriesInstanceUid() + 2870 "/series/" + instance.GetSeriesInstanceUid() +
2536 "/instances/" + instance.GetSopInstanceUid() + 2871 "/instances/" + instance.GetSopInstanceUid() +
2537 "/frames/" + boost::lexical_cast<std::string>(frameNumber + 1) + "/rendered"); 2872 "/frames/" + boost::lexical_cast<std::string>(frameNumber + 1) + "/rendered");
2538 2873
2539 std::map<std::string, std::string> headers, arguments; 2874 std::map<std::string, std::string> headers, arguments;
2540 // arguments["quality"] = "10"; // Low-level quality for test purpose 2875 // arguments["quality"] = "10"; // Low-level quality for test purpose
2820 } 3155 }
2821 3156
2822 return viewport; 3157 return viewport;
2823 } 3158 }
2824 3159
2825 void SetFrames(OrthancStone::SortedFrames* frames) 3160 void SetFrames(IFramesCollection* frames)
2826 { 3161 {
2827 if (frames == NULL) 3162 if (frames == NULL)
2828 { 3163 {
2829 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 3164 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
2830 } 3165 }
2890 uid != OrthancStone::SopClassUid_RTPlan && 3225 uid != OrthancStone::SopClassUid_RTPlan &&
2891 uid != OrthancStone::SopClassUid_RTStruct && 3226 uid != OrthancStone::SopClassUid_RTStruct &&
2892 GetSeriesThumbnailType(uid) != OrthancStone::SeriesThumbnailType_Video) 3227 GetSeriesThumbnailType(uid) != OrthancStone::SeriesThumbnailType_Video)
2893 { 3228 {
2894 // Fetch the details of the series from the central instance 3229 // Fetch the details of the series from the central instance
2895 const std::string uri = ("studies/" + frames_->GetStudyInstanceUid() + 3230 const std::string uri = ("studies/" + centralInstance.GetStudyInstanceUid() +
2896 "/series/" + frames_->GetSeriesInstanceUid() + 3231 "/series/" + centralInstance.GetSeriesInstanceUid() +
2897 "/instances/" + centralInstance.GetSopInstanceUid() + "/metadata"); 3232 "/instances/" + centralInstance.GetSopInstanceUid() + "/metadata");
2898 3233
2899 loader_->ScheduleGetDicomWeb( 3234 loader_->ScheduleGetDicomWeb(
2900 boost::make_shared<OrthancStone::LoadedDicomResources>(Orthanc::DICOM_TAG_SOP_INSTANCE_UID), 3235 boost::make_shared<OrthancStone::LoadedDicomResources>(Orthanc::DICOM_TAG_SOP_INSTANCE_UID),
2901 0, source_, uri, new LoadSeriesDetailsFromInstance(GetSharedObserver())); 3236 0, source_, uri, new LoadSeriesDetailsFromInstance(GetSharedObserver()));
3132 3467
3133 3468
3134 void SetWindowingPreset() 3469 void SetWindowingPreset()
3135 { 3470 {
3136 assert(windowingPresetCenters_.size() == windowingPresetWidths_.size()); 3471 assert(windowingPresetCenters_.size() == windowingPresetWidths_.size());
3137 3472
3138 if (windowingPresetCenters_.empty()) 3473 if (windowingPresetCenters_.empty())
3139 { 3474 {
3140 SetWindowing(128, 256); 3475 SetWindowing(128, 256);
3141 } 3476 }
3142 else 3477 else
4210 int LoadSeriesInViewport(const char* canvas, 4545 int LoadSeriesInViewport(const char* canvas,
4211 const char* seriesInstanceUid) 4546 const char* seriesInstanceUid)
4212 { 4547 {
4213 try 4548 try
4214 { 4549 {
4215 std::unique_ptr<OrthancStone::SortedFrames> frames(new OrthancStone::SortedFrames); 4550 std::unique_ptr<IFramesCollection> frames(GetResourcesLoader().GetSeriesFrames(seriesInstanceUid));
4216 4551
4217 if (GetResourcesLoader().SortSeriesFrames(*frames, seriesInstanceUid)) 4552 if (frames.get() != NULL)
4218 { 4553 {
4219 GetViewport(canvas)->SetFrames(frames.release()); 4554 GetViewport(canvas)->SetFrames(frames.release());
4220 return 1; 4555 return 1;
4221 } 4556 }
4222 else 4557 else
4233 int LoadVirtualSeriesInViewport(const char* canvas, 4568 int LoadVirtualSeriesInViewport(const char* canvas,
4234 const char* virtualSeriesId) 4569 const char* virtualSeriesId)
4235 { 4570 {
4236 try 4571 try
4237 { 4572 {
4238 std::unique_ptr<OrthancStone::SortedFrames> frames(new OrthancStone::SortedFrames); 4573 std::unique_ptr<IFramesCollection> frames(GetResourcesLoader().GetVirtualSeriesFrames(virtualSeriesId));
4239 4574
4240 if (GetResourcesLoader().SortVirtualSeriesFrames(*frames, virtualSeriesId)) 4575 if (frames.get() != NULL)
4241 { 4576 {
4242 GetViewport(canvas)->SetFrames(frames.release()); 4577 GetViewport(canvas)->SetFrames(frames.release());
4243 return 1; 4578 return 1;
4244 } 4579 }
4245 else 4580 else