# HG changeset patch # User Benjamin Golinvaux # Date 1557938390 -7200 # Node ID 28dca6cd827b485d9e36d3ed8e35ad0bb43740a4 # Parent 5dd496343fad9be493698f9d9974db2f853adc12# Parent 3805ffa2833d12585ae0f594eca6b438ce4d35f5 Merge default diff -r 5dd496343fad -r 28dca6cd827b Framework/Toolbox/SlicesSorter.cpp --- a/Framework/Toolbox/SlicesSorter.cpp Wed May 15 18:29:42 2019 +0200 +++ b/Framework/Toolbox/SlicesSorter.cpp Wed May 15 18:39:50 2019 +0200 @@ -285,4 +285,42 @@ return found; } + + + double SlicesSorter::ComputeSpacingBetweenSlices() const + { + if (GetSlicesCount() <= 1) + { + // This is a volume that is empty or that contains one single + // slice: Choose a dummy z-dimension for voxels + return 1.0; + } + + const OrthancStone::CoordinateSystem3D& reference = GetSliceGeometry(0); + + double referencePosition = reference.ProjectAlongNormal(reference.GetOrigin()); + + double p = reference.ProjectAlongNormal(GetSliceGeometry(1).GetOrigin()); + double spacingZ = p - referencePosition; + + if (spacingZ <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, + "Please call the Sort() method before"); + } + + for (size_t i = 1; i < GetSlicesCount(); i++) + { + OrthancStone::Vector p = reference.GetOrigin() + spacingZ * static_cast(i) * reference.GetNormal(); + double d = boost::numeric::ublas::norm_2(p - GetSliceGeometry(i).GetOrigin()); + + if (!OrthancStone::LinearAlgebra::IsNear(d, 0, 0.001 /* tolerance expressed in mm */)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, + "The origins of the slices of a volume image are not regularly spaced"); + } + } + + return spacingZ; + } } diff -r 5dd496343fad -r 28dca6cd827b Framework/Toolbox/SlicesSorter.h --- a/Framework/Toolbox/SlicesSorter.h Wed May 15 18:29:42 2019 +0200 +++ b/Framework/Toolbox/SlicesSorter.h Wed May 15 18:39:50 2019 +0200 @@ -88,5 +88,8 @@ bool LookupClosestSlice(size_t& index, double& distance, const CoordinateSystem3D& slice) const; + + // WARNING - The slices must have been sorted before calling this method + double ComputeSpacingBetweenSlices() const; }; } diff -r 5dd496343fad -r 28dca6cd827b Framework/Volumes/ImageBuffer3D.cpp --- a/Framework/Volumes/ImageBuffer3D.cpp Wed May 15 18:29:42 2019 +0200 +++ b/Framework/Volumes/ImageBuffer3D.cpp Wed May 15 18:39:50 2019 +0200 @@ -118,8 +118,9 @@ { LinearAlgebra::AssignVector(voxelDimensions_, 1, 1, 1); - LOG(INFO) << "Created an image of " - << (GetEstimatedMemorySize() / (1024ll * 1024ll)) << "MB"; + LOG(INFO) << "Created a 3D image of size " << width << "x" << height + << "x" << depth << " in " << Orthanc::EnumerationToString(format) + << " (" << (GetEstimatedMemorySize() / (1024ll * 1024ll)) << "MB)"; } diff -r 5dd496343fad -r 28dca6cd827b Resources/CMake/OrthancStoneConfiguration.cmake --- a/Resources/CMake/OrthancStoneConfiguration.cmake Wed May 15 18:29:42 2019 +0200 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Wed May 15 18:39:50 2019 +0200 @@ -147,7 +147,7 @@ if (ENABLE_OPENGL) if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") - # If including "FindOpenGL.cmake" using Emscripten (targetting + # If including "FindOpenGL.cmake" using Emscripten (targeting # WebAssembly), the "OPENGL_LIBRARIES" value incorrectly includes # the "nul" library, which leads to warning message in Emscripten: # 'shared:WARNING: emcc: cannot find library "nul"'. diff -r 5dd496343fad -r 28dca6cd827b Samples/Sdl/Loader.cpp --- a/Samples/Sdl/Loader.cpp Wed May 15 18:29:42 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Wed May 15 18:39:50 2019 +0200 @@ -1143,258 +1143,288 @@ public Orthanc::IDynamicObject /* to be used as a payload of SlicesSorter */ { private: - Orthanc::DicomImageInformation imageInformation_; - OrthancStone::SopClassUid sopClassUid_; - double thickness_; - double pixelSpacingX_; - double pixelSpacingY_; - OrthancStone::CoordinateSystem3D geometry_; - OrthancStone::Vector frameOffsets_; - bool isColor_; - bool hasRescale_; - double rescaleOffset_; - double rescaleSlope_; - bool hasDefaultWindowing_; - float defaultWindowingCenter_; - float defaultWindowingWidth_; - Orthanc::PixelFormat expectedPixelFormat_; + struct Data // Struct to ease the copy constructor + { + Orthanc::DicomImageInformation imageInformation_; + OrthancStone::SopClassUid sopClassUid_; + double thickness_; + double pixelSpacingX_; + double pixelSpacingY_; + OrthancStone::CoordinateSystem3D geometry_; + OrthancStone::Vector frameOffsets_; + bool isColor_; + bool hasRescale_; + double rescaleOffset_; + double rescaleSlope_; + bool hasDefaultWindowing_; + float defaultWindowingCenter_; + float defaultWindowingWidth_; + Orthanc::PixelFormat expectedPixelFormat_; + + void ComputeDoseOffsets(const Orthanc::DicomMap& dicom) + { + // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html + + { + std::string increment; - void ComputeDoseOffsets(const Orthanc::DicomMap& dicom) - { - // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html - - { - std::string increment; + if (dicom.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false)) + { + Orthanc::Toolbox::ToUpperCase(increment); + if (increment != "3004,000C") // This is the "Grid Frame Offset Vector" tag + { + LOG(ERROR) << "RT-DOSE: Bad value for the \"FrameIncrementPointer\" tag"; + return; + } + } + } - if (dicom.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false)) + if (!OrthancStone::LinearAlgebra::ParseVector(frameOffsets_, dicom, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR) || + frameOffsets_.size() < imageInformation_.GetNumberOfFrames()) + { + LOG(ERROR) << "RT-DOSE: No information about the 3D location of some slice(s)"; + frameOffsets_.clear(); + } + else { - Orthanc::Toolbox::ToUpperCase(increment); - if (increment != "3004,000C") // This is the "Grid Frame Offset Vector" tag + if (frameOffsets_.size() >= 2) { - LOG(ERROR) << "RT-DOSE: Bad value for the \"FrameIncrementPointer\" tag"; - return; + thickness_ = frameOffsets_[1] - frameOffsets_[0]; + + if (thickness_ < 0) + { + thickness_ = -thickness_; + } } } } - if (!OrthancStone::LinearAlgebra::ParseVector(frameOffsets_, dicom, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR) || - frameOffsets_.size() < imageInformation_.GetNumberOfFrames()) - { - LOG(ERROR) << "RT-DOSE: No information about the 3D location of some slice(s)"; - frameOffsets_.clear(); - } - else - { - if (frameOffsets_.size() >= 2) - { - thickness_ = frameOffsets_[1] - frameOffsets_[0]; - - if (thickness_ < 0) - { - thickness_ = -thickness_; - } - } - } - } - - public: - DicomInstanceParameters(const Orthanc::DicomMap& dicom) : - imageInformation_(dicom) - { - if (imageInformation_.GetNumberOfFrames() <= 0) + Data(const Orthanc::DicomMap& dicom) : + imageInformation_(dicom) { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } + if (imageInformation_.GetNumberOfFrames() <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } - std::string s; - if (!dicom.CopyToString(s, Orthanc::DICOM_TAG_SOP_CLASS_UID, false)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - else - { - sopClassUid_ = OrthancStone::StringToSopClassUid(s); - } + std::string s; + if (!dicom.CopyToString(s, Orthanc::DICOM_TAG_SOP_CLASS_UID, false)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + sopClassUid_ = OrthancStone::StringToSopClassUid(s); + } + + if (!dicom.ParseDouble(thickness_, Orthanc::DICOM_TAG_SLICE_THICKNESS)) + { + thickness_ = 100.0 * std::numeric_limits::epsilon(); + } + + OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dicom); + + std::string position, orientation; + if (dicom.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) && + dicom.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false)) + { + geometry_ = OrthancStone::CoordinateSystem3D(position, orientation); + } + + if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) + { + ComputeDoseOffsets(dicom); + } + + isColor_ = (imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome1 && + imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome2); + + double doseGridScaling; - if (!dicom.ParseDouble(thickness_, Orthanc::DICOM_TAG_SLICE_THICKNESS)) - { - thickness_ = 100.0 * std::numeric_limits::epsilon(); - } - - OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dicom); + if (dicom.ParseDouble(rescaleOffset_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) && + dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE)) + { + hasRescale_ = true; + } + else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING)) + { + hasRescale_ = true; + rescaleOffset_ = 0; + rescaleSlope_ = doseGridScaling; + } + else + { + hasRescale_ = false; + } - std::string position, orientation; - if (dicom.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) && - dicom.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false)) - { - geometry_ = OrthancStone::CoordinateSystem3D(position, orientation); - } + OrthancStone::Vector c, w; + if (OrthancStone::LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) && + OrthancStone::LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) && + c.size() > 0 && + w.size() > 0) + { + hasDefaultWindowing_ = true; + defaultWindowingCenter_ = static_cast(c[0]); + defaultWindowingWidth_ = static_cast(w[0]); + } + else + { + hasDefaultWindowing_ = false; + } - if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) - { - ComputeDoseOffsets(dicom); + if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) + { + switch (imageInformation_.GetBitsStored()) + { + case 16: + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; + break; + + case 32: + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + else if (isColor_) + { + expectedPixelFormat_ = Orthanc::PixelFormat_RGB24; + } + else if (imageInformation_.IsSigned()) + { + expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16; + } + else + { + expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; + } } - isColor_ = (imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome1 && - imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome2); - - double doseGridScaling; - - if (dicom.ParseDouble(rescaleOffset_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) && - dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE)) + OrthancStone::CoordinateSystem3D GetFrameGeometry(unsigned int frame) const { - hasRescale_ = true; - } - else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING)) - { - hasRescale_ = true; - rescaleOffset_ = 0; - rescaleSlope_ = doseGridScaling; - } - else - { - hasRescale_ = false; + if (frame == 0) + { + return geometry_; + } + else if (frame >= imageInformation_.GetNumberOfFrames()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) + { + if (frame >= frameOffsets_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + return OrthancStone::CoordinateSystem3D( + geometry_.GetOrigin() + frameOffsets_[frame] * geometry_.GetNormal(), + geometry_.GetAxisX(), + geometry_.GetAxisY()); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } } - OrthancStone::Vector c, w; - if (OrthancStone::LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) && - OrthancStone::LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) && - c.size() > 0 && - w.size() > 0) + // TODO - Is this necessary? + bool FrameContainsPlane(unsigned int frame, + const OrthancStone::CoordinateSystem3D& plane) const { - hasDefaultWindowing_ = true; - defaultWindowingCenter_ = static_cast(c[0]); - defaultWindowingWidth_ = static_cast(w[0]); - } - else - { - hasDefaultWindowing_ = false; - } + if (frame >= imageInformation_.GetNumberOfFrames()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } - if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) - { - switch (imageInformation_.GetBitsStored()) + OrthancStone::CoordinateSystem3D tmp = geometry_; + + if (frame != 0) { - case 16: - expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; - break; + tmp = GetFrameGeometry(frame); + } - case 32: - expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32; - break; + double distance; - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } + return (OrthancStone::CoordinateSystem3D::GetDistance(distance, tmp, plane) && + distance <= thickness_ / 2.0); } - else if (isColor_) - { - expectedPixelFormat_ = Orthanc::PixelFormat_RGB24; - } - else if (imageInformation_.IsSigned()) - { - expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16; - } - else - { - expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16; - } + }; + + Data data_; + + + public: + DicomInstanceParameters(const DicomInstanceParameters& other) : + data_(other.data_) + { + } + + + DicomInstanceParameters(const Orthanc::DicomMap& dicom) : + data_(dicom) + { } const Orthanc::DicomImageInformation& GetImageInformation() const { - return imageInformation_; + return data_.imageInformation_; } OrthancStone::SopClassUid GetSopClassUid() const { - return sopClassUid_; + return data_.sopClassUid_; } double GetThickness() const { - return thickness_; + return data_.thickness_; } double GetPixelSpacingX() const { - return pixelSpacingX_; + return data_.pixelSpacingX_; } double GetPixelSpacingY() const { - return pixelSpacingY_; + return data_.pixelSpacingY_; } const OrthancStone::CoordinateSystem3D& GetGeometry() const { - return geometry_; + return data_.geometry_; } OrthancStone::CoordinateSystem3D GetFrameGeometry(unsigned int frame) const { - if (frame == 0) - { - return geometry_; - } - else if (frame >= imageInformation_.GetNumberOfFrames()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) - { - if (frame >= frameOffsets_.size()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - return OrthancStone::CoordinateSystem3D( - geometry_.GetOrigin() + frameOffsets_[frame] * geometry_.GetNormal(), - geometry_.GetAxisX(), - geometry_.GetAxisY()); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } + return data_.GetFrameGeometry(frame); } + // TODO - Is this necessary? bool FrameContainsPlane(unsigned int frame, const OrthancStone::CoordinateSystem3D& plane) const { - if (frame >= imageInformation_.GetNumberOfFrames()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - OrthancStone::CoordinateSystem3D tmp = geometry_; - - if (frame != 0) - { - tmp = GetFrameGeometry(frame); - } - - double distance; - - return (OrthancStone::CoordinateSystem3D::GetDistance(distance, tmp, plane) && - distance <= thickness_ / 2.0); + return data_.FrameContainsPlane(frame, plane); } bool IsColor() const { - return isColor_; + return data_.isColor_; } bool HasRescale() const { - return hasRescale_; + return data_.hasRescale_; } double GetRescaleOffset() const { - if (hasRescale_) + if (data_.hasRescale_) { - return rescaleOffset_; + return data_.rescaleOffset_; } else { @@ -1404,9 +1434,9 @@ double GetRescaleSlope() const { - if (hasRescale_) + if (data_.hasRescale_) { - return rescaleSlope_; + return data_.rescaleSlope_; } else { @@ -1416,14 +1446,14 @@ bool HasDefaultWindowing() const { - return hasDefaultWindowing_; + return data_.hasDefaultWindowing_; } float GetDefaultWindowingCenter() const { - if (hasDefaultWindowing_) + if (data_.hasDefaultWindowing_) { - return defaultWindowingCenter_; + return data_.defaultWindowingCenter_; } else { @@ -1433,9 +1463,9 @@ float GetDefaultWindowingWidth() const { - if (hasDefaultWindowing_) + if (data_.hasDefaultWindowing_) { - return defaultWindowingWidth_; + return data_.defaultWindowingWidth_; } else { @@ -1445,11 +1475,161 @@ Orthanc::PixelFormat GetExpectedPixelFormat() const { - return expectedPixelFormat_; + return data_.expectedPixelFormat_; } }; + class DicomVolumeImage : public boost::noncopyable + { + private: + std::auto_ptr image_; + std::vector slices_; + + static const DicomInstanceParameters& + GetSliceParameters(const OrthancStone::SlicesSorter& slices, + size_t index) + { + return dynamic_cast(slices.GetSlicePayload(index)); + } + + static void CheckSlice(const OrthancStone::SlicesSorter& slices, + size_t index, + const OrthancStone::CoordinateSystem3D& reference, + const DicomInstanceParameters& a) + { + const OrthancStone::CoordinateSystem3D& slice = slices.GetSliceGeometry(index); + const DicomInstanceParameters& b = GetSliceParameters(slices, index); + + if (!OrthancStone::GeometryToolbox::IsParallel(reference.GetNormal(), slice.GetNormal())) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, + "A slice in the volume image is not parallel to the others"); + } + + if (a.GetExpectedPixelFormat() != b.GetExpectedPixelFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat, + "The pixel format changes across the slices of the volume image"); + } + + if (a.GetImageInformation().GetWidth() != b.GetImageInformation().GetWidth() || + a.GetImageInformation().GetHeight() != b.GetImageInformation().GetHeight()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize, + "The width/height of slices are not constant in the volume image"); + } + + if (!OrthancStone::LinearAlgebra::IsNear(a.GetPixelSpacingX(), b.GetPixelSpacingX()) || + !OrthancStone::LinearAlgebra::IsNear(a.GetPixelSpacingY(), b.GetPixelSpacingY())) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, + "The pixel spacing of the slices change across the volume image"); + } + } + + + static void CheckVolume(const OrthancStone::SlicesSorter& slices) + { + for (size_t i = 0; i < slices.GetSlicesCount(); i++) + { + if (GetSliceParameters(slices, i).GetImageInformation().GetNumberOfFrames() != 1) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, + "This class does not support multi-frame images"); + } + } + + if (slices.GetSlicesCount() != 0) + { + const OrthancStone::CoordinateSystem3D& reference = slices.GetSliceGeometry(0); + const DicomInstanceParameters& dicom = GetSliceParameters(slices, 0); + + for (size_t i = 1; i < slices.GetSlicesCount(); i++) + { + CheckSlice(slices, i, reference, dicom); + } + } + } + + + void Clear() + { + image_.reset(); + + for (size_t i = 0; i < slices_.size(); i++) + { + assert(slices_[i] != NULL); + delete slices_[i]; + } + } + + + public: + DicomVolumeImage() + { + } + + ~DicomVolumeImage() + { + Clear(); + } + + // WARNING: The payload of "slices" must be of class "DicomInstanceParameters" + void SetGeometry(OrthancStone::SlicesSorter& slices) + { + Clear(); + + if (!slices.Sort()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, + "Cannot sort the 3D slices of a DICOM series"); + } + + slices_.reserve(slices.GetSlicesCount()); + + for (size_t i = 0; i < slices.GetSlicesCount(); i++) + { + slices_.push_back(new DicomInstanceParameters(GetSliceParameters(slices, i))); + } + + CheckVolume(slices); + + const double spacingZ = slices.ComputeSpacingBetweenSlices(); + LOG(INFO) << "Computed spacing between slices: " << spacingZ << "mm"; + + const DicomInstanceParameters& parameters = GetSliceParameters(slices, 0); + + image_.reset(new OrthancStone::ImageBuffer3D(parameters.GetExpectedPixelFormat(), + parameters.GetImageInformation().GetWidth(), + parameters.GetImageInformation().GetHeight(), + slices.GetSlicesCount(), false /* don't compute range */)); + + image_->SetAxialGeometry(slices.GetSliceGeometry(0)); + image_->SetVoxelDimensions(parameters.GetPixelSpacingX(), parameters.GetPixelSpacingY(), spacingZ); + image_->Clear(); + } + + bool IsGeometryReady() const + { + return (image_.get() != NULL); + } + + const OrthancStone::ImageBuffer3D& GetImage() const + { + if (!IsGeometryReady()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return *image_; + } + } + }; + + + class AxialVolumeOrthancLoader : public OrthancStone::IObserver { private: @@ -1488,6 +1668,8 @@ Json::Value::Members instances = value.getMemberNames(); + OrthancStone::SlicesSorter slices; + for (size_t i = 0; i < instances.size(); i++) { Orthanc::DicomMap dicom; @@ -1496,16 +1678,10 @@ std::auto_ptr instance(new DicomInstanceParameters(dicom)); OrthancStone::CoordinateSystem3D geometry = instance->GetGeometry(); - that_.slices_.AddSlice(geometry, instance.release()); + slices.AddSlice(geometry, instance.release()); } - if (!that_.slices_.Sort()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, - "Cannot sort the 3D slices of a DICOM series"); - } - - printf("series sorted %d => %d\n", instances.size(), that_.slices_.GetSlicesCount()); + that_.image_.SetGeometry(slices); } }; @@ -1539,9 +1715,8 @@ }; - bool active_; - std::auto_ptr image_; - OrthancStone::SlicesSorter slices_; + bool active_; + DicomVolumeImage image_; public: AxialVolumeOrthancLoader(OrthancStone::IObservable& oracle) :