comparison Framework/Deprecated/SmartLoader.cpp @ 732:c35e98d22764

move Deprecated classes to a separate folder
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 21 May 2019 14:27:35 +0200
parents Framework/SmartLoader.cpp@4f2416d519b4
children be9c1530d40a
comparison
equal deleted inserted replaced
729:529189f399ec 732:c35e98d22764
1 /**
2 * Stone of Orthanc
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2019 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21
22 #include "SmartLoader.h"
23
24 #include "../Messages/MessageForwarder.h"
25 #include "../StoneException.h"
26 #include "Core/Images/Image.h"
27 #include "Core/Logging.h"
28 #include "Layers/DicomSeriesVolumeSlicer.h"
29 #include "Layers/FrameRenderer.h"
30 #include "Widgets/SliceViewerWidget.h"
31
32 namespace Deprecated
33 {
34 enum CachedSliceStatus
35 {
36 CachedSliceStatus_ScheduledToLoad,
37 CachedSliceStatus_GeometryLoaded,
38 CachedSliceStatus_ImageLoaded
39 };
40
41 class SmartLoader::CachedSlice : public IVolumeSlicer
42 {
43 public:
44 class RendererFactory : public LayerReadyMessage::IRendererFactory
45 {
46 private:
47 const CachedSlice& that_;
48
49 public:
50 RendererFactory(const CachedSlice& that) :
51 that_(that)
52 {
53 }
54
55 virtual ILayerRenderer* CreateRenderer() const
56 {
57 bool isFull = (that_.effectiveQuality_ == OrthancStone::SliceImageQuality_FullPng ||
58 that_.effectiveQuality_ == OrthancStone::SliceImageQuality_FullPam);
59
60 return FrameRenderer::CreateRenderer(*that_.image_, *that_.slice_, isFull);
61 }
62 };
63
64 unsigned int sliceIndex_;
65 std::auto_ptr<Slice> slice_;
66 boost::shared_ptr<Orthanc::ImageAccessor> image_;
67 OrthancStone::SliceImageQuality effectiveQuality_;
68 CachedSliceStatus status_;
69
70 public:
71 CachedSlice(OrthancStone::MessageBroker& broker) :
72 IVolumeSlicer(broker)
73 {
74 }
75
76 virtual ~CachedSlice()
77 {
78 }
79
80 virtual bool GetExtent(std::vector<OrthancStone::Vector>& points,
81 const OrthancStone::CoordinateSystem3D& viewportSlice)
82 {
83 // TODO: viewportSlice is not used !!!!
84 slice_->GetExtent(points);
85 return true;
86 }
87
88 virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice)
89 {
90 // TODO: viewportSlice is not used !!!!
91
92 // it has already been loaded -> trigger the "layer ready" message immediately otherwise, do nothing now. The LayerReady will be triggered
93 // once the VolumeSlicer is ready
94 if (status_ == CachedSliceStatus_ImageLoaded)
95 {
96 LOG(WARNING) << "ScheduleLayerCreation for CachedSlice (image is loaded): " << slice_->GetOrthancInstanceId();
97
98 RendererFactory factory(*this);
99 BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, slice_->GetGeometry()));
100 }
101 else
102 {
103 LOG(WARNING) << "ScheduleLayerCreation for CachedSlice (image is not loaded yet): " << slice_->GetOrthancInstanceId();
104 }
105 }
106
107 CachedSlice* Clone() const
108 {
109 CachedSlice* output = new CachedSlice(GetBroker());
110 output->sliceIndex_ = sliceIndex_;
111 output->slice_.reset(slice_->Clone());
112 output->image_ = image_;
113 output->effectiveQuality_ = effectiveQuality_;
114 output->status_ = status_;
115
116 return output;
117 }
118
119 };
120
121
122 SmartLoader::SmartLoader(OrthancStone::MessageBroker& broker,
123 OrthancApiClient& orthancApiClient) :
124 IObservable(broker),
125 IObserver(broker),
126 imageQuality_(OrthancStone::SliceImageQuality_FullPam),
127 orthancApiClient_(orthancApiClient)
128 {
129 }
130
131 void SmartLoader::SetFrameInWidget(SliceViewerWidget& sliceViewer,
132 size_t layerIndex,
133 const std::string& instanceId,
134 unsigned int frame)
135 {
136 // TODO: check if this frame has already been loaded or is already being loaded.
137 // - if already loaded: create a "clone" that will emit the GeometryReady/ImageReady messages "immediately"
138 // (it can not be immediate because Observers needs to register first and this is done after this method returns)
139 // - if currently loading, we need to return an object that will observe the existing VolumeSlicer and forward
140 // the messages to its observables
141 // in both cases, we must be carefull about objects lifecycle !!!
142
143 std::auto_ptr<IVolumeSlicer> layerSource;
144 std::string sliceKeyId = instanceId + ":" + boost::lexical_cast<std::string>(frame);
145 SmartLoader::CachedSlice* cachedSlice = NULL;
146
147 if (cachedSlices_.find(sliceKeyId) != cachedSlices_.end()) // && cachedSlices_[sliceKeyId]->status_ == CachedSliceStatus_Loaded)
148 {
149 layerSource.reset(cachedSlices_[sliceKeyId]->Clone());
150 cachedSlice = dynamic_cast<SmartLoader::CachedSlice*>(layerSource.get());
151 }
152 else
153 {
154 layerSource.reset(new DicomSeriesVolumeSlicer(IObserver::GetBroker(), orthancApiClient_));
155 dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->SetImageQuality(imageQuality_);
156 layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, IVolumeSlicer::GeometryReadyMessage>(*this, &SmartLoader::OnLayerGeometryReady));
157 layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, DicomSeriesVolumeSlicer::FrameReadyMessage>(*this, &SmartLoader::OnFrameReady));
158 layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, IVolumeSlicer::LayerReadyMessage>(*this, &SmartLoader::OnLayerReady));
159 dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->LoadFrame(instanceId, frame);
160 }
161
162 // make sure that the widget registers the events before we trigger them
163 if (sliceViewer.GetLayerCount() == layerIndex)
164 {
165 sliceViewer.AddLayer(layerSource.release());
166 }
167 else if (sliceViewer.GetLayerCount() > layerIndex)
168 {
169 sliceViewer.ReplaceLayer(layerIndex, layerSource.release());
170 }
171 else
172 {
173 throw OrthancStone::StoneException(OrthancStone::ErrorCode_CanOnlyAddOneLayerAtATime);
174 }
175
176 if (cachedSlice != NULL)
177 {
178 BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*cachedSlice));
179 }
180
181 }
182
183 void SmartLoader::PreloadSlice(const std::string instanceId,
184 unsigned int frame)
185 {
186 // TODO: reactivate -> need to be able to ScheduleLayerLoading in IVolumeSlicer without calling ScheduleLayerCreation
187 return;
188 // TODO: check if it is already in the cache
189
190
191
192 // create the slice in the cache with "empty" data
193 boost::shared_ptr<CachedSlice> cachedSlice(new CachedSlice(IObserver::GetBroker()));
194 cachedSlice->slice_.reset(new Slice(instanceId, frame));
195 cachedSlice->status_ = CachedSliceStatus_ScheduledToLoad;
196 std::string sliceKeyId = instanceId + ":" + boost::lexical_cast<std::string>(frame);
197
198 LOG(WARNING) << "Will preload: " << sliceKeyId;
199
200 cachedSlices_[sliceKeyId] = boost::shared_ptr<CachedSlice>(cachedSlice);
201
202 std::auto_ptr<IVolumeSlicer> layerSource(new DicomSeriesVolumeSlicer(IObserver::GetBroker(), orthancApiClient_));
203
204 dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->SetImageQuality(imageQuality_);
205 layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, IVolumeSlicer::GeometryReadyMessage>(*this, &SmartLoader::OnLayerGeometryReady));
206 layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, DicomSeriesVolumeSlicer::FrameReadyMessage>(*this, &SmartLoader::OnFrameReady));
207 layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, IVolumeSlicer::LayerReadyMessage>(*this, &SmartLoader::OnLayerReady));
208 dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->LoadFrame(instanceId, frame);
209
210 // keep a ref to the VolumeSlicer until the slice is fully loaded and saved to cache
211 preloadingInstances_[sliceKeyId] = boost::shared_ptr<IVolumeSlicer>(layerSource.release());
212 }
213
214
215 // void PreloadStudy(const std::string studyId)
216 // {
217 // /* TODO */
218 // }
219
220 // void PreloadSeries(const std::string seriesId)
221 // {
222 // /* TODO */
223 // }
224
225
226 void SmartLoader::OnLayerGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message)
227 {
228 const DicomSeriesVolumeSlicer& source =
229 dynamic_cast<const DicomSeriesVolumeSlicer&>(message.GetOrigin());
230
231 // save/replace the slice in cache
232 const Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount()
233 std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" +
234 boost::lexical_cast<std::string>(slice.GetFrame()));
235
236 LOG(WARNING) << "Geometry ready: " << sliceKeyId;
237
238 boost::shared_ptr<CachedSlice> cachedSlice(new CachedSlice(IObserver::GetBroker()));
239 cachedSlice->slice_.reset(slice.Clone());
240 cachedSlice->effectiveQuality_ = source.GetImageQuality();
241 cachedSlice->status_ = CachedSliceStatus_GeometryLoaded;
242
243 cachedSlices_[sliceKeyId] = boost::shared_ptr<CachedSlice>(cachedSlice);
244
245 // re-emit original Layer message to observers
246 BroadcastMessage(message);
247 }
248
249
250 void SmartLoader::OnFrameReady(const DicomSeriesVolumeSlicer::FrameReadyMessage& message)
251 {
252 // save/replace the slice in cache
253 const Slice& slice = message.GetSlice();
254 std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" +
255 boost::lexical_cast<std::string>(slice.GetFrame()));
256
257 LOG(WARNING) << "Image ready: " << sliceKeyId;
258
259 boost::shared_ptr<CachedSlice> cachedSlice(new CachedSlice(IObserver::GetBroker()));
260 cachedSlice->image_.reset(Orthanc::Image::Clone(message.GetFrame()));
261 cachedSlice->effectiveQuality_ = message.GetImageQuality();
262 cachedSlice->slice_.reset(message.GetSlice().Clone());
263 cachedSlice->status_ = CachedSliceStatus_ImageLoaded;
264
265 cachedSlices_[sliceKeyId] = cachedSlice;
266
267 // re-emit original Layer message to observers
268 BroadcastMessage(message);
269 }
270
271
272 void SmartLoader::OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message)
273 {
274 const DicomSeriesVolumeSlicer& source =
275 dynamic_cast<const DicomSeriesVolumeSlicer&>(message.GetOrigin());
276
277 const Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() ?
278 std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" +
279 boost::lexical_cast<std::string>(slice.GetFrame()));
280
281 LOG(WARNING) << "Layer ready: " << sliceKeyId;
282
283 // remove the slice from the preloading slices now that it has been fully loaded and it is referenced in the cache
284 if (preloadingInstances_.find(sliceKeyId) != preloadingInstances_.end())
285 {
286 preloadingInstances_.erase(sliceKeyId);
287 }
288
289 // re-emit original Layer message to observers
290 BroadcastMessage(message);
291 }
292 }