Mercurial > hg > orthanc-stone
comparison Framework/Radiography/RadiographyScene.cpp @ 426:660fe6f6bf4a am-vsol-upgrade
split Export in 2
author | am@osimis.io |
---|---|
date | Thu, 22 Nov 2018 23:15:24 +0100 |
parents | 087237703d63 |
children | 3f9017db1738 751fb354149e |
comparison
equal
deleted
inserted
replaced
425:087237703d63 | 426:660fe6f6bf4a |
---|---|
53 assert(layer->second != NULL); | 53 assert(layer->second != NULL); |
54 layer_ = layer->second; | 54 layer_ = layer->second; |
55 } | 55 } |
56 } | 56 } |
57 | 57 |
58 | 58 |
59 RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, | 59 RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, |
60 double x, | 60 double x, |
61 double y) : | 61 double y) : |
62 scene_(scene), | 62 scene_(scene), |
63 index_(0) // Dummy initialization | 63 index_(0) // Dummy initialization |
64 { | 64 { |
65 if (scene.LookupLayer(index_, x, y)) | 65 if (scene.LookupLayer(index_, x, y)) |
66 { | 66 { |
67 Layers::iterator layer = scene.layers_.find(index_); | 67 Layers::iterator layer = scene.layers_.find(index_); |
68 | 68 |
69 if (layer == scene.layers_.end()) | 69 if (layer == scene.layers_.end()) |
70 { | 70 { |
71 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | 71 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); |
72 } | 72 } |
73 else | 73 else |
117 } | 117 } |
118 else | 118 else |
119 { | 119 { |
120 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | 120 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); |
121 } | 121 } |
122 } | 122 } |
123 | 123 |
124 | 124 |
125 | 125 |
126 class RadiographyScene::AlphaLayer : public RadiographyLayer | 126 class RadiographyScene::AlphaLayer : public RadiographyLayer |
127 { | 127 { |
143 void SetForegroundValue(float foreground) | 143 void SetForegroundValue(float foreground) |
144 { | 144 { |
145 useWindowing_ = false; | 145 useWindowing_ = false; |
146 foreground_ = foreground; | 146 foreground_ = foreground; |
147 } | 147 } |
148 | 148 |
149 | 149 |
150 void SetAlpha(Orthanc::ImageAccessor* image) | 150 void SetAlpha(Orthanc::ImageAccessor* image) |
151 { | 151 { |
152 std::auto_ptr<Orthanc::ImageAccessor> raii(image); | 152 std::auto_ptr<Orthanc::ImageAccessor> raii(image); |
153 | 153 |
154 if (image == NULL) | 154 if (image == NULL) |
155 { | 155 { |
156 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 156 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
157 } | 157 } |
158 | 158 |
168 | 168 |
169 void LoadText(const Orthanc::Font& font, | 169 void LoadText(const Orthanc::Font& font, |
170 const std::string& utf8) | 170 const std::string& utf8) |
171 { | 171 { |
172 SetAlpha(font.RenderAlpha(utf8)); | 172 SetAlpha(font.RenderAlpha(utf8)); |
173 } | 173 } |
174 | 174 |
175 | 175 |
176 virtual bool GetDefaultWindowing(float& center, | 176 virtual bool GetDefaultWindowing(float& center, |
177 float& width) const | 177 float& width) const |
178 { | 178 { |
179 return false; | 179 return false; |
180 } | 180 } |
181 | 181 |
182 | 182 |
183 virtual void Render(Orthanc::ImageAccessor& buffer, | 183 virtual void Render(Orthanc::ImageAccessor& buffer, |
184 const AffineTransform2D& viewTransform, | 184 const AffineTransform2D& viewTransform, |
185 ImageInterpolation interpolation) const | 185 ImageInterpolation interpolation) const |
186 { | 186 { |
187 if (alpha_.get() == NULL) | 187 if (alpha_.get() == NULL) |
188 { | 188 { |
189 return; | 189 return; |
190 } | 190 } |
191 | 191 |
192 if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) | 192 if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) |
193 { | 193 { |
194 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | 194 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); |
195 } | 195 } |
196 | 196 |
197 unsigned int cropX, cropY, cropWidth, cropHeight; | 197 unsigned int cropX, cropY, cropWidth, cropHeight; |
198 GetCrop(cropX, cropY, cropWidth, cropHeight); | 198 GetCrop(cropX, cropY, cropWidth, cropHeight); |
199 | 199 |
200 const AffineTransform2D t = AffineTransform2D::Combine( | 200 const AffineTransform2D t = AffineTransform2D::Combine( |
201 viewTransform, GetTransform(), | 201 viewTransform, GetTransform(), |
202 AffineTransform2D::CreateOffset(cropX, cropY)); | 202 AffineTransform2D::CreateOffset(cropX, cropY)); |
203 | 203 |
204 Orthanc::ImageAccessor cropped; | 204 Orthanc::ImageAccessor cropped; |
205 alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); | 205 alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); |
206 | 206 |
207 Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); | 207 Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); |
208 | 208 |
209 t.Apply(tmp, cropped, interpolation, true /* clear */); | 209 t.Apply(tmp, cropped, interpolation, true /* clear */); |
210 | 210 |
211 // Blit | 211 // Blit |
212 const unsigned int width = buffer.GetWidth(); | 212 const unsigned int width = buffer.GetWidth(); |
213 const unsigned int height = buffer.GetHeight(); | 213 const unsigned int height = buffer.GetHeight(); |
214 | 214 |
215 float value = foreground_; | 215 float value = foreground_; |
216 | 216 |
217 if (useWindowing_) | 217 if (useWindowing_) |
218 { | 218 { |
219 float center, width; | 219 float center, width; |
220 if (scene_.GetWindowing(center, width)) | 220 if (scene_.GetWindowing(center, width)) |
221 { | 221 { |
222 value = center + width / 2.0f; | 222 value = center + width / 2.0f; |
223 } | 223 } |
224 } | 224 } |
225 | 225 |
226 for (unsigned int y = 0; y < height; y++) | 226 for (unsigned int y = 0; y < height; y++) |
227 { | 227 { |
228 float *q = reinterpret_cast<float*>(buffer.GetRow(y)); | 228 float *q = reinterpret_cast<float*>(buffer.GetRow(y)); |
229 const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)); | 229 const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)); |
230 | 230 |
231 for (unsigned int x = 0; x < width; x++, p++, q++) | 231 for (unsigned int x = 0; x < width; x++, p++, q++) |
232 { | 232 { |
233 float a = static_cast<float>(*p) / 255.0f; | 233 float a = static_cast<float>(*p) / 255.0f; |
234 | 234 |
235 *q = (a * value + (1.0f - a) * (*q)); | 235 *q = (a * value + (1.0f - a) * (*q)); |
236 } | 236 } |
237 } | 237 } |
238 } | 238 } |
239 | 239 |
240 | 240 |
241 virtual bool GetRange(float& minValue, | 241 virtual bool GetRange(float& minValue, |
242 float& maxValue) const | 242 float& maxValue) const |
243 { | 243 { |
244 if (useWindowing_) | 244 if (useWindowing_) |
245 { | 245 { |
262 | 262 |
263 return true; | 263 return true; |
264 } | 264 } |
265 } | 265 } |
266 }; | 266 }; |
267 | 267 |
268 | 268 |
269 | 269 |
270 class RadiographyScene::DicomLayer : public RadiographyLayer | 270 class RadiographyScene::DicomLayer : public RadiographyLayer |
271 { | 271 { |
272 private: | 272 private: |
273 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData | 273 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData |
276 | 276 |
277 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) | 277 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) |
278 { | 278 { |
279 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); | 279 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); |
280 } | 280 } |
281 | 281 |
282 | 282 |
283 void ApplyConverter() | 283 void ApplyConverter() |
284 { | 284 { |
285 if (source_.get() != NULL && | 285 if (source_.get() != NULL && |
286 converter_.get() != NULL) | 286 converter_.get() != NULL) |
287 { | 287 { |
288 converted_.reset(converter_->ConvertFrame(*source_)); | 288 converted_.reset(converter_->ConvertFrame(*source_)); |
289 } | 289 } |
290 } | 290 } |
291 | 291 |
292 public: | 292 public: |
293 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) | 293 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) |
294 { | 294 { |
295 converter_.reset(new DicomFrameConverter); | 295 converter_.reset(new DicomFrameConverter); |
296 converter_->ReadParameters(dataset); | 296 converter_->ReadParameters(dataset); |
297 ApplyConverter(); | 297 ApplyConverter(); |
298 | 298 |
299 std::string tmp; | 299 std::string tmp; |
300 Vector pixelSpacing; | 300 Vector pixelSpacing; |
301 | 301 |
302 if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && | 302 if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && |
303 LinearAlgebra::ParseVector(pixelSpacing, tmp) && | 303 LinearAlgebra::ParseVector(pixelSpacing, tmp) && |
304 pixelSpacing.size() == 2) | 304 pixelSpacing.size() == 2) |
305 { | 305 { |
306 SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); | 306 SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); |
320 { | 320 { |
321 SetSize(width, height); | 321 SetSize(width, height); |
322 } | 322 } |
323 } | 323 } |
324 | 324 |
325 | 325 |
326 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership | 326 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership |
327 { | 327 { |
328 std::auto_ptr<Orthanc::ImageAccessor> raii(image); | 328 std::auto_ptr<Orthanc::ImageAccessor> raii(image); |
329 | 329 |
330 if (image == NULL) | 330 if (image == NULL) |
331 { | 331 { |
332 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 332 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
333 } | 333 } |
334 | 334 |
335 SetSize(image->GetWidth(), image->GetHeight()); | 335 SetSize(image->GetWidth(), image->GetHeight()); |
336 | 336 |
337 source_ = raii; | 337 source_ = raii; |
338 ApplyConverter(); | 338 ApplyConverter(); |
339 } | 339 } |
340 | 340 |
341 | 341 |
342 virtual void Render(Orthanc::ImageAccessor& buffer, | 342 virtual void Render(Orthanc::ImageAccessor& buffer, |
343 const AffineTransform2D& viewTransform, | 343 const AffineTransform2D& viewTransform, |
344 ImageInterpolation interpolation) const | 344 ImageInterpolation interpolation) const |
345 { | 345 { |
346 if (converted_.get() != NULL) | 346 if (converted_.get() != NULL) |
352 | 352 |
353 unsigned int cropX, cropY, cropWidth, cropHeight; | 353 unsigned int cropX, cropY, cropWidth, cropHeight; |
354 GetCrop(cropX, cropY, cropWidth, cropHeight); | 354 GetCrop(cropX, cropY, cropWidth, cropHeight); |
355 | 355 |
356 AffineTransform2D t = AffineTransform2D::Combine( | 356 AffineTransform2D t = AffineTransform2D::Combine( |
357 viewTransform, GetTransform(), | 357 viewTransform, GetTransform(), |
358 AffineTransform2D::CreateOffset(cropX, cropY)); | 358 AffineTransform2D::CreateOffset(cropX, cropY)); |
359 | 359 |
360 Orthanc::ImageAccessor cropped; | 360 Orthanc::ImageAccessor cropped; |
361 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); | 361 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); |
362 | 362 |
363 t.Apply(buffer, cropped, interpolation, false); | 363 t.Apply(buffer, cropped, interpolation, false); |
409 { | 409 { |
410 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 410 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
411 } | 411 } |
412 | 412 |
413 std::auto_ptr<RadiographyLayer> raii(layer); | 413 std::auto_ptr<RadiographyLayer> raii(layer); |
414 | 414 |
415 size_t index = countLayers_++; | 415 size_t index = countLayers_++; |
416 raii->SetIndex(index); | 416 raii->SetIndex(index); |
417 layers_[index] = raii.release(); | 417 layers_[index] = raii.release(); |
418 | 418 |
419 EmitMessage(GeometryChangedMessage(*this)); | 419 EmitMessage(GeometryChangedMessage(*this)); |
420 EmitMessage(ContentChangedMessage(*this)); | 420 EmitMessage(ContentChangedMessage(*this)); |
421 | 421 |
422 return *layer; | 422 return *layer; |
423 } | 423 } |
424 | 424 |
425 | 425 |
426 RadiographyScene::RadiographyScene(MessageBroker& broker) : | 426 RadiographyScene::RadiographyScene(MessageBroker& broker) : |
427 IObserver(broker), | 427 IObserver(broker), |
428 IObservable(broker), | 428 IObservable(broker), |
429 countLayers_(0), | 429 countLayers_(0), |
506 alpha->LoadText(font, utf8); | 506 alpha->LoadText(font, utf8); |
507 | 507 |
508 return RegisterLayer(alpha.release()); | 508 return RegisterLayer(alpha.release()); |
509 } | 509 } |
510 | 510 |
511 | 511 |
512 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, | 512 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, |
513 unsigned int height) | 513 unsigned int height) |
514 { | 514 { |
515 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); | 515 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); |
516 | 516 |
537 alpha->SetAlpha(block.release()); | 537 alpha->SetAlpha(block.release()); |
538 | 538 |
539 return RegisterLayer(alpha.release()); | 539 return RegisterLayer(alpha.release()); |
540 } | 540 } |
541 | 541 |
542 | 542 |
543 RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, | 543 RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, |
544 const std::string& instance, | 544 const std::string& instance, |
545 unsigned int frame, | 545 unsigned int frame, |
546 bool httpCompression) | 546 bool httpCompression) |
547 { | 547 { |
548 RadiographyLayer& layer = RegisterLayer(new DicomLayer); | 548 RadiographyLayer& layer = RegisterLayer(new DicomLayer); |
549 | 549 |
550 { | 550 { |
551 IWebService::HttpHeaders headers; | 551 IWebService::HttpHeaders headers; |
552 std::string uri = "/instances/" + instance + "/tags"; | 552 std::string uri = "/instances/" + instance + "/tags"; |
553 | 553 |
554 orthanc.GetBinaryAsync( | 554 orthanc.GetBinaryAsync( |
555 uri, headers, | 555 uri, headers, |
556 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage> | 556 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage> |
557 (*this, &RadiographyScene::OnTagsReceived), NULL, | 557 (*this, &RadiographyScene::OnTagsReceived), NULL, |
558 new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); | 558 new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); |
559 } | 559 } |
560 | 560 |
561 { | 561 { |
562 IWebService::HttpHeaders headers; | 562 IWebService::HttpHeaders headers; |
563 headers["Accept"] = "image/x-portable-arbitrarymap"; | 563 headers["Accept"] = "image/x-portable-arbitrarymap"; |
564 | 564 |
565 if (httpCompression) | 565 if (httpCompression) |
566 { | 566 { |
567 headers["Accept-Encoding"] = "gzip"; | 567 headers["Accept-Encoding"] = "gzip"; |
568 } | 568 } |
569 | 569 |
570 std::string uri = ("/instances/" + instance + "/frames/" + | 570 std::string uri = ("/instances/" + instance + "/frames/" + |
571 boost::lexical_cast<std::string>(frame) + "/image-uint16"); | 571 boost::lexical_cast<std::string>(frame) + "/image-uint16"); |
572 | 572 |
573 orthanc.GetBinaryAsync( | 573 orthanc.GetBinaryAsync( |
574 uri, headers, | 574 uri, headers, |
575 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage> | 575 new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage> |
576 (*this, &RadiographyScene::OnFrameReceived), NULL, | 576 (*this, &RadiographyScene::OnFrameReceived), NULL, |
577 new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); | 577 new Orthanc::SingleValueObject<size_t>(layer.GetIndex())); |
578 } | 578 } |
579 | 579 |
580 return layer; | 580 return layer; |
581 } | 581 } |
582 | 582 |
583 | 583 |
584 RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web) | 584 RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web) |
585 { | 585 { |
586 RadiographyLayer& layer = RegisterLayer(new DicomLayer); | 586 RadiographyLayer& layer = RegisterLayer(new DicomLayer); |
587 | 587 |
588 | 588 |
589 return layer; | 589 return layer; |
590 } | 590 } |
591 | 591 |
592 | 592 |
593 | 593 |
594 void RadiographyScene::OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) | 594 void RadiographyScene::OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) |
595 { | 595 { |
596 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&> | 596 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&> |
597 (message.GetPayload()).GetValue(); | 597 (message.GetPayload()).GetValue(); |
598 | 598 |
599 LOG(INFO) << "JSON received: " << message.GetUri().c_str() | 599 LOG(INFO) << "JSON received: " << message.GetUri().c_str() |
600 << " (" << message.GetAnswerSize() << " bytes) for layer " << index; | 600 << " (" << message.GetAnswerSize() << " bytes) for layer " << index; |
601 | 601 |
602 Layers::iterator layer = layers_.find(index); | 602 Layers::iterator layer = layers_.find(index); |
603 if (layer != layers_.end()) | 603 if (layer != layers_.end()) |
604 { | 604 { |
605 assert(layer->second != NULL); | 605 assert(layer->second != NULL); |
606 | 606 |
607 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); | 607 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); |
608 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom); | 608 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom); |
609 | 609 |
610 float c, w; | 610 float c, w; |
611 if (!hasWindowing_ && | 611 if (!hasWindowing_ && |
617 } | 617 } |
618 | 618 |
619 EmitMessage(GeometryChangedMessage(*this)); | 619 EmitMessage(GeometryChangedMessage(*this)); |
620 } | 620 } |
621 } | 621 } |
622 | 622 |
623 | 623 |
624 void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) | 624 void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) |
625 { | 625 { |
626 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue(); | 626 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue(); |
627 | 627 |
628 LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str() | 628 LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str() |
629 << " (" << message.GetAnswerSize() << " bytes) for layer " << index; | 629 << " (" << message.GetAnswerSize() << " bytes) for layer " << index; |
630 | 630 |
631 Layers::iterator layer = layers_.find(index); | 631 Layers::iterator layer = layers_.find(index); |
632 if (layer != layers_.end()) | 632 if (layer != layers_.end()) |
633 { | 633 { |
634 assert(layer->second != NULL); | 634 assert(layer->second != NULL); |
635 | 635 |
636 std::string content; | 636 std::string content; |
637 if (message.GetAnswerSize() > 0) | 637 if (message.GetAnswerSize() > 0) |
638 { | 638 { |
639 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize()); | 639 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize()); |
640 } | 640 } |
641 | 641 |
642 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); | 642 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); |
643 reader->ReadFromMemory(content); | 643 reader->ReadFromMemory(content); |
644 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader.release()); | 644 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader.release()); |
645 | 645 |
646 EmitMessage(ContentChangedMessage(*this)); | 646 EmitMessage(ContentChangedMessage(*this)); |
659 extent.Union(it->second->GetExtent()); | 659 extent.Union(it->second->GetExtent()); |
660 } | 660 } |
661 | 661 |
662 return extent; | 662 return extent; |
663 } | 663 } |
664 | 664 |
665 | 665 |
666 void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, | 666 void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, |
667 const AffineTransform2D& viewTransform, | 667 const AffineTransform2D& viewTransform, |
668 ImageInterpolation interpolation) const | 668 ImageInterpolation interpolation) const |
669 { | 669 { |
702 } | 702 } |
703 | 703 |
704 return false; | 704 return false; |
705 } | 705 } |
706 | 706 |
707 | 707 |
708 void RadiographyScene::DrawBorder(CairoContext& context, | 708 void RadiographyScene::DrawBorder(CairoContext& context, |
709 unsigned int layer, | 709 unsigned int layer, |
710 double zoom) | 710 double zoom) |
711 { | 711 { |
712 Layers::const_iterator found = layers_.find(layer); | 712 Layers::const_iterator found = layers_.find(layer); |
713 | 713 |
714 if (found != layers_.end()) | 714 if (found != layers_.end()) |
715 { | 715 { |
716 context.SetSourceColor(255, 0, 0); | 716 context.SetSourceColor(255, 0, 0); |
717 found->second->DrawBorders(context, zoom); | 717 found->second->DrawBorders(context, zoom); |
718 } | 718 } |
721 | 721 |
722 void RadiographyScene::GetRange(float& minValue, | 722 void RadiographyScene::GetRange(float& minValue, |
723 float& maxValue) const | 723 float& maxValue) const |
724 { | 724 { |
725 bool first = true; | 725 bool first = true; |
726 | 726 |
727 for (Layers::const_iterator it = layers_.begin(); | 727 for (Layers::const_iterator it = layers_.begin(); |
728 it != layers_.end(); it++) | 728 it != layers_.end(); it++) |
729 { | 729 { |
730 assert(it->second != NULL); | 730 assert(it->second != NULL); |
731 | 731 |
752 maxValue = 0; | 752 maxValue = 0; |
753 } | 753 } |
754 } | 754 } |
755 | 755 |
756 | 756 |
757 // Export using PAM is faster than using PNG, but requires Orthanc | |
758 // core >= 1.4.3 | |
759 void RadiographyScene::ExportDicom(OrthancApiClient& orthanc, | 757 void RadiographyScene::ExportDicom(OrthancApiClient& orthanc, |
760 const Orthanc::DicomMap& dicom, | 758 const Orthanc::DicomMap& dicom, |
759 const std::string& parentOrthancId, | |
761 double pixelSpacingX, | 760 double pixelSpacingX, |
762 double pixelSpacingY, | 761 double pixelSpacingY, |
763 bool invert, | 762 bool invert, |
764 ImageInterpolation interpolation, | 763 ImageInterpolation interpolation, |
765 bool usePam) | 764 bool usePam) |
766 { | 765 { |
766 Json::Value createDicomRequestContent; | |
767 | |
768 Export(createDicomRequestContent, dicom, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam); | |
769 | |
770 if (!parentOrthancId.empty()) | |
771 { | |
772 createDicomRequestContent["Parent"] = parentOrthancId; | |
773 } | |
774 | |
775 orthanc.PostJsonAsyncExpectJson( | |
776 "/tools/create-dicom", createDicomRequestContent, | |
777 new Callable<RadiographyScene, OrthancApiClient::JsonResponseReadyMessage> | |
778 (*this, &RadiographyScene::OnDicomExported), | |
779 NULL, NULL); | |
780 } | |
781 | |
782 // Export using PAM is faster than using PNG, but requires Orthanc | |
783 // core >= 1.4.3 | |
784 void RadiographyScene::Export(Json::Value& createDicomRequestContent, | |
785 const Orthanc::DicomMap& dicom, | |
786 double pixelSpacingX, | |
787 double pixelSpacingY, | |
788 bool invert, | |
789 ImageInterpolation interpolation, | |
790 bool usePam) | |
791 { | |
767 if (pixelSpacingX <= 0 || | 792 if (pixelSpacingX <= 0 || |
768 pixelSpacingY <= 0) | 793 pixelSpacingY <= 0) |
769 { | 794 { |
770 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | 795 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); |
771 } | 796 } |
772 | 797 |
773 LOG(INFO) << "Exporting DICOM"; | 798 LOG(INFO) << "Exporting DICOM"; |
774 | 799 |
775 Extent2D extent = GetSceneExtent(); | 800 Extent2D extent = GetSceneExtent(); |
776 | 801 |
777 int w = std::ceil(extent.GetWidth() / pixelSpacingX); | 802 int w = std::ceil(extent.GetWidth() / pixelSpacingX); |
785 Orthanc::Image layers(Orthanc::PixelFormat_Float32, | 810 Orthanc::Image layers(Orthanc::PixelFormat_Float32, |
786 static_cast<unsigned int>(w), | 811 static_cast<unsigned int>(w), |
787 static_cast<unsigned int>(h), false); | 812 static_cast<unsigned int>(h), false); |
788 | 813 |
789 AffineTransform2D view = AffineTransform2D::Combine( | 814 AffineTransform2D view = AffineTransform2D::Combine( |
790 AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), | 815 AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), |
791 AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); | 816 AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); |
792 | 817 |
793 Render(layers, view, interpolation); | 818 Render(layers, view, interpolation); |
794 | 819 |
795 Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16, | 820 Orthanc::Image rendered(Orthanc::PixelFormat_Grayscale16, |
796 layers.GetWidth(), layers.GetHeight(), false); | 821 layers.GetWidth(), layers.GetHeight(), false); |
797 Orthanc::ImageProcessing::Convert(rendered, layers); | 822 Orthanc::ImageProcessing::Convert(rendered, layers); |
816 } | 841 } |
817 | 842 |
818 std::set<Orthanc::DicomTag> tags; | 843 std::set<Orthanc::DicomTag> tags; |
819 dicom.GetTags(tags); | 844 dicom.GetTags(tags); |
820 | 845 |
821 Json::Value json = Json::objectValue; | 846 createDicomRequestContent["Tags"] = Json::objectValue; |
822 json["Tags"] = Json::objectValue; | 847 |
823 | |
824 for (std::set<Orthanc::DicomTag>::const_iterator | 848 for (std::set<Orthanc::DicomTag>::const_iterator |
825 tag = tags.begin(); tag != tags.end(); ++tag) | 849 tag = tags.begin(); tag != tags.end(); ++tag) |
826 { | 850 { |
827 const Orthanc::DicomValue& value = dicom.GetValue(*tag); | 851 const Orthanc::DicomValue& value = dicom.GetValue(*tag); |
828 if (!value.IsNull() && | 852 if (!value.IsNull() && |
829 !value.IsBinary()) | 853 !value.IsBinary()) |
830 { | 854 { |
831 json["Tags"][tag->Format()] = value.GetContent(); | 855 createDicomRequestContent["Tags"][tag->Format()] = value.GetContent(); |
832 } | 856 } |
833 } | 857 } |
834 | 858 |
835 json["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] = | 859 createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] = |
836 (invert ? "MONOCHROME1" : "MONOCHROME2"); | 860 (invert ? "MONOCHROME1" : "MONOCHROME2"); |
837 | 861 |
838 // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to | 862 // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to |
839 // avoid floating-point numbers to grow over 16 characters, | 863 // avoid floating-point numbers to grow over 16 characters, |
840 // which would be invalid according to DICOM standard | 864 // which would be invalid according to DICOM standard |
841 // ("dciodvfy" would complain). | 865 // ("dciodvfy" would complain). |
842 char buf[32]; | 866 char buf[32]; |
843 sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX); | 867 sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX); |
844 | 868 |
845 json["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf; | 869 createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf; |
846 | 870 |
847 float center, width; | 871 float center, width; |
848 if (GetWindowing(center, width)) | 872 if (GetWindowing(center, width)) |
849 { | 873 { |
850 json["Tags"][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()] = | 874 createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_WINDOW_CENTER.Format()] = |
851 boost::lexical_cast<std::string>(boost::math::iround(center)); | 875 boost::lexical_cast<std::string>(boost::math::iround(center)); |
852 | 876 |
853 json["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] = | 877 createDicomRequestContent["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] = |
854 boost::lexical_cast<std::string>(boost::math::iround(width)); | 878 boost::lexical_cast<std::string>(boost::math::iround(width)); |
855 } | 879 } |
880 | |
856 | 881 |
857 // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme | 882 // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme |
858 json["Content"] = ("data:" + | 883 createDicomRequestContent["Content"] = ("data:" + |
859 std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) + | 884 std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) + |
860 ";base64," + base64); | 885 ";base64," + base64); |
861 | |
862 orthanc.PostJsonAsyncExpectJson( | |
863 "/tools/create-dicom", json, | |
864 new Callable<RadiographyScene, OrthancApiClient::JsonResponseReadyMessage> | |
865 (*this, &RadiographyScene::OnDicomExported), | |
866 NULL, NULL); | |
867 } | 886 } |
868 | 887 |
869 | 888 |
870 void RadiographyScene::OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message) | 889 void RadiographyScene::OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message) |
871 { | 890 { |
878 { | 897 { |
879 LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes"; | 898 LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes"; |
880 | 899 |
881 const IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); | 900 const IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); |
882 for (IWebService::HttpHeaders::const_iterator | 901 for (IWebService::HttpHeaders::const_iterator |
883 it = h.begin(); it != h.end(); ++it) | 902 it = h.begin(); it != h.end(); ++it) |
884 { | 903 { |
885 printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str()); | 904 printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str()); |
886 } | 905 } |
887 } | 906 } |
888 | 907 |