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