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 }