Mercurial > hg > orthanc-stone
annotate Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp @ 999:2d69b8bee484
Added tests for Dicom structure set classes (loaders and utils)
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Fri, 20 Sep 2019 11:58:33 +0200 |
parents | 262a0244e9b2 |
children | e713f1a99861 2d8ab34c8c91 |
rev | line source |
---|---|
814 | 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 "OrthancSeriesVolumeProgressiveLoader.h" | |
23 | |
24 #include "../Toolbox/GeometryToolbox.h" | |
25 #include "../Volumes/DicomVolumeImageMPRSlicer.h" | |
26 #include "BasicFetchingItemsSorter.h" | |
27 #include "BasicFetchingStrategy.h" | |
28 | |
29 #include <Core/Images/ImageProcessing.h> | |
30 #include <Core/OrthancException.h> | |
31 | |
32 namespace OrthancStone | |
33 { | |
34 class OrthancSeriesVolumeProgressiveLoader::ExtractedSlice : public DicomVolumeImageMPRSlicer::Slice | |
35 { | |
36 private: | |
37 const OrthancSeriesVolumeProgressiveLoader& that_; | |
38 | |
39 public: | |
40 ExtractedSlice(const OrthancSeriesVolumeProgressiveLoader& that, | |
41 const CoordinateSystem3D& plane) : | |
42 DicomVolumeImageMPRSlicer::Slice(*that.volume_, plane), | |
43 that_(that) | |
44 { | |
819 | 45 if (IsValid()) |
817
68f888812af4
simplification of DicomVolumeImageMPRSlicer::ExtractedSlice
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
814
diff
changeset
|
46 { |
819 | 47 if (GetProjection() == VolumeProjection_Axial) |
48 { | |
49 // For coronal and sagittal projections, we take the global | |
50 // revision of the volume because even if a single slice changes, | |
51 // this means the projection will yield a different result --> | |
52 // we must increase the revision as soon as any slice changes | |
53 SetRevision(that_.seriesGeometry_.GetSliceRevision(GetSliceIndex())); | |
54 } | |
817
68f888812af4
simplification of DicomVolumeImageMPRSlicer::ExtractedSlice
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
814
diff
changeset
|
55 |
819 | 56 if (that_.strategy_.get() != NULL && |
57 GetProjection() == VolumeProjection_Axial) | |
58 { | |
59 that_.strategy_->SetCurrent(GetSliceIndex()); | |
60 } | |
814 | 61 } |
62 } | |
63 }; | |
64 | |
65 | |
66 | |
67 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::CheckSlice(size_t index, | |
68 const DicomInstanceParameters& reference) const | |
69 { | |
70 const DicomInstanceParameters& slice = *slices_[index]; | |
71 | |
72 if (!GeometryToolbox::IsParallel( | |
73 reference.GetGeometry().GetNormal(), | |
74 slice.GetGeometry().GetNormal())) | |
75 { | |
76 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, | |
77 "A slice in the volume image is not parallel to the others"); | |
78 } | |
79 | |
80 if (reference.GetExpectedPixelFormat() != slice.GetExpectedPixelFormat()) | |
81 { | |
82 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat, | |
83 "The pixel format changes across the slices of the volume image"); | |
84 } | |
85 | |
86 if (reference.GetImageInformation().GetWidth() != slice.GetImageInformation().GetWidth() || | |
87 reference.GetImageInformation().GetHeight() != slice.GetImageInformation().GetHeight()) | |
88 { | |
89 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize, | |
90 "The width/height of slices are not constant in the volume image"); | |
91 } | |
92 | |
93 if (!LinearAlgebra::IsNear(reference.GetPixelSpacingX(), slice.GetPixelSpacingX()) || | |
94 !LinearAlgebra::IsNear(reference.GetPixelSpacingY(), slice.GetPixelSpacingY())) | |
95 { | |
96 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, | |
97 "The pixel spacing of the slices change across the volume image"); | |
98 } | |
99 } | |
100 | |
101 | |
102 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::CheckVolume() const | |
103 { | |
104 for (size_t i = 0; i < slices_.size(); i++) | |
105 { | |
106 assert(slices_[i] != NULL); | |
107 if (slices_[i]->GetImageInformation().GetNumberOfFrames() != 1) | |
108 { | |
109 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, | |
110 "This class does not support multi-frame images"); | |
111 } | |
112 } | |
113 | |
114 if (slices_.size() != 0) | |
115 { | |
116 const DicomInstanceParameters& reference = *slices_[0]; | |
117 | |
118 for (size_t i = 1; i < slices_.size(); i++) | |
119 { | |
120 CheckSlice(i, reference); | |
121 } | |
122 } | |
123 } | |
124 | |
125 | |
126 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::Clear() | |
127 { | |
128 for (size_t i = 0; i < slices_.size(); i++) | |
129 { | |
130 assert(slices_[i] != NULL); | |
131 delete slices_[i]; | |
132 } | |
133 | |
134 slices_.clear(); | |
135 slicesRevision_.clear(); | |
136 } | |
137 | |
138 | |
139 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::CheckSliceIndex(size_t index) const | |
140 { | |
141 if (!HasGeometry()) | |
142 { | |
956
a7351ad54960
Made IsContextLost automatically set the flag by checking with the emscripten
Benjamin Golinvaux <bgo@osimis.io>
parents:
949
diff
changeset
|
143 LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::CheckSliceIndex(size_t index): (!HasGeometry())"; |
814 | 144 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
145 } | |
146 else if (index >= slices_.size()) | |
147 { | |
148 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
149 } | |
150 else | |
151 { | |
152 assert(slices_.size() == GetImageGeometry().GetDepth() && | |
153 slices_.size() == slicesRevision_.size()); | |
154 } | |
155 } | |
156 | |
157 | |
158 // WARNING: The payload of "slices" must be of class "DicomInstanceParameters" | |
159 // (called with the slices created in LoadGeometry) | |
160 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::ComputeGeometry(SlicesSorter& slices) | |
161 { | |
162 Clear(); | |
163 | |
164 if (!slices.Sort()) | |
165 { | |
166 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, | |
167 "Cannot sort the 3D slices of a DICOM series"); | |
168 } | |
169 | |
170 if (slices.GetSlicesCount() == 0) | |
171 { | |
172 geometry_.reset(new VolumeImageGeometry); | |
173 } | |
174 else | |
175 { | |
176 slices_.reserve(slices.GetSlicesCount()); | |
177 slicesRevision_.resize(slices.GetSlicesCount(), 0); | |
178 | |
179 for (size_t i = 0; i < slices.GetSlicesCount(); i++) | |
180 { | |
181 const DicomInstanceParameters& slice = | |
182 dynamic_cast<const DicomInstanceParameters&>(slices.GetSlicePayload(i)); | |
183 slices_.push_back(new DicomInstanceParameters(slice)); | |
184 } | |
185 | |
186 CheckVolume(); | |
187 | |
188 const double spacingZ = slices.ComputeSpacingBetweenSlices(); | |
189 LOG(INFO) << "Computed spacing between slices: " << spacingZ << "mm"; | |
190 | |
191 const DicomInstanceParameters& parameters = *slices_[0]; | |
192 | |
193 geometry_.reset(new VolumeImageGeometry); | |
949
32eaf4929b08
OrthancMultiframeVolumeLoader and OrthancSeriesVolumeProgressiveLoader now implement IGeometryProvider so that the geometry reference can be switched (CT or DOSE, for instance) + VolumeImageGeometry::SetSize renamed to VolumeImageGeometry::SetSizeInVoxels + prevent text layer update if text or properties do not change + a few stream operator<< for debug (Vector, Matrix,...) + fixed memory access aligment issues in ImageBuffer3D::ExtractSagittalSlice + fix for wrong screen Y offset of mpr slices in DicomVolumeImageMPRSlicer.
Benjamin Golinvaux <bgo@osimis.io>
parents:
937
diff
changeset
|
194 geometry_->SetSizeInVoxels(parameters.GetImageInformation().GetWidth(), |
814 | 195 parameters.GetImageInformation().GetHeight(), |
196 static_cast<unsigned int>(slices.GetSlicesCount())); | |
197 geometry_->SetAxialGeometry(slices.GetSliceGeometry(0)); | |
198 geometry_->SetVoxelDimensions(parameters.GetPixelSpacingX(), | |
199 parameters.GetPixelSpacingY(), spacingZ); | |
200 } | |
201 } | |
202 | |
203 | |
204 const VolumeImageGeometry& OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::GetImageGeometry() const | |
205 { | |
206 if (!HasGeometry()) | |
207 { | |
956
a7351ad54960
Made IsContextLost automatically set the flag by checking with the emscripten
Benjamin Golinvaux <bgo@osimis.io>
parents:
949
diff
changeset
|
208 LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::GetImageGeometry(): (!HasGeometry())"; |
814 | 209 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
210 } | |
211 else | |
212 { | |
213 assert(slices_.size() == geometry_->GetDepth()); | |
214 return *geometry_; | |
215 } | |
216 } | |
217 | |
218 | |
219 const DicomInstanceParameters& OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::GetSliceParameters(size_t index) const | |
220 { | |
221 CheckSliceIndex(index); | |
222 return *slices_[index]; | |
223 } | |
224 | |
225 | |
226 uint64_t OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::GetSliceRevision(size_t index) const | |
227 { | |
228 CheckSliceIndex(index); | |
229 return slicesRevision_[index]; | |
230 } | |
231 | |
232 | |
233 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::IncrementSliceRevision(size_t index) | |
234 { | |
235 CheckSliceIndex(index); | |
236 slicesRevision_[index] ++; | |
237 } | |
238 | |
239 | |
240 static unsigned int GetSliceIndexPayload(const OracleCommandWithPayload& command) | |
241 { | |
242 return dynamic_cast< const Orthanc::SingleValueObject<unsigned int>& >(command.GetPayload()).GetValue(); | |
243 } | |
244 | |
245 | |
246 void OrthancSeriesVolumeProgressiveLoader::ScheduleNextSliceDownload() | |
247 { | |
248 assert(strategy_.get() != NULL); | |
249 | |
250 unsigned int sliceIndex, quality; | |
251 | |
252 if (strategy_->GetNext(sliceIndex, quality)) | |
253 { | |
254 assert(quality <= BEST_QUALITY); | |
255 | |
256 const DicomInstanceParameters& slice = seriesGeometry_.GetSliceParameters(sliceIndex); | |
257 | |
258 const std::string& instance = slice.GetOrthancInstanceIdentifier(); | |
259 if (instance.empty()) | |
260 { | |
261 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
262 } | |
263 | |
264 std::auto_ptr<OracleCommandWithPayload> command; | |
265 | |
266 if (quality == BEST_QUALITY) | |
267 { | |
268 std::auto_ptr<GetOrthancImageCommand> tmp(new GetOrthancImageCommand); | |
937
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
269 // TODO: review the following comment. |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
270 // - Commented out by bgo on 2019-07-19 | reason: Alain has seen cases |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
271 // where gzipping the uint16 image took 11 sec to produce 5mb. |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
272 // The unzipped request was much much faster. |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
273 // - Re-enabled on 2019-07-30. Reason: in Web Assembly, the browser |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
274 // does not use the Accept-Encoding header and always requests |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
275 // compression. Furthermore, NOT |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
276 tmp->SetHttpHeader("Accept-Encoding", "gzip"); |
814 | 277 tmp->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); |
819 | 278 tmp->SetInstanceUri(instance, slice.GetExpectedPixelFormat()); |
814 | 279 tmp->SetExpectedPixelFormat(slice.GetExpectedPixelFormat()); |
280 command.reset(tmp.release()); | |
281 } | |
282 else | |
283 { | |
284 std::auto_ptr<GetOrthancWebViewerJpegCommand> tmp(new GetOrthancWebViewerJpegCommand); | |
913
2b4b6b86520a
Re-enabled gl debug output in GuiAdapter +
Benjamin Golinvaux <bgo@osimis.io>
parents:
819
diff
changeset
|
285 // TODO: review the following comment. Commented out by bgo on 2019-07-19 |
2b4b6b86520a
Re-enabled gl debug output in GuiAdapter +
Benjamin Golinvaux <bgo@osimis.io>
parents:
819
diff
changeset
|
286 // (gzip for jpeg seems overkill) |
2b4b6b86520a
Re-enabled gl debug output in GuiAdapter +
Benjamin Golinvaux <bgo@osimis.io>
parents:
819
diff
changeset
|
287 //tmp->SetHttpHeader("Accept-Encoding", "gzip"); |
814 | 288 tmp->SetInstance(instance); |
289 tmp->SetQuality((quality == 0 ? 50 : 90)); | |
290 tmp->SetExpectedPixelFormat(slice.GetExpectedPixelFormat()); | |
291 command.reset(tmp.release()); | |
292 } | |
293 | |
294 command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(sliceIndex)); | |
295 oracle_.Schedule(*this, command.release()); | |
296 } | |
937
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
297 else |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
298 { |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
299 // loading is finished! |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
300 volumeImageReadyInHighQuality_ = true; |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
301 BroadcastMessage(OrthancSeriesVolumeProgressiveLoader::VolumeImageReadyInHighQuality(*this)); |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
302 } |
814 | 303 } |
304 | |
305 /** | |
306 This is called in response to GET "/series/XXXXXXXXXXXXX/instances-tags" | |
307 */ | |
308 void OrthancSeriesVolumeProgressiveLoader::LoadGeometry(const OrthancRestApiCommand::SuccessMessage& message) | |
309 { | |
310 Json::Value body; | |
311 message.ParseJsonBody(body); | |
312 | |
313 if (body.type() != Json::objectValue) | |
314 { | |
315 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
316 } | |
317 | |
318 { | |
319 Json::Value::Members instances = body.getMemberNames(); | |
320 | |
321 SlicesSorter slices; | |
322 | |
323 for (size_t i = 0; i < instances.size(); i++) | |
324 { | |
325 Orthanc::DicomMap dicom; | |
326 dicom.FromDicomAsJson(body[instances[i]]); | |
327 | |
328 std::auto_ptr<DicomInstanceParameters> instance(new DicomInstanceParameters(dicom)); | |
329 instance->SetOrthancInstanceIdentifier(instances[i]); | |
330 | |
331 // the 3D plane corresponding to the slice | |
332 CoordinateSystem3D geometry = instance->GetGeometry(); | |
333 slices.AddSlice(geometry, instance.release()); | |
334 } | |
335 | |
336 seriesGeometry_.ComputeGeometry(slices); | |
337 } | |
338 | |
339 size_t slicesCount = seriesGeometry_.GetImageGeometry().GetDepth(); | |
340 | |
341 if (slicesCount == 0) | |
342 { | |
343 volume_->Initialize(seriesGeometry_.GetImageGeometry(), Orthanc::PixelFormat_Grayscale8); | |
344 } | |
345 else | |
346 { | |
347 const DicomInstanceParameters& parameters = seriesGeometry_.GetSliceParameters(0); | |
348 | |
349 volume_->Initialize(seriesGeometry_.GetImageGeometry(), parameters.GetExpectedPixelFormat()); | |
350 volume_->SetDicomParameters(parameters); | |
351 volume_->GetPixelData().Clear(); | |
352 | |
353 strategy_.reset(new BasicFetchingStrategy(sorter_->CreateSorter(static_cast<unsigned int>(slicesCount)), BEST_QUALITY)); | |
354 | |
355 assert(simultaneousDownloads_ != 0); | |
356 for (unsigned int i = 0; i < simultaneousDownloads_; i++) | |
357 { | |
358 ScheduleNextSliceDownload(); | |
359 } | |
360 } | |
361 | |
362 slicesQuality_.resize(slicesCount, 0); | |
363 | |
364 BroadcastMessage(DicomVolumeImage::GeometryReadyMessage(*volume_)); | |
365 } | |
366 | |
367 | |
368 void OrthancSeriesVolumeProgressiveLoader::SetSliceContent(unsigned int sliceIndex, | |
369 const Orthanc::ImageAccessor& image, | |
370 unsigned int quality) | |
371 { | |
372 assert(sliceIndex < slicesQuality_.size() && | |
373 slicesQuality_.size() == volume_->GetPixelData().GetDepth()); | |
374 | |
375 if (quality >= slicesQuality_[sliceIndex]) | |
376 { | |
377 { | |
378 ImageBuffer3D::SliceWriter writer(volume_->GetPixelData(), VolumeProjection_Axial, sliceIndex); | |
379 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image); | |
380 } | |
381 | |
382 volume_->IncrementRevision(); | |
383 seriesGeometry_.IncrementSliceRevision(sliceIndex); | |
384 slicesQuality_[sliceIndex] = quality; | |
385 | |
386 BroadcastMessage(DicomVolumeImage::ContentUpdatedMessage(*volume_)); | |
387 } | |
388 | |
389 ScheduleNextSliceDownload(); | |
390 } | |
391 | |
392 | |
393 void OrthancSeriesVolumeProgressiveLoader::LoadBestQualitySliceContent(const GetOrthancImageCommand::SuccessMessage& message) | |
394 { | |
395 SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), message.GetImage(), BEST_QUALITY); | |
396 } | |
397 | |
398 | |
399 void OrthancSeriesVolumeProgressiveLoader::LoadJpegSliceContent(const GetOrthancWebViewerJpegCommand::SuccessMessage& message) | |
400 { | |
401 unsigned int quality; | |
402 | |
403 switch (message.GetOrigin().GetQuality()) | |
404 { | |
405 case 50: | |
406 quality = LOW_QUALITY; | |
407 break; | |
408 | |
409 case 90: | |
410 quality = MIDDLE_QUALITY; | |
411 break; | |
412 | |
413 default: | |
414 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
415 } | |
416 | |
417 SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), message.GetImage(), quality); | |
418 } | |
419 | |
420 | |
421 OrthancSeriesVolumeProgressiveLoader::OrthancSeriesVolumeProgressiveLoader(const boost::shared_ptr<DicomVolumeImage>& volume, | |
422 IOracle& oracle, | |
423 IObservable& oracleObservable) : | |
424 IObserver(oracleObservable.GetBroker()), | |
425 IObservable(oracleObservable.GetBroker()), | |
426 oracle_(oracle), | |
977
262a0244e9b2
Added missing Unregister for objects that register by the broker + logs + guard in FetchContext
Benjamin Golinvaux <bgo@osimis.io>
parents:
956
diff
changeset
|
427 oracleObservable_(oracleObservable), |
814 | 428 active_(false), |
429 simultaneousDownloads_(4), | |
430 volume_(volume), | |
937
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
431 sorter_(new BasicFetchingItemsSorter::Factory), |
86ac61a040c9
Added getters and notifications to allow clients of the loaders (DicomStructureSetLoader, OrthancSeriesVolumeProgressiveLoader and OrthancMultiframeVolumeLoader) to know when the loading is finished + added ability for SDL event loop to execute a callback repeatedly (used to check the view loading state)
Benjamin Golinvaux <bgo@osimis.io>
parents:
935
diff
changeset
|
432 volumeImageReadyInHighQuality_(false) |
814 | 433 { |
434 oracleObservable.RegisterObserverCallback( | |
435 new Callable<OrthancSeriesVolumeProgressiveLoader, OrthancRestApiCommand::SuccessMessage> | |
436 (*this, &OrthancSeriesVolumeProgressiveLoader::LoadGeometry)); | |
437 | |
438 oracleObservable.RegisterObserverCallback( | |
439 new Callable<OrthancSeriesVolumeProgressiveLoader, GetOrthancImageCommand::SuccessMessage> | |
440 (*this, &OrthancSeriesVolumeProgressiveLoader::LoadBestQualitySliceContent)); | |
441 | |
442 oracleObservable.RegisterObserverCallback( | |
443 new Callable<OrthancSeriesVolumeProgressiveLoader, GetOrthancWebViewerJpegCommand::SuccessMessage> | |
444 (*this, &OrthancSeriesVolumeProgressiveLoader::LoadJpegSliceContent)); | |
445 } | |
446 | |
935
401808e7ff2e
Added traces in LoaderCache objects dtors + fuse to prevent dead weak ptrs to be calleds in VisitWdigets (in GuiAdapter.h)
Benjamin Golinvaux <bgo@osimis.io>
parents:
933
diff
changeset
|
447 OrthancSeriesVolumeProgressiveLoader::~OrthancSeriesVolumeProgressiveLoader() |
401808e7ff2e
Added traces in LoaderCache objects dtors + fuse to prevent dead weak ptrs to be calleds in VisitWdigets (in GuiAdapter.h)
Benjamin Golinvaux <bgo@osimis.io>
parents:
933
diff
changeset
|
448 { |
977
262a0244e9b2
Added missing Unregister for objects that register by the broker + logs + guard in FetchContext
Benjamin Golinvaux <bgo@osimis.io>
parents:
956
diff
changeset
|
449 oracleObservable_.Unregister(this); |
935
401808e7ff2e
Added traces in LoaderCache objects dtors + fuse to prevent dead weak ptrs to be calleds in VisitWdigets (in GuiAdapter.h)
Benjamin Golinvaux <bgo@osimis.io>
parents:
933
diff
changeset
|
450 LOG(TRACE) << "OrthancSeriesVolumeProgressiveLoader::~OrthancSeriesVolumeProgressiveLoader()"; |
401808e7ff2e
Added traces in LoaderCache objects dtors + fuse to prevent dead weak ptrs to be calleds in VisitWdigets (in GuiAdapter.h)
Benjamin Golinvaux <bgo@osimis.io>
parents:
933
diff
changeset
|
451 } |
814 | 452 |
453 void OrthancSeriesVolumeProgressiveLoader::SetSimultaneousDownloads(unsigned int count) | |
454 { | |
455 if (active_) | |
456 { | |
956
a7351ad54960
Made IsContextLost automatically set the flag by checking with the emscripten
Benjamin Golinvaux <bgo@osimis.io>
parents:
949
diff
changeset
|
457 LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::SetSimultaneousDownloads(): (active_)"; |
814 | 458 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
459 } | |
460 else if (count == 0) | |
461 { | |
462 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
463 } | |
464 else | |
465 { | |
466 simultaneousDownloads_ = count; | |
467 } | |
468 } | |
469 | |
470 | |
471 void OrthancSeriesVolumeProgressiveLoader::LoadSeries(const std::string& seriesId) | |
472 { | |
933
f75f6cb69c1b
Commented-out traces for debug
Benjamin Golinvaux <bgo@osimis.io>
parents:
913
diff
changeset
|
473 // LOG(TRACE) << "OrthancSeriesVolumeProgressiveLoader::LoadSeries seriesId=" << seriesId; |
814 | 474 if (active_) |
475 { | |
933
f75f6cb69c1b
Commented-out traces for debug
Benjamin Golinvaux <bgo@osimis.io>
parents:
913
diff
changeset
|
476 // LOG(TRACE) << "OrthancSeriesVolumeProgressiveLoader::LoadSeries NOT ACTIVE! --> ERROR"; |
956
a7351ad54960
Made IsContextLost automatically set the flag by checking with the emscripten
Benjamin Golinvaux <bgo@osimis.io>
parents:
949
diff
changeset
|
477 LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::LoadSeries(const std::string& seriesId): (active_)"; |
814 | 478 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
479 } | |
480 else | |
481 { | |
482 active_ = true; | |
483 | |
484 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
485 command->SetUri("/series/" + seriesId + "/instances-tags"); | |
486 | |
933
f75f6cb69c1b
Commented-out traces for debug
Benjamin Golinvaux <bgo@osimis.io>
parents:
913
diff
changeset
|
487 // LOG(TRACE) << "OrthancSeriesVolumeProgressiveLoader::LoadSeries about to call oracle_.Schedule"; |
814 | 488 oracle_.Schedule(*this, command.release()); |
933
f75f6cb69c1b
Commented-out traces for debug
Benjamin Golinvaux <bgo@osimis.io>
parents:
913
diff
changeset
|
489 // LOG(TRACE) << "OrthancSeriesVolumeProgressiveLoader::LoadSeries called oracle_.Schedule"; |
814 | 490 } |
491 } | |
492 | |
493 | |
494 IVolumeSlicer::IExtractedSlice* | |
495 OrthancSeriesVolumeProgressiveLoader::ExtractSlice(const CoordinateSystem3D& cuttingPlane) | |
496 { | |
497 if (volume_->HasGeometry()) | |
498 { | |
499 return new ExtractedSlice(*this, cuttingPlane); | |
500 } | |
501 else | |
502 { | |
503 return new IVolumeSlicer::InvalidSlice; | |
504 } | |
505 } | |
506 } |