# HG changeset patch # User Sebastien Jodogne # Date 1557759826 -7200 # Node ID 6af3099ed8da6d4c6b055588fc5b7401ca3ee38b # Parent b4fe9642e83b3858d881fabe3d8e3b786e163647 uncoupling OrthancStone::SlicesSorter from OrthancStone::Slice diff -r b4fe9642e83b -r 6af3099ed8da Applications/Samples/SingleFrameApplication.h --- a/Applications/Samples/SingleFrameApplication.h Mon May 13 15:22:08 2019 +0200 +++ b/Applications/Samples/SingleFrameApplication.h Mon May 13 17:03:46 2019 +0200 @@ -136,9 +136,9 @@ slice = 0; } - if (slice >= static_cast(source_->GetSliceCount())) + if (slice >= static_cast(source_->GetSlicesCount())) { - slice = static_cast(source_->GetSliceCount()) - 1; + slice = static_cast(source_->GetSlicesCount()) - 1; } if (slice != static_cast(slice_)) @@ -158,7 +158,7 @@ void SetSlice(size_t index) { if (source_ != NULL && - index < source_->GetSliceCount()) + index < source_->GetSlicesCount()) { slice_ = static_cast(index); @@ -191,7 +191,7 @@ // slice if (source_ == &message.GetOrigin()) { - SetSlice(source_->GetSliceCount() / 2); + SetSlice(source_->GetSlicesCount() / 2); } GetMainWidget().FitContent(); diff -r b4fe9642e83b -r 6af3099ed8da Framework/Layers/DicomSeriesVolumeSlicer.cpp --- a/Framework/Layers/DicomSeriesVolumeSlicer.cpp Mon May 13 15:22:08 2019 +0200 +++ b/Framework/Layers/DicomSeriesVolumeSlicer.cpp Mon May 13 17:03:46 2019 +0200 @@ -34,7 +34,7 @@ void DicomSeriesVolumeSlicer::OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message) { - if (message.GetOrigin().GetSliceCount() > 0) + if (message.GetOrigin().GetSlicesCount() > 0) { BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this)); } diff -r b4fe9642e83b -r 6af3099ed8da Framework/Layers/DicomSeriesVolumeSlicer.h --- a/Framework/Layers/DicomSeriesVolumeSlicer.h Mon May 13 15:22:08 2019 +0200 +++ b/Framework/Layers/DicomSeriesVolumeSlicer.h Mon May 13 17:03:46 2019 +0200 @@ -102,9 +102,9 @@ return quality_; } - size_t GetSliceCount() const + size_t GetSlicesCount() const { - return loader_.GetSliceCount(); + return loader_.GetSlicesCount(); } const Slice& GetSlice(size_t slice) const diff -r b4fe9642e83b -r 6af3099ed8da Framework/Toolbox/CoordinateSystem3D.cpp --- a/Framework/Toolbox/CoordinateSystem3D.cpp Mon May 13 15:22:08 2019 +0200 +++ b/Framework/Toolbox/CoordinateSystem3D.cpp Mon May 13 17:03:46 2019 +0200 @@ -187,4 +187,24 @@ { return GeometryToolbox::IntersectPlaneAndLine(p, normal_, d_, origin, direction); } + + + bool CoordinateSystem3D::GetDistance(double& distance, + const CoordinateSystem3D& a, + const CoordinateSystem3D& b) + { + bool opposite; // Ignored + + if (OrthancStone::GeometryToolbox::IsParallelOrOpposite( + opposite, a.GetNormal(), b.GetNormal())) + { + distance = std::abs(a.ProjectAlongNormal(a.GetOrigin()) - + a.ProjectAlongNormal(b.GetOrigin())); + return true; + } + else + { + return false; + } + } } diff -r b4fe9642e83b -r 6af3099ed8da Framework/Toolbox/CoordinateSystem3D.h --- a/Framework/Toolbox/CoordinateSystem3D.h Mon May 13 15:22:08 2019 +0200 +++ b/Framework/Toolbox/CoordinateSystem3D.h Mon May 13 17:03:46 2019 +0200 @@ -102,5 +102,10 @@ bool IntersectLine(Vector& p, const Vector& origin, const Vector& direction) const; + + // Returns "false" is the two planes are not parallel + static bool GetDistance(double& distance, + const CoordinateSystem3D& a, + const CoordinateSystem3D& b); }; } diff -r b4fe9642e83b -r 6af3099ed8da Framework/Toolbox/LinearAlgebra.h --- a/Framework/Toolbox/LinearAlgebra.h Mon May 13 15:22:08 2019 +0200 +++ b/Framework/Toolbox/LinearAlgebra.h Mon May 13 17:03:46 2019 +0200 @@ -137,7 +137,7 @@ double y, double threshold) { - return fabs(x - y) < threshold; + return fabs(x - y) <= threshold; } inline bool IsNear(double x, diff -r b4fe9642e83b -r 6af3099ed8da Framework/Toolbox/OrthancSlicesLoader.cpp --- a/Framework/Toolbox/OrthancSlicesLoader.cpp Mon May 13 15:22:08 2019 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.cpp Mon May 13 17:03:46 2019 +0200 @@ -193,7 +193,7 @@ { bool ok = false; - if (slices_.GetSliceCount() > 0) + if (slices_.GetSlicesCount() > 0) { Vector normal; if (slices_.SelectNormal(normal)) @@ -209,7 +209,7 @@ if (ok) { - LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)"; + LOG(INFO) << "Loaded a series with " << slices_.GetSlicesCount() << " slice(s)"; BroadcastMessage(SliceGeometryReadyMessage(*this)); } else @@ -256,7 +256,8 @@ std::auto_ptr slice(new Slice); if (slice->ParseOrthancFrame(dicom, instances[i], frame)) { - slices_.AddSlice(slice.release()); + CoordinateSystem3D geometry = slice->GetGeometry(); + slices_.AddSlice(geometry, slice.release()); } else { @@ -291,7 +292,8 @@ std::auto_ptr slice(new Slice); if (slice->ParseOrthancFrame(dicom, instanceId, frame)) { - slices_.AddSlice(slice.release()); + CoordinateSystem3D geometry = slice->GetGeometry(); + slices_.AddSlice(geometry, slice.release()); } else { @@ -322,7 +324,10 @@ if (slice->ParseOrthancFrame(dicom, instanceId, frame)) { LOG(INFO) << "Loaded instance geometry " << instanceId; - slices_.AddSlice(slice.release()); + + CoordinateSystem3D geometry = slice->GetGeometry(); + slices_.AddSlice(geometry, slice.release()); + BroadcastMessage(SliceGeometryReadyMessage(*this)); } else @@ -717,14 +722,14 @@ } - size_t OrthancSlicesLoader::GetSliceCount() const + size_t OrthancSlicesLoader::GetSlicesCount() const { if (state_ != State_GeometryReady) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - return slices_.GetSliceCount(); + return slices_.GetSlicesCount(); } @@ -734,8 +739,8 @@ { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - - return slices_.GetSlice(index); + + return dynamic_cast(slices_.GetSlicePayload(index)); } @@ -746,8 +751,10 @@ { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - - return slices_.LookupSlice(index, plane); + + double distance; + return (slices_.LookupClosestSlice(index, distance, plane) && + distance <= GetSlice(index).GetThickness() / 2.0); } diff -r b4fe9642e83b -r 6af3099ed8da Framework/Toolbox/OrthancSlicesLoader.h --- a/Framework/Toolbox/OrthancSlicesLoader.h Mon May 13 15:22:08 2019 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.h Mon May 13 17:03:46 2019 +0200 @@ -26,6 +26,7 @@ #include "IWebService.h" #include "OrthancApiClient.h" #include "SlicesSorter.h" +#include "Slice.h" #include @@ -195,7 +196,7 @@ bool IsGeometryReady() const; - size_t GetSliceCount() const; + size_t GetSlicesCount() const; const Slice& GetSlice(size_t index) const; diff -r b4fe9642e83b -r 6af3099ed8da Framework/Toolbox/Slice.h --- a/Framework/Toolbox/Slice.h Mon May 13 15:22:08 2019 +0200 +++ b/Framework/Toolbox/Slice.h Mon May 13 17:03:46 2019 +0200 @@ -25,10 +25,11 @@ #include "DicomFrameConverter.h" #include +#include namespace OrthancStone { - class Slice : public boost::noncopyable + class Slice : public Orthanc::IDynamicObject { private: enum Type diff -r b4fe9642e83b -r 6af3099ed8da Framework/Toolbox/SlicesSorter.cpp --- a/Framework/Toolbox/SlicesSorter.cpp Mon May 13 15:22:08 2019 +0200 +++ b/Framework/Toolbox/SlicesSorter.cpp Mon May 13 17:03:46 2019 +0200 @@ -30,25 +30,23 @@ class SlicesSorter::SliceWithDepth : public boost::noncopyable { private: - std::auto_ptr slice_; - double depth_; + CoordinateSystem3D geometry_; + double depth_; + + std::auto_ptr payload_; public: - SliceWithDepth(Slice* slice) : - slice_(slice), - depth_(0) + SliceWithDepth(const CoordinateSystem3D& geometry, + Orthanc::IDynamicObject* payload) : + geometry_(geometry), + depth_(0), + payload_(payload) { - if (slice == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } } void SetNormal(const Vector& normal) { - assert(slice_.get() != NULL); - depth_ = boost::numeric::ublas::inner_prod - (slice_->GetGeometry().GetOrigin(), normal); + depth_ = boost::numeric::ublas::inner_prod(geometry_.GetOrigin(), normal); } double GetDepth() const @@ -56,10 +54,26 @@ return depth_; } - const Slice& GetSlice() const + const CoordinateSystem3D& GetGeometry() const + { + return geometry_; + } + + bool HasPayload() const { - assert(slice_.get() != NULL); - return *slice_; + return (payload_.get() != NULL); + } + + const Orthanc::IDynamicObject& GetPayload() const + { + if (HasPayload()) + { + return *payload_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } } }; @@ -84,21 +98,42 @@ } - void SlicesSorter::AddSlice(Slice* slice) + void SlicesSorter::AddSlice(const CoordinateSystem3D& slice, + Orthanc::IDynamicObject* payload) { - slices_.push_back(new SliceWithDepth(slice)); + slices_.push_back(new SliceWithDepth(slice, payload)); } - const Slice& SlicesSorter::GetSlice(size_t i) const + const SlicesSorter::SliceWithDepth& SlicesSorter::GetSlice(size_t i) const { if (i >= slices_.size()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } + else + { + assert(slices_[i] != NULL); + return *slices_[i]; + } + } - assert(slices_[i] != NULL); - return slices_[i]->GetSlice(); + + const CoordinateSystem3D& SlicesSorter::GetSliceGeometry(size_t i) const + { + return GetSlice(i).GetGeometry(); + } + + + bool SlicesSorter::HasSlicePayload(size_t i) const + { + return GetSlice(i).HasPayload(); + } + + + const Orthanc::IDynamicObject& SlicesSorter::GetSlicePayload(size_t i) const + { + return GetSlice(i).GetPayload(); } @@ -131,7 +166,7 @@ for (size_t i = 0; i < slices_.size(); i++) { - if (GeometryToolbox::IsParallel(normal, slices_[i]->GetSlice().GetGeometry().GetNormal())) + if (GeometryToolbox::IsParallel(normal, slices_[i]->GetGeometry().GetNormal())) { // This slice is compatible with the selected normal slices_[pos] = slices_[i]; @@ -155,7 +190,7 @@ bool found = false; - for (size_t i = 0; !found && i < GetSliceCount(); i++) + for (size_t i = 0; !found && i < GetSlicesCount(); i++) { const Vector& normal = GetSlice(i).GetGeometry().GetNormal(); @@ -190,8 +225,8 @@ for (size_t i = 0; !found && i < normalCandidates.size(); i++) { unsigned int count = normalCount[i]; - if (count == GetSliceCount() || - count + 1 == GetSliceCount()) + if (count == GetSlicesCount() || + count + 1 == GetSlicesCount()) { normal = normalCandidates[i]; found = true; @@ -202,21 +237,34 @@ } - bool SlicesSorter::LookupSlice(size_t& index, - const CoordinateSystem3D& slice) const + bool SlicesSorter::LookupClosestSlice(size_t& index, + double& distance, + const CoordinateSystem3D& slice) const { // TODO Turn this linear-time lookup into a log-time lookup, // keeping track of whether the slices are sorted along the normal + bool found = false; + + distance = std::numeric_limits::infinity(); + for (size_t i = 0; i < slices_.size(); i++) { - if (slices_[i]->GetSlice().ContainsPlane(slice)) + assert(slices_[i] != NULL); + + double tmp; + if (CoordinateSystem3D::GetDistance(tmp, slices_[i]->GetGeometry(), slice)) { - index = i; - return true; + if (!found || + tmp < distance) + { + index = i; + distance = tmp; + found = true; + } } } - return false; + return found; } } diff -r b4fe9642e83b -r 6af3099ed8da Framework/Toolbox/SlicesSorter.h --- a/Framework/Toolbox/SlicesSorter.h Mon May 13 15:22:08 2019 +0200 +++ b/Framework/Toolbox/SlicesSorter.h Mon May 13 17:03:46 2019 +0200 @@ -21,10 +21,13 @@ #pragma once -#include "Slice.h" +#include "CoordinateSystem3D.h" + +#include namespace OrthancStone { + // TODO - Rename this as "PlanesSorter" class SlicesSorter : public boost::noncopyable { private: @@ -36,6 +39,8 @@ Slices slices_; bool hasNormal_; + const SliceWithDepth& GetSlice(size_t i) const; + public: SlicesSorter() : hasNormal_(false) { @@ -48,15 +53,25 @@ slices_.reserve(count); } - void AddSlice(Slice* slice); // Takes ownership + void AddSlice(const CoordinateSystem3D& plane) + { + AddSlice(plane, NULL); + } - size_t GetSliceCount() const + void AddSlice(const CoordinateSystem3D& plane, + Orthanc::IDynamicObject* payload); // Takes ownership + + size_t GetSlicesCount() const { return slices_.size(); } - const Slice& GetSlice(size_t i) const; + const CoordinateSystem3D& GetSliceGeometry(size_t i) const; + bool HasSlicePayload(size_t i) const; + + const Orthanc::IDynamicObject& GetSlicePayload(size_t i) const; + void SetNormal(const Vector& normal); void Sort(); @@ -65,7 +80,8 @@ bool SelectNormal(Vector& normal) const; - bool LookupSlice(size_t& index, - const CoordinateSystem3D& slice) const; + bool LookupClosestSlice(size_t& index, + double& distance, + const CoordinateSystem3D& slice) const; }; } diff -r b4fe9642e83b -r 6af3099ed8da Framework/dev.h --- a/Framework/dev.h Mon May 13 15:22:08 2019 +0200 +++ b/Framework/dev.h Mon May 13 17:03:46 2019 +0200 @@ -110,14 +110,14 @@ { assert(&message.GetOrigin() == &loader_); - if (loader_.GetSliceCount() == 0) + if (loader_.GetSlicesCount() == 0) { LOG(ERROR) << "Empty volume image"; BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this)); return; } - for (size_t i = 1; i < loader_.GetSliceCount(); i++) + for (size_t i = 1; i < loader_.GetSlicesCount(); i++) { if (!IsCompatible(loader_.GetSlice(0), loader_.GetSlice(i))) { @@ -128,7 +128,7 @@ double spacingZ; - if (loader_.GetSliceCount() > 1) + if (loader_.GetSlicesCount() > 1) { spacingZ = GetDistance(loader_.GetSlice(0), loader_.GetSlice(1)); } @@ -139,7 +139,7 @@ spacingZ = 1; } - for (size_t i = 1; i < loader_.GetSliceCount(); i++) + for (size_t i = 1; i < loader_.GetSlicesCount(); i++) { if (!LinearAlgebra::IsNear(spacingZ, GetDistance(loader_.GetSlice(i - 1), loader_.GetSlice(i)), 0.001 /* this is expressed in mm */)) @@ -154,16 +154,16 @@ unsigned int height = loader_.GetSlice(0).GetHeight(); Orthanc::PixelFormat format = loader_.GetSlice(0).GetConverter().GetExpectedPixelFormat(); LOG(INFO) << "Creating a volume image of size " << width << "x" << height - << "x" << loader_.GetSliceCount() << " in " << Orthanc::EnumerationToString(format); + << "x" << loader_.GetSlicesCount() << " in " << Orthanc::EnumerationToString(format); - image_.reset(new ImageBuffer3D(format, width, height, static_cast(loader_.GetSliceCount()), computeRange_)); + image_.reset(new ImageBuffer3D(format, width, height, static_cast(loader_.GetSlicesCount()), computeRange_)); image_->SetAxialGeometry(loader_.GetSlice(0).GetGeometry()); image_->SetVoxelDimensions(loader_.GetSlice(0).GetPixelSpacingX(), loader_.GetSlice(0).GetPixelSpacingY(), spacingZ); image_->Clear(); - downloadStack_.reset(new DownloadStack(static_cast(loader_.GetSliceCount()))); - pendingSlices_ = loader_.GetSliceCount(); + downloadStack_.reset(new DownloadStack(static_cast(loader_.GetSlicesCount()))); + pendingSlices_ = loader_.GetSlicesCount(); for (unsigned int i = 0; i < 4; i++) // Limit to 4 simultaneous downloads { @@ -263,9 +263,9 @@ loader_.ScheduleLoadFrame(instanceId, frame); } - virtual size_t GetSliceCount() const + virtual size_t GetSlicesCount() const { - return loader_.GetSliceCount(); + return loader_.GetSlicesCount(); } virtual const Slice& GetSlice(size_t index) const @@ -317,7 +317,7 @@ { double thickness; - size_t n = volume.GetSliceCount(); + size_t n = volume.GetSlicesCount(); if (n > 1) { const Slice& a = volume.GetSlice(0); @@ -349,7 +349,7 @@ width_ = axial.GetWidth(); height_ = axial.GetHeight(); - depth_ = volume.GetSliceCount(); + depth_ = volume.GetSlicesCount(); pixelSpacingX_ = axial.GetPixelSpacingX(); pixelSpacingY_ = axial.GetPixelSpacingY(); @@ -364,7 +364,7 @@ double axialThickness = ComputeAxialThickness(volume); width_ = axial.GetWidth(); - height_ = static_cast(volume.GetSliceCount()); + height_ = static_cast(volume.GetSlicesCount()); depth_ = axial.GetHeight(); pixelSpacingX_ = axial.GetPixelSpacingX(); @@ -372,7 +372,7 @@ sliceThickness_ = axial.GetPixelSpacingY(); Vector origin = axial.GetGeometry().GetOrigin(); - origin += (static_cast(volume.GetSliceCount() - 1) * + origin += (static_cast(volume.GetSlicesCount() - 1) * axialThickness * axial.GetGeometry().GetNormal()); reference_ = CoordinateSystem3D(origin, @@ -386,7 +386,7 @@ double axialThickness = ComputeAxialThickness(volume); width_ = axial.GetHeight(); - height_ = static_cast(volume.GetSliceCount()); + height_ = static_cast(volume.GetSlicesCount()); depth_ = axial.GetWidth(); pixelSpacingX_ = axial.GetPixelSpacingY(); @@ -394,7 +394,7 @@ sliceThickness_ = axial.GetPixelSpacingX(); Vector origin = axial.GetGeometry().GetOrigin(); - origin += (static_cast(volume.GetSliceCount() - 1) * + origin += (static_cast(volume.GetSlicesCount() - 1) * axialThickness * axial.GetGeometry().GetNormal()); reference_ = CoordinateSystem3D(origin, @@ -406,7 +406,7 @@ VolumeImageGeometry(const OrthancVolumeImage& volume, VolumeProjection projection) { - if (volume.GetSliceCount() == 0) + if (volume.GetSlicesCount() == 0) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } @@ -432,7 +432,7 @@ } } - size_t GetSliceCount() const + size_t GetSlicesCount() const { return depth_; } @@ -728,7 +728,7 @@ dynamic_cast(message.GetOrigin()); slices_.reset(new VolumeImageGeometry(image, projection_)); - SetSlice(slices_->GetSliceCount() / 2); + SetSlice(slices_->GetSlicesCount() / 2); widget_.FitContent(); } @@ -817,7 +817,7 @@ return slices_.get() != NULL; } - size_t GetSliceCount() const + size_t GetSlicesCount() const { if (slices_.get() == NULL) { @@ -825,7 +825,7 @@ } else { - return slices_->GetSliceCount(); + return slices_->GetSlicesCount(); } } @@ -840,9 +840,9 @@ slice = 0; } - if (slice >= static_cast(slices_->GetSliceCount())) + if (slice >= static_cast(slices_->GetSlicesCount())) { - slice = static_cast(slices_->GetSliceCount()) - 1; + slice = static_cast(slices_->GetSlicesCount()) - 1; } if (slice != static_cast(slice_)) diff -r b4fe9642e83b -r 6af3099ed8da Samples/Sdl/Loader.cpp --- a/Samples/Sdl/Loader.cpp Mon May 13 15:22:08 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Mon May 13 17:03:46 2019 +0200 @@ -899,8 +899,6 @@ if (object.get() != NULL) { - printf("===========================> REQUEST\n"); - const Item& item = dynamic_cast(*object); try @@ -1374,13 +1372,10 @@ tmp = GetFrameGeometry(frame); } - bool opposite; // Ignored - return (OrthancStone::GeometryToolbox::IsParallelOrOpposite( - opposite, tmp.GetNormal(), plane.GetNormal()) && - OrthancStone::LinearAlgebra::IsNear( - tmp.ProjectAlongNormal(tmp.GetOrigin()), - tmp.ProjectAlongNormal(plane.GetOrigin()), - thickness_ / 2.0)); + double distance; + + return (OrthancStone::CoordinateSystem3D::GetDistance(distance, tmp, plan) && + OrthancStone::LinearAlgebra::IsNear(distance, thickness_ / 2.0)); } bool IsColor() const @@ -1497,6 +1492,7 @@ dicom.FromDicomAsJson(value[instances[i]]); DicomInstanceParameters instance(dicom); + } } };