Mercurial > hg > orthanc-stone
comparison Framework/Loaders/OrthancMultiframeVolumeLoader.cpp @ 1337:b1396be5aa27 broker
Moved the fixed loaders back from the dead
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Fri, 03 Apr 2020 16:13:06 +0200 |
parents | |
children | b1e6bef86955 |
comparison
equal
deleted
inserted
replaced
1334:04055b6b9e2c | 1337:b1396be5aa27 |
---|---|
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 <Core/Endianness.h> | |
25 #include <Core/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 | |
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 } | |
333 } | |
334 } | |
335 | |
336 template <typename T> | |
337 void OrthancMultiframeVolumeLoader::ComputeMinMaxWithOutlierRejection( | |
338 const std::map<T, uint64_t>& distribution) | |
339 { | |
340 if (distribution.size() == 0) | |
341 { | |
342 LOG(ERROR) << "ComputeMinMaxWithOutlierRejection -- Volume image empty."; | |
343 } | |
344 else | |
345 { | |
346 OrthancStone::ImageBuffer3D& target = volume_->GetPixelData(); | |
347 | |
348 const uint64_t width = target.GetWidth(); | |
349 const uint64_t height = target.GetHeight(); | |
350 const uint64_t depth = target.GetDepth(); | |
351 const uint64_t voxelCount = width * height * depth; | |
352 | |
353 // now that we have distribution[pixelValue] == numberOfPixelsWithValue | |
354 // compute number of values and check (assertion) that it is equal to | |
355 // width * height * depth | |
356 { | |
357 typename std::map<T, uint64_t>::const_iterator it = distribution.begin(); | |
358 uint64_t totalCount = 0; | |
359 distributionRawMin_ = static_cast<float>(it->first); | |
360 | |
361 while (it != distribution.end()) | |
362 { | |
363 T pixelValue = it->first; | |
364 uint64_t count = it->second; | |
365 totalCount += count; | |
366 it++; | |
367 if (it == distribution.end()) | |
368 distributionRawMax_ = static_cast<float>(pixelValue); | |
369 } | |
370 LOG(INFO) << "Volume image. First distribution value = " | |
371 << static_cast<float>(distributionRawMin_) | |
372 << " | Last distribution value = " | |
373 << static_cast<float>(distributionRawMax_); | |
374 | |
375 if (totalCount != voxelCount) | |
376 { | |
377 LOG(ERROR) << "Internal error in dose distribution computation. TC (" | |
378 << totalCount << ") != VoxC (" << voxelCount; | |
379 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
380 } | |
381 } | |
382 | |
383 // compute the number of voxels to reject at each end of the distribution | |
384 uint64_t endRejectionCount = static_cast<uint64_t>( | |
385 outliersHalfRejectionRate_ * voxelCount); | |
386 | |
387 if (endRejectionCount > voxelCount) | |
388 { | |
389 LOG(ERROR) << "Internal error in dose distribution computation." | |
390 << " endRejectionCount = " << endRejectionCount | |
391 << " | voxelCount = " << voxelCount; | |
392 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
393 } | |
394 | |
395 // this will contain the actual distribution minimum after outlier | |
396 // rejection | |
397 T resultMin = 0; | |
398 | |
399 // then start from start and remove pixel values up to | |
400 // endRejectionCount voxels rejected | |
401 { | |
402 typename std::map<T, uint64_t>::const_iterator it = distribution.begin(); | |
403 | |
404 uint64_t currentCount = 0; | |
405 | |
406 while (it != distribution.end()) | |
407 { | |
408 T pixelValue = it->first; | |
409 uint64_t count = it->second; | |
410 | |
411 // if this pixelValue crosses the rejection threshold, let's set it | |
412 // and exit the loop | |
413 if ((currentCount <= endRejectionCount) && | |
414 (currentCount + count > endRejectionCount)) | |
415 { | |
416 resultMin = pixelValue; | |
417 break; | |
418 } | |
419 else | |
420 { | |
421 currentCount += count; | |
422 } | |
423 // and continue walking along the distribution | |
424 it++; | |
425 } | |
426 } | |
427 | |
428 // this will contain the actual distribution maximum after outlier | |
429 // rejection | |
430 T resultMax = 0; | |
431 // now start from END and remove pixel values up to | |
432 // endRejectionCount voxels rejected | |
433 { | |
434 typename std::map<T, uint64_t>::const_reverse_iterator it = distribution.rbegin(); | |
435 | |
436 uint64_t currentCount = 0; | |
437 | |
438 while (it != distribution.rend()) | |
439 { | |
440 T pixelValue = it->first; | |
441 uint64_t count = it->second; | |
442 | |
443 if ((currentCount <= endRejectionCount) && | |
444 (currentCount + count > endRejectionCount)) | |
445 { | |
446 resultMax = pixelValue; | |
447 break; | |
448 } | |
449 else | |
450 { | |
451 currentCount += count; | |
452 } | |
453 // and continue walking along the distribution | |
454 it++; | |
455 } | |
456 } | |
457 if (resultMin > resultMax) | |
458 { | |
459 LOG(ERROR) << "Internal error in dose distribution computation! " << | |
460 "resultMin (" << resultMin << ") > resultMax (" << resultMax << ")"; | |
461 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
462 } | |
463 computedDistributionMin_ = static_cast<float>(resultMin); | |
464 computedDistributionMax_ = static_cast<float>(resultMax); | |
465 } | |
466 } | |
467 | |
468 template <typename T> | |
469 void OrthancMultiframeVolumeLoader::CopyPixelDataAndComputeMinMax( | |
470 const std::string& pixelData) | |
471 { | |
472 std::map<T, uint64_t> distribution; | |
473 CopyPixelDataAndComputeDistribution(pixelData, distribution); | |
474 ComputeMinMaxWithOutlierRejection(distribution); | |
475 } | |
476 | |
477 void OrthancMultiframeVolumeLoader::SetUncompressedPixelData(const std::string& pixelData) | |
478 { | |
479 switch (volume_->GetPixelData().GetFormat()) | |
480 { | |
481 case Orthanc::PixelFormat_Grayscale32: | |
482 CopyPixelDataAndComputeMinMax<uint32_t>(pixelData); | |
483 break; | |
484 case Orthanc::PixelFormat_Grayscale16: | |
485 CopyPixelDataAndComputeMinMax<uint16_t>(pixelData); | |
486 break; | |
487 case Orthanc::PixelFormat_SignedGrayscale16: | |
488 CopyPixelDataAndComputeMinMax<int16_t>(pixelData); | |
489 break; | |
490 default: | |
491 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
492 } | |
493 | |
494 volume_->IncrementRevision(); | |
495 | |
496 pixelDataLoaded_ = true; | |
497 BroadcastMessage(OrthancStone::DicomVolumeImage::ContentUpdatedMessage(*volume_)); | |
498 } | |
499 | |
500 bool OrthancMultiframeVolumeLoader::HasGeometry() const | |
501 { | |
502 return volume_->HasGeometry(); | |
503 } | |
504 | |
505 const OrthancStone::VolumeImageGeometry& OrthancMultiframeVolumeLoader::GetImageGeometry() const | |
506 { | |
507 return volume_->GetGeometry(); | |
508 } | |
509 | |
510 OrthancMultiframeVolumeLoader::OrthancMultiframeVolumeLoader( | |
511 OrthancStone::ILoadersContext& loadersContext, | |
512 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume, | |
513 float outliersHalfRejectionRate) | |
514 : LoaderStateMachine(loadersContext) | |
515 , volume_(volume) | |
516 , pixelDataLoaded_(false) | |
517 , outliersHalfRejectionRate_(outliersHalfRejectionRate) | |
518 , distributionRawMin_(0) | |
519 , distributionRawMax_(0) | |
520 , computedDistributionMin_(0) | |
521 , computedDistributionMax_(0) | |
522 { | |
523 if (volume.get() == NULL) | |
524 { | |
525 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
526 } | |
527 } | |
528 | |
529 | |
530 boost::shared_ptr<OrthancMultiframeVolumeLoader> | |
531 OrthancMultiframeVolumeLoader::Create( | |
532 OrthancStone::ILoadersContext& loadersContext, | |
533 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume, | |
534 float outliersHalfRejectionRate /*= 0.0005*/) | |
535 { | |
536 boost::shared_ptr<OrthancMultiframeVolumeLoader> obj( | |
537 new OrthancMultiframeVolumeLoader( | |
538 loadersContext, | |
539 volume, | |
540 outliersHalfRejectionRate)); | |
541 obj->LoaderStateMachine::PostConstructor(); | |
542 return obj; | |
543 } | |
544 | |
545 OrthancMultiframeVolumeLoader::~OrthancMultiframeVolumeLoader() | |
546 { | |
547 LOG(TRACE) << "OrthancMultiframeVolumeLoader::~OrthancMultiframeVolumeLoader()"; | |
548 } | |
549 | |
550 void OrthancMultiframeVolumeLoader::GetDistributionMinMax | |
551 (float& minValue, float& maxValue) const | |
552 { | |
553 if (distributionRawMin_ == 0 && distributionRawMax_ == 0) | |
554 { | |
555 LOG(WARNING) << "GetDistributionMinMaxWithOutliersRejection called before computation!"; | |
556 } | |
557 minValue = distributionRawMin_; | |
558 maxValue = distributionRawMax_; | |
559 } | |
560 | |
561 void OrthancMultiframeVolumeLoader::GetDistributionMinMaxWithOutliersRejection | |
562 (float& minValue, float& maxValue) const | |
563 { | |
564 if (computedDistributionMin_ == 0 && computedDistributionMax_ == 0) | |
565 { | |
566 LOG(WARNING) << "GetDistributionMinMaxWithOutliersRejection called before computation!"; | |
567 } | |
568 minValue = computedDistributionMin_; | |
569 maxValue = computedDistributionMax_; | |
570 } | |
571 | |
572 void OrthancMultiframeVolumeLoader::LoadInstance(const std::string& instanceId) | |
573 { | |
574 Start(); | |
575 | |
576 instanceId_ = instanceId; | |
577 | |
578 { | |
579 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); | |
580 command->SetHttpHeader("Accept-Encoding", "gzip"); | |
581 command->SetUri("/instances/" + instanceId + "/tags"); | |
582 command->AcquirePayload(new LoadGeometry(*this)); | |
583 Schedule(command.release()); | |
584 } | |
585 | |
586 { | |
587 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); | |
588 command->SetUri("/instances/" + instanceId + "/metadata/TransferSyntax"); | |
589 command->AcquirePayload(new LoadTransferSyntax(*this)); | |
590 Schedule(command.release()); | |
591 } | |
592 } | |
593 } |