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 {