Mercurial > hg > orthanc-stone
comparison OrthancStone/Sources/Loaders/OrthancMultiframeVolumeLoader.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/OrthancMultiframeVolumeLoader.cpp@30deba7bc8e2 |
children | 85e117739eca |
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 "OrthancMultiframeVolumeLoader.h" | |
23 | |
24 #include <Endianness.h> | |
25 #include <Toolbox.h> | |
26 | |
27 namespace OrthancStone | |
28 { | |
29 class OrthancMultiframeVolumeLoader::LoadRTDoseGeometry : public LoaderStateMachine::State | |
30 { | |
31 private: | |
32 std::unique_ptr<Orthanc::DicomMap> dicom_; | |
33 | |
34 public: | |
35 LoadRTDoseGeometry(OrthancMultiframeVolumeLoader& that, | |
36 Orthanc::DicomMap* dicom) : | |
37 State(that), | |
38 dicom_(dicom) | |
39 { | |
40 if (dicom == NULL) | |
41 { | |
42 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
43 } | |
44 | |
45 } | |
46 | |
47 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) | |
48 { | |
49 // Complete the DICOM tags with just-received "Grid Frame Offset Vector" | |
50 std::string s = Orthanc::Toolbox::StripSpaces(message.GetAnswer()); | |
51 dicom_->SetValue(Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, s, false); | |
52 | |
53 GetLoader<OrthancMultiframeVolumeLoader>().SetGeometry(*dicom_); | |
54 } | |
55 }; | |
56 | |
57 | |
58 static std::string GetSopClassUid(const Orthanc::DicomMap& dicom) | |
59 { | |
60 std::string s; | |
61 if (!dicom.LookupStringValue(s, Orthanc::DICOM_TAG_SOP_CLASS_UID, false)) | |
62 { | |
63 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
64 "DICOM file without SOP class UID"); | |
65 } | |
66 else | |
67 { | |
68 return s; | |
69 } | |
70 } | |
71 | |
72 | |
73 class OrthancMultiframeVolumeLoader::LoadGeometry : public State | |
74 { | |
75 public: | |
76 LoadGeometry(OrthancMultiframeVolumeLoader& that) : | |
77 State(that) | |
78 { | |
79 } | |
80 | |
81 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) | |
82 { | |
83 OrthancMultiframeVolumeLoader& loader = GetLoader<OrthancMultiframeVolumeLoader>(); | |
84 | |
85 Json::Value body; | |
86 message.ParseJsonBody(body); | |
87 | |
88 if (body.type() != Json::objectValue) | |
89 { | |
90 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
91 } | |
92 | |
93 std::unique_ptr<Orthanc::DicomMap> dicom(new Orthanc::DicomMap); | |
94 dicom->FromDicomAsJson(body); | |
95 | |
96 if (OrthancStone::StringToSopClassUid(GetSopClassUid(*dicom)) == OrthancStone::SopClassUid_RTDose) | |
97 { | |
98 // Download the "Grid Frame Offset Vector" DICOM tag, that is | |
99 // mandatory for RT-DOSE, but is too long to be returned by default | |
100 | |
101 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); | |
102 command->SetUri("/instances/" + loader.GetInstanceId() + "/content/" + | |
103 Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR.Format()); | |
104 command->AcquirePayload(new LoadRTDoseGeometry(loader, dicom.release())); | |
105 | |
106 Schedule(command.release()); | |
107 } | |
108 else | |
109 { | |
110 loader.SetGeometry(*dicom); | |
111 } | |
112 } | |
113 }; | |
114 | |
115 class OrthancMultiframeVolumeLoader::LoadTransferSyntax : public State | |
116 { | |
117 public: | |
118 LoadTransferSyntax(OrthancMultiframeVolumeLoader& that) : | |
119 State(that) | |
120 { | |
121 } | |
122 | |
123 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) | |
124 { | |
125 GetLoader<OrthancMultiframeVolumeLoader>().SetTransferSyntax(message.GetAnswer()); | |
126 } | |
127 }; | |
128 | |
129 class OrthancMultiframeVolumeLoader::LoadUncompressedPixelData : public State | |
130 { | |
131 public: | |
132 LoadUncompressedPixelData(OrthancMultiframeVolumeLoader& that) : | |
133 State(that) | |
134 { | |
135 } | |
136 | |
137 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) | |
138 { | |
139 GetLoader<OrthancMultiframeVolumeLoader>().SetUncompressedPixelData(message.GetAnswer()); | |
140 } | |
141 }; | |
142 | |
143 const std::string& OrthancMultiframeVolumeLoader::GetInstanceId() const | |
144 { | |
145 if (IsActive()) | |
146 { | |
147 return instanceId_; | |
148 } | |
149 else | |
150 { | |
151 LOG(ERROR) << "OrthancMultiframeVolumeLoader::GetInstanceId(): (!IsActive())"; | |
152 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
153 } | |
154 } | |
155 | |
156 void OrthancMultiframeVolumeLoader::ScheduleFrameDownloads() | |
157 { | |
158 if (transferSyntaxUid_.empty() || | |
159 !volume_->HasGeometry()) | |
160 { | |
161 return; | |
162 } | |
163 /* | |
164 1.2.840.10008.1.2 Implicit VR Endian: Default Transfer Syntax for DICOM | |
165 1.2.840.10008.1.2.1 Explicit VR Little Endian | |
166 1.2.840.10008.1.2.2 Explicit VR Big Endian | |
167 | |
168 See https://www.dicomlibrary.com/dicom/transfer-syntax/ | |
169 */ | |
170 if (transferSyntaxUid_ == "1.2.840.10008.1.2" || | |
171 transferSyntaxUid_ == "1.2.840.10008.1.2.1" || | |
172 transferSyntaxUid_ == "1.2.840.10008.1.2.2") | |
173 { | |
174 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); | |
175 command->SetHttpHeader("Accept-Encoding", "gzip"); | |
176 command->SetUri("/instances/" + instanceId_ + "/content/" + | |
177 Orthanc::DICOM_TAG_PIXEL_DATA.Format() + "/0"); | |
178 command->AcquirePayload(new LoadUncompressedPixelData(*this)); | |
179 Schedule(command.release()); | |
180 } | |
181 else | |
182 { | |
183 throw Orthanc::OrthancException( | |
184 Orthanc::ErrorCode_NotImplemented, | |
185 "No support for multiframe instances with transfer syntax: " + transferSyntaxUid_); | |
186 } | |
187 } | |
188 | |
189 void OrthancMultiframeVolumeLoader::SetTransferSyntax(const std::string& transferSyntax) | |
190 { | |
191 transferSyntaxUid_ = Orthanc::Toolbox::StripSpaces(transferSyntax); | |
192 ScheduleFrameDownloads(); | |
193 } | |
194 | |
195 void OrthancMultiframeVolumeLoader::SetGeometry(const Orthanc::DicomMap& dicom) | |
196 { | |
197 OrthancStone::DicomInstanceParameters parameters(dicom); | |
198 volume_->SetDicomParameters(parameters); | |
199 | |
200 Orthanc::PixelFormat format; | |
201 if (!parameters.GetImageInformation().ExtractPixelFormat(format, true)) | |
202 { | |
203 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
204 } | |
205 | |
206 double spacingZ; | |
207 switch (parameters.GetSopClassUid()) | |
208 { | |
209 case OrthancStone::SopClassUid_RTDose: | |
210 spacingZ = parameters.GetThickness(); | |
211 break; | |
212 | |
213 default: | |
214 throw Orthanc::OrthancException( | |
215 Orthanc::ErrorCode_NotImplemented, | |
216 "No support for multiframe instances with SOP class UID: " + GetSopClassUid(dicom)); | |
217 } | |
218 | |
219 const unsigned int width = parameters.GetImageInformation().GetWidth(); | |
220 const unsigned int height = parameters.GetImageInformation().GetHeight(); | |
221 const unsigned int depth = parameters.GetImageInformation().GetNumberOfFrames(); | |
222 | |
223 { | |
224 OrthancStone::VolumeImageGeometry geometry; | |
225 geometry.SetSizeInVoxels(width, height, depth); | |
226 geometry.SetAxialGeometry(parameters.GetGeometry()); | |
227 geometry.SetVoxelDimensions(parameters.GetPixelSpacingX(), | |
228 parameters.GetPixelSpacingY(), spacingZ); | |
229 volume_->Initialize(geometry, format, true /* Do compute range */); | |
230 } | |
231 | |
232 volume_->GetPixelData().Clear(); | |
233 | |
234 ScheduleFrameDownloads(); | |
235 | |
236 | |
237 | |
238 BroadcastMessage(OrthancStone::DicomVolumeImage::GeometryReadyMessage(*volume_)); | |
239 } | |
240 | |
241 | |
242 ORTHANC_FORCE_INLINE | |
243 static void CopyPixel(uint32_t& target, const void* source) | |
244 { | |
245 // TODO - check alignement? | |
246 target = le32toh(*reinterpret_cast<const uint32_t*>(source)); | |
247 } | |
248 | |
249 ORTHANC_FORCE_INLINE | |
250 static void CopyPixel(uint16_t& target, const void* source) | |
251 { | |
252 // TODO - check alignement? | |
253 target = le16toh(*reinterpret_cast<const uint16_t*>(source)); | |
254 } | |
255 | |
256 ORTHANC_FORCE_INLINE | |
257 static void CopyPixel(int16_t& target, const void* source) | |
258 { | |
259 // byte swapping is the same for unsigned and signed integers | |
260 // (the sign bit is always stored with the MSByte) | |
261 uint16_t* targetUp = reinterpret_cast<uint16_t*>(&target); | |
262 CopyPixel(*targetUp, source); | |
263 } | |
264 | |
265 template <typename T> | |
266 void OrthancMultiframeVolumeLoader::CopyPixelDataAndComputeDistribution( | |
267 const std::string& pixelData, std::map<T,uint64_t>& distribution) | |
268 { | |
269 OrthancStone::ImageBuffer3D& target = volume_->GetPixelData(); | |
270 | |
271 const unsigned int bpp = target.GetBytesPerPixel(); | |
272 const unsigned int width = target.GetWidth(); | |
273 const unsigned int height = target.GetHeight(); | |
274 const unsigned int depth = target.GetDepth(); | |
275 | |
276 if (pixelData.size() != bpp * width * height * depth) | |
277 { | |
278 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
279 "The pixel data has not the proper size"); | |
280 } | |
281 | |
282 if (pixelData.empty()) | |
283 { | |
284 return; | |
285 } | |
286 | |
287 // first pass to initialize map | |
288 { | |
289 const uint8_t* source = reinterpret_cast<const uint8_t*>(pixelData.c_str()); | |
290 | |
291 for (unsigned int z = 0; z < depth; z++) | |
292 { | |
293 for (unsigned int y = 0; y < height; y++) | |
294 { | |
295 for (unsigned int x = 0; x < width; x++) | |
296 { | |
297 T value; | |
298 CopyPixel(value, source); | |
299 distribution[value] = 0; | |
300 source += bpp; | |
301 } | |
302 } | |
303 } | |
304 } | |
305 | |
306 { | |
307 const uint8_t* source = reinterpret_cast<const uint8_t*>(pixelData.c_str()); | |
308 | |
309 for (unsigned int z = 0; z < depth; z++) | |
310 { | |
311 OrthancStone::ImageBuffer3D::SliceWriter writer(target, OrthancStone::VolumeProjection_Axial, z); | |
312 | |
313 assert(writer.GetAccessor().GetWidth() == width && | |
314 writer.GetAccessor().GetHeight() == height); | |
315 #if 0 | |
316 for (unsigned int y = 0; y < height; y++) | |
317 { | |
318 assert(sizeof(T) == Orthanc::GetBytesPerPixel(target.GetFormat())); | |
319 | |
320 T* target = reinterpret_cast<T*>(writer.GetAccessor().GetRow(y)); | |
321 | |
322 for (unsigned int x = 0; x < width; x++) | |
323 { | |
324 CopyPixel(*target, source); | |
325 | |
326 distribution[*target] += 1; | |
327 | |
328 target++; | |
329 source += bpp; | |
330 } | |
331 } | |
332 #else | |
333 // optimized version (fixed) as of 2020-04-15 | |
334 unsigned int pitch = writer.GetAccessor().GetPitch(); | |
335 T* targetAddrLine = reinterpret_cast<T*>(writer.GetAccessor().GetRow(0)); | |
336 assert(sizeof(T) == Orthanc::GetBytesPerPixel(target.GetFormat())); | |
337 | |
338 for (unsigned int y = 0; y < height; y++) | |
339 { | |
340 T* targetAddrPix = targetAddrLine; | |
341 for (unsigned int x = 0; x < width; x++) | |
342 { | |
343 CopyPixel(*targetAddrPix, source); | |
344 | |
345 distribution[*targetAddrPix] += 1; | |
346 | |
347 targetAddrPix++; | |
348 source += bpp; | |
349 } | |
350 uint8_t* targetAddrLineBytes = reinterpret_cast<uint8_t*>(targetAddrLine) + pitch; | |
351 targetAddrLine = reinterpret_cast<T*>(targetAddrLineBytes); | |
352 } | |
353 #endif | |
354 } | |
355 } | |
356 } | |
357 | |
358 template <typename T> | |
359 void OrthancMultiframeVolumeLoader::ComputeMinMaxWithOutlierRejection( | |
360 const std::map<T, uint64_t>& distribution) | |
361 { | |
362 if (distribution.size() == 0) | |
363 { | |
364 LOG(ERROR) << "ComputeMinMaxWithOutlierRejection -- Volume image empty."; | |
365 } | |
366 else | |
367 { | |
368 OrthancStone::ImageBuffer3D& target = volume_->GetPixelData(); | |
369 | |
370 const uint64_t width = target.GetWidth(); | |
371 const uint64_t height = target.GetHeight(); | |
372 const uint64_t depth = target.GetDepth(); | |
373 const uint64_t voxelCount = width * height * depth; | |
374 | |
375 // now that we have distribution[pixelValue] == numberOfPixelsWithValue | |
376 // compute number of values and check (assertion) that it is equal to | |
377 // width * height * depth | |
378 { | |
379 typename std::map<T, uint64_t>::const_iterator it = distribution.begin(); | |
380 uint64_t totalCount = 0; | |
381 distributionRawMin_ = static_cast<float>(it->first); | |
382 | |
383 while (it != distribution.end()) | |
384 { | |
385 T pixelValue = it->first; | |
386 uint64_t count = it->second; | |
387 totalCount += count; | |
388 it++; | |
389 if (it == distribution.end()) | |
390 distributionRawMax_ = static_cast<float>(pixelValue); | |
391 } | |
392 LOG(INFO) << "Volume image. First distribution value = " | |
393 << static_cast<float>(distributionRawMin_) | |
394 << " | Last distribution value = " | |
395 << static_cast<float>(distributionRawMax_); | |
396 | |
397 if (totalCount != voxelCount) | |
398 { | |
399 LOG(ERROR) << "Internal error in dose distribution computation. TC (" | |
400 << totalCount << ") != VoxC (" << voxelCount; | |
401 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
402 } | |
403 } | |
404 | |
405 // compute the number of voxels to reject at each end of the distribution | |
406 uint64_t endRejectionCount = static_cast<uint64_t>( | |
407 outliersHalfRejectionRate_ * voxelCount); | |
408 | |
409 if (endRejectionCount > voxelCount) | |
410 { | |
411 LOG(ERROR) << "Internal error in dose distribution computation." | |
412 << " endRejectionCount = " << endRejectionCount | |
413 << " | voxelCount = " << voxelCount; | |
414 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
415 } | |
416 | |
417 // this will contain the actual distribution minimum after outlier | |
418 // rejection | |
419 T resultMin = 0; | |
420 | |
421 // then start from start and remove pixel values up to | |
422 // endRejectionCount voxels rejected | |
423 { | |
424 typename std::map<T, uint64_t>::const_iterator it = distribution.begin(); | |
425 | |
426 uint64_t currentCount = 0; | |
427 | |
428 while (it != distribution.end()) | |
429 { | |
430 T pixelValue = it->first; | |
431 uint64_t count = it->second; | |
432 | |
433 // if this pixelValue crosses the rejection threshold, let's set it | |
434 // and exit the loop | |
435 if ((currentCount <= endRejectionCount) && | |
436 (currentCount + count > endRejectionCount)) | |
437 { | |
438 resultMin = pixelValue; | |
439 break; | |
440 } | |
441 else | |
442 { | |
443 currentCount += count; | |
444 } | |
445 // and continue walking along the distribution | |
446 it++; | |
447 } | |
448 } | |
449 | |
450 // this will contain the actual distribution maximum after outlier | |
451 // rejection | |
452 T resultMax = 0; | |
453 // now start from END and remove pixel values up to | |
454 // endRejectionCount voxels rejected | |
455 { | |
456 typename std::map<T, uint64_t>::const_reverse_iterator it = distribution.rbegin(); | |
457 | |
458 uint64_t currentCount = 0; | |
459 | |
460 while (it != distribution.rend()) | |
461 { | |
462 T pixelValue = it->first; | |
463 uint64_t count = it->second; | |
464 | |
465 if ((currentCount <= endRejectionCount) && | |
466 (currentCount + count > endRejectionCount)) | |
467 { | |
468 resultMax = pixelValue; | |
469 break; | |
470 } | |
471 else | |
472 { | |
473 currentCount += count; | |
474 } | |
475 // and continue walking along the distribution | |
476 it++; | |
477 } | |
478 } | |
479 if (resultMin > resultMax) | |
480 { | |
481 LOG(ERROR) << "Internal error in dose distribution computation! " << | |
482 "resultMin (" << resultMin << ") > resultMax (" << resultMax << ")"; | |
483 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
484 } | |
485 computedDistributionMin_ = static_cast<float>(resultMin); | |
486 computedDistributionMax_ = static_cast<float>(resultMax); | |
487 } | |
488 } | |
489 | |
490 template <typename T> | |
491 void OrthancMultiframeVolumeLoader::CopyPixelDataAndComputeMinMax( | |
492 const std::string& pixelData) | |
493 { | |
494 std::map<T, uint64_t> distribution; | |
495 CopyPixelDataAndComputeDistribution(pixelData, distribution); | |
496 ComputeMinMaxWithOutlierRejection(distribution); | |
497 } | |
498 | |
499 void OrthancMultiframeVolumeLoader::SetUncompressedPixelData(const std::string& pixelData) | |
500 { | |
501 switch (volume_->GetPixelData().GetFormat()) | |
502 { | |
503 case Orthanc::PixelFormat_Grayscale32: | |
504 CopyPixelDataAndComputeMinMax<uint32_t>(pixelData); | |
505 break; | |
506 case Orthanc::PixelFormat_Grayscale16: | |
507 CopyPixelDataAndComputeMinMax<uint16_t>(pixelData); | |
508 break; | |
509 case Orthanc::PixelFormat_SignedGrayscale16: | |
510 CopyPixelDataAndComputeMinMax<int16_t>(pixelData); | |
511 break; | |
512 default: | |
513 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
514 } | |
515 | |
516 volume_->IncrementRevision(); | |
517 | |
518 pixelDataLoaded_ = true; | |
519 BroadcastMessage(OrthancStone::DicomVolumeImage::ContentUpdatedMessage(*volume_)); | |
520 } | |
521 | |
522 bool OrthancMultiframeVolumeLoader::HasGeometry() const | |
523 { | |
524 return volume_->HasGeometry(); | |
525 } | |
526 | |
527 const OrthancStone::VolumeImageGeometry& OrthancMultiframeVolumeLoader::GetImageGeometry() const | |
528 { | |
529 return volume_->GetGeometry(); | |
530 } | |
531 | |
532 OrthancMultiframeVolumeLoader::OrthancMultiframeVolumeLoader( | |
533 OrthancStone::ILoadersContext& loadersContext, | |
534 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume, | |
535 float outliersHalfRejectionRate) | |
536 : LoaderStateMachine(loadersContext) | |
537 , volume_(volume) | |
538 , pixelDataLoaded_(false) | |
539 , outliersHalfRejectionRate_(outliersHalfRejectionRate) | |
540 , distributionRawMin_(0) | |
541 , distributionRawMax_(0) | |
542 , computedDistributionMin_(0) | |
543 , computedDistributionMax_(0) | |
544 { | |
545 if (volume.get() == NULL) | |
546 { | |
547 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
548 } | |
549 } | |
550 | |
551 | |
552 boost::shared_ptr<OrthancMultiframeVolumeLoader> | |
553 OrthancMultiframeVolumeLoader::Create( | |
554 OrthancStone::ILoadersContext& loadersContext, | |
555 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume, | |
556 float outliersHalfRejectionRate /*= 0.0005*/) | |
557 { | |
558 boost::shared_ptr<OrthancMultiframeVolumeLoader> obj( | |
559 new OrthancMultiframeVolumeLoader( | |
560 loadersContext, | |
561 volume, | |
562 outliersHalfRejectionRate)); | |
563 obj->LoaderStateMachine::PostConstructor(); | |
564 return obj; | |
565 } | |
566 | |
567 OrthancMultiframeVolumeLoader::~OrthancMultiframeVolumeLoader() | |
568 { | |
569 LOG(TRACE) << "OrthancMultiframeVolumeLoader::~OrthancMultiframeVolumeLoader()"; | |
570 } | |
571 | |
572 void OrthancMultiframeVolumeLoader::GetDistributionMinMax | |
573 (float& minValue, float& maxValue) const | |
574 { | |
575 if (distributionRawMin_ == 0 && distributionRawMax_ == 0) | |
576 { | |
577 LOG(WARNING) << "GetDistributionMinMaxWithOutliersRejection called before computation!"; | |
578 } | |
579 minValue = distributionRawMin_; | |
580 maxValue = distributionRawMax_; | |
581 } | |
582 | |
583 void OrthancMultiframeVolumeLoader::GetDistributionMinMaxWithOutliersRejection | |
584 (float& minValue, float& maxValue) const | |
585 { | |
586 if (computedDistributionMin_ == 0 && computedDistributionMax_ == 0) | |
587 { | |
588 LOG(WARNING) << "GetDistributionMinMaxWithOutliersRejection called before computation!"; | |
589 } | |
590 minValue = computedDistributionMin_; | |
591 maxValue = computedDistributionMax_; | |
592 } | |
593 | |
594 void OrthancMultiframeVolumeLoader::LoadInstance(const std::string& instanceId) | |
595 { | |
596 Start(); | |
597 | |
598 instanceId_ = instanceId; | |
599 | |
600 { | |
601 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); | |
602 command->SetHttpHeader("Accept-Encoding", "gzip"); | |
603 command->SetUri("/instances/" + instanceId + "/tags"); | |
604 command->AcquirePayload(new LoadGeometry(*this)); | |
605 Schedule(command.release()); | |
606 } | |
607 | |
608 { | |
609 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); | |
610 command->SetUri("/instances/" + instanceId + "/metadata/TransferSyntax"); | |
611 command->AcquirePayload(new LoadTransferSyntax(*this)); | |
612 Schedule(command.release()); | |
613 } | |
614 } | |
615 } |