# HG changeset patch # User Benjamin Golinvaux # Date 1558427323 -7200 # Node ID 562cdd083b9e53a40cb003344893d10a33364425 # Parent 28b9e3a542003cedef439d32718f927199cd430e# Parent d2c0e347ddc284f43c343b707f0ba1d1e2070da2 Merge from default diff -r 28b9e3a54200 -r 562cdd083b9e Applications/Samples/SimpleViewer/Messages.h --- a/Applications/Samples/SimpleViewer/Messages.h Tue May 21 10:27:54 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -#pragma once - -namespace SimpleViewer -{ - enum SimpleViewerMessageType - { - SimpleViewerMessageType_First = OrthancStone::MessageType_CustomMessage, - SimpleViewerMessageType_AppStatusUpdated - }; -} diff -r 28b9e3a54200 -r 562cdd083b9e Applications/Samples/SimpleViewer/SimpleViewerApplication.h --- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.h Tue May 21 10:27:54 2019 +0200 +++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.h Tue May 21 10:28:43 2019 +0200 @@ -51,7 +51,6 @@ #include "ThumbnailInteractor.h" #include "MainWidgetInteractor.h" #include "AppStatus.h" -#include "Messages.h" using namespace OrthancStone; @@ -67,13 +66,14 @@ { public: - struct StatusUpdatedMessage : public BaseMessage + struct StatusUpdatedMessage : public IMessage { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + const AppStatus& status_; StatusUpdatedMessage(const AppStatus& status) - : BaseMessage(), - status_(status) + : status_(status) { } }; diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Layers/DicomSeriesVolumeSlicer.h --- a/Framework/Layers/DicomSeriesVolumeSlicer.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Layers/DicomSeriesVolumeSlicer.h Tue May 21 10:28:43 2019 +0200 @@ -45,13 +45,13 @@ private: const Orthanc::ImageAccessor& frame_; SliceImageQuality imageQuality_; - const Slice& slice_; + const Deprecated::Slice& slice_; public: FrameReadyMessage(DicomSeriesVolumeSlicer& origin, const Orthanc::ImageAccessor& frame, SliceImageQuality imageQuality, - const Slice& slice) : + const Deprecated::Slice& slice) : OriginMessage(origin), frame_(frame), imageQuality_(imageQuality), @@ -69,7 +69,7 @@ return imageQuality_; } - const Slice& GetSlice() const + const Deprecated::Slice& GetSlice() const { return slice_; } @@ -107,7 +107,7 @@ return loader_.GetSlicesCount(); } - const Slice& GetSlice(size_t slice) const + const Deprecated::Slice& GetSlice(size_t slice) const { return loader_.GetSlice(slice); } diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Layers/FrameRenderer.cpp --- a/Framework/Layers/FrameRenderer.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Layers/FrameRenderer.cpp Tue May 21 10:28:43 2019 +0200 @@ -118,7 +118,7 @@ ILayerRenderer* FrameRenderer::CreateRenderer(const Orthanc::ImageAccessor& frame, - const Slice& framePlane, + const Deprecated::Slice& framePlane, bool isFullQuality) { if (frame.GetFormat() == Orthanc::PixelFormat_RGB24) diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Layers/FrameRenderer.h --- a/Framework/Layers/FrameRenderer.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Layers/FrameRenderer.h Tue May 21 10:28:43 2019 +0200 @@ -63,7 +63,7 @@ // TODO: Avoid cloning the "frame" static ILayerRenderer* CreateRenderer(const Orthanc::ImageAccessor& frame, - const Slice& framePlane, + const Deprecated::Slice& framePlane, bool isFullQuality); }; } diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Layers/GrayscaleFrameRenderer.cpp --- a/Framework/Layers/GrayscaleFrameRenderer.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Layers/GrayscaleFrameRenderer.cpp Tue May 21 10:28:43 2019 +0200 @@ -114,7 +114,7 @@ GrayscaleFrameRenderer::GrayscaleFrameRenderer(const Orthanc::ImageAccessor& frame, - const DicomFrameConverter& converter, + const Deprecated::DicomFrameConverter& converter, const CoordinateSystem3D& framePlane, double pixelSpacingX, double pixelSpacingY, diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Layers/GrayscaleFrameRenderer.h --- a/Framework/Layers/GrayscaleFrameRenderer.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Layers/GrayscaleFrameRenderer.h Tue May 21 10:28:43 2019 +0200 @@ -39,7 +39,7 @@ public: GrayscaleFrameRenderer(const Orthanc::ImageAccessor& frame, - const DicomFrameConverter& converter, + const Deprecated::DicomFrameConverter& converter, const CoordinateSystem3D& framePlane, double pixelSpacingX, double pixelSpacingY, diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Layers/IVolumeSlicer.h --- a/Framework/Layers/IVolumeSlicer.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Layers/IVolumeSlicer.h Tue May 21 10:28:43 2019 +0200 @@ -42,17 +42,17 @@ ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); private: - const Slice& slice_; + const Deprecated::Slice& slice_; public: SliceContentChangedMessage(IVolumeSlicer& origin, - const Slice& slice) : + const Deprecated::Slice& slice) : OriginMessage(origin), slice_(slice) { } - const Slice& GetSlice() const + const Deprecated::Slice& GetSlice() const { return slice_; } diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Layers/SliceOutlineRenderer.h --- a/Framework/Layers/SliceOutlineRenderer.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Layers/SliceOutlineRenderer.h Tue May 21 10:28:43 2019 +0200 @@ -37,7 +37,7 @@ RenderStyle style_; public: - SliceOutlineRenderer(const Slice& slice) : + SliceOutlineRenderer(const Deprecated::Slice& slice) : geometry_(slice.GetGeometry()), pixelSpacingX_(slice.GetPixelSpacingX()), pixelSpacingY_(slice.GetPixelSpacingY()), diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Loaders/BasicFetchingItemsSorter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Loaders/BasicFetchingItemsSorter.cpp Tue May 21 10:28:43 2019 +0200 @@ -0,0 +1,74 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "BasicFetchingItemsSorter.h" + +#include + +namespace OrthancStone +{ + BasicFetchingItemsSorter::BasicFetchingItemsSorter(unsigned int itemsCount) : + itemsCount_(itemsCount) + { + if (itemsCount == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + void BasicFetchingItemsSorter::Sort(std::vector& target, + unsigned int current) + { + if (current >= itemsCount_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + target.clear(); + target.reserve(itemsCount_); + target.push_back(current); + + const unsigned int countBelow = current; + const unsigned int countAbove = (itemsCount_ - 1) - current; + const unsigned int n = std::min(countBelow, countAbove); + + for (unsigned int i = 1; i <= n; i++) + { + assert(current + i < itemsCount_ && + current >= i); + target.push_back(current + i); + target.push_back(current - i); + } + + for (unsigned int i = current - n; i > 0; i--) + { + target.push_back(i - 1); + } + + for (unsigned int i = current + n + 1; i < itemsCount_; i++) + { + target.push_back(i); + } + + assert(target.size() == itemsCount_); + } +} diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Loaders/BasicFetchingItemsSorter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Loaders/BasicFetchingItemsSorter.h Tue May 21 10:28:43 2019 +0200 @@ -0,0 +1,44 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IFetchingItemsSorter.h" + +namespace OrthancStone +{ + class BasicFetchingItemsSorter : public IFetchingItemsSorter + { + private: + unsigned int itemsCount_; + + public: + BasicFetchingItemsSorter(unsigned int itemsCount); + + virtual unsigned int GetItemsCount() const + { + return itemsCount_; + } + + virtual void Sort(std::vector& target, + unsigned int current); + }; +} diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Loaders/BasicFetchingStrategy.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Loaders/BasicFetchingStrategy.cpp Tue May 21 10:28:43 2019 +0200 @@ -0,0 +1,145 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "BasicFetchingStrategy.h" + +#include + +namespace OrthancStone +{ + void BasicFetchingStrategy::Schedule(unsigned int item, + unsigned int quality) + { + assert(item < GetItemsCount() && + quality <= maxQuality_); + + if (nextQuality_[item] <= quality) + { + content_.push_back(ContentItem(item, quality)); + } + } + + + BasicFetchingStrategy::BasicFetchingStrategy(IFetchingItemsSorter* sorter, // Takes ownership + unsigned int maxQuality) : + sorter_(sorter), + maxQuality_(maxQuality), + position_(0), + blockSize_(2) + { + if (sorter == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + nextQuality_.resize(sorter_->GetItemsCount(), 0); // Does not change along calls to "SetCurrent()" + + SetCurrent(0); + } + + + void BasicFetchingStrategy::SetBlockSize(unsigned int size) + { + if (size <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + blockSize_ = size; + } + + + bool BasicFetchingStrategy::GetNext(unsigned int& item, + unsigned int& quality) + { + if (position_ >= content_.size()) + { + return false; + } + else + { + item = content_[position_].GetItem(); + quality = content_[position_].GetQuality(); + + assert(nextQuality_[item] <= quality); + nextQuality_[item] = quality + 1; + + position_ ++; + return true; + } + } + + + void BasicFetchingStrategy::SetCurrent(unsigned int item) + { + // TODO - This function is O(N) complexity where "N" is the + // number of items times the max quality. Could use a LRU index. + + position_ = 0; + + std::vector v; + sorter_->Sort(v, item); + + assert(v.size() == GetItemsCount()); + + if (v.size() == 0) + { + return; + } + + content_.clear(); + content_.reserve(v.size() * maxQuality_); + + Schedule(v.front(), maxQuality_); + + for (unsigned int q = 0; q <= maxQuality_; q++) + { + unsigned int start = 1 + q * blockSize_; + unsigned int end = start + blockSize_; + + if (q == maxQuality_ || + end > v.size()) + { + end = v.size(); + } + + unsigned int a = 0; + if (maxQuality_ >= q + 1) + { + a = maxQuality_ - q - 1; + } + + for (unsigned int j = a; j <= maxQuality_; j++) + { + for (unsigned int i = start; i < end; i++) + { + Schedule(v[i], j); + } + } + } + } + + + void BasicFetchingStrategy::RecycleFurthest(unsigned int& item) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } +} diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Loaders/BasicFetchingStrategy.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Loaders/BasicFetchingStrategy.h Tue May 21 10:28:43 2019 +0200 @@ -0,0 +1,94 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IFetchingItemsSorter.h" +#include "IFetchingStrategy.h" + +#include + +namespace OrthancStone +{ + class BasicFetchingStrategy : public IFetchingStrategy + { + private: + class ContentItem + { + private: + unsigned int item_; + unsigned int quality_; + + public: + ContentItem(unsigned int item, + unsigned int quality) : + item_(item), + quality_(quality) + { + } + + unsigned int GetItem() const + { + return item_; + } + + unsigned int GetQuality() const + { + return quality_; + } + }; + + std::auto_ptr sorter_; + std::vector nextQuality_; + unsigned int maxQuality_; + std::vector content_; + size_t position_; + unsigned int blockSize_; + + void Schedule(unsigned int item, + unsigned int quality); + + public: + BasicFetchingStrategy(IFetchingItemsSorter* sorter, // Takes ownership + unsigned int maxQuality); + + virtual unsigned int GetItemsCount() const + { + return sorter_->GetItemsCount(); + } + + virtual unsigned int GetMaxQuality() const + { + return maxQuality_; + } + + // WARNING - This parameters is only considered during the next + // call to SetCurrent(). + void SetBlockSize(unsigned int size); + + virtual bool GetNext(unsigned int& item, + unsigned int& quality); + + virtual void SetCurrent(unsigned int item); + + virtual void RecycleFurthest(unsigned int& item); + }; +} diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Loaders/IFetchingItemsSorter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Loaders/IFetchingItemsSorter.h Tue May 21 10:28:43 2019 +0200 @@ -0,0 +1,42 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include +#include + +namespace OrthancStone +{ + class IFetchingItemsSorter : public boost::noncopyable + { + public: + virtual ~IFetchingItemsSorter() + { + } + + virtual unsigned int GetItemsCount() const = 0; + + // Sort a set of items given the current item + virtual void Sort(std::vector& target, + unsigned int current) = 0; + }; +}; diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Loaders/IFetchingStrategy.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Loaders/IFetchingStrategy.h Tue May 21 10:28:43 2019 +0200 @@ -0,0 +1,49 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include + +namespace OrthancStone +{ + class IFetchingStrategy : public boost::noncopyable + { + public: + virtual ~IFetchingStrategy() + { + } + + virtual unsigned int GetItemsCount() const = 0; + + virtual unsigned int GetMaxQuality() const = 0; + + virtual bool GetNext(unsigned int& item, + unsigned int& quality) = 0; + + virtual void SetCurrent(unsigned int item) = 0; + + // Ask the strategy to re-schedule the item with the lowest + // priority in the fetching order. This allows to know which item + // should be dropped from a cache. + virtual void RecycleFurthest(unsigned int& item) = 0; + }; +}; diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Radiography/RadiographyDicomLayer.cpp --- a/Framework/Radiography/RadiographyDicomLayer.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Radiography/RadiographyDicomLayer.cpp Tue May 21 10:28:43 2019 +0200 @@ -54,7 +54,7 @@ void RadiographyDicomLayer::SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) { - converter_.reset(new DicomFrameConverter); + converter_.reset(new Deprecated::DicomFrameConverter); converter_->ReadParameters(dataset); ApplyConverter(); @@ -112,7 +112,7 @@ } - void RadiographyDicomLayer::SetDicomFrameConverter(DicomFrameConverter* converter) + void RadiographyDicomLayer::SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter) { converter_.reset(converter); } diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Radiography/RadiographyDicomLayer.h --- a/Framework/Radiography/RadiographyDicomLayer.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Radiography/RadiographyDicomLayer.h Tue May 21 10:28:43 2019 +0200 @@ -21,19 +21,20 @@ #pragma once +#include "../Toolbox/DicomFrameConverter.h" #include "RadiographyLayer.h" + #include namespace OrthancStone { class RadiographyScene; - class DicomFrameConverter; class RadiographyDicomLayer : public RadiographyLayer { private: std::auto_ptr source_; // Content of PixelData - std::auto_ptr converter_; + std::auto_ptr converter_; std::auto_ptr converted_; // Float32 std::string instanceId_; unsigned int frame_; @@ -65,10 +66,10 @@ const Orthanc::ImageAccessor* GetSourceImage() const {return source_.get();} // currently need this access to serialize scene in plain old data to send to a WASM worker - const DicomFrameConverter& GetDicomFrameConverter() const {return *converter_;} // currently need this access to serialize scene in plain old data to send to a WASM worker + const Deprecated::DicomFrameConverter& GetDicomFrameConverter() const {return *converter_;} // currently need this access to serialize scene in plain old data to send to a WASM worker // Takes ownership - void SetDicomFrameConverter(DicomFrameConverter* converter); + void SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter); virtual void Render(Orthanc::ImageAccessor& buffer, const AffineTransform2D& viewTransform, diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Radiography/RadiographyScene.cpp --- a/Framework/Radiography/RadiographyScene.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.cpp Tue May 21 10:28:43 2019 +0200 @@ -344,7 +344,7 @@ RadiographyLayer& RadiographyScene::LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership const std::string& instance, unsigned int frame, - DicomFrameConverter* converter, // takes ownership + Deprecated::DicomFrameConverter* converter, // takes ownership PhotometricDisplayMode preferredPhotometricDisplayMode, RadiographyLayer::Geometry* geometry) { diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Radiography/RadiographyScene.h --- a/Framework/Radiography/RadiographyScene.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.h Tue May 21 10:28:43 2019 +0200 @@ -22,6 +22,7 @@ #pragma once #include "RadiographyLayer.h" +#include "../Toolbox/DicomFrameConverter.h" #include "../Toolbox/OrthancApiClient.h" #include "Framework/StoneEnumerations.h" #include "Core/Images/Image.h" @@ -30,7 +31,6 @@ namespace OrthancStone { class RadiographyDicomLayer; - class DicomFrameConverter; class RadiographyScene : public IObserver, @@ -193,7 +193,7 @@ virtual RadiographyLayer& LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership const std::string& instance, unsigned int frame, - DicomFrameConverter* converter, // takes ownership + Deprecated::DicomFrameConverter* converter, // takes ownership PhotometricDisplayMode preferredPhotometricDisplayMode, RadiographyLayer::Geometry* geometry); diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Radiography/RadiographySceneReader.cpp --- a/Framework/Radiography/RadiographySceneReader.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Radiography/RadiographySceneReader.cpp Tue May 21 10:28:43 2019 +0200 @@ -32,7 +32,7 @@ { void RadiographySceneBuilder::Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage /* takes ownership */, - DicomFrameConverter* dicomFrameConverter /* takes ownership */, + Deprecated::DicomFrameConverter* dicomFrameConverter /* takes ownership */, PhotometricDisplayMode preferredPhotometricDisplayMode ) { diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Radiography/RadiographySceneReader.h --- a/Framework/Radiography/RadiographySceneReader.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Radiography/RadiographySceneReader.h Tue May 21 10:28:43 2019 +0200 @@ -42,7 +42,7 @@ RadiographyScene& scene_; const Orthanc::FontRegistry* fontRegistry_; std::auto_ptr dicomImage_; - std::auto_ptr dicomFrameConverter_; + std::auto_ptr dicomFrameConverter_; PhotometricDisplayMode preferredPhotometricDisplayMode_; public: @@ -55,7 +55,7 @@ void Read(const Json::Value& input); void Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage, // takes ownership - DicomFrameConverter* dicomFrameConverter, // takes ownership + Deprecated::DicomFrameConverter* dicomFrameConverter, // takes ownership PhotometricDisplayMode preferredPhotometricDisplayMode ); diff -r 28b9e3a54200 -r 562cdd083b9e Framework/SmartLoader.cpp --- a/Framework/SmartLoader.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/SmartLoader.cpp Tue May 21 10:28:43 2019 +0200 @@ -61,7 +61,7 @@ }; unsigned int sliceIndex_; - std::auto_ptr slice_; + std::auto_ptr slice_; boost::shared_ptr image_; SliceImageQuality effectiveQuality_; CachedSliceStatus status_; @@ -190,7 +190,7 @@ // create the slice in the cache with "empty" data boost::shared_ptr cachedSlice(new CachedSlice(IObserver::GetBroker())); - cachedSlice->slice_.reset(new Slice(instanceId, frame)); + cachedSlice->slice_.reset(new Deprecated::Slice(instanceId, frame)); cachedSlice->status_ = CachedSliceStatus_ScheduledToLoad; std::string sliceKeyId = instanceId + ":" + boost::lexical_cast(frame); @@ -228,7 +228,7 @@ dynamic_cast(message.GetOrigin()); // save/replace the slice in cache - const Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() + const Deprecated::Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + boost::lexical_cast(slice.GetFrame())); @@ -249,7 +249,7 @@ void SmartLoader::OnFrameReady(const DicomSeriesVolumeSlicer::FrameReadyMessage& message) { // save/replace the slice in cache - const Slice& slice = message.GetSlice(); + const Deprecated::Slice& slice = message.GetSlice(); std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + boost::lexical_cast(slice.GetFrame())); @@ -273,7 +273,7 @@ const DicomSeriesVolumeSlicer& source = dynamic_cast(message.GetOrigin()); - const Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() ? + const Deprecated::Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() ? std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + boost::lexical_cast(slice.GetFrame())); diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Toolbox/DicomFrameConverter.cpp --- a/Framework/Toolbox/DicomFrameConverter.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Toolbox/DicomFrameConverter.cpp Tue May 21 10:28:43 2019 +0200 @@ -28,7 +28,7 @@ #include #include -namespace OrthancStone +namespace Deprecated { static const Orthanc::DicomTag IMAGE_TAGS[] = { @@ -61,9 +61,9 @@ { SetDefaultParameters(); - Vector c, w; - if (LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) && - LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) && + 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) { diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Toolbox/DicomFrameConverter.h --- a/Framework/Toolbox/DicomFrameConverter.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Toolbox/DicomFrameConverter.h Tue May 21 10:28:43 2019 +0200 @@ -27,7 +27,7 @@ #include -namespace OrthancStone +namespace Deprecated { /** * This class is responsible for converting the pixel format of a diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Toolbox/OrthancSlicesLoader.cpp --- a/Framework/Toolbox/OrthancSlicesLoader.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.cpp Tue May 21 10:28:43 2019 +0200 @@ -76,7 +76,7 @@ Mode mode_; unsigned int frame_; unsigned int sliceIndex_; - const Slice* slice_; + const Deprecated::Slice* slice_; std::string instanceId_; SliceImageQuality quality_; @@ -105,7 +105,7 @@ return sliceIndex_; } - const Slice& GetSlice() const + const Deprecated::Slice& GetSlice() const { assert(mode_ == Mode_LoadImage || mode_ == Mode_LoadRawImage); @@ -143,7 +143,7 @@ } static Operation* DownloadSliceImage(unsigned int sliceIndex, - const Slice& slice, + const Deprecated::Slice& slice, SliceImageQuality quality) { std::auto_ptr tmp(new Operation(Mode_LoadImage)); @@ -154,7 +154,7 @@ } static Operation* DownloadSliceRawImage(unsigned int sliceIndex, - const Slice& slice) + const Deprecated::Slice& slice) { std::auto_ptr tmp(new Operation(Mode_LoadRawImage)); tmp->sliceIndex_ = sliceIndex; @@ -163,7 +163,7 @@ return tmp.release(); } - static Operation* DownloadDicomFile(const Slice& slice) + static Operation* DownloadDicomFile(const Deprecated::Slice& slice) { std::auto_ptr tmp(new Operation(Mode_LoadDicomFile)); tmp->slice_ = &slice; @@ -241,7 +241,7 @@ for (unsigned int frame = 0; frame < frames; frame++) { - std::auto_ptr slice(new Slice); + std::auto_ptr slice(new Deprecated::Slice); if (slice->ParseOrthancFrame(dicom, instances[i], frame)) { CoordinateSystem3D geometry = slice->GetGeometry(); @@ -277,7 +277,7 @@ for (unsigned int frame = 0; frame < frames; frame++) { - std::auto_ptr slice(new Slice); + std::auto_ptr slice(new Deprecated::Slice); if (slice->ParseOrthancFrame(dicom, instanceId, frame)) { CoordinateSystem3D geometry = slice->GetGeometry(); @@ -308,7 +308,7 @@ Orthanc::DicomMap dicom; MessagingToolbox::ConvertDataset(dicom, dataset); - std::auto_ptr slice(new Slice); + std::auto_ptr slice(new Deprecated::Slice); if (slice->ParseOrthancFrame(dicom, instanceId, frame)) { LOG(INFO) << "Loaded instance geometry " << instanceId; @@ -721,14 +721,14 @@ } - const Slice& OrthancSlicesLoader::GetSlice(size_t index) const + const Deprecated::Slice& OrthancSlicesLoader::GetSlice(size_t index) const { if (state_ != State_GeometryReady) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - return dynamic_cast(slices_.GetSlicePayload(index)); + return dynamic_cast(slices_.GetSlicePayload(index)); } @@ -746,7 +746,7 @@ } - void OrthancSlicesLoader::ScheduleSliceImagePng(const Slice& slice, + void OrthancSlicesLoader::ScheduleSliceImagePng(const Deprecated::Slice& slice, size_t index) { std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + @@ -781,7 +781,7 @@ static_cast(index), slice, SliceImageQuality_FullPng)); } - void OrthancSlicesLoader::ScheduleSliceImagePam(const Slice& slice, + void OrthancSlicesLoader::ScheduleSliceImagePam(const Deprecated::Slice& slice, size_t index) { std::string uri = @@ -819,7 +819,7 @@ - void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Slice& slice, + void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Deprecated::Slice& slice, size_t index, SliceImageQuality quality) { @@ -870,7 +870,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - const Slice& slice = GetSlice(index); + const Deprecated::Slice& slice = GetSlice(index); if (slice.HasOrthancDecoding()) { diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Toolbox/OrthancSlicesLoader.h --- a/Framework/Toolbox/OrthancSlicesLoader.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.h Tue May 21 10:28:43 2019 +0200 @@ -46,14 +46,14 @@ private: unsigned int sliceIndex_; - const Slice& slice_; + const Deprecated::Slice& slice_; const Orthanc::ImageAccessor& image_; SliceImageQuality effectiveQuality_; public: SliceImageReadyMessage(const OrthancSlicesLoader& origin, unsigned int sliceIndex, - const Slice& slice, + const Deprecated::Slice& slice, const Orthanc::ImageAccessor& image, SliceImageQuality effectiveQuality) : OriginMessage(origin), @@ -69,7 +69,7 @@ return sliceIndex_; } - const Slice& GetSlice() const + const Deprecated::Slice& GetSlice() const { return slice_; } @@ -91,14 +91,14 @@ ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); private: - const Slice& slice_; + const Deprecated::Slice& slice_; unsigned int sliceIndex_; SliceImageQuality effectiveQuality_; public: SliceImageErrorMessage(const OrthancSlicesLoader& origin, unsigned int sliceIndex, - const Slice& slice, + const Deprecated::Slice& slice, SliceImageQuality effectiveQuality) : OriginMessage(origin), slice_(slice), @@ -111,7 +111,7 @@ return sliceIndex_; } - const Slice& GetSlice() const + const Deprecated::Slice& GetSlice() const { return slice_; } @@ -170,13 +170,13 @@ void ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message); - void ScheduleSliceImagePng(const Slice& slice, + void ScheduleSliceImagePng(const Deprecated::Slice& slice, size_t index); - void ScheduleSliceImagePam(const Slice& slice, + void ScheduleSliceImagePam(const Deprecated::Slice& slice, size_t index); - void ScheduleSliceImageJpeg(const Slice& slice, + void ScheduleSliceImageJpeg(const Deprecated::Slice& slice, size_t index, SliceImageQuality quality); @@ -198,7 +198,7 @@ size_t GetSlicesCount() const; - const Slice& GetSlice(size_t index) const; + const Deprecated::Slice& GetSlice(size_t index) const; bool LookupSlice(size_t& index, const CoordinateSystem3D& plane) const; diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Toolbox/Slice.cpp --- a/Framework/Toolbox/Slice.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Toolbox/Slice.cpp Tue May 21 10:28:43 2019 +0200 @@ -30,7 +30,7 @@ #include -namespace OrthancStone +namespace Deprecated { static bool ParseDouble(double& target, const std::string& source) @@ -118,16 +118,16 @@ return false; } - if (!LinearAlgebra::IsCloseToZero(offset0)) + if (!OrthancStone::LinearAlgebra::IsCloseToZero(offset0)) { LOG(ERROR) << "Invalid syntax"; return false; } - geometry_ = CoordinateSystem3D(geometry_.GetOrigin() + z * geometry_.GetNormal(), - //+ 650 * geometry_.GetAxisX(), - geometry_.GetAxisX(), - geometry_.GetAxisY()); + geometry_ = OrthancStone::CoordinateSystem3D(geometry_.GetOrigin() + z * geometry_.GetNormal(), + //+ 650 * geometry_.GetAxisX(), + geometry_.GetAxisX(), + geometry_.GetAxisY()); thickness_ = offset1 - offset0; if (thickness_ < 0) @@ -185,19 +185,19 @@ converter_.ReadParameters(dataset); - GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset); + OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset); std::string position, orientation; if (dataset.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) && dataset.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false)) { - geometry_ = CoordinateSystem3D(position, orientation); + geometry_ = OrthancStone::CoordinateSystem3D(position, orientation); bool ok = true; - switch (StringToSopClassUid(sopClassUid_)) + switch (OrthancStone::StringToSopClassUid(sopClassUid_)) { - case SopClassUid_RTDose: + case OrthancStone::SopClassUid_RTDose: type_ = Type_OrthancRawFrame; ok = ComputeRTDoseGeometry(dataset, frame); break; @@ -243,7 +243,7 @@ } - const CoordinateSystem3D& Slice::GetGeometry() const + const OrthancStone::CoordinateSystem3D& Slice::GetGeometry() const { if (type_ == Type_Invalid) { @@ -320,7 +320,7 @@ } - bool Slice::ContainsPlane(const CoordinateSystem3D& plane) const + bool Slice::ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const { if (type_ == Type_Invalid) { @@ -328,16 +328,16 @@ } bool opposite; - return (GeometryToolbox::IsParallelOrOpposite(opposite, - GetGeometry().GetNormal(), - plane.GetNormal()) && - LinearAlgebra::IsNear(GetGeometry().ProjectAlongNormal(GetGeometry().GetOrigin()), - GetGeometry().ProjectAlongNormal(plane.GetOrigin()), - thickness_ / 2.0)); + return (OrthancStone::GeometryToolbox::IsParallelOrOpposite(opposite, + GetGeometry().GetNormal(), + plane.GetNormal()) && + OrthancStone::LinearAlgebra::IsNear(GetGeometry().ProjectAlongNormal(GetGeometry().GetOrigin()), + GetGeometry().ProjectAlongNormal(plane.GetOrigin()), + thickness_ / 2.0)); } - void Slice::GetExtent(std::vector& points) const + void Slice::GetExtent(std::vector& points) const { double sx = GetPixelSpacingX(); double sy = GetPixelSpacingY(); diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Toolbox/Slice.h --- a/Framework/Toolbox/Slice.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Toolbox/Slice.h Tue May 21 10:28:43 2019 +0200 @@ -27,7 +27,7 @@ #include #include -namespace OrthancStone +namespace Deprecated { // TODO - Remove this class class Slice : @@ -51,7 +51,7 @@ std::string sopClassUid_; unsigned int frame_; unsigned int frameCount_; // TODO : Redundant with "imageInformation_" - CoordinateSystem3D geometry_; + OrthancStone::CoordinateSystem3D geometry_; double pixelSpacingX_; double pixelSpacingY_; double thickness_; @@ -79,7 +79,7 @@ // TODO Is this constructor the best way to go to tackle missing // layers within SliceViewerWidget? - Slice(const CoordinateSystem3D& plane, + Slice(const OrthancStone::CoordinateSystem3D& plane, double thickness) : type_(Type_Standalone), frame_(0), @@ -93,7 +93,7 @@ { } - Slice(const CoordinateSystem3D& plane, + Slice(const OrthancStone::CoordinateSystem3D& plane, double pixelSpacingX, double pixelSpacingY, double thickness, @@ -130,7 +130,7 @@ unsigned int GetFrame() const; - const CoordinateSystem3D& GetGeometry() const; + const OrthancStone::CoordinateSystem3D& GetGeometry() const; double GetThickness() const; @@ -144,9 +144,9 @@ const DicomFrameConverter& GetConverter() const; - bool ContainsPlane(const CoordinateSystem3D& plane) const; + bool ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const; - void GetExtent(std::vector& points) const; + void GetExtent(std::vector& points) const; const Orthanc::DicomImageInformation& GetImageInformation() const; diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Volumes/ISlicedVolume.h --- a/Framework/Volumes/ISlicedVolume.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Volumes/ISlicedVolume.h Tue May 21 10:28:43 2019 +0200 @@ -41,12 +41,12 @@ private: size_t sliceIndex_; - const Slice& slice_; + const Deprecated::Slice& slice_; public: SliceContentChangedMessage(ISlicedVolume& origin, size_t sliceIndex, - const Slice& slice) : + const Deprecated::Slice& slice) : OriginMessage(origin), sliceIndex_(sliceIndex), slice_(slice) @@ -58,7 +58,7 @@ return sliceIndex_; } - const Slice& GetSlice() const + const Deprecated::Slice& GetSlice() const { return slice_; } @@ -72,6 +72,6 @@ virtual size_t GetSliceCount() const = 0; - virtual const Slice& GetSlice(size_t slice) const = 0; + virtual const Deprecated::Slice& GetSlice(size_t slice) const = 0; }; } diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Volumes/ImageBuffer3D.cpp --- a/Framework/Volumes/ImageBuffer3D.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Volumes/ImageBuffer3D.cpp Tue May 21 10:28:43 2019 +0200 @@ -259,7 +259,7 @@ bool ImageBuffer3D::FitWindowingToRange(RenderStyle& style, - const DicomFrameConverter& converter) const + const Deprecated::DicomFrameConverter& converter) const { if (hasRange_) { diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Volumes/ImageBuffer3D.h --- a/Framework/Volumes/ImageBuffer3D.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Volumes/ImageBuffer3D.h Tue May 21 10:28:43 2019 +0200 @@ -122,7 +122,7 @@ float& maxValue) const; bool FitWindowingToRange(RenderStyle& style, - const DicomFrameConverter& converter) const; + const Deprecated::DicomFrameConverter& converter) const; uint8_t GetVoxelGrayscale8Unchecked(unsigned int x, unsigned int y, diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Widgets/SliceViewerWidget.cpp --- a/Framework/Widgets/SliceViewerWidget.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Widgets/SliceViewerWidget.cpp Tue May 21 10:28:43 2019 +0200 @@ -510,7 +510,7 @@ << "," << plane.GetOrigin()[1] << "," << plane.GetOrigin()[2] << ")"; - Slice displayedSlice(plane_, THIN_SLICE_THICKNESS); + Deprecated::Slice displayedSlice(plane_, THIN_SLICE_THICKNESS); //if (!displayedSlice.ContainsPlane(slice)) { diff -r 28b9e3a54200 -r 562cdd083b9e Framework/Widgets/SliceViewerWidget.h --- a/Framework/Widgets/SliceViewerWidget.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/Widgets/SliceViewerWidget.h Tue May 21 10:28:43 2019 +0200 @@ -46,17 +46,17 @@ ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); private: - const Slice& slice_; + const Deprecated::Slice& slice_; public: DisplayedSliceMessage(SliceViewerWidget& origin, - const Slice& slice) : + const Deprecated::Slice& slice) : OriginMessage(origin), slice_(slice) { } - const Slice& GetSlice() const + const Deprecated::Slice& GetSlice() const { return slice_; } diff -r 28b9e3a54200 -r 562cdd083b9e Framework/dev.h --- a/Framework/dev.h Tue May 21 10:27:54 2019 +0200 +++ b/Framework/dev.h Tue May 21 10:28:43 2019 +0200 @@ -64,8 +64,8 @@ } - static bool IsCompatible(const Slice& a, - const Slice& b) + static bool IsCompatible(const Deprecated::Slice& a, + const Deprecated::Slice& b) { if (!GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(), b.GetGeometry().GetNormal())) @@ -98,8 +98,8 @@ } - static double GetDistance(const Slice& a, - const Slice& b) + static double GetDistance(const Deprecated::Slice& a, + const Deprecated::Slice& b) { return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) - a.GetGeometry().ProjectAlongNormal(b.GetGeometry().GetOrigin())); @@ -268,7 +268,7 @@ return loader_.GetSlicesCount(); } - virtual const Slice& GetSlice(size_t index) const + virtual const Deprecated::Slice& GetSlice(size_t index) const { return loader_.GetSlice(index); } @@ -287,7 +287,7 @@ } bool FitWindowingToRange(RenderStyle& style, - const DicomFrameConverter& converter) const + const Deprecated::DicomFrameConverter& converter) const { if (image_.get() == NULL) { @@ -311,7 +311,7 @@ double pixelSpacingY_; double sliceThickness_; CoordinateSystem3D reference_; - DicomFrameConverter converter_; + Deprecated::DicomFrameConverter converter_; double ComputeAxialThickness(const OrthancVolumeImage& volume) const { @@ -320,8 +320,8 @@ size_t n = volume.GetSlicesCount(); if (n > 1) { - const Slice& a = volume.GetSlice(0); - const Slice& b = volume.GetSlice(n - 1); + const Deprecated::Slice& a = volume.GetSlice(0); + const Deprecated::Slice& b = volume.GetSlice(n - 1); thickness = ((reference_.ProjectAlongNormal(b.GetGeometry().GetOrigin()) - reference_.ProjectAlongNormal(a.GetGeometry().GetOrigin())) / (static_cast(n) - 1.0)); @@ -345,7 +345,7 @@ void SetupAxial(const OrthancVolumeImage& volume) { - const Slice& axial = volume.GetSlice(0); + const Deprecated::Slice& axial = volume.GetSlice(0); width_ = axial.GetWidth(); height_ = axial.GetHeight(); @@ -360,7 +360,7 @@ void SetupCoronal(const OrthancVolumeImage& volume) { - const Slice& axial = volume.GetSlice(0); + const Deprecated::Slice& axial = volume.GetSlice(0); double axialThickness = ComputeAxialThickness(volume); width_ = axial.GetWidth(); @@ -382,7 +382,7 @@ void SetupSagittal(const OrthancVolumeImage& volume) { - const Slice& axial = volume.GetSlice(0); + const Deprecated::Slice& axial = volume.GetSlice(0); double axialThickness = ComputeAxialThickness(volume); width_ = axial.GetHeight(); @@ -470,7 +470,7 @@ } } - Slice* GetSlice(size_t slice) const + Deprecated::Slice* GetSlice(size_t slice) const { if (slice >= depth_) { @@ -483,8 +483,8 @@ reference_.GetAxisX(), reference_.GetAxisY()); - return new Slice(origin, pixelSpacingX_, pixelSpacingY_, sliceThickness_, - width_, height_, converter_); + return new Deprecated::Slice(origin, pixelSpacingX_, pixelSpacingY_, sliceThickness_, + width_, height_, converter_); } } }; @@ -500,12 +500,12 @@ { private: const Orthanc::ImageAccessor& frame_; - const Slice& slice_; + const Deprecated::Slice& slice_; bool isFullQuality_; public: RendererFactory(const Orthanc::ImageAccessor& frame, - const Slice& slice, + const Deprecated::Slice& slice, bool isFullQuality) : frame_(frame), slice_(slice), @@ -662,7 +662,7 @@ { // As the slices of the volumic image are arranged in a box, // we only consider one single reference slice (the one with index 0). - std::auto_ptr slice(GetProjectionGeometry(projection).GetSlice(0)); + std::auto_ptr slice(GetProjectionGeometry(projection).GetSlice(0)); slice->GetExtent(points); return true; @@ -693,7 +693,7 @@ frame.reset(Orthanc::Image::Clone(reader.GetAccessor())); } - std::auto_ptr slice(geometry.GetSlice(closest)); + std::auto_ptr slice(geometry.GetSlice(closest)); RendererFactory factory(*frame, *slice, isFullQuality); @@ -858,7 +858,7 @@ { slice_ = slice; - std::auto_ptr tmp(slices_->GetSlice(slice_)); + std::auto_ptr tmp(slices_->GetSlice(slice_)); widget_.SetSlice(tmp->GetGeometry()); } } @@ -917,7 +917,7 @@ virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) { - Slice reference(viewportSlice, 0.001); + Deprecated::Slice reference(viewportSlice, 0.001); Vector p, d; diff -r 28b9e3a54200 -r 562cdd083b9e Resources/CMake/OrthancStoneConfiguration.cmake --- a/Resources/CMake/OrthancStoneConfiguration.cmake Tue May 21 10:27:54 2019 +0200 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Tue May 21 10:28:43 2019 +0200 @@ -362,7 +362,7 @@ ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/PointerTypes.h ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.cpp ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.h - + ${ORTHANC_STONE_ROOT}/Framework/Fonts/FontRenderer.cpp ${ORTHANC_STONE_ROOT}/Framework/Fonts/Glyph.cpp ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphAlphabet.cpp @@ -380,6 +380,8 @@ ${ORTHANC_STONE_ROOT}/Framework/Layers/LineMeasureTracker.cpp ${ORTHANC_STONE_ROOT}/Framework/Layers/RenderStyle.cpp ${ORTHANC_STONE_ROOT}/Framework/Layers/SliceOutlineRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Loaders/BasicFetchingItemsSorter.cpp + ${ORTHANC_STONE_ROOT}/Framework/Loaders/BasicFetchingStrategy.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyAlphaLayer.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyDicomLayer.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayer.cpp diff -r 28b9e3a54200 -r 562cdd083b9e Samples/Sdl/Loader.cpp --- a/Samples/Sdl/Loader.cpp Tue May 21 10:27:54 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Tue May 21 10:28:43 2019 +0200 @@ -19,15 +19,19 @@ **/ // From Stone +#include "../../Framework/Loaders/BasicFetchingItemsSorter.h" +#include "../../Framework/Loaders/BasicFetchingStrategy.h" #include "../../Framework/Messages/ICallable.h" #include "../../Framework/Messages/IMessage.h" #include "../../Framework/Messages/IObservable.h" #include "../../Framework/Messages/MessageBroker.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/FloatTextureSceneLayer.h" +#include "../../Framework/Scene2D/Scene2D.h" #include "../../Framework/StoneInitialization.h" #include "../../Framework/Toolbox/GeometryToolbox.h" #include "../../Framework/Toolbox/SlicesSorter.h" #include "../../Framework/Volumes/ImageBuffer3D.h" -#include "../../Framework/Scene2D/Scene2D.h" // From Orthanc framework #include @@ -399,6 +403,30 @@ uri_ = uri; } + void SetInstanceUri(const std::string& instance, + Orthanc::PixelFormat pixelFormat) + { + uri_ = "/instances/" + instance; + + switch (pixelFormat) + { + case Orthanc::PixelFormat_RGB24: + uri_ += "/preview"; + break; + + case Orthanc::PixelFormat_Grayscale16: + uri_ += "/image-uint16"; + break; + + case Orthanc::PixelFormat_SignedGrayscale16: + uri_ += "/image-int16"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + void SetHttpHeader(const std::string& key, const std::string& value) { @@ -788,7 +816,7 @@ OrthancStone::Vector frameOffsets_; bool isColor_; bool hasRescale_; - double rescaleOffset_; + double rescaleIntercept_; double rescaleSlope_; bool hasDefaultWindowing_; float defaultWindowingCenter_; @@ -882,7 +910,7 @@ double doseGridScaling; - if (dicom.ParseDouble(rescaleOffset_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) && + if (dicom.ParseDouble(rescaleIntercept_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) && dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE)) { hasRescale_ = true; @@ -890,7 +918,7 @@ else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING)) { hasRescale_ = true; - rescaleOffset_ = 0; + rescaleIntercept_ = 0; rescaleSlope_ = doseGridScaling; } else @@ -992,7 +1020,47 @@ return (OrthancStone::CoordinateSystem3D::GetDistance(distance, tmp, plane) && distance <= thickness_ / 2.0); } + + + void ApplyRescale(Orthanc::ImageAccessor& image, + bool useDouble) const + { + if (image.GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + if (hasRescale_) + { + const unsigned int width = image.GetWidth(); + const unsigned int height = image.GetHeight(); + + for (unsigned int y = 0; y < height; y++) + { + float* p = reinterpret_cast(image.GetRow(y)); + + if (useDouble) + { + // Slower, accurate implementation using double + for (unsigned int x = 0; x < width; x++, p++) + { + double value = static_cast(*p); + *p = static_cast(value * rescaleSlope_ + rescaleIntercept_); + } + } + else + { + // Fast, approximate implementation using float + for (unsigned int x = 0; x < width; x++, p++) + { + *p = (*p) * static_cast(rescaleSlope_) + static_cast(rescaleIntercept_); + } + } + } + } + } }; + Data data_; @@ -1085,11 +1153,11 @@ return data_.hasRescale_; } - double GetRescaleOffset() const + double GetRescaleIntercept() const { if (data_.hasRescale_) { - return data_.rescaleOffset_; + return data_.rescaleIntercept_; } else { @@ -1142,6 +1210,58 @@ { return data_.expectedPixelFormat_; } + + + OrthancStone::TextureBaseSceneLayer* CreateTexture(const Orthanc::ImageAccessor& source) const + { + assert(sizeof(float) == 4); + + Orthanc::PixelFormat sourceFormat = source.GetFormat(); + + if (sourceFormat != GetExpectedPixelFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + if (sourceFormat == Orthanc::PixelFormat_RGB24) + { + // This is the case of a color image. No conversion has to be done. + return new OrthancStone::ColorTextureSceneLayer(source); + } + else + { + if (sourceFormat != Orthanc::PixelFormat_Grayscale16 && + sourceFormat != Orthanc::PixelFormat_Grayscale32 && + sourceFormat != Orthanc::PixelFormat_SignedGrayscale16) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + std::auto_ptr texture; + + { + // This is the case of a grayscale frame. Convert it to Float32. + std::auto_ptr converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, + source.GetWidth(), + source.GetHeight(), + false)); + Orthanc::ImageProcessing::Convert(*converted, source); + + // Correct rescale slope/intercept if need be + data_.ApplyRescale(*converted, (sourceFormat == Orthanc::PixelFormat_Grayscale32)); + + texture.reset(new OrthancStone::FloatTextureSceneLayer(*converted)); + } + + if (data_.hasDefaultWindowing_) + { + texture->SetCustomWindowing(data_.defaultWindowingCenter_, + data_.defaultWindowingWidth_); + } + + return texture.release(); + } + } }; @@ -1152,6 +1272,7 @@ std::vector slices_; uint64_t revision_; std::vector slicesRevision_; + std::vector slicesQuality_; void CheckSlice(size_t index, const DicomInstanceParameters& reference) const @@ -1221,6 +1342,10 @@ assert(slices_[i] != NULL); delete slices_[i]; } + + slices_.clear(); + slicesRevision_.clear(); + slicesQuality_.clear(); } @@ -1270,14 +1395,14 @@ else { slices_.reserve(slices.GetSlicesCount()); - slicesRevision_.resize(slices.GetSlicesCount()); + slicesRevision_.resize(slices.GetSlicesCount(), 0); + slicesQuality_.resize(slices.GetSlicesCount(), 0); for (size_t i = 0; i < slices.GetSlicesCount(); i++) { const DicomInstanceParameters& slice = dynamic_cast(slices.GetSlicePayload(i)); slices_.push_back(new DicomInstanceParameters(slice)); - slicesRevision_[i] = 0; } CheckVolume(); @@ -1349,97 +1474,115 @@ } void SetSliceContent(size_t index, - const Orthanc::ImageAccessor& image) + const Orthanc::ImageAccessor& image, + unsigned int quality) { CheckSliceIndex(index); - + + // If a better image quality is already available, don't update the content + if (quality >= slicesQuality_[index]) { - OrthancStone::ImageBuffer3D::SliceWriter writer - (*image_, OrthancStone::VolumeProjection_Axial, index); - Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image); + { + OrthancStone::ImageBuffer3D::SliceWriter writer + (*image_, OrthancStone::VolumeProjection_Axial, index); + Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image); + } + + revision_ ++; + slicesRevision_[index] += 1; } + } + }; - revision_ ++; - slicesRevision_[index] += 1; + + + class IDicomVolumeSource : public boost::noncopyable + { + public: + virtual ~IDicomVolumeSource() + { } + + virtual const DicomVolumeImage& GetVolume() const = 0; + + virtual void NotifyAxialSliceAccessed(unsigned int sliceIndex) = 0; }; - class VolumeSeriesOrthancLoader : public OrthancStone::IObserver + class VolumeSeriesOrthancLoader : + public OrthancStone::IObserver, + public IDicomVolumeSource { private: - class MessageHandler : public Orthanc::IDynamicObject + static const unsigned int LOW_QUALITY = 0; + static const unsigned int MIDDLE_QUALITY = 1; + static const unsigned int BEST_QUALITY = 2; + + + static unsigned int GetSliceIndexPayload(const OracleCommandWithPayload& command) + { + return dynamic_cast< const Orthanc::SingleValueObject& >(command.GetPayload()).GetValue(); + } + + + void ScheduleNextSliceDownload() { - public: - virtual void Handle(const Json::Value& body) const + assert(strategy_.get() != NULL); + + unsigned int sliceIndex, quality; + + if (strategy_->GetNext(sliceIndex, quality)) { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } + assert(quality <= BEST_QUALITY); + + const DicomInstanceParameters& slice = volume_.GetSliceParameters(sliceIndex); + + const std::string& instance = slice.GetOrthancInstanceIdentifier(); + if (instance.empty()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } - virtual void Handle(const Orthanc::ImageAccessor& image) const - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + std::auto_ptr command; + + if (quality == BEST_QUALITY) + { + std::auto_ptr tmp( + new Refactoring::GetOrthancImageCommand); + tmp->SetHttpHeader("Accept-Encoding", "gzip"); + tmp->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); + tmp->SetInstanceUri(instance, slice.GetExpectedPixelFormat()); + tmp->SetExpectedPixelFormat(slice.GetExpectedPixelFormat()); + command.reset(tmp.release()); + } + else + { + std::auto_ptr tmp( + new Refactoring::GetOrthancWebViewerJpegCommand); + tmp->SetHttpHeader("Accept-Encoding", "gzip"); + tmp->SetInstance(instance); + tmp->SetQuality((quality == 0 ? 50 : 90)); + tmp->SetExpectedPixelFormat(slice.GetExpectedPixelFormat()); + command.reset(tmp.release()); + } + + command->SetPayload(new Orthanc::SingleValueObject(sliceIndex)); + oracle_.Schedule(*this, command.release()); } - }; + } - void Handle(const OrthancRestApiCommand::SuccessMessage& message) + + void LoadGeometry(const OrthancRestApiCommand::SuccessMessage& message) { Json::Value body; message.ParseJsonBody(body); - + if (body.type() != Json::objectValue) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); } - dynamic_cast(message.GetOrigin().GetPayload()).Handle(body); - } - - void Handle(const Refactoring::GetOrthancImageCommand::SuccessMessage& message) - { - dynamic_cast(message.GetOrigin().GetPayload()).Handle(message.GetImage()); - } - - void Handle(const Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage& message) - { - dynamic_cast(message.GetOrigin().GetPayload()).Handle(message.GetImage()); - } - - - class LoadSliceImage : public MessageHandler - { - private: - DicomVolumeImage& target_; - size_t slice_; - - public: - LoadSliceImage(DicomVolumeImage& target, - size_t slice) : - target_(target), - slice_(slice) - { - } - - virtual void Handle(const Orthanc::ImageAccessor& image) const - { - target_.SetSliceContent(slice_, image); - } - }; - - - class LoadSeriesGeometryHandler : public MessageHandler - { - private: - VolumeSeriesOrthancLoader& that_; - - public: - LoadSeriesGeometryHandler(VolumeSeriesOrthancLoader& that) : - that_(that) - { - } - - virtual void Handle(const Json::Value& body) const { Json::Value::Members instances = body.getMemberNames(); @@ -1457,90 +1600,60 @@ slices.AddSlice(geometry, instance.release()); } - that_.volume_.SetGeometry(slices); - - { - OrthancStone::LinearAlgebra::Print(that_.volume_.GetImage().GetGeometry().GetCoordinates(0, 0, 0)); - OrthancStone::LinearAlgebra::Print(that_.volume_.GetImage().GetGeometry().GetCoordinates(1, 1, 1)); - return; - } - - for (size_t i = 0; i < that_.volume_.GetSlicesCount(); i++) - { - const DicomInstanceParameters& slice = that_.volume_.GetSliceParameters(i); - - const std::string& instance = slice.GetOrthancInstanceIdentifier(); - if (instance.empty()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } + volume_.SetGeometry(slices); + } -#if 0 - std::auto_ptr command( - new Refactoring::GetOrthancWebViewerJpegCommand); - command->SetInstance(instance); - command->SetQuality(95); -#else - std::string uri = "/instances/" + instance; - - switch (slice.GetExpectedPixelFormat()) - { - case Orthanc::PixelFormat_RGB24: - uri += "/preview"; - break; - - case Orthanc::PixelFormat_Grayscale16: - uri += "/image-uint16"; - break; - - case Orthanc::PixelFormat_SignedGrayscale16: - uri += "/image-int16"; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::auto_ptr command( - new Refactoring::GetOrthancImageCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); - command->SetUri(uri); -#endif + if (volume_.GetSlicesCount() != 0) + { + strategy_.reset(new OrthancStone::BasicFetchingStrategy( + new OrthancStone::BasicFetchingItemsSorter(volume_.GetSlicesCount()), BEST_QUALITY)); - command->SetExpectedPixelFormat(slice.GetExpectedPixelFormat()); - command->SetPayload(new LoadSliceImage(that_.volume_, i)); - - that_.oracle_.Schedule(that_, command.release()); + for (unsigned int i = 0; i < 4; i++) // Schedule up to 4 simultaneous downloads (TODO - parameter) + { + ScheduleNextSliceDownload(); } } - }; + } + + + void LoadBestQualitySliceContent(const Refactoring::GetOrthancImageCommand::SuccessMessage& message) + { + volume_.SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), + message.GetImage(), BEST_QUALITY); + + ScheduleNextSliceDownload(); + } - class LoadInstanceGeometryHandler : public MessageHandler + void LoadJpegSliceContent(const Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage& message) { - private: - VolumeSeriesOrthancLoader& that_; + unsigned int quality; + + switch (message.GetOrigin().GetQuality()) + { + case 50: + quality = LOW_QUALITY; + break; - public: - LoadInstanceGeometryHandler(VolumeSeriesOrthancLoader& that) : - that_(that) - { + case 90: + quality = MIDDLE_QUALITY; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } + + volume_.SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), message.GetImage(), quality); - virtual void Handle(const Json::Value& body) const - { - Orthanc::DicomMap dicom; - dicom.FromDicomAsJson(body); - - DicomInstanceParameters instance(dicom); - } - }; + ScheduleNextSliceDownload(); + } IOracle& oracle_; bool active_; DicomVolumeImage volume_; + + std::auto_ptr strategy_; public: VolumeSeriesOrthancLoader(IOracle& oracle, @@ -1551,15 +1664,15 @@ { oracleObservable.RegisterObserverCallback( new OrthancStone::Callable - (*this, &VolumeSeriesOrthancLoader::Handle)); + (*this, &VolumeSeriesOrthancLoader::LoadGeometry)); oracleObservable.RegisterObserverCallback( new OrthancStone::Callable - (*this, &VolumeSeriesOrthancLoader::Handle)); + (*this, &VolumeSeriesOrthancLoader::LoadBestQualitySliceContent)); oracleObservable.RegisterObserverCallback( new OrthancStone::Callable - (*this, &VolumeSeriesOrthancLoader::Handle)); + (*this, &VolumeSeriesOrthancLoader::LoadJpegSliceContent)); } void LoadSeries(const std::string& seriesId) @@ -1573,123 +1686,159 @@ std::auto_ptr command(new Refactoring::OrthancRestApiCommand); command->SetUri("/series/" + seriesId + "/instances-tags"); - command->SetPayload(new LoadSeriesGeometryHandler(*this)); oracle_.Schedule(*this, command.release()); } + - void LoadInstance(const std::string& instanceId) + virtual const DicomVolumeImage& GetVolume() const { - if (active_) + return volume_; + } + + + virtual void NotifyAxialSliceAccessed(unsigned int sliceIndex) + { + if (strategy_.get() == NULL) { + // Should have called GetVolume().HasGeometry() before throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - - active_ = true; - - // Tag "3004-000c" is "Grid Frame Offset Vector", which is - // mandatory to read RT DOSE, but is too long to be returned by default - - // TODO => Should be part of a second call if needed - - std::auto_ptr command(new Refactoring::OrthancRestApiCommand); - command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3004-000c"); - command->SetPayload(new LoadInstanceGeometryHandler(*this)); - - oracle_.Schedule(*this, command.release()); + else + { + strategy_->SetCurrent(sliceIndex); + } } }; +#if 0 + void LoadInstance(const std::string& instanceId) + { + if (active_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + active_ = true; + + // Tag "3004-000c" is "Grid Frame Offset Vector", which is + // mandatory to read RT DOSE, but is too long to be returned by default + + // TODO => Should be part of a second call if needed + + std::auto_ptr command(new Refactoring::OrthancRestApiCommand); + command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3004-000c"); + command->SetPayload(new LoadInstanceGeometryHandler(*this)); + + oracle_.Schedule(*this, command.release()); + } +#endif + /* class VolumeSlicerBase : public IVolumeSlicer - { - private: - OrthancStone::Scene2D& scene_; - int layerDepth_; - bool first_; - OrthancStone::CoordinateSystem3D lastPlane_; + { + private: + OrthancStone::Scene2D& scene_; + int layerDepth_; + bool first_; + OrthancStone::CoordinateSystem3D lastPlane_; - protected: - bool HasViewportPlaneChanged(const OrthancStone::CoordinateSystem3D& plane) const - { + protected: + bool HasViewportPlaneChanged(const OrthancStone::CoordinateSystem3D& plane) const + { if (first_ || - !OrthancStone::LinearAlgebra::IsCloseToZero( - boost::numeric::ublas::norm_2(lastPlane_.GetNormal() - plane.GetNormal()))) + !OrthancStone::LinearAlgebra::IsCloseToZero( + boost::numeric::ublas::norm_2(lastPlane_.GetNormal() - plane.GetNormal()))) { - // This is the first rendering, or the plane has not the same orientation - return false; + // This is the first rendering, or the plane has not the same orientation + return false; } else { - double offset1 = lastPlane_.ProjectAlongNormal(plane.GetOrigin()); - double offset2 = lastPlane_.ProjectAlongNormal(lastPlane_.GetOrigin()); - return OrthancStone::LinearAlgebra::IsCloseToZero(offset2 - offset1); + double offset1 = lastPlane_.ProjectAlongNormal(plane.GetOrigin()); + double offset2 = lastPlane_.ProjectAlongNormal(lastPlane_.GetOrigin()); + return OrthancStone::LinearAlgebra::IsCloseToZero(offset2 - offset1); } - } + } - void SetLastViewportPlane(const OrthancStone::CoordinateSystem3D& plane) - { + void SetLastViewportPlane(const OrthancStone::CoordinateSystem3D& plane) + { first_ = false; lastPlane_ = plane; - } + } - void SetLayer(OrthancStone::ISceneLayer* layer) - { + void SetLayer(OrthancStone::ISceneLayer* layer) + { scene_.SetLayer(layerDepth_, layer); - } + } - void DeleteLayer() - { + void DeleteLayer() + { scene_.DeleteLayer(layerDepth_); - } + } - public: - VolumeSlicerBase(OrthancStone::Scene2D& scene, - int layerDepth) : + public: + VolumeSlicerBase(OrthancStone::Scene2D& scene, + int layerDepth) : scene_(scene), layerDepth_(layerDepth), first_(true) - { - } - };*/ + { + } + };*/ - class DicomVolumeSlicer : public IVolumeSlicer + class DicomVolumeMPRSlicer : public IVolumeSlicer { private: + bool linearInterpolation_; OrthancStone::Scene2D& scene_; int layerDepth_; - const DicomVolumeImage& volume_; + IDicomVolumeSource& source_; bool first_; OrthancStone::VolumeProjection lastProjection_; unsigned int lastSliceIndex_; uint64_t lastSliceRevision_; public: - DicomVolumeSlicer(OrthancStone::Scene2D& scene, - int layerDepth, - const DicomVolumeImage& volume) : + DicomVolumeMPRSlicer(OrthancStone::Scene2D& scene, + int layerDepth, + IDicomVolumeSource& source) : + linearInterpolation_(false), scene_(scene), layerDepth_(layerDepth), - volume_(volume), + source_(source), first_(true) { } + + void SetLinearInterpolation(bool enabled) + { + linearInterpolation_ = enabled; + } + + bool IsLinearInterpolation() const + { + return linearInterpolation_; + } virtual void SetViewportPlane(const OrthancStone::CoordinateSystem3D& plane) { - if (!volume_.HasGeometry()) + if (!source_.GetVolume().HasGeometry() || + source_.GetVolume().GetSlicesCount() == 0) { scene_.DeleteLayer(layerDepth_); return; } + const OrthancStone::VolumeImageGeometry& geometry = source_.GetVolume().GetImage().GetGeometry(); + OrthancStone::VolumeProjection projection; unsigned int sliceIndex; - if (!volume_.GetImage().GetGeometry().DetectSlice(projection, sliceIndex, plane)) + if (!geometry.DetectSlice(projection, sliceIndex, plane)) { // The cutting plane is neither axial, nor coronal, nor // sagittal. Could use "VolumeReslicer" here. @@ -1700,35 +1849,67 @@ uint64_t sliceRevision; if (projection == OrthancStone::VolumeProjection_Axial) { - sliceRevision = volume_.GetSliceRevision(sliceIndex); + sliceRevision = source_.GetVolume().GetSliceRevision(sliceIndex); + + if (first_ || + lastSliceIndex_ != sliceIndex) + { + // Reorder the prefetching queue + source_.NotifyAxialSliceAccessed(sliceIndex); + } } else { // For coronal and sagittal projections, we take the global // revision of the volume - sliceRevision = volume_.GetRevision(); + sliceRevision = source_.GetVolume().GetRevision(); } if (first_ || - lastProjection_ == projection || - lastSliceIndex_ == sliceIndex || - lastSliceRevision_ == sliceRevision) + lastProjection_ != projection || + lastSliceIndex_ != sliceIndex || + lastSliceRevision_ != sliceRevision) { - // Eiter the viewport plane, or the content of the slice have not + // Either the viewport plane, or the content of the slice have not // changed since the last time the layer was set: Update is needed first_ = false; lastProjection_ = projection; lastSliceIndex_ = sliceIndex; lastSliceRevision_ = sliceRevision; + + std::auto_ptr texture; { - OrthancStone::ImageBuffer3D::SliceReader reader(volume_.GetImage(), projection, sliceIndex); + const DicomInstanceParameters& parameters = source_.GetVolume().GetSliceParameters + (projection == OrthancStone::VolumeProjection_Axial ? sliceIndex : 0); + + OrthancStone::ImageBuffer3D::SliceReader reader(source_.GetVolume().GetImage(), projection, sliceIndex); + texture.reset(parameters.CreateTexture(reader.GetAccessor())); + } + + const OrthancStone::CoordinateSystem3D& system = geometry.GetProjectionGeometry(projection); + + double x0, y0, x1, y1; + system.ProjectPoint(x0, y0, system.GetOrigin()); + system.ProjectPoint(x0, y0, system.GetOrigin() + system.GetAxisX()); + texture->SetOrigin(x0, y0); - // TODO: Convert the image to Float32 or RGB24 - - // TODO: Set the layer + double dx = x1 - x0; + double dy = y1 - y0; + if (!OrthancStone::LinearAlgebra::IsCloseToZero(dx) || + !OrthancStone::LinearAlgebra::IsCloseToZero(dy)) + { + texture->SetAngle(atan2(dy, dx)); } + + OrthancStone::Vector tmp; + geometry.GetVoxelDimensions(projection); + texture->SetPixelSpacing(tmp[0], tmp[1]); + + texture->SetLinearInterpolation(linearInterpolation_); + + scene_.SetLayer(layerDepth_, texture.release()); } } }; @@ -1995,7 +2176,7 @@ public: NativeOracle(IMessageEmitter& emitter) : - emitter_(emitter), + emitter_(emitter), state_(State_Setup), workers_(4) { @@ -2107,8 +2288,8 @@ public: ReaderLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) + that_(that), + lock_(that.mutex_) { } }; @@ -2122,8 +2303,8 @@ public: WriterLock(NativeApplicationContext& that) : - that_(that), - lock_(that.mutex_) + that_(that), + lock_(that.mutex_) { } @@ -2193,7 +2374,7 @@ oracle.RegisterObserverCallback (new OrthancStone::Callable - (*this, &Toto::Handle)); + (*this, &Toto::Handle)); oracle.RegisterObserverCallback (new OrthancStone::Callable @@ -2282,7 +2463,7 @@ // 2017-11-17-Anonymized //loader1->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT - loader2->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE + //loader2->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE // Delphine loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // CT diff -r 28b9e3a54200 -r 562cdd083b9e UnitTestsSources/TestStrategy.cpp --- a/UnitTestsSources/TestStrategy.cpp Tue May 21 10:27:54 2019 +0200 +++ b/UnitTestsSources/TestStrategy.cpp Tue May 21 10:28:43 2019 +0200 @@ -21,275 +21,10 @@ #include "gtest/gtest.h" -#include - -#include -#include - -namespace OrthancStone -{ - class IFetchingStrategy : public boost::noncopyable - { - public: - virtual ~IFetchingStrategy() - { - } - - virtual unsigned int GetItemsCount() const = 0; - - virtual unsigned int GetMaxQuality() const = 0; - - virtual bool GetNext(unsigned int& item, - unsigned int& quality) = 0; - - virtual void SetCurrent(unsigned int item) = 0; - - // Ask the strategy to re-schedule the item with the lowest - // priority in the fetching order. This allows to know which item - // should be dropped from a cache. - virtual void RecycleFurthest(unsigned int& item) = 0; - }; - - - class IFetchingItemsSorter : public boost::noncopyable - { - public: - virtual ~IFetchingItemsSorter() - { - } - - virtual unsigned int GetItemsCount() const = 0; - - // Sort a set of items given the current item - virtual void Sort(std::vector& target, - unsigned int current) = 0; - }; - - - - class BasicFetchingItemsSorter : public IFetchingItemsSorter - { - private: - unsigned int itemsCount_; - - public: - BasicFetchingItemsSorter(unsigned int itemsCount) : - itemsCount_(itemsCount) - { - if (itemsCount == 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - } - - virtual unsigned int GetItemsCount() const - { - return itemsCount_; - } - - virtual void Sort(std::vector& target, - unsigned int current) - { - if (current >= itemsCount_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - target.clear(); - target.reserve(itemsCount_); - target.push_back(current); - - const unsigned int countBelow = current; - const unsigned int countAbove = (itemsCount_ - 1) - current; - const unsigned int n = std::min(countBelow, countAbove); - - for (unsigned int i = 1; i <= n; i++) - { - assert(current + i < itemsCount_ && - current >= i); - target.push_back(current + i); - target.push_back(current - i); - } - - for (unsigned int i = current - n; i > 0; i--) - { - target.push_back(i - 1); - } - - for (unsigned int i = current + n + 1; i < itemsCount_; i++) - { - target.push_back(i); - } - - assert(target.size() == itemsCount_); - } - }; - - - class BasicFetchingStrategy : public IFetchingStrategy - { - private: - class ContentItem - { - private: - unsigned int item_; - unsigned int quality_; - - public: - ContentItem(unsigned int item, - unsigned int quality) : - item_(item), - quality_(quality) - { - } - - unsigned int GetItem() const - { - return item_; - } - - unsigned int GetQuality() const - { - return quality_; - } - }; +#include "../Framework/Loaders/BasicFetchingStrategy.h" +#include "../Framework/Loaders/BasicFetchingItemsSorter.h" - std::auto_ptr sorter_; - std::vector nextQuality_; - unsigned int maxQuality_; - std::vector content_; - size_t position_; - unsigned int blockSize_; - - void Schedule(unsigned int item, - unsigned int quality) - { - assert(item < GetItemsCount() && - quality <= maxQuality_); - - if (nextQuality_[item] <= quality) - { - content_.push_back(ContentItem(item, quality)); - } - } - - public: - BasicFetchingStrategy(IFetchingItemsSorter* sorter, // Takes ownership - unsigned int maxQuality) : - sorter_(sorter), - maxQuality_(maxQuality), - position_(0), - blockSize_(2) - { - if (sorter == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - nextQuality_.resize(sorter_->GetItemsCount(), 0), // Does not change along calls to "SetCurrent()" - - SetCurrent(0); - } - - virtual unsigned int GetItemsCount() const - { - return sorter_->GetItemsCount(); - } - - virtual unsigned int GetMaxQuality() const - { - return maxQuality_; - } - - void SetBlockSize(unsigned int size) - { - if (size <= 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - blockSize_ = size; - } - - virtual bool GetNext(unsigned int& item, - unsigned int& quality) - { - if (position_ >= content_.size()) - { - return false; - } - else - { - item = content_[position_].GetItem(); - quality = content_[position_].GetQuality(); - - assert(nextQuality_[item] <= quality); - nextQuality_[item] = quality + 1; - - position_ ++; - return true; - } - } - - virtual void SetCurrent(unsigned int item) - { - // TODO - This function is O(N) complexity where "N" is the - // number of items times the max quality. Could use a LRU index. - - position_ = 0; - - std::vector v; - sorter_->Sort(v, item); - - assert(v.size() == GetItemsCount()); - - if (v.size() == 0) - { - return; - } - - content_.clear(); - content_.reserve(v.size() * maxQuality_); - - Schedule(v.front(), maxQuality_); - - for (unsigned int q = 0; q <= maxQuality_; q++) - { - unsigned int start = 1 + q * blockSize_; - unsigned int end = start + blockSize_; - - if (q == maxQuality_ || - end > v.size()) - { - end = v.size(); - } - - unsigned int a = 0; - if (maxQuality_ >= q + 1) - { - a = maxQuality_ - q - 1; - } - - for (unsigned int j = a; j <= maxQuality_; j++) - { - for (unsigned int i = start; i < end; i++) - { - Schedule(v[i], j); - } - } - } - } - - // Ask the strategy to re-schedule the item with the lowest - // priority in the fetching order. This allows to know which item - // should be dropped from a cache. - virtual void RecycleFurthest(unsigned int& item) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - }; -} - +#include namespace