Mercurial > hg > orthanc-stone
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 |