comparison OrthancServer/Internals/DicomImageDecoder.cpp @ 1826:ac5b0b4e2434

refactoring of DicomImageDecoder
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 25 Nov 2015 16:00:57 +0100
parents b530c3dfe2a6
children b1291df2f780
comparison
equal deleted inserted replaced
1825:f0f8a94c0858 1826:ac5b0b4e2434
80 #include "../PrecompiledHeadersServer.h" 80 #include "../PrecompiledHeadersServer.h"
81 #include "DicomImageDecoder.h" 81 #include "DicomImageDecoder.h"
82 82
83 #include "../../Core/Logging.h" 83 #include "../../Core/Logging.h"
84 #include "../../Core/OrthancException.h" 84 #include "../../Core/OrthancException.h"
85 #include "../../Core/Images/Image.h"
85 #include "../../Core/Images/ImageProcessing.h" 86 #include "../../Core/Images/ImageProcessing.h"
86 #include "../../Core/Images/PngWriter.h" // TODO REMOVE THIS
87 #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h" 87 #include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h"
88 #include "../ToDcmtkBridge.h" 88 #include "../ToDcmtkBridge.h"
89 #include "../FromDcmtkBridge.h" 89 #include "../FromDcmtkBridge.h"
90 #include "../ParsedDicomFile.h" 90 #include "../ParsedDicomFile.h"
91 91
92 #include <boost/lexical_cast.hpp> 92 #include <boost/lexical_cast.hpp>
93
94 #include <dcmtk/dcmdata/dcfilefo.h>
93 95
94 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 96 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
95 #include <dcmtk/dcmjpls/djcodecd.h> 97 #include <dcmtk/dcmjpls/djcodecd.h>
96 #include <dcmtk/dcmjpls/djcparam.h> 98 #include <dcmtk/dcmjpls/djcparam.h>
97 #include <dcmtk/dcmjpeg/djrplol.h> 99 #include <dcmtk/dcmjpeg/djrplol.h>
302 return slowAccessor_->GetSize(); 304 return slowAccessor_->GetSize();
303 } 305 }
304 }; 306 };
305 307
306 308
307 void DicomImageDecoder::SetupImageBuffer(ImageBuffer& target, 309 ImageAccessor* DicomImageDecoder::CreateImage(DcmDataset& dataset)
308 DcmDataset& dataset)
309 { 310 {
310 DicomMap m; 311 DicomMap m;
311 FromDcmtkBridge::Convert(m, dataset); 312 FromDcmtkBridge::Convert(m, dataset);
312 313
313 DicomImageInformation info(m); 314 DicomImageInformation info(m);
322 << EnumerationToString(info.GetPhotometricInterpretation()) 323 << EnumerationToString(info.GetPhotometricInterpretation())
323 << " photometric interpretation"; 324 << " photometric interpretation";
324 throw OrthancException(ErrorCode_NotImplemented); 325 throw OrthancException(ErrorCode_NotImplemented);
325 } 326 }
326 327
327 target.SetHeight(info.GetHeight()); 328 return new Image(format, info.GetWidth(), info.GetHeight());
328 target.SetWidth(info.GetWidth());
329 target.SetFormat(format);
330 } 329 }
331 330
332 331
333 bool DicomImageDecoder::IsUncompressedImage(const DcmDataset& dataset) 332 bool DicomImageDecoder::IsUncompressedImage(const DcmDataset& dataset)
334 { 333 {
372 } 371 }
373 } 372 }
374 } 373 }
375 374
376 375
377 void DicomImageDecoder::DecodeUncompressedImage(ImageBuffer& target, 376 ImageAccessor* DicomImageDecoder::DecodeUncompressedImage(DcmDataset& dataset,
378 DcmDataset& dataset, 377 unsigned int frame)
379 unsigned int frame)
380 { 378 {
381 if (!IsUncompressedImage(dataset)) 379 if (!IsUncompressedImage(dataset))
382 { 380 {
383 throw OrthancException(ErrorCode_BadParameterType); 381 throw OrthancException(ErrorCode_BadParameterType);
384 } 382 }
385 383
386 DecodeUncompressedImageInternal(target, dataset, frame); 384 return DecodeUncompressedImageInternal(dataset, frame);
387 } 385 }
388 386
389 387
390 void DicomImageDecoder::DecodeUncompressedImageInternal(ImageBuffer& target, 388 ImageAccessor* DicomImageDecoder::DecodeUncompressedImageInternal(DcmDataset& dataset,
391 DcmDataset& dataset, 389 unsigned int frame)
392 unsigned int frame)
393 { 390 {
394 ImageSource source; 391 ImageSource source;
395 source.Setup(dataset, frame); 392 source.Setup(dataset, frame);
396 393
397 394
398 /** 395 /**
399 * Resize the target image. 396 * Resize the target image.
400 **/ 397 **/
401 398
402 SetupImageBuffer(target, dataset); 399 std::auto_ptr<ImageAccessor> target(CreateImage(dataset));
403 400
404 if (source.GetWidth() != target.GetWidth() || 401 if (source.GetWidth() != target->GetWidth() ||
405 source.GetHeight() != target.GetHeight()) 402 source.GetHeight() != target->GetHeight())
406 { 403 {
407 throw OrthancException(ErrorCode_InternalError); 404 throw OrthancException(ErrorCode_InternalError);
408 } 405 }
409 406
410 407
411 /** 408 /**
412 * If the format of the DICOM buffer is natively supported, use a 409 * If the format of the DICOM buffer is natively supported, use a
413 * direct access to copy its values. 410 * direct access to copy its values.
414 **/ 411 **/
415 412
416 ImageAccessor targetAccessor(target.GetAccessor());
417 const DicomImageInformation& info = source.GetAccessor().GetInformation(); 413 const DicomImageInformation& info = source.GetAccessor().GetInformation();
418 414
419 bool fastVersionSuccess = false; 415 bool fastVersionSuccess = false;
420 PixelFormat sourceFormat; 416 PixelFormat sourceFormat;
421 if (!info.IsPlanar() && 417 if (!info.IsPlanar() &&
433 info.GetWidth(), 429 info.GetWidth(),
434 info.GetHeight(), 430 info.GetHeight(),
435 info.GetWidth() * GetBytesPerPixel(sourceFormat), 431 info.GetWidth() * GetBytesPerPixel(sourceFormat),
436 buffer + frame * frameSize); 432 buffer + frame * frameSize);
437 433
438 ImageProcessing::Convert(targetAccessor, sourceImage); 434 ImageProcessing::Convert(*target, sourceImage);
439 ImageProcessing::ShiftRight(targetAccessor, info.GetShift()); 435 ImageProcessing::ShiftRight(*target, info.GetShift());
440 fastVersionSuccess = true; 436 fastVersionSuccess = true;
441 } 437 }
442 } 438 }
443 catch (OrthancException&) 439 catch (OrthancException&)
444 { 440 {
451 * into the target image. 447 * into the target image.
452 **/ 448 **/
453 449
454 if (!fastVersionSuccess) 450 if (!fastVersionSuccess)
455 { 451 {
456 switch (target.GetFormat()) 452 switch (target->GetFormat())
457 { 453 {
458 case PixelFormat_RGB24: 454 case PixelFormat_RGB24:
459 case PixelFormat_RGBA32: 455 case PixelFormat_RGBA32:
460 case PixelFormat_Grayscale8: 456 case PixelFormat_Grayscale8:
461 CopyPixels<uint8_t>(targetAccessor, source.GetAccessor()); 457 CopyPixels<uint8_t>(*target, source.GetAccessor());
462 break; 458 break;
463 459
464 case PixelFormat_Grayscale16: 460 case PixelFormat_Grayscale16:
465 CopyPixels<uint16_t>(targetAccessor, source.GetAccessor()); 461 CopyPixels<uint16_t>(*target, source.GetAccessor());
466 break; 462 break;
467 463
468 case PixelFormat_SignedGrayscale16: 464 case PixelFormat_SignedGrayscale16:
469 CopyPixels<int16_t>(targetAccessor, source.GetAccessor()); 465 CopyPixels<int16_t>(*target, source.GetAccessor());
470 break; 466 break;
471 467
472 default: 468 default:
473 throw OrthancException(ErrorCode_InternalError); 469 throw OrthancException(ErrorCode_InternalError);
474 } 470 }
475 } 471 }
472
473 return target.release();
476 } 474 }
477 475
478 476
479 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 477 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
480 void DicomImageDecoder::DecodeJpegLossless(ImageBuffer& target, 478 ImageAccessor* DicomImageDecoder::DecodeJpegLossless(DcmDataset& dataset,
481 DcmDataset& dataset, 479 unsigned int frame)
482 unsigned int frame)
483 { 480 {
484 if (!IsJpegLossless(dataset)) 481 if (!IsJpegLossless(dataset))
485 { 482 {
486 throw OrthancException(ErrorCode_BadParameterType); 483 throw OrthancException(ErrorCode_BadParameterType);
487 } 484 }
498 (dataset.getOriginalXfer(), NULL, pixelSequence).good()) 495 (dataset.getOriginalXfer(), NULL, pixelSequence).good())
499 { 496 {
500 throw OrthancException(ErrorCode_BadFileFormat); 497 throw OrthancException(ErrorCode_BadFileFormat);
501 } 498 }
502 499
503 SetupImageBuffer(target, dataset); 500 std::auto_ptr<ImageAccessor> target(CreateImage(dataset));
504
505 ImageAccessor targetAccessor(target.GetAccessor());
506 501
507 /** 502 /**
508 * The "DJLSLosslessDecoder" and "DJLSNearLosslessDecoder" in DCMTK 503 * The "DJLSLosslessDecoder" and "DJLSNearLosslessDecoder" in DCMTK
509 * are exactly the same, except for the "supportedTransferSyntax()" 504 * are exactly the same, except for the "supportedTransferSyntax()"
510 * virtual function. 505 * virtual function.
516 511
517 Uint32 startFragment = 0; // Default 512 Uint32 startFragment = 0; // Default
518 OFString decompressedColorModel; // Out 513 OFString decompressedColorModel; // Out
519 DJ_RPLossless representationParameter; 514 DJ_RPLossless representationParameter;
520 OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, &parameters, 515 OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, &parameters,
521 &dataset, frame, startFragment, targetAccessor.GetBuffer(), 516 &dataset, frame, startFragment, target->GetBuffer(),
522 targetAccessor.GetSize(), decompressedColorModel); 517 target->GetSize(), decompressedColorModel);
523 518
524 if (!c.good()) 519 if (!c.good())
525 { 520 {
526 throw OrthancException(ErrorCode_InternalError); 521 throw OrthancException(ErrorCode_InternalError);
527 } 522 }
523
524 return target.release();
528 } 525 }
529 #endif 526 #endif
530 527
531 528
532 529
533 530
534 bool DicomImageDecoder::Decode(ImageBuffer& target, 531 ImageAccessor* DicomImageDecoder::Decode(ParsedDicomFile& dicom,
535 ParsedDicomFile& dicom, 532 unsigned int frame)
536 unsigned int frame)
537 { 533 {
538 DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset(); 534 DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset();
539 535
540 if (IsUncompressedImage(dataset)) 536 if (IsUncompressedImage(dataset))
541 { 537 {
542 DecodeUncompressedImage(target, dataset, frame); 538 return DecodeUncompressedImage(dataset, frame);
543 return true; 539 }
544 }
545
546 540
547 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1 541 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
548 if (IsJpegLossless(dataset)) 542 if (IsJpegLossless(dataset))
549 { 543 {
550 LOG(INFO) << "Decoding a JPEG-LS image"; 544 LOG(INFO) << "Decoding a JPEG-LS image";
551 DecodeJpegLossless(target, dataset, frame); 545 return DecodeJpegLossless(dataset, frame);
552 return true;
553 } 546 }
554 #endif 547 #endif
555 548
556 549
557 #if ORTHANC_JPEG_ENABLED == 1 550 #if ORTHANC_JPEG_ENABLED == 1
558 // TODO Implement this part to speed up JPEG decompression 551 // TODO Implement this part to speed up JPEG decompression
559 #endif 552 #endif
560
561 553
562 /** 554 /**
563 * This DICOM image format is not natively supported by 555 * This DICOM image format is not natively supported by
564 * Orthanc. As a last resort, try and decode it through 556 * Orthanc. As a last resort, try and decode it through
565 * DCMTK. This will result in higher memory consumption. This is 557 * DCMTK. This will result in higher memory consumption. This is
573 std::auto_ptr<DcmDataset> converted(dynamic_cast<DcmDataset*>(dataset.clone())); 565 std::auto_ptr<DcmDataset> converted(dynamic_cast<DcmDataset*>(dataset.clone()));
574 converted->chooseRepresentation(EXS_LittleEndianExplicit, NULL); 566 converted->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
575 567
576 if (converted->canWriteXfer(EXS_LittleEndianExplicit)) 568 if (converted->canWriteXfer(EXS_LittleEndianExplicit))
577 { 569 {
578 DecodeUncompressedImageInternal(target, *converted, frame); 570 return DecodeUncompressedImageInternal(*converted, frame);
579 return true; 571 }
580 } 572 }
581 } 573
582 574 return NULL;
583 return false;
584 } 575 }
585 576
586 577
587 static bool IsColorImage(PixelFormat format) 578 static bool IsColorImage(PixelFormat format)
588 { 579 {
589 return (format == PixelFormat_RGB24 || 580 return (format == PixelFormat_RGB24 ||
590 format == PixelFormat_RGBA32); 581 format == PixelFormat_RGBA32);
591 } 582 }
592 583
593 584
594 bool DicomImageDecoder::TruncateDecodedImage(ImageBuffer& target, 585 bool DicomImageDecoder::TruncateDecodedImage(std::auto_ptr<ImageAccessor>& image,
595 ImageBuffer& source,
596 PixelFormat format, 586 PixelFormat format,
597 bool allowColorConversion) 587 bool allowColorConversion)
598 { 588 {
599 // If specified, prevent the conversion between color and 589 // If specified, prevent the conversion between color and
600 // grayscale images 590 // grayscale images
601 bool isSourceColor = IsColorImage(source.GetFormat()); 591 bool isSourceColor = IsColorImage(image->GetFormat());
602 bool isTargetColor = IsColorImage(format); 592 bool isTargetColor = IsColorImage(format);
603 593
604 if (!allowColorConversion) 594 if (!allowColorConversion)
605 { 595 {
606 if (isSourceColor ^ isTargetColor) 596 if (isSourceColor ^ isTargetColor)
607 { 597 {
608 return false; 598 return false;
609 } 599 }
610 } 600 }
611 601
612 if (source.GetFormat() == format) 602 if (image->GetFormat() != format)
613 { 603 {
614 // No conversion is required, return the temporary image 604 // A conversion is required
615 target.AcquireOwnership(source); 605 std::auto_ptr<ImageAccessor> target(new Image(format, image->GetWidth(), image->GetHeight()));
616 return true; 606 ImageProcessing::Convert(*target, *image);
617 } 607 image = target;
618 608 }
619 target.SetFormat(format);
620 target.SetWidth(source.GetWidth());
621 target.SetHeight(source.GetHeight());
622
623 ImageAccessor targetAccessor(target.GetAccessor());
624 ImageAccessor sourceAccessor(source.GetAccessor());
625 ImageProcessing::Convert(targetAccessor, sourceAccessor);
626 609
627 return true; 610 return true;
628 } 611 }
629 612
630 613
631 bool DicomImageDecoder::PreviewDecodedImage(ImageBuffer& target, 614 bool DicomImageDecoder::PreviewDecodedImage(std::auto_ptr<ImageAccessor>& image)
632 ImageBuffer& source) 615 {
633 { 616 switch (image->GetFormat())
634 switch (source.GetFormat())
635 { 617 {
636 case PixelFormat_RGB24: 618 case PixelFormat_RGB24:
637 { 619 {
638 // Directly return color images (RGB) 620 // Directly return color images without modification (RGB)
639 target.AcquireOwnership(source);
640 return true; 621 return true;
641 } 622 }
642 623
643 case PixelFormat_Grayscale8: 624 case PixelFormat_Grayscale8:
644 case PixelFormat_Grayscale16: 625 case PixelFormat_Grayscale16:
645 case PixelFormat_SignedGrayscale16: 626 case PixelFormat_SignedGrayscale16:
646 { 627 {
647 // Grayscale image: Stretch its dynamics to the [0,255] range 628 // Grayscale image: Stretch its dynamics to the [0,255] range
648 target.SetFormat(PixelFormat_Grayscale8);
649 target.SetWidth(source.GetWidth());
650 target.SetHeight(source.GetHeight());
651
652 ImageAccessor targetAccessor(target.GetAccessor());
653 ImageAccessor sourceAccessor(source.GetAccessor());
654
655 int64_t a, b; 629 int64_t a, b;
656 ImageProcessing::GetMinMaxValue(a, b, sourceAccessor); 630 ImageProcessing::GetMinMaxValue(a, b, *image);
657 631
658 if (a == b) 632 if (a == b)
659 { 633 {
660 ImageProcessing::Set(targetAccessor, 0); 634 ImageProcessing::Set(*image, 0);
661 } 635 }
662 else 636 else
663 { 637 {
664 ImageProcessing::ShiftScale(sourceAccessor, static_cast<float>(-a), 255.0f / static_cast<float>(b - a)); 638 ImageProcessing::ShiftScale(*image, static_cast<float>(-a), 255.0f / static_cast<float>(b - a));
665 639 }
666 if (source.GetFormat() == PixelFormat_Grayscale8) 640
667 { 641 // If the source image is not grayscale 8bpp, convert it
668 target.AcquireOwnership(source); 642 if (image->GetFormat() != PixelFormat_Grayscale8)
669 } 643 {
670 else 644 std::auto_ptr<ImageAccessor> target(new Image(PixelFormat_Grayscale8, image->GetWidth(), image->GetHeight()));
671 { 645 ImageProcessing::Convert(*target, *image);
672 ImageProcessing::Convert(targetAccessor, sourceAccessor); 646 image = target;
673 }
674 } 647 }
675 648
676 return true; 649 return true;
677 } 650 }
678 651