Mercurial > hg > orthanc-stone
comparison Samples/Sdl/Loader.cpp @ 781:2bb72826fa3f
IVolumeImage
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 27 May 2019 11:45:18 +0200 |
parents | f81655ed96ab |
children | b24c208fa953 |
comparison
equal
deleted
inserted
replaced
780:f81655ed96ab | 781:2bb72826fa3f |
---|---|
37 #include "../../Framework/StoneInitialization.h" | 37 #include "../../Framework/StoneInitialization.h" |
38 #include "../../Framework/Toolbox/GeometryToolbox.h" | 38 #include "../../Framework/Toolbox/GeometryToolbox.h" |
39 #include "../../Framework/Toolbox/SlicesSorter.h" | 39 #include "../../Framework/Toolbox/SlicesSorter.h" |
40 #include "../../Framework/Volumes/ImageBuffer3D.h" | 40 #include "../../Framework/Volumes/ImageBuffer3D.h" |
41 #include "../../Framework/Volumes/VolumeImageGeometry.h" | 41 #include "../../Framework/Volumes/VolumeImageGeometry.h" |
42 #include "../../Framework/Volumes/VolumeReslicer.h" | |
42 | 43 |
43 // From Orthanc framework | 44 // From Orthanc framework |
44 #include <Core/DicomFormat/DicomArray.h> | 45 #include <Core/DicomFormat/DicomArray.h> |
45 #include <Core/Images/Image.h> | 46 #include <Core/Images/Image.h> |
46 #include <Core/Images/ImageProcessing.h> | 47 #include <Core/Images/ImageProcessing.h> |
189 { | 190 { |
190 } | 191 } |
191 }; | 192 }; |
192 | 193 |
193 | 194 |
195 class IVolumeImage : public boost::noncopyable | |
196 { | |
197 public: | |
198 ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeImage); | |
199 ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentUpdatedMessage, IVolumeImage); | |
200 | |
201 virtual ~IVolumeImage() | |
202 { | |
203 } | |
204 | |
205 virtual uint64_t GetRevision() const = 0; | |
206 | |
207 virtual bool HasGeometry() const = 0; | |
208 | |
209 virtual const ImageBuffer3D& GetPixelData() const = 0; | |
210 | |
211 virtual const VolumeImageGeometry& GetGeometry() const = 0; | |
212 }; | |
213 | |
214 | |
194 class IVolumeSlicer : public boost::noncopyable | 215 class IVolumeSlicer : public boost::noncopyable |
195 { | 216 { |
196 public: | 217 public: |
197 class ExtractedSlice : public boost::noncopyable | 218 class IExtractedSlice : public boost::noncopyable |
198 { | 219 { |
199 public: | 220 public: |
200 virtual ~ExtractedSlice() | 221 virtual ~IExtractedSlice() |
201 { | 222 { |
202 } | 223 } |
203 | 224 |
204 virtual bool IsValid() = 0; | 225 virtual bool IsValid() = 0; |
205 | 226 |
213 | 234 |
214 virtual ~IVolumeSlicer() | 235 virtual ~IVolumeSlicer() |
215 { | 236 { |
216 } | 237 } |
217 | 238 |
218 virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const = 0; | 239 virtual bool HasVolumeImage() const = 0; |
240 | |
241 virtual const IVolumeImage& GetVolumeImage() const = 0; | |
242 | |
243 virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const = 0; | |
219 }; | 244 }; |
220 | 245 |
221 | 246 |
222 | 247 class InvalidExtractedSlice : public IVolumeSlicer::IExtractedSlice |
223 class VolumeGeometryReadyMessage : public OriginMessage<IVolumeSlicer> | |
224 { | |
225 ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); | |
226 | |
227 private: | |
228 const VolumeImageGeometry& geometry_; | |
229 | |
230 public: | |
231 VolumeGeometryReadyMessage(const IVolumeSlicer& slicer, | |
232 const VolumeImageGeometry& geometry) : | |
233 OriginMessage(slicer), | |
234 geometry_(geometry) | |
235 { | |
236 } | |
237 | |
238 const VolumeImageGeometry& GetGeometry() const | |
239 { | |
240 return geometry_; | |
241 } | |
242 }; | |
243 | |
244 | |
245 | |
246 class InvalidExtractedSlice : public IVolumeSlicer::ExtractedSlice | |
247 { | 248 { |
248 public: | 249 public: |
249 virtual bool IsValid() | 250 virtual bool IsValid() |
250 { | 251 { |
251 return false; | 252 return false; |
262 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 263 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
263 } | 264 } |
264 }; | 265 }; |
265 | 266 |
266 | 267 |
267 class DicomVolumeImageOrthogonalSlice : public IVolumeSlicer::ExtractedSlice | 268 class DicomVolumeImageOrthogonalSlice : public IVolumeSlicer::IExtractedSlice |
268 { | 269 { |
269 private: | 270 private: |
270 const ImageBuffer3D& image_; | 271 const ImageBuffer3D& image_; |
271 const VolumeImageGeometry& geometry_; | 272 const VolumeImageGeometry& geometry_; |
272 bool valid_; | 273 bool valid_; |
384 #endif | 385 #endif |
385 } | 386 } |
386 }; | 387 }; |
387 | 388 |
388 | 389 |
390 class VolumeImageBase : public IVolumeImage | |
391 { | |
392 private: | |
393 uint64_t revision_; | |
394 std::auto_ptr<VolumeImageGeometry> geometry_; | |
395 std::auto_ptr<ImageBuffer3D> image_; | |
396 | |
397 protected: | |
398 void Finalize() | |
399 { | |
400 geometry_.reset(); | |
401 image_.reset(); | |
402 | |
403 revision_ ++; | |
404 } | |
405 | |
406 void Initialize(VolumeImageGeometry* geometry, // Takes ownership | |
407 Orthanc::PixelFormat format) | |
408 { | |
409 if (geometry == NULL) | |
410 { | |
411 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
412 } | |
413 | |
414 geometry_.reset(geometry); | |
415 image_.reset(new ImageBuffer3D(format, geometry_->GetWidth(), geometry_->GetHeight(), | |
416 geometry_->GetDepth(), false /* don't compute range */)); | |
417 | |
418 revision_ ++; | |
419 } | |
420 | |
421 ImageBuffer3D& GetPixelData() | |
422 { | |
423 return *image_; | |
424 } | |
425 | |
426 void IncrementRevision() | |
427 { | |
428 revision_ ++; | |
429 } | |
430 | |
431 public: | |
432 VolumeImageBase() : | |
433 revision_(0) | |
434 { | |
435 } | |
436 | |
437 virtual uint64_t GetRevision() const | |
438 { | |
439 return revision_; | |
440 } | |
441 | |
442 virtual bool HasGeometry() const | |
443 { | |
444 return (geometry_.get() != NULL && | |
445 image_.get() != NULL); | |
446 } | |
447 | |
448 virtual const ImageBuffer3D& GetPixelData() const | |
449 { | |
450 if (HasGeometry()) | |
451 { | |
452 return *image_; | |
453 } | |
454 else | |
455 { | |
456 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
457 } | |
458 } | |
459 | |
460 virtual const VolumeImageGeometry& GetGeometry() const | |
461 { | |
462 if (HasGeometry()) | |
463 { | |
464 return *geometry_; | |
465 } | |
466 else | |
467 { | |
468 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
469 } | |
470 } | |
471 }; | |
472 | |
473 | |
474 | |
475 | |
389 // This class combines a 3D image buffer, a 3D volume geometry and | 476 // This class combines a 3D image buffer, a 3D volume geometry and |
390 // information about the DICOM parameters of each slice. | 477 // information about the DICOM parameters of each slice. |
391 class DicomSeriesVolumeImage : public boost::noncopyable | 478 class DicomSeriesVolumeImage : public VolumeImageBase |
392 { | 479 { |
393 public: | 480 public: |
394 class ExtractedOrthogonalSlice : public DicomVolumeImageOrthogonalSlice | 481 class ExtractedOrthogonalSlice : public DicomVolumeImageOrthogonalSlice |
395 { | 482 { |
396 private: | 483 private: |
419 } | 506 } |
420 | 507 |
421 public: | 508 public: |
422 ExtractedOrthogonalSlice(const DicomSeriesVolumeImage& that, | 509 ExtractedOrthogonalSlice(const DicomSeriesVolumeImage& that, |
423 const CoordinateSystem3D& plane) : | 510 const CoordinateSystem3D& plane) : |
424 DicomVolumeImageOrthogonalSlice(that.GetImage(), that.GetGeometry(), plane), | 511 DicomVolumeImageOrthogonalSlice(that.GetPixelData(), that.GetGeometry(), plane), |
425 that_(that) | 512 that_(that) |
426 { | 513 { |
427 } | 514 } |
428 }; | 515 }; |
429 | 516 |
430 | 517 |
431 private: | 518 private: |
432 std::auto_ptr<ImageBuffer3D> image_; | |
433 std::auto_ptr<VolumeImageGeometry> geometry_; | |
434 std::vector<DicomInstanceParameters*> slices_; | 519 std::vector<DicomInstanceParameters*> slices_; |
435 uint64_t revision_; | |
436 std::vector<uint64_t> slicesRevision_; | 520 std::vector<uint64_t> slicesRevision_; |
437 std::vector<unsigned int> slicesQuality_; | 521 std::vector<unsigned int> slicesQuality_; |
438 | 522 |
439 void CheckSlice(size_t index, | 523 void CheckSlice(size_t index, |
440 const DicomInstanceParameters& reference) const | 524 const DicomInstanceParameters& reference) const |
495 } | 579 } |
496 | 580 |
497 | 581 |
498 void Clear() | 582 void Clear() |
499 { | 583 { |
500 image_.reset(); | 584 VolumeImageBase::Finalize(); |
501 geometry_.reset(); | |
502 | 585 |
503 for (size_t i = 0; i < slices_.size(); i++) | 586 for (size_t i = 0; i < slices_.size(); i++) |
504 { | 587 { |
505 assert(slices_[i] != NULL); | 588 assert(slices_[i] != NULL); |
506 delete slices_[i]; | 589 delete slices_[i]; |
512 } | 595 } |
513 | 596 |
514 | 597 |
515 void CheckSliceIndex(size_t index) const | 598 void CheckSliceIndex(size_t index) const |
516 { | 599 { |
517 assert(slices_.size() == image_->GetDepth() && | 600 assert(slices_.size() == GetPixelData().GetDepth() && |
518 slices_.size() == slicesRevision_.size()); | 601 slices_.size() == slicesRevision_.size()); |
519 | 602 |
520 if (!HasGeometry()) | 603 if (!HasGeometry()) |
521 { | 604 { |
522 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 605 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
527 } | 610 } |
528 } | 611 } |
529 | 612 |
530 | 613 |
531 public: | 614 public: |
532 DicomSeriesVolumeImage() : | |
533 revision_(0) | |
534 { | |
535 } | |
536 | |
537 ~DicomSeriesVolumeImage() | 615 ~DicomSeriesVolumeImage() |
538 { | 616 { |
539 Clear(); | 617 Clear(); |
540 } | 618 } |
541 | 619 |
548 { | 626 { |
549 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, | 627 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, |
550 "Cannot sort the 3D slices of a DICOM series"); | 628 "Cannot sort the 3D slices of a DICOM series"); |
551 } | 629 } |
552 | 630 |
553 geometry_.reset(new VolumeImageGeometry); | |
554 | |
555 if (slices.GetSlicesCount() == 0) | 631 if (slices.GetSlicesCount() == 0) |
556 { | 632 { |
557 // Empty volume | 633 // Empty volume |
558 image_.reset(new ImageBuffer3D(Orthanc::PixelFormat_Grayscale8, 0, 0, 0, | 634 VolumeImageBase::Initialize(new VolumeImageGeometry, Orthanc::PixelFormat_Grayscale8); |
559 false /* don't compute range */)); | |
560 } | 635 } |
561 else | 636 else |
562 { | 637 { |
563 slices_.reserve(slices.GetSlicesCount()); | 638 slices_.reserve(slices.GetSlicesCount()); |
564 slicesRevision_.resize(slices.GetSlicesCount(), 0); | 639 slicesRevision_.resize(slices.GetSlicesCount(), 0); |
576 const double spacingZ = slices.ComputeSpacingBetweenSlices(); | 651 const double spacingZ = slices.ComputeSpacingBetweenSlices(); |
577 LOG(INFO) << "Computed spacing between slices: " << spacingZ << "mm"; | 652 LOG(INFO) << "Computed spacing between slices: " << spacingZ << "mm"; |
578 | 653 |
579 const DicomInstanceParameters& parameters = *slices_[0]; | 654 const DicomInstanceParameters& parameters = *slices_[0]; |
580 | 655 |
581 image_.reset(new ImageBuffer3D(parameters.GetExpectedPixelFormat(), | 656 std::auto_ptr<VolumeImageGeometry> geometry(new VolumeImageGeometry); |
582 parameters.GetImageInformation().GetWidth(), | 657 geometry->SetSize(parameters.GetImageInformation().GetWidth(), |
583 parameters.GetImageInformation().GetHeight(), | 658 parameters.GetImageInformation().GetHeight(), |
584 static_cast<unsigned int>(slices.GetSlicesCount()), | 659 static_cast<unsigned int>(slices.GetSlicesCount())); |
585 false /* don't compute range */)); | 660 geometry->SetAxialGeometry(slices.GetSliceGeometry(0)); |
586 | 661 geometry->SetVoxelDimensions(parameters.GetPixelSpacingX(), |
587 geometry_->SetSize(image_->GetWidth(), image_->GetHeight(), image_->GetDepth()); | 662 parameters.GetPixelSpacingY(), spacingZ); |
588 geometry_->SetAxialGeometry(slices.GetSliceGeometry(0)); | 663 |
589 geometry_->SetVoxelDimensions(parameters.GetPixelSpacingX(), | 664 VolumeImageBase::Initialize(geometry.release(), parameters.GetExpectedPixelFormat()); |
590 parameters.GetPixelSpacingY(), spacingZ); | 665 } |
591 } | 666 |
592 | 667 GetPixelData().Clear(); |
593 image_->Clear(); | |
594 | |
595 revision_++; | |
596 } | |
597 | |
598 uint64_t GetRevision() const | |
599 { | |
600 return revision_; | |
601 } | |
602 | |
603 bool HasGeometry() const | |
604 { | |
605 return (image_.get() != NULL && | |
606 geometry_.get() != NULL); | |
607 } | |
608 | |
609 const ImageBuffer3D& GetImage() const | |
610 { | |
611 if (!HasGeometry()) | |
612 { | |
613 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
614 } | |
615 else | |
616 { | |
617 return *image_; | |
618 } | |
619 } | |
620 | |
621 const VolumeImageGeometry& GetGeometry() const | |
622 { | |
623 if (!HasGeometry()) | |
624 { | |
625 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
626 } | |
627 else | |
628 { | |
629 return *geometry_; | |
630 } | |
631 } | 668 } |
632 | 669 |
633 size_t GetSlicesCount() const | 670 size_t GetSlicesCount() const |
634 { | 671 { |
635 if (!HasGeometry()) | 672 if (!HasGeometry()) |
663 // If a better image quality is already available, don't update the content | 700 // If a better image quality is already available, don't update the content |
664 if (quality >= slicesQuality_[index]) | 701 if (quality >= slicesQuality_[index]) |
665 { | 702 { |
666 { | 703 { |
667 ImageBuffer3D::SliceWriter writer | 704 ImageBuffer3D::SliceWriter writer |
668 (*image_, VolumeProjection_Axial, static_cast<unsigned int>(index)); | 705 (GetPixelData(), VolumeProjection_Axial, static_cast<unsigned int>(index)); |
669 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image); | 706 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image); |
670 } | 707 } |
671 | 708 |
672 revision_ ++; | |
673 slicesRevision_[index] += 1; | 709 slicesRevision_[index] += 1; |
710 | |
711 VolumeImageBase::IncrementRevision(); | |
674 } | 712 } |
675 } | 713 } |
676 }; | 714 }; |
677 | 715 |
678 | 716 |
779 { | 817 { |
780 ScheduleNextSliceDownload(); | 818 ScheduleNextSliceDownload(); |
781 } | 819 } |
782 } | 820 } |
783 | 821 |
784 BroadcastMessage(VolumeGeometryReadyMessage(*this, volume_.GetGeometry())); | 822 BroadcastMessage(IVolumeImage::GeometryReadyMessage(volume_)); |
785 } | 823 } |
786 | 824 |
787 | 825 |
788 void LoadBestQualitySliceContent(const GetOrthancImageCommand::SuccessMessage& message) | 826 void LoadBestQualitySliceContent(const GetOrthancImageCommand::SuccessMessage& message) |
789 { | 827 { |
790 volume_.SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), | 828 volume_.SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), |
791 message.GetImage(), BEST_QUALITY); | 829 message.GetImage(), BEST_QUALITY); |
792 | 830 |
793 ScheduleNextSliceDownload(); | 831 ScheduleNextSliceDownload(); |
832 | |
833 BroadcastMessage(IVolumeImage::ContentUpdatedMessage(volume_)); | |
794 } | 834 } |
795 | 835 |
796 | 836 |
797 void LoadJpegSliceContent(const GetOrthancWebViewerJpegCommand::SuccessMessage& message) | 837 void LoadJpegSliceContent(const GetOrthancWebViewerJpegCommand::SuccessMessage& message) |
798 { | 838 { |
813 } | 853 } |
814 | 854 |
815 volume_.SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), message.GetImage(), quality); | 855 volume_.SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), message.GetImage(), quality); |
816 | 856 |
817 ScheduleNextSliceDownload(); | 857 ScheduleNextSliceDownload(); |
858 | |
859 BroadcastMessage(IVolumeImage::ContentUpdatedMessage(volume_)); | |
818 } | 860 } |
819 | 861 |
820 | 862 |
821 IOracle& oracle_; | 863 IOracle& oracle_; |
822 bool active_; | 864 bool active_; |
880 command->SetUri("/series/" + seriesId + "/instances-tags"); | 922 command->SetUri("/series/" + seriesId + "/instances-tags"); |
881 | 923 |
882 oracle_.Schedule(*this, command.release()); | 924 oracle_.Schedule(*this, command.release()); |
883 } | 925 } |
884 } | 926 } |
885 | 927 |
886 | 928 |
887 const DicomSeriesVolumeImage& GetVolume() const | 929 virtual bool HasVolumeImage() const |
930 { | |
931 return true; | |
932 } | |
933 | |
934 | |
935 virtual const IVolumeImage& GetVolumeImage() const | |
888 { | 936 { |
889 return volume_; | 937 return volume_; |
890 } | 938 } |
891 | 939 |
892 | 940 |
893 virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const | 941 virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const |
894 { | 942 { |
895 if (volume_.HasGeometry() && | 943 if (volume_.HasGeometry() && |
896 volume_.GetSlicesCount() != 0) | 944 volume_.GetSlicesCount() != 0) |
897 { | 945 { |
898 std::auto_ptr<DicomVolumeImageOrthogonalSlice> slice | 946 std::auto_ptr<DicomVolumeImageOrthogonalSlice> slice |
919 | 967 |
920 | 968 |
921 class OrthancMultiframeVolumeLoader : | 969 class OrthancMultiframeVolumeLoader : |
922 public IObserver, | 970 public IObserver, |
923 public IObservable, | 971 public IObservable, |
924 public IVolumeSlicer | 972 public IVolumeSlicer, |
973 public VolumeImageBase | |
925 { | 974 { |
926 private: | 975 private: |
927 class State : public Orthanc::IDynamicObject | 976 class State : public Orthanc::IDynamicObject |
928 { | 977 { |
929 private: | 978 private: |
1073 | 1122 |
1074 IOracle& oracle_; | 1123 IOracle& oracle_; |
1075 bool active_; | 1124 bool active_; |
1076 std::string instanceId_; | 1125 std::string instanceId_; |
1077 std::string transferSyntaxUid_; | 1126 std::string transferSyntaxUid_; |
1078 uint64_t revision_; | |
1079 | 1127 |
1080 std::auto_ptr<DicomInstanceParameters> dicom_; | 1128 std::auto_ptr<DicomInstanceParameters> dicom_; |
1081 std::auto_ptr<VolumeImageGeometry> geometry_; | |
1082 std::auto_ptr<ImageBuffer3D> image_; | |
1083 | 1129 |
1084 | 1130 |
1085 const std::string& GetInstanceId() const | 1131 const std::string& GetInstanceId() const |
1086 { | 1132 { |
1087 if (active_) | 1133 if (active_) |
1155 | 1201 |
1156 const unsigned int width = dicom_->GetImageInformation().GetWidth(); | 1202 const unsigned int width = dicom_->GetImageInformation().GetWidth(); |
1157 const unsigned int height = dicom_->GetImageInformation().GetHeight(); | 1203 const unsigned int height = dicom_->GetImageInformation().GetHeight(); |
1158 const unsigned int depth = dicom_->GetImageInformation().GetNumberOfFrames(); | 1204 const unsigned int depth = dicom_->GetImageInformation().GetNumberOfFrames(); |
1159 | 1205 |
1160 geometry_.reset(new VolumeImageGeometry); | 1206 { |
1161 geometry_->SetSize(width, height, depth); | 1207 std::auto_ptr<VolumeImageGeometry> geometry(new VolumeImageGeometry); |
1162 geometry_->SetAxialGeometry(dicom_->GetGeometry()); | 1208 geometry->SetSize(width, height, depth); |
1163 geometry_->SetVoxelDimensions(dicom_->GetPixelSpacingX(), | 1209 geometry->SetAxialGeometry(dicom_->GetGeometry()); |
1164 dicom_->GetPixelSpacingY(), | 1210 geometry->SetVoxelDimensions(dicom_->GetPixelSpacingX(), |
1165 spacingZ); | 1211 dicom_->GetPixelSpacingY(), |
1166 | 1212 spacingZ); |
1167 image_.reset(new ImageBuffer3D(format, width, height, depth, | 1213 VolumeImageBase::Initialize(geometry.release(), format); |
1168 false /* don't compute range */)); | 1214 } |
1169 image_->Clear(); | 1215 |
1216 GetPixelData().Clear(); | |
1170 | 1217 |
1171 ScheduleFrameDownloads(); | 1218 ScheduleFrameDownloads(); |
1172 | 1219 |
1173 BroadcastMessage(VolumeGeometryReadyMessage(*this, *geometry_)); | 1220 BroadcastMessage(IVolumeImage::GeometryReadyMessage(*this)); |
1174 } | 1221 } |
1175 | 1222 |
1176 | 1223 |
1177 ORTHANC_FORCE_INLINE | 1224 ORTHANC_FORCE_INLINE |
1178 static void CopyPixel(uint32_t& target, | 1225 static void CopyPixel(uint32_t& target, |
1184 | 1231 |
1185 | 1232 |
1186 template <typename T> | 1233 template <typename T> |
1187 void CopyPixelData(const std::string& pixelData) | 1234 void CopyPixelData(const std::string& pixelData) |
1188 { | 1235 { |
1189 const Orthanc::PixelFormat format = image_->GetFormat(); | 1236 const Orthanc::PixelFormat format = GetPixelData().GetFormat(); |
1190 const unsigned int bpp = image_->GetBytesPerPixel(); | 1237 const unsigned int bpp = GetPixelData().GetBytesPerPixel(); |
1191 const unsigned int width = image_->GetWidth(); | 1238 const unsigned int width = GetPixelData().GetWidth(); |
1192 const unsigned int height = image_->GetHeight(); | 1239 const unsigned int height = GetPixelData().GetHeight(); |
1193 const unsigned int depth = image_->GetDepth(); | 1240 const unsigned int depth = GetPixelData().GetDepth(); |
1194 | 1241 |
1195 if (pixelData.size() != bpp * width * height * depth) | 1242 if (pixelData.size() != bpp * width * height * depth) |
1196 { | 1243 { |
1197 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | 1244 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, |
1198 "The pixel data has not the proper size"); | 1245 "The pixel data has not the proper size"); |
1205 | 1252 |
1206 const uint8_t* source = reinterpret_cast<const uint8_t*>(pixelData.c_str()); | 1253 const uint8_t* source = reinterpret_cast<const uint8_t*>(pixelData.c_str()); |
1207 | 1254 |
1208 for (unsigned int z = 0; z < depth; z++) | 1255 for (unsigned int z = 0; z < depth; z++) |
1209 { | 1256 { |
1210 ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, z); | 1257 ImageBuffer3D::SliceWriter writer(GetPixelData(), VolumeProjection_Axial, z); |
1211 | 1258 |
1212 assert (writer.GetAccessor().GetWidth() == width && | 1259 assert (writer.GetAccessor().GetWidth() == width && |
1213 writer.GetAccessor().GetHeight() == height); | 1260 writer.GetAccessor().GetHeight() == height); |
1214 | 1261 |
1215 for (unsigned int y = 0; y < height; y++) | 1262 for (unsigned int y = 0; y < height; y++) |
1230 } | 1277 } |
1231 | 1278 |
1232 | 1279 |
1233 void SetUncompressedPixelData(const std::string& pixelData) | 1280 void SetUncompressedPixelData(const std::string& pixelData) |
1234 { | 1281 { |
1235 switch (image_->GetFormat()) | 1282 switch (GetPixelData().GetFormat()) |
1236 { | 1283 { |
1237 case Orthanc::PixelFormat_Grayscale32: | 1284 case Orthanc::PixelFormat_Grayscale32: |
1238 CopyPixelData<uint32_t>(pixelData); | 1285 CopyPixelData<uint32_t>(pixelData); |
1239 break; | 1286 break; |
1240 | 1287 |
1241 default: | 1288 default: |
1242 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | 1289 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); |
1243 } | 1290 } |
1244 | 1291 |
1245 revision_ ++; | 1292 IncrementRevision(); |
1293 | |
1294 BroadcastMessage(IVolumeImage::ContentUpdatedMessage(*this)); | |
1246 } | 1295 } |
1247 | 1296 |
1248 | 1297 |
1249 private: | 1298 private: |
1250 class ExtractedOrthogonalSlice : public DicomVolumeImageOrthogonalSlice | 1299 class ExtractedOrthogonalSlice : public DicomVolumeImageOrthogonalSlice |
1254 | 1303 |
1255 protected: | 1304 protected: |
1256 virtual uint64_t GetRevisionInternal(VolumeProjection projection, | 1305 virtual uint64_t GetRevisionInternal(VolumeProjection projection, |
1257 unsigned int sliceIndex) const | 1306 unsigned int sliceIndex) const |
1258 { | 1307 { |
1259 return that_.revision_; | 1308 return that_.GetRevision(); |
1260 } | 1309 } |
1261 | 1310 |
1262 virtual const DicomInstanceParameters& GetDicomParameters(VolumeProjection projection, | 1311 virtual const DicomInstanceParameters& GetDicomParameters(VolumeProjection projection, |
1263 unsigned int sliceIndex) const | 1312 unsigned int sliceIndex) const |
1264 { | 1313 { |
1266 } | 1315 } |
1267 | 1316 |
1268 public: | 1317 public: |
1269 ExtractedOrthogonalSlice(const OrthancMultiframeVolumeLoader& that, | 1318 ExtractedOrthogonalSlice(const OrthancMultiframeVolumeLoader& that, |
1270 const CoordinateSystem3D& plane) : | 1319 const CoordinateSystem3D& plane) : |
1271 DicomVolumeImageOrthogonalSlice(that.GetImage(), that.GetGeometry(), plane), | 1320 DicomVolumeImageOrthogonalSlice(that.GetPixelData(), that.GetGeometry(), plane), |
1272 that_(that) | 1321 that_(that) |
1273 { | 1322 { |
1274 } | 1323 } |
1275 }; | 1324 }; |
1276 | 1325 |
1279 OrthancMultiframeVolumeLoader(IOracle& oracle, | 1328 OrthancMultiframeVolumeLoader(IOracle& oracle, |
1280 IObservable& oracleObservable) : | 1329 IObservable& oracleObservable) : |
1281 IObserver(oracleObservable.GetBroker()), | 1330 IObserver(oracleObservable.GetBroker()), |
1282 IObservable(oracleObservable.GetBroker()), | 1331 IObservable(oracleObservable.GetBroker()), |
1283 oracle_(oracle), | 1332 oracle_(oracle), |
1284 active_(false), | 1333 active_(false) |
1285 revision_(0) | |
1286 { | 1334 { |
1287 oracleObservable.RegisterObserverCallback( | 1335 oracleObservable.RegisterObserverCallback( |
1288 new Callable<OrthancMultiframeVolumeLoader, OrthancRestApiCommand::SuccessMessage> | 1336 new Callable<OrthancMultiframeVolumeLoader, OrthancRestApiCommand::SuccessMessage> |
1289 (*this, &OrthancMultiframeVolumeLoader::Handle)); | 1337 (*this, &OrthancMultiframeVolumeLoader::Handle)); |
1290 } | 1338 } |
1291 | 1339 |
1292 | 1340 |
1293 bool HasGeometry() const | 1341 virtual bool HasVolumeImage() const |
1294 { | 1342 { |
1295 return (dicom_.get() != NULL && | 1343 return true; |
1296 geometry_.get() != NULL && | 1344 } |
1297 image_.get() != NULL); | 1345 |
1298 } | 1346 |
1299 | 1347 virtual const IVolumeImage& GetVolumeImage() const |
1300 | 1348 { |
1301 const ImageBuffer3D& GetImage() const | 1349 return *this; |
1302 { | |
1303 if (!HasGeometry()) | |
1304 { | |
1305 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
1306 } | |
1307 else | |
1308 { | |
1309 return *image_; | |
1310 } | |
1311 } | |
1312 | |
1313 | |
1314 const VolumeImageGeometry& GetGeometry() const | |
1315 { | |
1316 if (!HasGeometry()) | |
1317 { | |
1318 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
1319 } | |
1320 else | |
1321 { | |
1322 return *geometry_; | |
1323 } | |
1324 } | 1350 } |
1325 | 1351 |
1326 | 1352 |
1327 const DicomInstanceParameters& GetDicomParameters() const | 1353 const DicomInstanceParameters& GetDicomParameters() const |
1328 { | 1354 { |
1364 } | 1390 } |
1365 } | 1391 } |
1366 } | 1392 } |
1367 | 1393 |
1368 | 1394 |
1369 virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const | 1395 virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const |
1370 { | 1396 { |
1371 if (HasGeometry()) | 1397 if (HasGeometry()) |
1372 { | 1398 { |
1373 return new ExtractedOrthogonalSlice(*this, cuttingPlane); | 1399 return new ExtractedOrthogonalSlice(*this, cuttingPlane); |
1374 } | 1400 } |
1375 else | 1401 else |
1376 { | 1402 { |
1377 return new InvalidExtractedSlice; | 1403 return new InvalidExtractedSlice; |
1378 } | 1404 } |
1405 } | |
1406 }; | |
1407 | |
1408 | |
1409 | |
1410 class DicomSeriesVolumeImageReslicer : public IVolumeSlicer | |
1411 { | |
1412 private: | |
1413 class Slice : public IExtractedSlice | |
1414 { | |
1415 public: | |
1416 virtual bool IsValid() | |
1417 { | |
1418 } | |
1419 | |
1420 virtual uint64_t GetRevision() | |
1421 { | |
1422 } | |
1423 | |
1424 virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, // possibly absent | |
1425 const CoordinateSystem3D& cuttingPlane) | |
1426 { | |
1427 } | |
1428 }; | |
1429 | |
1430 VolumeReslicer reslicer_; | |
1431 boost::shared_ptr<DicomSeriesVolumeImage> image_; | |
1432 | |
1433 public: | |
1434 DicomSeriesVolumeImageReslicer(const boost::shared_ptr<DicomSeriesVolumeImage>& image) : | |
1435 image_(image) | |
1436 { | |
1437 } | |
1438 | |
1439 virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const | |
1440 { | |
1379 } | 1441 } |
1380 }; | 1442 }; |
1381 | 1443 |
1382 | 1444 |
1383 | 1445 |
1455 } | 1517 } |
1456 | 1518 |
1457 void Update(const CoordinateSystem3D& plane) | 1519 void Update(const CoordinateSystem3D& plane) |
1458 { | 1520 { |
1459 assert(slicer_.get() != NULL); | 1521 assert(slicer_.get() != NULL); |
1460 std::auto_ptr<IVolumeSlicer::ExtractedSlice> slice(slicer_->ExtractSlice(plane)); | 1522 std::auto_ptr<IVolumeSlicer::IExtractedSlice> slice(slicer_->ExtractSlice(plane)); |
1461 | 1523 |
1462 if (slice.get() == NULL) | 1524 if (slice.get() == NULL) |
1463 { | 1525 { |
1464 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | 1526 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); |
1465 } | 1527 } |
1624 writer.WriteToFile(buf, tmp); | 1686 writer.WriteToFile(buf, tmp); |
1625 } | 1687 } |
1626 } | 1688 } |
1627 | 1689 |
1628 | 1690 |
1629 void Handle(const OrthancStone::VolumeGeometryReadyMessage& message) | 1691 void Handle(const OrthancStone::IVolumeImage::GeometryReadyMessage& message) |
1630 { | 1692 { |
1631 printf("Geometry ready\n"); | 1693 printf("Geometry ready\n"); |
1632 | 1694 |
1633 if (&source1_->GetSlicer() == &message.GetOrigin()) | 1695 if (source2_->GetSlicer().HasVolumeImage() && |
1634 { | 1696 &source2_->GetSlicer().GetVolumeImage() == &message.GetOrigin()) |
1635 //plane_ = message.GetGeometry().GetSagittalGeometry(); | 1697 { |
1636 //plane_ = message.GetGeometry().GetAxialGeometry(); | 1698 //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); |
1637 plane_ = message.GetGeometry().GetCoronalGeometry(); | 1699 //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); |
1638 plane_.SetOrigin(message.GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); | 1700 plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); |
1701 plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); | |
1639 } | 1702 } |
1640 | 1703 |
1641 Refresh(); | 1704 Refresh(); |
1642 } | 1705 } |
1643 | 1706 |
1730 const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, | 1793 const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, |
1731 OrthancStone::ILayerStyleConfigurator* style) | 1794 OrthancStone::ILayerStyleConfigurator* style) |
1732 { | 1795 { |
1733 dynamic_cast<OrthancStone::IObservable&>(*volume).RegisterObserverCallback | 1796 dynamic_cast<OrthancStone::IObservable&>(*volume).RegisterObserverCallback |
1734 (new OrthancStone::Callable | 1797 (new OrthancStone::Callable |
1735 <Toto, OrthancStone::VolumeGeometryReadyMessage>(*this, &Toto::Handle)); | 1798 <Toto, OrthancStone::IVolumeImage::GeometryReadyMessage>(*this, &Toto::Handle)); |
1736 | 1799 |
1737 source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); | 1800 source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); |
1738 | 1801 |
1739 if (style != NULL) | 1802 if (style != NULL) |
1740 { | 1803 { |
1746 const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, | 1809 const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume, |
1747 OrthancStone::ILayerStyleConfigurator* style) | 1810 OrthancStone::ILayerStyleConfigurator* style) |
1748 { | 1811 { |
1749 dynamic_cast<OrthancStone::IObservable&>(*volume).RegisterObserverCallback | 1812 dynamic_cast<OrthancStone::IObservable&>(*volume).RegisterObserverCallback |
1750 (new OrthancStone::Callable | 1813 (new OrthancStone::Callable |
1751 <Toto, OrthancStone::VolumeGeometryReadyMessage>(*this, &Toto::Handle)); | 1814 <Toto, OrthancStone::IVolumeImage::GeometryReadyMessage>(*this, &Toto::Handle)); |
1752 | 1815 |
1753 source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); | 1816 source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); |
1754 | 1817 |
1755 if (style != NULL) | 1818 if (style != NULL) |
1756 { | 1819 { |