# HG changeset patch # User Sebastien Jodogne # Date 1558950318 -7200 # Node ID 2bb72826fa3fe20d86fc04d10b06d5e349feeacd # Parent f81655ed96ab268befbb6a08dc05a575b542c175 IVolumeImage diff -r f81655ed96ab -r 2bb72826fa3f Samples/Sdl/Loader.cpp --- a/Samples/Sdl/Loader.cpp Mon May 27 10:39:35 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Mon May 27 11:45:18 2019 +0200 @@ -39,6 +39,7 @@ #include "../../Framework/Toolbox/SlicesSorter.h" #include "../../Framework/Volumes/ImageBuffer3D.h" #include "../../Framework/Volumes/VolumeImageGeometry.h" +#include "../../Framework/Volumes/VolumeReslicer.h" // From Orthanc framework #include @@ -191,13 +192,33 @@ }; + class IVolumeImage : public boost::noncopyable + { + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeImage); + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentUpdatedMessage, IVolumeImage); + + virtual ~IVolumeImage() + { + } + + virtual uint64_t GetRevision() const = 0; + + virtual bool HasGeometry() const = 0; + + virtual const ImageBuffer3D& GetPixelData() const = 0; + + virtual const VolumeImageGeometry& GetGeometry() const = 0; + }; + + class IVolumeSlicer : public boost::noncopyable { public: - class ExtractedSlice : public boost::noncopyable + class IExtractedSlice : public boost::noncopyable { public: - virtual ~ExtractedSlice() + virtual ~IExtractedSlice() { } @@ -215,35 +236,15 @@ { } - virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const = 0; + virtual bool HasVolumeImage() const = 0; + + virtual const IVolumeImage& GetVolumeImage() const = 0; + + virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const = 0; }; - - class VolumeGeometryReadyMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const VolumeImageGeometry& geometry_; - - public: - VolumeGeometryReadyMessage(const IVolumeSlicer& slicer, - const VolumeImageGeometry& geometry) : - OriginMessage(slicer), - geometry_(geometry) - { - } - - const VolumeImageGeometry& GetGeometry() const - { - return geometry_; - } - }; - - - - class InvalidExtractedSlice : public IVolumeSlicer::ExtractedSlice + class InvalidExtractedSlice : public IVolumeSlicer::IExtractedSlice { public: virtual bool IsValid() @@ -264,7 +265,7 @@ }; - class DicomVolumeImageOrthogonalSlice : public IVolumeSlicer::ExtractedSlice + class DicomVolumeImageOrthogonalSlice : public IVolumeSlicer::IExtractedSlice { private: const ImageBuffer3D& image_; @@ -386,9 +387,95 @@ }; + class VolumeImageBase : public IVolumeImage + { + private: + uint64_t revision_; + std::auto_ptr geometry_; + std::auto_ptr image_; + + protected: + void Finalize() + { + geometry_.reset(); + image_.reset(); + + revision_ ++; + } + + void Initialize(VolumeImageGeometry* geometry, // Takes ownership + Orthanc::PixelFormat format) + { + if (geometry == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + geometry_.reset(geometry); + image_.reset(new ImageBuffer3D(format, geometry_->GetWidth(), geometry_->GetHeight(), + geometry_->GetDepth(), false /* don't compute range */)); + + revision_ ++; + } + + ImageBuffer3D& GetPixelData() + { + return *image_; + } + + void IncrementRevision() + { + revision_ ++; + } + + public: + VolumeImageBase() : + revision_(0) + { + } + + virtual uint64_t GetRevision() const + { + return revision_; + } + + virtual bool HasGeometry() const + { + return (geometry_.get() != NULL && + image_.get() != NULL); + } + + virtual const ImageBuffer3D& GetPixelData() const + { + if (HasGeometry()) + { + return *image_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + virtual const VolumeImageGeometry& GetGeometry() const + { + if (HasGeometry()) + { + return *geometry_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + }; + + + + // This class combines a 3D image buffer, a 3D volume geometry and // information about the DICOM parameters of each slice. - class DicomSeriesVolumeImage : public boost::noncopyable + class DicomSeriesVolumeImage : public VolumeImageBase { public: class ExtractedOrthogonalSlice : public DicomVolumeImageOrthogonalSlice @@ -421,7 +508,7 @@ public: ExtractedOrthogonalSlice(const DicomSeriesVolumeImage& that, const CoordinateSystem3D& plane) : - DicomVolumeImageOrthogonalSlice(that.GetImage(), that.GetGeometry(), plane), + DicomVolumeImageOrthogonalSlice(that.GetPixelData(), that.GetGeometry(), plane), that_(that) { } @@ -429,10 +516,7 @@ private: - std::auto_ptr image_; - std::auto_ptr geometry_; std::vector slices_; - uint64_t revision_; std::vector slicesRevision_; std::vector slicesQuality_; @@ -497,8 +581,7 @@ void Clear() { - image_.reset(); - geometry_.reset(); + VolumeImageBase::Finalize(); for (size_t i = 0; i < slices_.size(); i++) { @@ -514,7 +597,7 @@ void CheckSliceIndex(size_t index) const { - assert(slices_.size() == image_->GetDepth() && + assert(slices_.size() == GetPixelData().GetDepth() && slices_.size() == slicesRevision_.size()); if (!HasGeometry()) @@ -529,11 +612,6 @@ public: - DicomSeriesVolumeImage() : - revision_(0) - { - } - ~DicomSeriesVolumeImage() { Clear(); @@ -550,13 +628,10 @@ "Cannot sort the 3D slices of a DICOM series"); } - geometry_.reset(new VolumeImageGeometry); - if (slices.GetSlicesCount() == 0) { // Empty volume - image_.reset(new ImageBuffer3D(Orthanc::PixelFormat_Grayscale8, 0, 0, 0, - false /* don't compute range */)); + VolumeImageBase::Initialize(new VolumeImageGeometry, Orthanc::PixelFormat_Grayscale8); } else { @@ -578,56 +653,18 @@ const DicomInstanceParameters& parameters = *slices_[0]; - image_.reset(new ImageBuffer3D(parameters.GetExpectedPixelFormat(), - parameters.GetImageInformation().GetWidth(), + std::auto_ptr geometry(new VolumeImageGeometry); + geometry->SetSize(parameters.GetImageInformation().GetWidth(), parameters.GetImageInformation().GetHeight(), - static_cast(slices.GetSlicesCount()), - false /* don't compute range */)); + static_cast(slices.GetSlicesCount())); + geometry->SetAxialGeometry(slices.GetSliceGeometry(0)); + geometry->SetVoxelDimensions(parameters.GetPixelSpacingX(), + parameters.GetPixelSpacingY(), spacingZ); - geometry_->SetSize(image_->GetWidth(), image_->GetHeight(), image_->GetDepth()); - geometry_->SetAxialGeometry(slices.GetSliceGeometry(0)); - geometry_->SetVoxelDimensions(parameters.GetPixelSpacingX(), - parameters.GetPixelSpacingY(), spacingZ); + VolumeImageBase::Initialize(geometry.release(), parameters.GetExpectedPixelFormat()); } - image_->Clear(); - - revision_++; - } - - uint64_t GetRevision() const - { - return revision_; - } - - bool HasGeometry() const - { - return (image_.get() != NULL && - geometry_.get() != NULL); - } - - const ImageBuffer3D& GetImage() const - { - if (!HasGeometry()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - return *image_; - } - } - - const VolumeImageGeometry& GetGeometry() const - { - if (!HasGeometry()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - return *geometry_; - } + GetPixelData().Clear(); } size_t GetSlicesCount() const @@ -665,12 +702,13 @@ { { ImageBuffer3D::SliceWriter writer - (*image_, VolumeProjection_Axial, static_cast(index)); + (GetPixelData(), VolumeProjection_Axial, static_cast(index)); Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image); } - revision_ ++; slicesRevision_[index] += 1; + + VolumeImageBase::IncrementRevision(); } } }; @@ -781,7 +819,7 @@ } } - BroadcastMessage(VolumeGeometryReadyMessage(*this, volume_.GetGeometry())); + BroadcastMessage(IVolumeImage::GeometryReadyMessage(volume_)); } @@ -791,6 +829,8 @@ message.GetImage(), BEST_QUALITY); ScheduleNextSliceDownload(); + + BroadcastMessage(IVolumeImage::ContentUpdatedMessage(volume_)); } @@ -815,6 +855,8 @@ volume_.SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), message.GetImage(), quality); ScheduleNextSliceDownload(); + + BroadcastMessage(IVolumeImage::ContentUpdatedMessage(volume_)); } @@ -882,15 +924,21 @@ oracle_.Schedule(*this, command.release()); } } + + + virtual bool HasVolumeImage() const + { + return true; + } - const DicomSeriesVolumeImage& GetVolume() const + virtual const IVolumeImage& GetVolumeImage() const { return volume_; } - - virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const + + virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const { if (volume_.HasGeometry() && volume_.GetSlicesCount() != 0) @@ -921,7 +969,8 @@ class OrthancMultiframeVolumeLoader : public IObserver, public IObservable, - public IVolumeSlicer + public IVolumeSlicer, + public VolumeImageBase { private: class State : public Orthanc::IDynamicObject @@ -1075,11 +1124,8 @@ bool active_; std::string instanceId_; std::string transferSyntaxUid_; - uint64_t revision_; std::auto_ptr dicom_; - std::auto_ptr geometry_; - std::auto_ptr image_; const std::string& GetInstanceId() const @@ -1157,20 +1203,21 @@ const unsigned int height = dicom_->GetImageInformation().GetHeight(); const unsigned int depth = dicom_->GetImageInformation().GetNumberOfFrames(); - geometry_.reset(new VolumeImageGeometry); - geometry_->SetSize(width, height, depth); - geometry_->SetAxialGeometry(dicom_->GetGeometry()); - geometry_->SetVoxelDimensions(dicom_->GetPixelSpacingX(), - dicom_->GetPixelSpacingY(), - spacingZ); + { + std::auto_ptr geometry(new VolumeImageGeometry); + geometry->SetSize(width, height, depth); + geometry->SetAxialGeometry(dicom_->GetGeometry()); + geometry->SetVoxelDimensions(dicom_->GetPixelSpacingX(), + dicom_->GetPixelSpacingY(), + spacingZ); + VolumeImageBase::Initialize(geometry.release(), format); + } - image_.reset(new ImageBuffer3D(format, width, height, depth, - false /* don't compute range */)); - image_->Clear(); + GetPixelData().Clear(); ScheduleFrameDownloads(); - BroadcastMessage(VolumeGeometryReadyMessage(*this, *geometry_)); + BroadcastMessage(IVolumeImage::GeometryReadyMessage(*this)); } @@ -1186,11 +1233,11 @@ template void CopyPixelData(const std::string& pixelData) { - const Orthanc::PixelFormat format = image_->GetFormat(); - const unsigned int bpp = image_->GetBytesPerPixel(); - const unsigned int width = image_->GetWidth(); - const unsigned int height = image_->GetHeight(); - const unsigned int depth = image_->GetDepth(); + const Orthanc::PixelFormat format = GetPixelData().GetFormat(); + const unsigned int bpp = GetPixelData().GetBytesPerPixel(); + const unsigned int width = GetPixelData().GetWidth(); + const unsigned int height = GetPixelData().GetHeight(); + const unsigned int depth = GetPixelData().GetDepth(); if (pixelData.size() != bpp * width * height * depth) { @@ -1207,7 +1254,7 @@ for (unsigned int z = 0; z < depth; z++) { - ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, z); + ImageBuffer3D::SliceWriter writer(GetPixelData(), VolumeProjection_Axial, z); assert (writer.GetAccessor().GetWidth() == width && writer.GetAccessor().GetHeight() == height); @@ -1232,7 +1279,7 @@ void SetUncompressedPixelData(const std::string& pixelData) { - switch (image_->GetFormat()) + switch (GetPixelData().GetFormat()) { case Orthanc::PixelFormat_Grayscale32: CopyPixelData(pixelData); @@ -1242,7 +1289,9 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } - revision_ ++; + IncrementRevision(); + + BroadcastMessage(IVolumeImage::ContentUpdatedMessage(*this)); } @@ -1256,7 +1305,7 @@ virtual uint64_t GetRevisionInternal(VolumeProjection projection, unsigned int sliceIndex) const { - return that_.revision_; + return that_.GetRevision(); } virtual const DicomInstanceParameters& GetDicomParameters(VolumeProjection projection, @@ -1268,7 +1317,7 @@ public: ExtractedOrthogonalSlice(const OrthancMultiframeVolumeLoader& that, const CoordinateSystem3D& plane) : - DicomVolumeImageOrthogonalSlice(that.GetImage(), that.GetGeometry(), plane), + DicomVolumeImageOrthogonalSlice(that.GetPixelData(), that.GetGeometry(), plane), that_(that) { } @@ -1281,8 +1330,7 @@ IObserver(oracleObservable.GetBroker()), IObservable(oracleObservable.GetBroker()), oracle_(oracle), - active_(false), - revision_(0) + active_(false) { oracleObservable.RegisterObserverCallback( new Callable @@ -1290,37 +1338,15 @@ } - bool HasGeometry() const - { - return (dicom_.get() != NULL && - geometry_.get() != NULL && - image_.get() != NULL); - } - - - const ImageBuffer3D& GetImage() const + virtual bool HasVolumeImage() const { - if (!HasGeometry()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - return *image_; - } + return true; } - - const VolumeImageGeometry& GetGeometry() const + + virtual const IVolumeImage& GetVolumeImage() const { - if (!HasGeometry()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - return *geometry_; - } + return *this; } @@ -1366,7 +1392,7 @@ } - virtual ExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const + virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const { if (HasGeometry()) { @@ -1381,6 +1407,42 @@ + class DicomSeriesVolumeImageReslicer : public IVolumeSlicer + { + private: + class Slice : public IExtractedSlice + { + public: + virtual bool IsValid() + { + } + + virtual uint64_t GetRevision() + { + } + + virtual ISceneLayer* CreateSceneLayer(const ILayerStyleConfigurator* configurator, // possibly absent + const CoordinateSystem3D& cuttingPlane) + { + } + }; + + VolumeReslicer reslicer_; + boost::shared_ptr image_; + + public: + DicomSeriesVolumeImageReslicer(const boost::shared_ptr& image) : + image_(image) + { + } + + virtual IExtractedSlice* ExtractSlice(const CoordinateSystem3D& cuttingPlane) const + { + } + }; + + + class VolumeSceneLayerSource : public boost::noncopyable { private: @@ -1457,7 +1519,7 @@ void Update(const CoordinateSystem3D& plane) { assert(slicer_.get() != NULL); - std::auto_ptr slice(slicer_->ExtractSlice(plane)); + std::auto_ptr slice(slicer_->ExtractSlice(plane)); if (slice.get() == NULL) { @@ -1626,16 +1688,17 @@ } - void Handle(const OrthancStone::VolumeGeometryReadyMessage& message) + void Handle(const OrthancStone::IVolumeImage::GeometryReadyMessage& message) { printf("Geometry ready\n"); - if (&source1_->GetSlicer() == &message.GetOrigin()) + if (source2_->GetSlicer().HasVolumeImage() && + &source2_->GetSlicer().GetVolumeImage() == &message.GetOrigin()) { - //plane_ = message.GetGeometry().GetSagittalGeometry(); - //plane_ = message.GetGeometry().GetAxialGeometry(); - plane_ = message.GetGeometry().GetCoronalGeometry(); - plane_.SetOrigin(message.GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); + //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry(); + //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry(); + plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry(); + plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f)); } Refresh(); @@ -1732,7 +1795,7 @@ { dynamic_cast(*volume).RegisterObserverCallback (new OrthancStone::Callable - (*this, &Toto::Handle)); + (*this, &Toto::Handle)); source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume)); @@ -1748,7 +1811,7 @@ { dynamic_cast(*volume).RegisterObserverCallback (new OrthancStone::Callable - (*this, &Toto::Handle)); + (*this, &Toto::Handle)); source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));