Mercurial > hg > orthanc-stone
comparison Framework/Loaders/OrthancMultiframeVolumeLoader.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 104e0b0f2316 |
children | 30deba7bc8e2 75ac66d5f4b2 |
comparison
equal
deleted
inserted
replaced
1375:4431ffdcc2a4 | 1381:f4a06ad1580b |
---|---|
42 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 42 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
43 } | 43 } |
44 | 44 |
45 } | 45 } |
46 | 46 |
47 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) | 47 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) |
48 { | 48 { |
49 // Complete the DICOM tags with just-received "Grid Frame Offset Vector" | 49 // Complete the DICOM tags with just-received "Grid Frame Offset Vector" |
50 std::string s = Orthanc::Toolbox::StripSpaces(message.GetAnswer()); | 50 std::string s = Orthanc::Toolbox::StripSpaces(message.GetAnswer()); |
51 dicom_->SetValue(Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, s, false); | 51 dicom_->SetValue(Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, s, false); |
52 | 52 |
76 LoadGeometry(OrthancMultiframeVolumeLoader& that) : | 76 LoadGeometry(OrthancMultiframeVolumeLoader& that) : |
77 State(that) | 77 State(that) |
78 { | 78 { |
79 } | 79 } |
80 | 80 |
81 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) | 81 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) |
82 { | 82 { |
83 OrthancMultiframeVolumeLoader& loader = GetLoader<OrthancMultiframeVolumeLoader>(); | 83 OrthancMultiframeVolumeLoader& loader = GetLoader<OrthancMultiframeVolumeLoader>(); |
84 | 84 |
85 Json::Value body; | 85 Json::Value body; |
86 message.ParseJsonBody(body); | 86 message.ParseJsonBody(body); |
91 } | 91 } |
92 | 92 |
93 std::unique_ptr<Orthanc::DicomMap> dicom(new Orthanc::DicomMap); | 93 std::unique_ptr<Orthanc::DicomMap> dicom(new Orthanc::DicomMap); |
94 dicom->FromDicomAsJson(body); | 94 dicom->FromDicomAsJson(body); |
95 | 95 |
96 if (StringToSopClassUid(GetSopClassUid(*dicom)) == SopClassUid_RTDose) | 96 if (OrthancStone::StringToSopClassUid(GetSopClassUid(*dicom)) == OrthancStone::SopClassUid_RTDose) |
97 { | 97 { |
98 // Download the "Grid Frame Offset Vector" DICOM tag, that is | 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 | 99 // mandatory for RT-DOSE, but is too long to be returned by default |
100 | 100 |
101 std::unique_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | 101 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); |
102 command->SetUri("/instances/" + loader.GetInstanceId() + "/content/" + | 102 command->SetUri("/instances/" + loader.GetInstanceId() + "/content/" + |
103 Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR.Format()); | 103 Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR.Format()); |
104 command->SetPayload(new LoadRTDoseGeometry(loader, dicom.release())); | 104 command->AcquirePayload(new LoadRTDoseGeometry(loader, dicom.release())); |
105 | 105 |
106 Schedule(command.release()); | 106 Schedule(command.release()); |
107 } | 107 } |
108 else | 108 else |
109 { | 109 { |
118 LoadTransferSyntax(OrthancMultiframeVolumeLoader& that) : | 118 LoadTransferSyntax(OrthancMultiframeVolumeLoader& that) : |
119 State(that) | 119 State(that) |
120 { | 120 { |
121 } | 121 } |
122 | 122 |
123 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) | 123 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) |
124 { | 124 { |
125 GetLoader<OrthancMultiframeVolumeLoader>().SetTransferSyntax(message.GetAnswer()); | 125 GetLoader<OrthancMultiframeVolumeLoader>().SetTransferSyntax(message.GetAnswer()); |
126 } | 126 } |
127 }; | 127 }; |
128 | 128 |
132 LoadUncompressedPixelData(OrthancMultiframeVolumeLoader& that) : | 132 LoadUncompressedPixelData(OrthancMultiframeVolumeLoader& that) : |
133 State(that) | 133 State(that) |
134 { | 134 { |
135 } | 135 } |
136 | 136 |
137 virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) | 137 virtual void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message) |
138 { | 138 { |
139 GetLoader<OrthancMultiframeVolumeLoader>().SetUncompressedPixelData(message.GetAnswer()); | 139 GetLoader<OrthancMultiframeVolumeLoader>().SetUncompressedPixelData(message.GetAnswer()); |
140 } | 140 } |
141 }; | 141 }; |
142 | 142 |
169 */ | 169 */ |
170 if (transferSyntaxUid_ == "1.2.840.10008.1.2" || | 170 if (transferSyntaxUid_ == "1.2.840.10008.1.2" || |
171 transferSyntaxUid_ == "1.2.840.10008.1.2.1" || | 171 transferSyntaxUid_ == "1.2.840.10008.1.2.1" || |
172 transferSyntaxUid_ == "1.2.840.10008.1.2.2") | 172 transferSyntaxUid_ == "1.2.840.10008.1.2.2") |
173 { | 173 { |
174 std::unique_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | 174 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); |
175 command->SetHttpHeader("Accept-Encoding", "gzip"); | 175 command->SetHttpHeader("Accept-Encoding", "gzip"); |
176 command->SetUri("/instances/" + instanceId_ + "/content/" + | 176 command->SetUri("/instances/" + instanceId_ + "/content/" + |
177 Orthanc::DICOM_TAG_PIXEL_DATA.Format() + "/0"); | 177 Orthanc::DICOM_TAG_PIXEL_DATA.Format() + "/0"); |
178 command->SetPayload(new LoadUncompressedPixelData(*this)); | 178 command->AcquirePayload(new LoadUncompressedPixelData(*this)); |
179 Schedule(command.release()); | 179 Schedule(command.release()); |
180 } | 180 } |
181 else | 181 else |
182 { | 182 { |
183 throw Orthanc::OrthancException( | 183 throw Orthanc::OrthancException( |
192 ScheduleFrameDownloads(); | 192 ScheduleFrameDownloads(); |
193 } | 193 } |
194 | 194 |
195 void OrthancMultiframeVolumeLoader::SetGeometry(const Orthanc::DicomMap& dicom) | 195 void OrthancMultiframeVolumeLoader::SetGeometry(const Orthanc::DicomMap& dicom) |
196 { | 196 { |
197 DicomInstanceParameters parameters(dicom); | 197 OrthancStone::DicomInstanceParameters parameters(dicom); |
198 volume_->SetDicomParameters(parameters); | 198 volume_->SetDicomParameters(parameters); |
199 | 199 |
200 Orthanc::PixelFormat format; | 200 Orthanc::PixelFormat format; |
201 if (!parameters.GetImageInformation().ExtractPixelFormat(format, true)) | 201 if (!parameters.GetImageInformation().ExtractPixelFormat(format, true)) |
202 { | 202 { |
204 } | 204 } |
205 | 205 |
206 double spacingZ; | 206 double spacingZ; |
207 switch (parameters.GetSopClassUid()) | 207 switch (parameters.GetSopClassUid()) |
208 { | 208 { |
209 case SopClassUid_RTDose: | 209 case OrthancStone::SopClassUid_RTDose: |
210 spacingZ = parameters.GetThickness(); | 210 spacingZ = parameters.GetThickness(); |
211 break; | 211 break; |
212 | 212 |
213 default: | 213 default: |
214 throw Orthanc::OrthancException( | 214 throw Orthanc::OrthancException( |
219 const unsigned int width = parameters.GetImageInformation().GetWidth(); | 219 const unsigned int width = parameters.GetImageInformation().GetWidth(); |
220 const unsigned int height = parameters.GetImageInformation().GetHeight(); | 220 const unsigned int height = parameters.GetImageInformation().GetHeight(); |
221 const unsigned int depth = parameters.GetImageInformation().GetNumberOfFrames(); | 221 const unsigned int depth = parameters.GetImageInformation().GetNumberOfFrames(); |
222 | 222 |
223 { | 223 { |
224 VolumeImageGeometry geometry; | 224 OrthancStone::VolumeImageGeometry geometry; |
225 geometry.SetSizeInVoxels(width, height, depth); | 225 geometry.SetSizeInVoxels(width, height, depth); |
226 geometry.SetAxialGeometry(parameters.GetGeometry()); | 226 geometry.SetAxialGeometry(parameters.GetGeometry()); |
227 geometry.SetVoxelDimensions(parameters.GetPixelSpacingX(), | 227 geometry.SetVoxelDimensions(parameters.GetPixelSpacingX(), |
228 parameters.GetPixelSpacingY(), spacingZ); | 228 parameters.GetPixelSpacingY(), spacingZ); |
229 volume_->Initialize(geometry, format, true /* Do compute range */); | 229 volume_->Initialize(geometry, format, true /* Do compute range */); |
233 | 233 |
234 ScheduleFrameDownloads(); | 234 ScheduleFrameDownloads(); |
235 | 235 |
236 | 236 |
237 | 237 |
238 BroadcastMessage(DicomVolumeImage::GeometryReadyMessage(*volume_)); | 238 BroadcastMessage(OrthancStone::DicomVolumeImage::GeometryReadyMessage(*volume_)); |
239 } | 239 } |
240 | 240 |
241 | 241 |
242 ORTHANC_FORCE_INLINE | 242 ORTHANC_FORCE_INLINE |
243 static void CopyPixel(uint32_t& target, const void* source) | 243 static void CopyPixel(uint32_t& target, const void* source) |
264 | 264 |
265 template <typename T> | 265 template <typename T> |
266 void OrthancMultiframeVolumeLoader::CopyPixelDataAndComputeDistribution( | 266 void OrthancMultiframeVolumeLoader::CopyPixelDataAndComputeDistribution( |
267 const std::string& pixelData, std::map<T,uint64_t>& distribution) | 267 const std::string& pixelData, std::map<T,uint64_t>& distribution) |
268 { | 268 { |
269 ImageBuffer3D& target = volume_->GetPixelData(); | 269 OrthancStone::ImageBuffer3D& target = volume_->GetPixelData(); |
270 | 270 |
271 const unsigned int bpp = target.GetBytesPerPixel(); | 271 const unsigned int bpp = target.GetBytesPerPixel(); |
272 const unsigned int width = target.GetWidth(); | 272 const unsigned int width = target.GetWidth(); |
273 const unsigned int height = target.GetHeight(); | 273 const unsigned int height = target.GetHeight(); |
274 const unsigned int depth = target.GetDepth(); | 274 const unsigned int depth = target.GetDepth(); |
306 { | 306 { |
307 const uint8_t* source = reinterpret_cast<const uint8_t*>(pixelData.c_str()); | 307 const uint8_t* source = reinterpret_cast<const uint8_t*>(pixelData.c_str()); |
308 | 308 |
309 for (unsigned int z = 0; z < depth; z++) | 309 for (unsigned int z = 0; z < depth; z++) |
310 { | 310 { |
311 ImageBuffer3D::SliceWriter writer(target, VolumeProjection_Axial, z); | 311 OrthancStone::ImageBuffer3D::SliceWriter writer(target, OrthancStone::VolumeProjection_Axial, z); |
312 | 312 |
313 assert(writer.GetAccessor().GetWidth() == width && | 313 assert(writer.GetAccessor().GetWidth() == width && |
314 writer.GetAccessor().GetHeight() == height); | 314 writer.GetAccessor().GetHeight() == height); |
315 | 315 #if 0 |
316 for (unsigned int y = 0; y < height; y++) | 316 for (unsigned int y = 0; y < height; y++) |
317 { | 317 { |
318 assert(sizeof(T) == Orthanc::GetBytesPerPixel(target.GetFormat())); | 318 assert(sizeof(T) == Orthanc::GetBytesPerPixel(target.GetFormat())); |
319 | 319 |
320 T* target = reinterpret_cast<T*>(writer.GetAccessor().GetRow(y)); | 320 T* target = reinterpret_cast<T*>(writer.GetAccessor().GetRow(y)); |
327 | 327 |
328 target++; | 328 target++; |
329 source += bpp; | 329 source += bpp; |
330 } | 330 } |
331 } | 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 | |
332 } | 354 } |
333 } | 355 } |
334 } | 356 } |
335 | 357 |
336 template <typename T> | 358 template <typename T> |
341 { | 363 { |
342 LOG(ERROR) << "ComputeMinMaxWithOutlierRejection -- Volume image empty."; | 364 LOG(ERROR) << "ComputeMinMaxWithOutlierRejection -- Volume image empty."; |
343 } | 365 } |
344 else | 366 else |
345 { | 367 { |
346 ImageBuffer3D& target = volume_->GetPixelData(); | 368 OrthancStone::ImageBuffer3D& target = volume_->GetPixelData(); |
347 | 369 |
348 const uint64_t width = target.GetWidth(); | 370 const uint64_t width = target.GetWidth(); |
349 const uint64_t height = target.GetHeight(); | 371 const uint64_t height = target.GetHeight(); |
350 const uint64_t depth = target.GetDepth(); | 372 const uint64_t depth = target.GetDepth(); |
351 const uint64_t voxelCount = width * height * depth; | 373 const uint64_t voxelCount = width * height * depth; |
492 } | 514 } |
493 | 515 |
494 volume_->IncrementRevision(); | 516 volume_->IncrementRevision(); |
495 | 517 |
496 pixelDataLoaded_ = true; | 518 pixelDataLoaded_ = true; |
497 BroadcastMessage(DicomVolumeImage::ContentUpdatedMessage(*volume_)); | 519 BroadcastMessage(OrthancStone::DicomVolumeImage::ContentUpdatedMessage(*volume_)); |
498 } | 520 } |
499 | 521 |
500 bool OrthancMultiframeVolumeLoader::HasGeometry() const | 522 bool OrthancMultiframeVolumeLoader::HasGeometry() const |
501 { | 523 { |
502 return volume_->HasGeometry(); | 524 return volume_->HasGeometry(); |
506 { | 528 { |
507 return volume_->GetGeometry(); | 529 return volume_->GetGeometry(); |
508 } | 530 } |
509 | 531 |
510 OrthancMultiframeVolumeLoader::OrthancMultiframeVolumeLoader( | 532 OrthancMultiframeVolumeLoader::OrthancMultiframeVolumeLoader( |
511 boost::shared_ptr<DicomVolumeImage> volume, | 533 OrthancStone::ILoadersContext& loadersContext, |
512 IOracle& oracle, | 534 boost::shared_ptr<OrthancStone::DicomVolumeImage> volume, |
513 IObservable& oracleObservable, | 535 float outliersHalfRejectionRate) |
514 float outliersHalfRejectionRate) : | 536 : LoaderStateMachine(loadersContext) |
515 LoaderStateMachine(oracle, oracleObservable), | 537 , volume_(volume) |
516 IObservable(oracleObservable.GetBroker()), | 538 , pixelDataLoaded_(false) |
517 volume_(volume), | 539 , outliersHalfRejectionRate_(outliersHalfRejectionRate) |
518 pixelDataLoaded_(false), | 540 , distributionRawMin_(0) |
519 outliersHalfRejectionRate_(outliersHalfRejectionRate), | 541 , distributionRawMax_(0) |
520 distributionRawMin_(0), | 542 , computedDistributionMin_(0) |
521 distributionRawMax_(0), | 543 , computedDistributionMax_(0) |
522 computedDistributionMin_(0), | |
523 computedDistributionMax_(0) | |
524 { | 544 { |
525 if (volume.get() == NULL) | 545 if (volume.get() == NULL) |
526 { | 546 { |
527 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 547 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
528 } | 548 } |
529 } | 549 } |
530 | 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 | |
531 OrthancMultiframeVolumeLoader::~OrthancMultiframeVolumeLoader() | 567 OrthancMultiframeVolumeLoader::~OrthancMultiframeVolumeLoader() |
532 { | 568 { |
533 LOG(TRACE) << "OrthancMultiframeVolumeLoader::~OrthancMultiframeVolumeLoader()"; | 569 LOG(TRACE) << "OrthancMultiframeVolumeLoader::~OrthancMultiframeVolumeLoader()"; |
534 } | 570 } |
535 | 571 |
536 | |
537 void OrthancMultiframeVolumeLoader::GetDistributionMinMax | 572 void OrthancMultiframeVolumeLoader::GetDistributionMinMax |
538 (float& minValue, float& maxValue) const | 573 (float& minValue, float& maxValue) const |
539 { | 574 { |
540 if (distributionRawMin_ == 0 && distributionRawMax_ == 0) | 575 if (distributionRawMin_ == 0 && distributionRawMax_ == 0) |
541 { | 576 { |
561 Start(); | 596 Start(); |
562 | 597 |
563 instanceId_ = instanceId; | 598 instanceId_ = instanceId; |
564 | 599 |
565 { | 600 { |
566 std::unique_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | 601 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); |
567 command->SetHttpHeader("Accept-Encoding", "gzip"); | 602 command->SetHttpHeader("Accept-Encoding", "gzip"); |
568 command->SetUri("/instances/" + instanceId + "/tags"); | 603 command->SetUri("/instances/" + instanceId + "/tags"); |
569 command->SetPayload(new LoadGeometry(*this)); | 604 command->AcquirePayload(new LoadGeometry(*this)); |
570 Schedule(command.release()); | 605 Schedule(command.release()); |
571 } | 606 } |
572 | 607 |
573 { | 608 { |
574 std::unique_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | 609 std::unique_ptr<OrthancStone::OrthancRestApiCommand> command(new OrthancStone::OrthancRestApiCommand); |
575 command->SetUri("/instances/" + instanceId + "/metadata/TransferSyntax"); | 610 command->SetUri("/instances/" + instanceId + "/metadata/TransferSyntax"); |
576 command->SetPayload(new LoadTransferSyntax(*this)); | 611 command->AcquirePayload(new LoadTransferSyntax(*this)); |
577 Schedule(command.release()); | 612 Schedule(command.release()); |
578 } | 613 } |
579 } | 614 } |
580 } | 615 } |