Mercurial > hg > orthanc-stone
comparison OrthancStone/Sources/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp @ 1512:244ad1e4e76a
reorganization of folders
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 07 Jul 2020 16:21:02 +0200 |
parents | Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp@28c64c246312 |
children | 4fb8fdf03314 |
comparison
equal
deleted
inserted
replaced
1511:9dfeee74c1e6 | 1512:244ad1e4e76a |
---|---|
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-2020 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 "../StoneException.h" | |
25 #include "../Loaders/ILoadersContext.h" | |
26 #include "../Loaders/BasicFetchingItemsSorter.h" | |
27 #include "../Loaders/BasicFetchingStrategy.h" | |
28 #include "../Toolbox/GeometryToolbox.h" | |
29 #include "../Volumes/DicomVolumeImageMPRSlicer.h" | |
30 | |
31 #include <Compatibility.h> | |
32 #include <Images/ImageProcessing.h> | |
33 #include <OrthancException.h> | |
34 | |
35 | |
36 namespace OrthancStone | |
37 { | |
38 using OrthancStone::ILoadersContext; | |
39 | |
40 class OrthancSeriesVolumeProgressiveLoader::ExtractedSlice : public OrthancStone::DicomVolumeImageMPRSlicer::Slice | |
41 { | |
42 private: | |
43 const OrthancSeriesVolumeProgressiveLoader& that_; | |
44 | |
45 public: | |
46 ExtractedSlice(const OrthancSeriesVolumeProgressiveLoader& that, | |
47 const OrthancStone::CoordinateSystem3D& plane) : | |
48 OrthancStone::DicomVolumeImageMPRSlicer::Slice(*that.volume_, plane), | |
49 that_(that) | |
50 { | |
51 if (IsValid()) | |
52 { | |
53 if (GetProjection() == OrthancStone::VolumeProjection_Axial) | |
54 { | |
55 // For coronal and sagittal projections, we take the global | |
56 // revision of the volume because even if a single slice changes, | |
57 // this means the projection will yield a different result --> | |
58 // we must increase the revision as soon as any slice changes | |
59 SetRevision(that_.seriesGeometry_.GetSliceRevision(GetSliceIndex())); | |
60 } | |
61 | |
62 if (that_.strategy_.get() != NULL && | |
63 GetProjection() == OrthancStone::VolumeProjection_Axial) | |
64 { | |
65 that_.strategy_->SetCurrent(GetSliceIndex()); | |
66 } | |
67 } | |
68 } | |
69 }; | |
70 | |
71 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::CheckSlice( | |
72 size_t index, const OrthancStone::DicomInstanceParameters& reference) const | |
73 { | |
74 const OrthancStone::DicomInstanceParameters& slice = *slices_[index]; | |
75 | |
76 if (!OrthancStone::GeometryToolbox::IsParallel( | |
77 reference.GetGeometry().GetNormal(), | |
78 slice.GetGeometry().GetNormal())) | |
79 { | |
80 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, | |
81 "A slice in the volume image is not parallel to the others"); | |
82 } | |
83 | |
84 if (reference.GetExpectedPixelFormat() != slice.GetExpectedPixelFormat()) | |
85 { | |
86 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat, | |
87 "The pixel format changes across the slices of the volume image"); | |
88 } | |
89 | |
90 if (reference.GetImageInformation().GetWidth() != slice.GetImageInformation().GetWidth() || | |
91 reference.GetImageInformation().GetHeight() != slice.GetImageInformation().GetHeight()) | |
92 { | |
93 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize, | |
94 "The width/height of slices are not constant in the volume image"); | |
95 } | |
96 | |
97 if (!OrthancStone::LinearAlgebra::IsNear(reference.GetPixelSpacingX(), slice.GetPixelSpacingX()) || | |
98 !OrthancStone::LinearAlgebra::IsNear(reference.GetPixelSpacingY(), slice.GetPixelSpacingY())) | |
99 { | |
100 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, | |
101 "The pixel spacing of the slices change across the volume image"); | |
102 } | |
103 } | |
104 | |
105 | |
106 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::CheckVolume() const | |
107 { | |
108 for (size_t i = 0; i < slices_.size(); i++) | |
109 { | |
110 assert(slices_[i] != NULL); | |
111 if (slices_[i]->GetImageInformation().GetNumberOfFrames() != 1) | |
112 { | |
113 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, | |
114 "This class does not support multi-frame images"); | |
115 } | |
116 } | |
117 | |
118 if (slices_.size() != 0) | |
119 { | |
120 const OrthancStone::DicomInstanceParameters& reference = *slices_[0]; | |
121 | |
122 for (size_t i = 1; i < slices_.size(); i++) | |
123 { | |
124 CheckSlice(i, reference); | |
125 } | |
126 } | |
127 } | |
128 | |
129 | |
130 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::Clear() | |
131 { | |
132 for (size_t i = 0; i < slices_.size(); i++) | |
133 { | |
134 assert(slices_[i] != NULL); | |
135 delete slices_[i]; | |
136 } | |
137 | |
138 slices_.clear(); | |
139 slicesRevision_.clear(); | |
140 } | |
141 | |
142 | |
143 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::CheckSliceIndex(size_t index) const | |
144 { | |
145 if (!HasGeometry()) | |
146 { | |
147 LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::CheckSliceIndex(size_t index): (!HasGeometry())"; | |
148 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
149 } | |
150 else if (index >= slices_.size()) | |
151 { | |
152 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
153 } | |
154 else | |
155 { | |
156 assert(slices_.size() == GetImageGeometry().GetDepth() && | |
157 slices_.size() == slicesRevision_.size()); | |
158 } | |
159 } | |
160 | |
161 | |
162 // WARNING: The payload of "slices" must be of class "DicomInstanceParameters" | |
163 // (called with the slices created in LoadGeometry) | |
164 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::ComputeGeometry(OrthancStone::SlicesSorter& slices) | |
165 { | |
166 Clear(); | |
167 | |
168 if (!slices.Sort()) | |
169 { | |
170 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, | |
171 "Cannot sort the 3D slices of a DICOM series"); | |
172 } | |
173 | |
174 if (slices.GetSlicesCount() == 0) | |
175 { | |
176 geometry_.reset(new OrthancStone::VolumeImageGeometry); | |
177 } | |
178 else | |
179 { | |
180 slices_.reserve(slices.GetSlicesCount()); | |
181 slicesRevision_.resize(slices.GetSlicesCount(), 0); | |
182 | |
183 for (size_t i = 0; i < slices.GetSlicesCount(); i++) | |
184 { | |
185 const OrthancStone::DicomInstanceParameters& slice = | |
186 dynamic_cast<const OrthancStone::DicomInstanceParameters&>(slices.GetSlicePayload(i)); | |
187 slices_.push_back(new OrthancStone::DicomInstanceParameters(slice)); | |
188 } | |
189 | |
190 CheckVolume(); | |
191 | |
192 double spacingZ; | |
193 | |
194 if (slices.ComputeSpacingBetweenSlices(spacingZ)) | |
195 { | |
196 LOG(TRACE) << "Computed spacing between slices: " << spacingZ << "mm"; | |
197 | |
198 const OrthancStone::DicomInstanceParameters& parameters = *slices_[0]; | |
199 | |
200 geometry_.reset(new OrthancStone::VolumeImageGeometry); | |
201 geometry_->SetSizeInVoxels(parameters.GetImageInformation().GetWidth(), | |
202 parameters.GetImageInformation().GetHeight(), | |
203 static_cast<unsigned int>(slices.GetSlicesCount())); | |
204 geometry_->SetAxialGeometry(slices.GetSliceGeometry(0)); | |
205 geometry_->SetVoxelDimensions(parameters.GetPixelSpacingX(), | |
206 parameters.GetPixelSpacingY(), spacingZ); | |
207 } | |
208 else | |
209 { | |
210 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, | |
211 "The origins of the slices of a volume image are not regularly spaced"); | |
212 } | |
213 } | |
214 } | |
215 | |
216 | |
217 const OrthancStone::VolumeImageGeometry& OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::GetImageGeometry() const | |
218 { | |
219 if (!HasGeometry()) | |
220 { | |
221 LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::GetImageGeometry(): (!HasGeometry())"; | |
222 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
223 } | |
224 else | |
225 { | |
226 assert(slices_.size() == geometry_->GetDepth()); | |
227 return *geometry_; | |
228 } | |
229 } | |
230 | |
231 | |
232 const OrthancStone::DicomInstanceParameters& OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::GetSliceParameters(size_t index) const | |
233 { | |
234 CheckSliceIndex(index); | |
235 return *slices_[index]; | |
236 } | |
237 | |
238 | |
239 uint64_t OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::GetSliceRevision(size_t index) const | |
240 { | |
241 CheckSliceIndex(index); | |
242 return slicesRevision_[index]; | |
243 } | |
244 | |
245 | |
246 void OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::IncrementSliceRevision(size_t index) | |
247 { | |
248 CheckSliceIndex(index); | |
249 slicesRevision_[index] ++; | |
250 } | |
251 | |
252 | |
253 static unsigned int GetSliceIndexPayload(const OrthancStone::OracleCommandBase& command) | |
254 { | |
255 assert(command.HasPayload()); | |
256 return dynamic_cast< const Orthanc::SingleValueObject<unsigned int>& >(command.GetPayload()).GetValue(); | |
257 } | |
258 | |
259 | |
260 void OrthancSeriesVolumeProgressiveLoader::ScheduleNextSliceDownload() | |
261 { | |
262 assert(strategy_.get() != NULL); | |
263 | |
264 unsigned int sliceIndex = 0, quality = 0; | |
265 | |
266 if (strategy_->GetNext(sliceIndex, quality)) | |
267 { | |
268 if (!progressiveQuality_) | |
269 { | |
270 ORTHANC_ASSERT(quality == QUALITY_00, "INTERNAL ERROR. quality != QUALITY_00 in " | |
271 << "OrthancSeriesVolumeProgressiveLoader::ScheduleNextSliceDownload"); | |
272 } | |
273 | |
274 const OrthancStone::DicomInstanceParameters& slice = seriesGeometry_.GetSliceParameters(sliceIndex); | |
275 | |
276 const std::string& instance = slice.GetOrthancInstanceIdentifier(); | |
277 if (instance.empty()) | |
278 { | |
279 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
280 } | |
281 | |
282 std::unique_ptr<OrthancStone::OracleCommandBase> command; | |
283 | |
284 if (!progressiveQuality_ || quality == QUALITY_02) | |
285 { | |
286 std::unique_ptr<OrthancStone::GetOrthancImageCommand> tmp(new OrthancStone::GetOrthancImageCommand); | |
287 // TODO: review the following comment. | |
288 // - Commented out by bgo on 2019-07-19 | reason: Alain has seen cases | |
289 // where gzipping the uint16 image took 11 sec to produce 5mb. | |
290 // The unzipped request was much much faster. | |
291 // - Re-enabled on 2019-07-30. Reason: in Web Assembly, the browser | |
292 // does not use the Accept-Encoding header and always requests | |
293 // compression. Furthermore, NOT | |
294 tmp->SetHttpHeader("Accept-Encoding", "gzip"); | |
295 tmp->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); | |
296 tmp->SetInstanceUri(instance, slice.GetExpectedPixelFormat()); | |
297 tmp->SetExpectedPixelFormat(slice.GetExpectedPixelFormat()); | |
298 //LOG(INFO) | |
299 // << "OrthancSeriesVolumeProgressiveLoader.ScheduleNextSliceDownload()" | |
300 // << " sliceIndex = " << sliceIndex << " slice quality = " << quality | |
301 // << " URI = " << tmp->GetUri(); | |
302 command.reset(tmp.release()); | |
303 } | |
304 else // progressive mode is true AND quality is not final (different from QUALITY_02 | |
305 { | |
306 std::unique_ptr<OrthancStone::GetOrthancWebViewerJpegCommand> tmp( | |
307 new OrthancStone::GetOrthancWebViewerJpegCommand); | |
308 | |
309 // TODO: review the following comment. Commented out by bgo on 2019-07-19 | |
310 // (gzip for jpeg seems overkill) | |
311 //tmp->SetHttpHeader("Accept-Encoding", "gzip"); | |
312 tmp->SetInstance(instance); | |
313 tmp->SetQuality((quality == 0 ? 50 : 90)); // QUALITY_00 is Jpeg50 while QUALITY_01 is Jpeg90 | |
314 tmp->SetExpectedPixelFormat(slice.GetExpectedPixelFormat()); | |
315 LOG(TRACE) | |
316 << "OrthancSeriesVolumeProgressiveLoader.ScheduleNextSliceDownload()" | |
317 << " sliceIndex = " << sliceIndex << " slice quality = " << quality; | |
318 command.reset(tmp.release()); | |
319 } | |
320 | |
321 command->AcquirePayload(new Orthanc::SingleValueObject<unsigned int>(sliceIndex)); | |
322 | |
323 { | |
324 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(loadersContext_.Lock()); | |
325 boost::shared_ptr<IObserver> observer(GetSharedObserver()); | |
326 lock->Schedule(observer, sliceSchedulingPriority_, command.release()); | |
327 } | |
328 } | |
329 else | |
330 { | |
331 // loading is finished! | |
332 volumeImageReadyInHighQuality_ = true; | |
333 BroadcastMessage(OrthancSeriesVolumeProgressiveLoader::VolumeImageReadyInHighQuality(*this)); | |
334 } | |
335 } | |
336 | |
337 /** | |
338 This is called in response to GET "/series/XXXXXXXXXXXXX/instances-tags" | |
339 */ | |
340 void OrthancSeriesVolumeProgressiveLoader::LoadGeometry(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) | |
341 { | |
342 Json::Value body; | |
343 message.ParseJsonBody(body); | |
344 | |
345 if (body.type() != Json::objectValue) | |
346 { | |
347 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
348 } | |
349 | |
350 { | |
351 Json::Value::Members instances = body.getMemberNames(); | |
352 | |
353 OrthancStone::SlicesSorter slices; | |
354 | |
355 for (size_t i = 0; i < instances.size(); i++) | |
356 { | |
357 Orthanc::DicomMap dicom; | |
358 dicom.FromDicomAsJson(body[instances[i]]); | |
359 | |
360 std::unique_ptr<OrthancStone::DicomInstanceParameters> instance(new OrthancStone::DicomInstanceParameters(dicom)); | |
361 instance->SetOrthancInstanceIdentifier(instances[i]); | |
362 | |
363 // the 3D plane corresponding to the slice | |
364 OrthancStone::CoordinateSystem3D geometry = instance->GetGeometry(); | |
365 slices.AddSlice(geometry, instance.release()); | |
366 | |
367 if (slicePostProcessor_) | |
368 slicePostProcessor_->ProcessCTDicomSlice(dicom); | |
369 } | |
370 | |
371 seriesGeometry_.ComputeGeometry(slices); | |
372 } | |
373 | |
374 size_t slicesCount = seriesGeometry_.GetImageGeometry().GetDepth(); | |
375 | |
376 if (slicesCount == 0) | |
377 { | |
378 volume_->Initialize(seriesGeometry_.GetImageGeometry(), Orthanc::PixelFormat_Grayscale8); | |
379 } | |
380 else | |
381 { | |
382 const OrthancStone::DicomInstanceParameters& parameters = seriesGeometry_.GetSliceParameters(0); | |
383 | |
384 volume_->Initialize(seriesGeometry_.GetImageGeometry(), parameters.GetExpectedPixelFormat()); | |
385 volume_->SetDicomParameters(parameters); | |
386 volume_->GetPixelData().Clear(); | |
387 | |
388 // If we are in progressive mode, the Fetching strategy will first request QUALITY_00, then QUALITY_01, then | |
389 // QUALITY_02... Otherwise, it's only QUALITY_00 | |
390 unsigned int maxQuality = QUALITY_00; | |
391 if (progressiveQuality_) | |
392 maxQuality = QUALITY_02; | |
393 | |
394 strategy_.reset(new OrthancStone::BasicFetchingStrategy( | |
395 sorter_->CreateSorter(static_cast<unsigned int>(slicesCount)), | |
396 maxQuality)); | |
397 | |
398 assert(simultaneousDownloads_ != 0); | |
399 for (unsigned int i = 0; i < simultaneousDownloads_; i++) | |
400 { | |
401 ScheduleNextSliceDownload(); | |
402 } | |
403 } | |
404 | |
405 slicesQuality_.resize(slicesCount, 0); | |
406 | |
407 BroadcastMessage(OrthancStone::DicomVolumeImage::GeometryReadyMessage(*volume_)); | |
408 } | |
409 | |
410 | |
411 void OrthancSeriesVolumeProgressiveLoader::SetSliceContent(unsigned int sliceIndex, | |
412 const Orthanc::ImageAccessor& image, | |
413 unsigned int quality) | |
414 { | |
415 ORTHANC_ASSERT(sliceIndex < slicesQuality_.size() && | |
416 slicesQuality_.size() == volume_->GetPixelData().GetDepth()); | |
417 | |
418 if (!progressiveQuality_) | |
419 { | |
420 ORTHANC_ASSERT(quality == QUALITY_00); | |
421 ORTHANC_ASSERT(slicesQuality_[sliceIndex] == QUALITY_00); | |
422 } | |
423 | |
424 if (quality >= slicesQuality_[sliceIndex]) | |
425 { | |
426 { | |
427 OrthancStone::ImageBuffer3D::SliceWriter writer(volume_->GetPixelData(), | |
428 OrthancStone::VolumeProjection_Axial, | |
429 sliceIndex); | |
430 | |
431 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image); | |
432 } | |
433 | |
434 volume_->IncrementRevision(); | |
435 seriesGeometry_.IncrementSliceRevision(sliceIndex); | |
436 slicesQuality_[sliceIndex] = quality; | |
437 | |
438 BroadcastMessage(OrthancStone::DicomVolumeImage::ContentUpdatedMessage(*volume_)); | |
439 } | |
440 LOG(TRACE) << "SetSliceContent sliceIndex = " << sliceIndex << " -- will " | |
441 << " now call ScheduleNextSliceDownload()"; | |
442 ScheduleNextSliceDownload(); | |
443 } | |
444 | |
445 void OrthancSeriesVolumeProgressiveLoader::LoadBestQualitySliceContent( | |
446 const OrthancStone::GetOrthancImageCommand::SuccessMessage& message) | |
447 { | |
448 unsigned int quality = QUALITY_00; | |
449 if (progressiveQuality_) | |
450 quality = QUALITY_02; | |
451 | |
452 SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), | |
453 message.GetImage(), | |
454 quality); | |
455 } | |
456 | |
457 void OrthancSeriesVolumeProgressiveLoader::LoadJpegSliceContent( | |
458 const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message) | |
459 { | |
460 ORTHANC_ASSERT(progressiveQuality_, "INTERNAL ERROR: OrthancSeriesVolumeProgressiveLoader::LoadJpegSliceContent" | |
461 << " called while progressiveQuality_ is false!"); | |
462 | |
463 LOG(TRACE) << "OrthancSeriesVolumeProgressiveLoader::LoadJpegSliceContent"; | |
464 unsigned int quality; | |
465 | |
466 switch (dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&>(message.GetOrigin()).GetQuality()) | |
467 { | |
468 case 50: | |
469 quality = QUALITY_00; | |
470 break; | |
471 | |
472 case 90: | |
473 quality = QUALITY_01; | |
474 break; | |
475 | |
476 default: | |
477 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
478 } | |
479 | |
480 SetSliceContent(GetSliceIndexPayload(message.GetOrigin()), message.GetImage(), quality); | |
481 } | |
482 | |
483 | |
484 void OrthancSeriesVolumeProgressiveLoader::SetMetadataSchedulingPriority(int p) | |
485 { | |
486 medadataSchedulingPriority_ = p; | |
487 } | |
488 | |
489 int OrthancSeriesVolumeProgressiveLoader::GetMetadataSchedulingPriority() const | |
490 { | |
491 return medadataSchedulingPriority_; | |
492 } | |
493 | |
494 void OrthancSeriesVolumeProgressiveLoader::SetSliceSchedulingPriority(int p) | |
495 { | |
496 sliceSchedulingPriority_ = p; | |
497 } | |
498 | |
499 int OrthancSeriesVolumeProgressiveLoader::GetSliceSchedulingPriority() const | |
500 { | |
501 return sliceSchedulingPriority_; | |
502 } | |
503 | |
504 void OrthancSeriesVolumeProgressiveLoader::SetSchedulingPriority(int p) | |
505 { | |
506 medadataSchedulingPriority_ = p; | |
507 sliceSchedulingPriority_ = p; | |
508 } | |
509 | |
510 OrthancSeriesVolumeProgressiveLoader::OrthancSeriesVolumeProgressiveLoader( | |
511 OrthancStone::ILoadersContext& loadersContext, | |
512 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume, | |
513 bool progressiveQuality) | |
514 : loadersContext_(loadersContext) | |
515 , active_(false) | |
516 , progressiveQuality_(progressiveQuality) | |
517 , simultaneousDownloads_(4) | |
518 , volume_(volume) | |
519 , sorter_(new OrthancStone::BasicFetchingItemsSorter::Factory) | |
520 , volumeImageReadyInHighQuality_(false) | |
521 , medadataSchedulingPriority_(0) | |
522 , sliceSchedulingPriority_(0) | |
523 { | |
524 } | |
525 | |
526 boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> | |
527 OrthancSeriesVolumeProgressiveLoader::Create( | |
528 OrthancStone::ILoadersContext& loadersContext, | |
529 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume, | |
530 bool progressiveQuality) | |
531 { | |
532 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(loadersContext.Lock()); | |
533 | |
534 boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> obj( | |
535 new OrthancSeriesVolumeProgressiveLoader( | |
536 loadersContext, volume, progressiveQuality)); | |
537 | |
538 obj->Register<OrthancStone::OrthancRestApiCommand::SuccessMessage>( | |
539 lock->GetOracleObservable(), | |
540 &OrthancSeriesVolumeProgressiveLoader::LoadGeometry); | |
541 | |
542 obj->Register<OrthancStone::GetOrthancImageCommand::SuccessMessage>( | |
543 lock->GetOracleObservable(), | |
544 &OrthancSeriesVolumeProgressiveLoader::LoadBestQualitySliceContent); | |
545 | |
546 obj->Register<OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>( | |
547 lock->GetOracleObservable(), | |
548 &OrthancSeriesVolumeProgressiveLoader::LoadJpegSliceContent); | |
549 | |
550 return obj; | |
551 } | |
552 | |
553 | |
554 OrthancSeriesVolumeProgressiveLoader::~OrthancSeriesVolumeProgressiveLoader() | |
555 { | |
556 LOG(TRACE) << "OrthancSeriesVolumeProgressiveLoader::~OrthancSeriesVolumeProgressiveLoader()"; | |
557 } | |
558 | |
559 void OrthancSeriesVolumeProgressiveLoader::SetSimultaneousDownloads(unsigned int count) | |
560 { | |
561 if (active_) | |
562 { | |
563 LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::SetSimultaneousDownloads(): (active_)"; | |
564 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
565 } | |
566 else if (count == 0) | |
567 { | |
568 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
569 } | |
570 else | |
571 { | |
572 simultaneousDownloads_ = count; | |
573 } | |
574 } | |
575 | |
576 | |
577 void OrthancSeriesVolumeProgressiveLoader::LoadSeries(const std::string& seriesId) | |
578 { | |
579 if (active_) | |
580 { | |
581 LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::LoadSeries(const std::string& seriesId): (active_)"; | |
582 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
583 } | |
584 else | |
585 { | |
586 active_ = true; | |
587 | |
588 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); | |
589 command->SetUri("/series/" + seriesId + "/instances-tags"); | |
590 { | |
591 std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(loadersContext_.Lock()); | |
592 boost::shared_ptr<IObserver> observer(GetSharedObserver()); | |
593 lock->Schedule(observer, medadataSchedulingPriority_, command.release()); | |
594 } | |
595 } | |
596 } | |
597 | |
598 | |
599 OrthancStone::IVolumeSlicer::IExtractedSlice* | |
600 OrthancSeriesVolumeProgressiveLoader::ExtractSlice(const OrthancStone::CoordinateSystem3D& cuttingPlane) | |
601 { | |
602 if (volume_->HasGeometry()) | |
603 { | |
604 return new ExtractedSlice(*this, cuttingPlane); | |
605 } | |
606 else | |
607 { | |
608 return new IVolumeSlicer::InvalidSlice; | |
609 } | |
610 } | |
611 } |