comparison Core/DicomParsing/Internals/DicomImageDecoder.cpp @ 2423:5a7c5c541a1d

Built-in decoding of palette images
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 09 Oct 2017 21:58:08 +0200
parents 7e217a1cc63f
children a6fab385b89e
comparison
equal deleted inserted replaced
2422:b340f0a9022c 2423:5a7c5c541a1d
94 # include "../../Images/JpegWriter.h" 94 # include "../../Images/JpegWriter.h"
95 #endif 95 #endif
96 96
97 #include <boost/lexical_cast.hpp> 97 #include <boost/lexical_cast.hpp>
98 98
99 #include <dcmtk/dcmdata/dcdeftag.h>
99 #include <dcmtk/dcmdata/dcfilefo.h> 100 #include <dcmtk/dcmdata/dcfilefo.h>
100 #include <dcmtk/dcmdata/dcrleccd.h> 101 #include <dcmtk/dcmdata/dcrleccd.h>
101 #include <dcmtk/dcmdata/dcrlecp.h> 102 #include <dcmtk/dcmdata/dcrlecp.h>
103 #include <dcmtk/dcmdata/dcrlerp.h>
102 104
103 #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1 105 #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1
106 # include <dcmtk/dcmjpeg/djrplol.h>
104 # include <dcmtk/dcmjpls/djcodecd.h> 107 # include <dcmtk/dcmjpls/djcodecd.h>
105 # include <dcmtk/dcmjpls/djcparam.h> 108 # include <dcmtk/dcmjpls/djcparam.h>
106 # include <dcmtk/dcmjpeg/djrplol.h> 109 # include <dcmtk/dcmjpls/djrparam.h>
107 #endif 110 #endif
108 111
109 #if ORTHANC_ENABLE_DCMTK_JPEG == 1 112 #if ORTHANC_ENABLE_DCMTK_JPEG == 1
110 # include <dcmtk/dcmjpeg/djcodecd.h> 113 # include <dcmtk/dcmjpeg/djcodecd.h>
111 # include <dcmtk/dcmjpeg/djcparam.h> 114 # include <dcmtk/dcmjpeg/djcparam.h>
113 # include <dcmtk/dcmjpeg/djdecext.h> 116 # include <dcmtk/dcmjpeg/djdecext.h>
114 # include <dcmtk/dcmjpeg/djdeclol.h> 117 # include <dcmtk/dcmjpeg/djdeclol.h>
115 # include <dcmtk/dcmjpeg/djdecpro.h> 118 # include <dcmtk/dcmjpeg/djdecpro.h>
116 # include <dcmtk/dcmjpeg/djdecsps.h> 119 # include <dcmtk/dcmjpeg/djdecsps.h>
117 # include <dcmtk/dcmjpeg/djdecsv1.h> 120 # include <dcmtk/dcmjpeg/djdecsv1.h>
121 # include <dcmtk/dcmjpeg/djrploss.h>
118 #endif 122 #endif
119 123
120 #if DCMTK_VERSION_NUMBER <= 360 124 #if DCMTK_VERSION_NUMBER <= 360
121 # define EXS_JPEGProcess1 EXS_JPEGProcess1TransferSyntax 125 # define EXS_JPEGProcess1 EXS_JPEGProcess1TransferSyntax
122 # define EXS_JPEGProcess2_4 EXS_JPEGProcess2_4TransferSyntax 126 # define EXS_JPEGProcess2_4 EXS_JPEGProcess2_4TransferSyntax
379 } 383 }
380 } 384 }
381 } 385 }
382 386
383 387
388 static ImageAccessor* DecodeLookupTable(std::auto_ptr<ImageAccessor>& target,
389 const DicomImageInformation& info,
390 DcmDataset& dataset,
391 const uint8_t* pixelData,
392 unsigned long pixelLength)
393 {
394 LOG(INFO) << "Decoding a lookup table";
395
396 OFString r, g, b;
397 PixelFormat format;
398 const uint16_t* lutRed = NULL;
399 const uint16_t* lutGreen = NULL;
400 const uint16_t* lutBlue = NULL;
401 unsigned long rc = 0;
402 unsigned long gc = 0;
403 unsigned long bc = 0;
404
405 if (pixelData == NULL &&
406 !dataset.findAndGetUint8Array(DCM_PixelData, pixelData, &pixelLength).good())
407 {
408 throw OrthancException(ErrorCode_NotImplemented);
409 }
410
411 if (info.IsPlanar() ||
412 info.GetNumberOfFrames() != 1 ||
413 !info.ExtractPixelFormat(format, false) ||
414 !dataset.findAndGetOFStringArray(DCM_BluePaletteColorLookupTableDescriptor, b).good() ||
415 !dataset.findAndGetOFStringArray(DCM_GreenPaletteColorLookupTableDescriptor, g).good() ||
416 !dataset.findAndGetOFStringArray(DCM_RedPaletteColorLookupTableDescriptor, r).good() ||
417 !dataset.findAndGetUint16Array(DCM_BluePaletteColorLookupTableData, lutBlue, &bc).good() ||
418 !dataset.findAndGetUint16Array(DCM_GreenPaletteColorLookupTableData, lutGreen, &gc).good() ||
419 !dataset.findAndGetUint16Array(DCM_RedPaletteColorLookupTableData, lutRed, &rc).good() ||
420 r != g ||
421 r != b ||
422 g != b ||
423 lutRed == NULL ||
424 lutGreen == NULL ||
425 lutBlue == NULL ||
426 pixelData == NULL)
427 {
428 throw OrthancException(ErrorCode_NotImplemented);
429 }
430
431 switch (format)
432 {
433 case PixelFormat_RGB24:
434 {
435 if (r != "256\\0\\16" ||
436 rc != 256 ||
437 gc != 256 ||
438 bc != 256 ||
439 pixelLength != target->GetWidth() * target->GetHeight())
440 {
441 throw OrthancException(ErrorCode_NotImplemented);
442 }
443
444 const uint8_t* source = reinterpret_cast<const uint8_t*>(pixelData);
445
446 for (unsigned int y = 0; y < target->GetHeight(); y++)
447 {
448 uint8_t* p = reinterpret_cast<uint8_t*>(target->GetRow(y));
449
450 for (unsigned int x = 0; x < target->GetWidth(); x++)
451 {
452 p[0] = lutRed[*source] >> 8;
453 p[1] = lutGreen[*source] >> 8;
454 p[2] = lutBlue[*source] >> 8;
455 source++;
456 p += 3;
457 }
458 }
459
460 return target.release();
461 }
462
463 case PixelFormat_RGB48:
464 {
465 if (r != "0\\0\\16" ||
466 rc != 65536 ||
467 gc != 65536 ||
468 bc != 65536 ||
469 pixelLength != 2 * target->GetWidth() * target->GetHeight())
470 {
471 throw OrthancException(ErrorCode_NotImplemented);
472 }
473
474 const uint16_t* source = reinterpret_cast<const uint16_t*>(pixelData);
475
476 for (unsigned int y = 0; y < target->GetHeight(); y++)
477 {
478 uint16_t* p = reinterpret_cast<uint16_t*>(target->GetRow(y));
479
480 for (unsigned int x = 0; x < target->GetWidth(); x++)
481 {
482 p[0] = lutRed[*source];
483 p[1] = lutGreen[*source];
484 p[2] = lutBlue[*source];
485 source++;
486 p += 3;
487 }
488 }
489
490 return target.release();
491 }
492
493 default:
494 break;
495 }
496
497 throw OrthancException(ErrorCode_InternalError);
498 }
499
500
384 ImageAccessor* DicomImageDecoder::DecodeUncompressedImage(DcmDataset& dataset, 501 ImageAccessor* DicomImageDecoder::DecodeUncompressedImage(DcmDataset& dataset,
385 unsigned int frame) 502 unsigned int frame)
386 { 503 {
504 /**
505 * Create the target image.
506 **/
507
508 std::auto_ptr<ImageAccessor> target(CreateImage(dataset, false));
509
387 ImageSource source; 510 ImageSource source;
388 source.Setup(dataset, frame); 511 source.Setup(dataset, frame);
389 512
390
391 /**
392 * Resize the target image.
393 **/
394
395 std::auto_ptr<ImageAccessor> target(CreateImage(dataset, false));
396
397 if (source.GetWidth() != target->GetWidth() || 513 if (source.GetWidth() != target->GetWidth() ||
398 source.GetHeight() != target->GetHeight()) 514 source.GetHeight() != target->GetHeight())
399 { 515 {
400 throw OrthancException(ErrorCode_InternalError); 516 throw OrthancException(ErrorCode_InternalError);
401 } 517 }
518
519
520 /**
521 * Deal with lookup tables
522 **/
523
524 const DicomImageInformation& info = source.GetAccessor().GetInformation();
525
526 if (info.GetPhotometricInterpretation() == PhotometricInterpretation_Palette)
527 {
528 return DecodeLookupTable(target, info, dataset, NULL, 0);
529 }
402 530
403 531
404 /** 532 /**
405 * If the format of the DICOM buffer is natively supported, use a 533 * If the format of the DICOM buffer is natively supported, use a
406 * direct access to copy its values. 534 * direct access to copy its values.
407 **/ 535 **/
408
409 const DicomImageInformation& info = source.GetAccessor().GetInformation();
410 536
411 bool fastVersionSuccess = false; 537 bool fastVersionSuccess = false;
412 PixelFormat sourceFormat; 538 PixelFormat sourceFormat;
413 if (!info.IsPlanar() && 539 if (!info.IsPlanar() &&
414 info.ExtractPixelFormat(sourceFormat, false)) 540 info.ExtractPixelFormat(sourceFormat, false))
468 594
469 return target.release(); 595 return target.release();
470 } 596 }
471 597
472 598
473 ImageAccessor* DicomImageDecoder::ApplyCodec(const DcmCodec& codec, 599 ImageAccessor* DicomImageDecoder::ApplyCodec
474 const DcmCodecParameter& parameters, 600 (const DcmCodec& codec,
475 DcmDataset& dataset, 601 const DcmCodecParameter& parameters,
476 unsigned int frame) 602 const DcmRepresentationParameter& representationParameter,
603 DcmDataset& dataset,
604 unsigned int frame)
477 { 605 {
478 DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset); 606 DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset);
479 if (pixelSequence == NULL) 607 if (pixelSequence == NULL)
480 { 608 {
481 throw OrthancException(ErrorCode_BadFileFormat); 609 throw OrthancException(ErrorCode_BadFileFormat);
482 } 610 }
483 611
612 DicomMap m;
613 FromDcmtkBridge::ExtractDicomSummary(m, dataset);
614 DicomImageInformation info(m);
615
484 std::auto_ptr<ImageAccessor> target(CreateImage(dataset, true)); 616 std::auto_ptr<ImageAccessor> target(CreateImage(dataset, true));
485 617
486 Uint32 startFragment = 0; // Default 618 Uint32 startFragment = 0; // Default
487 OFString decompressedColorModel; // Out 619 OFString decompressedColorModel; // Out
488 DJ_RPLossless representationParameter; 620
489 OFCondition c = codec.decodeFrame(&representationParameter, 621 OFCondition c;
490 pixelSequence, &parameters, 622
491 &dataset, frame, startFragment, target->GetBuffer(), 623 if (info.GetPhotometricInterpretation() == PhotometricInterpretation_Palette &&
492 target->GetSize(), decompressedColorModel); 624 info.GetChannelCount() == 1)
493 625 {
494 if (c.good()) 626 std::string uncompressed;
495 { 627 uncompressed.resize(info.GetWidth() * info.GetHeight() * info.GetBytesPerValue());
496 return target.release(); 628
629 if (uncompressed.size() == 0 ||
630 !codec.decodeFrame(&representationParameter,
631 pixelSequence, &parameters,
632 &dataset, frame, startFragment, &uncompressed[0],
633 uncompressed.size(), decompressedColorModel).good())
634 {
635 LOG(ERROR) << "Cannot decode a palette image";
636 throw OrthancException(ErrorCode_BadFileFormat);
637 }
638
639 return DecodeLookupTable(target, info, dataset,
640 reinterpret_cast<const uint8_t*>(uncompressed.c_str()),
641 uncompressed.size());
497 } 642 }
498 else 643 else
499 { 644 {
500 LOG(ERROR) << "Cannot decode an image"; 645 if (!codec.decodeFrame(&representationParameter,
501 throw OrthancException(ErrorCode_BadFileFormat); 646 pixelSequence, &parameters,
647 &dataset, frame, startFragment, target->GetBuffer(),
648 target->GetSize(), decompressedColorModel).good())
649 {
650 LOG(ERROR) << "Cannot decode a non-palette image";
651 throw OrthancException(ErrorCode_BadFileFormat);
652 }
653
654 return target.release();
502 } 655 }
503 } 656 }
504 657
505 658
506 ImageAccessor* DicomImageDecoder::Decode(ParsedDicomFile& dicom, 659 ImageAccessor* DicomImageDecoder::Decode(ParsedDicomFile& dicom,
530 683
531 if (syntax == EXS_JPEGLSLossless || 684 if (syntax == EXS_JPEGLSLossless ||
532 syntax == EXS_JPEGLSLossy) 685 syntax == EXS_JPEGLSLossy)
533 { 686 {
534 DJLSCodecParameter parameters; 687 DJLSCodecParameter parameters;
688 DJLSRepresentationParameter representationParameter;
535 std::auto_ptr<DJLSDecoderBase> decoder; 689 std::auto_ptr<DJLSDecoderBase> decoder;
536 690
537 switch (syntax) 691 switch (syntax)
538 { 692 {
539 case EXS_JPEGLSLossless: 693 case EXS_JPEGLSLossless:
548 702
549 default: 703 default:
550 throw OrthancException(ErrorCode_InternalError); 704 throw OrthancException(ErrorCode_InternalError);
551 } 705 }
552 706
553 return ApplyCodec(*decoder, parameters, dataset, frame); 707 return ApplyCodec(*decoder, parameters, representationParameter, dataset, frame);
554 } 708 }
555 #endif 709 #endif
556 710
557 711
558 #if ORTHANC_ENABLE_DCMTK_JPEG == 1 712 #if ORTHANC_ENABLE_DCMTK_JPEG == 1
571 DJCodecParameter parameters( 725 DJCodecParameter parameters(
572 ECC_lossyYCbCr, // Mode for color conversion for compression, Unused for decompression 726 ECC_lossyYCbCr, // Mode for color conversion for compression, Unused for decompression
573 EDC_photometricInterpretation, // Perform color space conversion from YCbCr to RGB if DICOM photometric interpretation indicates YCbCr 727 EDC_photometricInterpretation, // Perform color space conversion from YCbCr to RGB if DICOM photometric interpretation indicates YCbCr
574 EUC_default, // Mode for UID creation, unused for decompression 728 EUC_default, // Mode for UID creation, unused for decompression
575 EPC_default); // Automatically determine whether color-by-plane is required from the SOP Class UID and decompressed photometric interpretation 729 EPC_default); // Automatically determine whether color-by-plane is required from the SOP Class UID and decompressed photometric interpretation
730 DJ_RPLossy representationParameter;
576 std::auto_ptr<DJCodecDecoder> decoder; 731 std::auto_ptr<DJCodecDecoder> decoder;
577 732
578 switch (syntax) 733 switch (syntax)
579 { 734 {
580 case EXS_JPEGProcess1: 735 case EXS_JPEGProcess1:
609 764
610 default: 765 default:
611 throw OrthancException(ErrorCode_InternalError); 766 throw OrthancException(ErrorCode_InternalError);
612 } 767 }
613 768
614 return ApplyCodec(*decoder, parameters, dataset, frame); 769 return ApplyCodec(*decoder, parameters, representationParameter, dataset, frame);
615 } 770 }
616 #endif 771 #endif
617 772
618 773
619 if (syntax == EXS_RLELossless) 774 if (syntax == EXS_RLELossless)
620 { 775 {
621 LOG(INFO) << "Decoding a RLE lossless DICOM image"; 776 LOG(INFO) << "Decoding a RLE lossless DICOM image";
622 DcmRLECodecParameter parameters; 777 DcmRLECodecParameter parameters;
623 DcmRLECodecDecoder decoder; 778 DcmRLECodecDecoder decoder;
624 return ApplyCodec(decoder, parameters, dataset, frame); 779 DcmRLERepresentationParameter representationParameter;
780 return ApplyCodec(decoder, parameters, representationParameter, dataset, frame);
625 } 781 }
626 782
627 783
628 /** 784 /**
629 * This DICOM image format is not natively supported by 785 * This DICOM image format is not natively supported by
676 } 832 }
677 833
678 if (image->GetFormat() != format) 834 if (image->GetFormat() != format)
679 { 835 {
680 // A conversion is required 836 // A conversion is required
681 std::auto_ptr<ImageAccessor> target(new Image(format, image->GetWidth(), image->GetHeight(), false)); 837 std::auto_ptr<ImageAccessor> target
838 (new Image(format, image->GetWidth(), image->GetHeight(), false));
682 ImageProcessing::Convert(*target, *image); 839 ImageProcessing::Convert(*target, *image);
683 image = target; 840 image = target;
684 } 841 }
685 842
686 return true; 843 return true;
692 switch (image->GetFormat()) 849 switch (image->GetFormat())
693 { 850 {
694 case PixelFormat_RGB24: 851 case PixelFormat_RGB24:
695 { 852 {
696 // Directly return color images without modification (RGB) 853 // Directly return color images without modification (RGB)
854 return true;
855 }
856
857 case PixelFormat_RGB48:
858 {
859 std::auto_ptr<ImageAccessor> target
860 (new Image(PixelFormat_RGB24, image->GetWidth(), image->GetHeight(), false));
861 ImageProcessing::Convert(*target, *image);
862 image = target;
697 return true; 863 return true;
698 } 864 }
699 865
700 case PixelFormat_Grayscale8: 866 case PixelFormat_Grayscale8:
701 case PixelFormat_Grayscale16: 867 case PixelFormat_Grayscale16:
709 { 875 {
710 ImageProcessing::Set(*image, 0); 876 ImageProcessing::Set(*image, 0);
711 } 877 }
712 else 878 else
713 { 879 {
714 ImageProcessing::ShiftScale(*image, static_cast<float>(-a), 255.0f / static_cast<float>(b - a)); 880 ImageProcessing::ShiftScale(*image, static_cast<float>(-a),
881 255.0f / static_cast<float>(b - a));
715 } 882 }
716 883
717 // If the source image is not grayscale 8bpp, convert it 884 // If the source image is not grayscale 8bpp, convert it
718 if (image->GetFormat() != PixelFormat_Grayscale8) 885 if (image->GetFormat() != PixelFormat_Grayscale8)
719 { 886 {
720 std::auto_ptr<ImageAccessor> target(new Image(PixelFormat_Grayscale8, image->GetWidth(), image->GetHeight(), false)); 887 std::auto_ptr<ImageAccessor> target
888 (new Image(PixelFormat_Grayscale8, image->GetWidth(), image->GetHeight(), false));
721 ImageProcessing::Convert(*target, *image); 889 ImageProcessing::Convert(*target, *image);
722 image = target; 890 image = target;
723 } 891 }
724 892
725 return true; 893 return true;