comparison Framework/Radiography/RadiographyScene.cpp @ 430:b85f635f1eb5 am-vsol-upgrade

added serialization for RadiographyScene
author am@osimis.io
date Thu, 29 Nov 2018 15:11:19 +0100
parents 751fb354149e 3f9017db1738
children 26b90b110719
comparison
equal deleted inserted replaced
429:c7fb700a7d12 430:b85f635f1eb5
19 **/ 19 **/
20 20
21 21
22 #include "RadiographyScene.h" 22 #include "RadiographyScene.h"
23 23
24 #include "RadiographyAlphaLayer.h"
25 #include "RadiographyDicomLayer.h"
26 #include "RadiographyTextLayer.h"
24 #include "../Toolbox/DicomFrameConverter.h" 27 #include "../Toolbox/DicomFrameConverter.h"
25 28
26 #include <Core/Images/Image.h> 29 #include <Core/Images/Image.h>
27 #include <Core/Images/ImageProcessing.h> 30 #include <Core/Images/ImageProcessing.h>
28 #include <Core/Images/PamReader.h> 31 #include <Core/Images/PamReader.h>
119 { 122 {
120 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 123 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
121 } 124 }
122 } 125 }
123 126
124
125
126 class RadiographyScene::AlphaLayer : public RadiographyLayer
127 {
128 private:
129 const RadiographyScene& scene_;
130 std::auto_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8
131 bool useWindowing_;
132 float foreground_;
133
134 public:
135 AlphaLayer(const RadiographyScene& scene) :
136 scene_(scene),
137 useWindowing_(true),
138 foreground_(0)
139 {
140 }
141
142
143 void SetForegroundValue(float foreground)
144 {
145 useWindowing_ = false;
146 foreground_ = foreground;
147 }
148
149
150 void SetAlpha(Orthanc::ImageAccessor* image)
151 {
152 std::auto_ptr<Orthanc::ImageAccessor> raii(image);
153
154 if (image == NULL)
155 {
156 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
157 }
158
159 if (image->GetFormat() != Orthanc::PixelFormat_Grayscale8)
160 {
161 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
162 }
163
164 SetSize(image->GetWidth(), image->GetHeight());
165 alpha_ = raii;
166 }
167
168
169 void LoadText(const Orthanc::Font& font,
170 const std::string& utf8)
171 {
172 SetAlpha(font.RenderAlpha(utf8));
173 }
174
175
176 virtual bool GetDefaultWindowing(float& center,
177 float& width) const
178 {
179 return false;
180 }
181
182
183 virtual void Render(Orthanc::ImageAccessor& buffer,
184 const AffineTransform2D& viewTransform,
185 ImageInterpolation interpolation) const
186 {
187 if (alpha_.get() == NULL)
188 {
189 return;
190 }
191
192 if (buffer.GetFormat() != Orthanc::PixelFormat_Float32)
193 {
194 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
195 }
196
197 unsigned int cropX, cropY, cropWidth, cropHeight;
198 GetCrop(cropX, cropY, cropWidth, cropHeight);
199
200 const AffineTransform2D t = AffineTransform2D::Combine(
201 viewTransform, GetTransform(),
202 AffineTransform2D::CreateOffset(cropX, cropY));
203
204 Orthanc::ImageAccessor cropped;
205 alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
206
207 Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false);
208
209 t.Apply(tmp, cropped, interpolation, true /* clear */);
210
211 // Blit
212 const unsigned int width = buffer.GetWidth();
213 const unsigned int height = buffer.GetHeight();
214
215 float value = foreground_;
216
217 if (useWindowing_)
218 {
219 float center, width;
220 if (scene_.GetWindowing(center, width))
221 {
222 value = center + width / 2.0f;
223 }
224 }
225
226 for (unsigned int y = 0; y < height; y++)
227 {
228 float *q = reinterpret_cast<float*>(buffer.GetRow(y));
229 const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y));
230
231 for (unsigned int x = 0; x < width; x++, p++, q++)
232 {
233 float a = static_cast<float>(*p) / 255.0f;
234
235 *q = (a * value + (1.0f - a) * (*q));
236 }
237 }
238 }
239
240
241 virtual bool GetRange(float& minValue,
242 float& maxValue) const
243 {
244 if (useWindowing_)
245 {
246 return false;
247 }
248 else
249 {
250 minValue = 0;
251 maxValue = 0;
252
253 if (foreground_ < 0)
254 {
255 minValue = foreground_;
256 }
257
258 if (foreground_ > 0)
259 {
260 maxValue = foreground_;
261 }
262
263 return true;
264 }
265 }
266 };
267
268
269
270 class RadiographyScene::DicomLayer : public RadiographyLayer
271 {
272 private:
273 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData
274 std::auto_ptr<DicomFrameConverter> converter_;
275 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32
276
277 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag)
278 {
279 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement());
280 }
281
282
283 void ApplyConverter()
284 {
285 if (source_.get() != NULL &&
286 converter_.get() != NULL)
287 {
288 converted_.reset(converter_->ConvertFrame(*source_));
289 }
290 }
291
292 public:
293 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset)
294 {
295 converter_.reset(new DicomFrameConverter);
296 converter_->ReadParameters(dataset);
297 ApplyConverter();
298
299 std::string tmp;
300 Vector pixelSpacing;
301
302 if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) &&
303 LinearAlgebra::ParseVector(pixelSpacing, tmp) &&
304 pixelSpacing.size() == 2)
305 {
306 SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]);
307 }
308
309 //SetPan(-0.5 * GetPixelSpacingX(), -0.5 * GetPixelSpacingY());
310
311 OrthancPlugins::DicomDatasetReader reader(dataset);
312
313 unsigned int width, height;
314 if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) ||
315 !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS)))
316 {
317 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
318 }
319 else
320 {
321 SetSize(width, height);
322 }
323 }
324
325
326 void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership
327 {
328 std::auto_ptr<Orthanc::ImageAccessor> raii(image);
329
330 if (image == NULL)
331 {
332 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
333 }
334
335 SetSize(image->GetWidth(), image->GetHeight());
336
337 source_ = raii;
338 ApplyConverter();
339 }
340
341
342 virtual void Render(Orthanc::ImageAccessor& buffer,
343 const AffineTransform2D& viewTransform,
344 ImageInterpolation interpolation) const
345 {
346 if (converted_.get() != NULL)
347 {
348 if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
349 {
350 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
351 }
352
353 unsigned int cropX, cropY, cropWidth, cropHeight;
354 GetCrop(cropX, cropY, cropWidth, cropHeight);
355
356 AffineTransform2D t = AffineTransform2D::Combine(
357 viewTransform, GetTransform(),
358 AffineTransform2D::CreateOffset(cropX, cropY));
359
360 Orthanc::ImageAccessor cropped;
361 converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
362
363 t.Apply(buffer, cropped, interpolation, false);
364 }
365 }
366
367
368 virtual bool GetDefaultWindowing(float& center,
369 float& width) const
370 {
371 if (converter_.get() != NULL &&
372 converter_->HasDefaultWindow())
373 {
374 center = static_cast<float>(converter_->GetDefaultWindowCenter());
375 width = static_cast<float>(converter_->GetDefaultWindowWidth());
376 return true;
377 }
378 else
379 {
380 return false;
381 }
382 }
383
384
385 virtual bool GetRange(float& minValue,
386 float& maxValue) const
387 {
388 if (converted_.get() != NULL)
389 {
390 if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
391 {
392 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
393 }
394
395 Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_);
396 return true;
397 }
398 else
399 {
400 return false;
401 }
402 }
403 };
404
405
406 RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer) 127 RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer)
407 { 128 {
408 if (layer == NULL) 129 if (layer == NULL)
409 { 130 {
410 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 131 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
416 137
417 size_t index = countLayers_++; 138 size_t index = countLayers_++;
418 raii->SetIndex(index); 139 raii->SetIndex(index);
419 layers_[index] = raii.release(); 140 layers_[index] = raii.release();
420 141
421 EmitMessage(GeometryChangedMessage(*this)); 142 EmitMessage(GeometryChangedMessage(*this, *layer));
422 EmitMessage(ContentChangedMessage(*this)); 143 EmitMessage(ContentChangedMessage(*this, *layer));
423 144
424 return *layer; 145 return *layer;
425 } 146 }
426 147
427 148
443 assert(it->second != NULL); 164 assert(it->second != NULL);
444 delete it->second; 165 delete it->second;
445 } 166 }
446 } 167 }
447 168
169 void RadiographyScene::GetLayersIndexes(std::vector<size_t>& output) const
170 {
171 for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++)
172 {
173 output.push_back(it->first);
174 }
175 }
176
448 void RadiographyScene::RemoveLayer(size_t layerIndex) 177 void RadiographyScene::RemoveLayer(size_t layerIndex)
449 { 178 {
450 LOG(INFO) << "Removing layer: " << layerIndex; 179 LOG(INFO) << "Removing layer: " << layerIndex;
451 180
452 if (layerIndex > countLayers_) 181 if (layerIndex > countLayers_)
457 layers_.erase(layerIndex); 186 layers_.erase(layerIndex);
458 countLayers_--; 187 countLayers_--;
459 LOG(INFO) << "Removing layer, there are now : " << countLayers_ << " layers"; 188 LOG(INFO) << "Removing layer, there are now : " << countLayers_ << " layers";
460 } 189 }
461 190
462 RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) 191 const RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) const
463 { 192 {
464 if (layerIndex > countLayers_) 193 if (layerIndex > countLayers_)
465 { 194 {
466 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 195 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
467 } 196 }
468 return *(layers_[layerIndex]); 197
198 return *(layers_.at(layerIndex));
469 } 199 }
470 200
471 bool RadiographyScene::GetWindowing(float& center, 201 bool RadiographyScene::GetWindowing(float& center,
472 float& width) const 202 float& width) const
473 { 203 {
503 windowingWidth_ = width; 233 windowingWidth_ = width;
504 } 234 }
505 235
506 236
507 RadiographyLayer& RadiographyScene::LoadText(const Orthanc::Font& font, 237 RadiographyLayer& RadiographyScene::LoadText(const Orthanc::Font& font,
508 const std::string& utf8) 238 const std::string& utf8,
509 { 239 RadiographyLayer::Geometry* geometry)
510 std::auto_ptr<AlphaLayer> alpha(new AlphaLayer(*this)); 240 {
241 std::auto_ptr<RadiographyTextLayer> alpha(new RadiographyTextLayer(*this));
511 alpha->LoadText(font, utf8); 242 alpha->LoadText(font, utf8);
243 if (geometry != NULL)
244 {
245 alpha->SetGeometry(*geometry);
246 }
512 247
513 return RegisterLayer(alpha.release()); 248 return RegisterLayer(alpha.release());
514 } 249 }
515 250
516 251
517 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, 252 RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width,
518 unsigned int height) 253 unsigned int height,
254 RadiographyLayer::Geometry* geometry)
519 { 255 {
520 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); 256 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false));
521 257
522 for (unsigned int padding = 0; 258 for (unsigned int padding = 0;
523 (width > 2 * padding) && (height > 2 * padding); 259 (width > 2 * padding) && (height > 2 * padding);
536 Orthanc::ImageAccessor region; 272 Orthanc::ImageAccessor region;
537 block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding); 273 block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding);
538 Orthanc::ImageProcessing::Set(region, color); 274 Orthanc::ImageProcessing::Set(region, color);
539 } 275 }
540 276
541 std::auto_ptr<AlphaLayer> alpha(new AlphaLayer(*this)); 277 return LoadAlphaBitmap(block.release(), geometry);
542 alpha->SetAlpha(block.release()); 278 }
279
280 RadiographyLayer& RadiographyScene::LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, RadiographyLayer::Geometry *geometry)
281 {
282 std::auto_ptr<RadiographyAlphaLayer> alpha(new RadiographyAlphaLayer(*this));
283 alpha->SetAlpha(bitmap);
284 if (geometry != NULL)
285 {
286 alpha->SetGeometry(*geometry);
287 }
543 288
544 return RegisterLayer(alpha.release()); 289 return RegisterLayer(alpha.release());
545 } 290 }
546
547 291
548 RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, 292 RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc,
549 const std::string& instance, 293 const std::string& instance,
550 unsigned int frame, 294 unsigned int frame,
551 bool httpCompression) 295 bool httpCompression,
552 { 296 RadiographyLayer::Geometry* geometry)
553 RadiographyLayer& layer = RegisterLayer(new DicomLayer); 297 {
298 RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer));
299 layer.SetInstance(instance, frame);
300
301 if (geometry != NULL)
302 {
303 layer.SetGeometry(*geometry);
304 }
554 305
555 { 306 {
556 IWebService::HttpHeaders headers; 307 IWebService::HttpHeaders headers;
557 std::string uri = "/instances/" + instance + "/tags"; 308 std::string uri = "/instances/" + instance + "/tags";
558 309
586 } 337 }
587 338
588 339
589 RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web) 340 RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web)
590 { 341 {
591 RadiographyLayer& layer = RegisterLayer(new DicomLayer); 342 RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer);
592 343
593 344
594 return layer; 345 return layer;
595 } 346 }
596 347
608 if (layer != layers_.end()) 359 if (layer != layers_.end())
609 { 360 {
610 assert(layer->second != NULL); 361 assert(layer->second != NULL);
611 362
612 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); 363 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize());
613 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom); 364 dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetDicomTags(dicom);
614 365
615 float c, w; 366 float c, w;
616 if (!hasWindowing_ && 367 if (!hasWindowing_ &&
617 layer->second->GetDefaultWindowing(c, w)) 368 layer->second->GetDefaultWindowing(c, w))
618 { 369 {
619 hasWindowing_ = true; 370 hasWindowing_ = true;
620 windowingCenter_ = c; 371 windowingCenter_ = c;
621 windowingWidth_ = w; 372 windowingWidth_ = w;
622 } 373 }
623 374
624 EmitMessage(GeometryChangedMessage(*this)); 375 EmitMessage(GeometryChangedMessage(*this, *(layer->second)));
625 } 376 }
626 } 377 }
627 378
628 379
629 void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) 380 void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
644 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize()); 395 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize());
645 } 396 }
646 397
647 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); 398 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader);
648 reader->ReadFromMemory(content); 399 reader->ReadFromMemory(content);
649 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader.release()); 400 dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetSourceImage(reader.release());
650 401
651 EmitMessage(ContentChangedMessage(*this)); 402 EmitMessage(ContentChangedMessage(*this, *(layer->second)));
652 } 403 }
653 } 404 }
654 405
655 406
656 Extent2D RadiographyScene::GetSceneExtent() const 407 Extent2D RadiographyScene::GetSceneExtent() const
670 421
671 void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, 422 void RadiographyScene::Render(Orthanc::ImageAccessor& buffer,
672 const AffineTransform2D& viewTransform, 423 const AffineTransform2D& viewTransform,
673 ImageInterpolation interpolation) const 424 ImageInterpolation interpolation) const
674 { 425 {
675 Orthanc::ImageProcessing::Set(buffer, 0); 426 Orthanc::ImageProcessing::Set(buffer, 0); // TODO: get background color (depending on inverted state)
676 427
677 // Render layers in the background-to-foreground order 428 // Render layers in the background-to-foreground order
678 for (size_t index = 0; index < countLayers_; index++) 429 for (size_t index = 0; index < countLayers_; index++)
679 { 430 {
680 Layers::const_iterator it = layers_.find(index); 431 Layers::const_iterator it = layers_.find(index);
768 ImageInterpolation interpolation, 519 ImageInterpolation interpolation,
769 bool usePam) 520 bool usePam)
770 { 521 {
771 Json::Value createDicomRequestContent; 522 Json::Value createDicomRequestContent;
772 523
773 Export(createDicomRequestContent, dicom, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam); 524 ExportToCreateDicomRequest(createDicomRequestContent, dicom, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam);
774 525
775 if (!parentOrthancId.empty()) 526 if (!parentOrthancId.empty())
776 { 527 {
777 createDicomRequestContent["Parent"] = parentOrthancId; 528 createDicomRequestContent["Parent"] = parentOrthancId;
778 } 529 }
784 NULL, NULL); 535 NULL, NULL);
785 } 536 }
786 537
787 // Export using PAM is faster than using PNG, but requires Orthanc 538 // Export using PAM is faster than using PNG, but requires Orthanc
788 // core >= 1.4.3 539 // core >= 1.4.3
789 void RadiographyScene::Export(Json::Value& createDicomRequestContent, 540 void RadiographyScene::ExportToCreateDicomRequest(Json::Value& createDicomRequestContent,
790 const Orthanc::DicomMap& dicom, 541 const Orthanc::DicomMap& dicom,
791 double pixelSpacingX, 542 double pixelSpacingX,
792 double pixelSpacingY, 543 double pixelSpacingY,
793 bool invert, 544 bool invert,
794 ImageInterpolation interpolation, 545 ImageInterpolation interpolation,