comparison OrthancServer/OrthancRestApi/OrthancRestResources.cpp @ 3685:2cc34837d694

rendering of RGB24
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 24 Feb 2020 17:58:59 +0100
parents 12253ddefe5a
children a79aecf1f9ae
comparison
equal deleted inserted replaced
3684:3971ec6b1f72 3685:2cc34837d694
38 #include "../../Core/DicomFormat/DicomImageInformation.h" 38 #include "../../Core/DicomFormat/DicomImageInformation.h"
39 #include "../../Core/DicomParsing/DicomWebJsonVisitor.h" 39 #include "../../Core/DicomParsing/DicomWebJsonVisitor.h"
40 #include "../../Core/DicomParsing/FromDcmtkBridge.h" 40 #include "../../Core/DicomParsing/FromDcmtkBridge.h"
41 #include "../../Core/DicomParsing/Internals/DicomImageDecoder.h" 41 #include "../../Core/DicomParsing/Internals/DicomImageDecoder.h"
42 #include "../../Core/HttpServer/HttpContentNegociation.h" 42 #include "../../Core/HttpServer/HttpContentNegociation.h"
43 #include "../../Core/Images/Image.h"
44 #include "../../Core/Images/ImageProcessing.h"
43 #include "../../Core/Logging.h" 45 #include "../../Core/Logging.h"
44 #include "../DefaultDicomImageDecoder.h" 46 #include "../DefaultDicomImageDecoder.h"
45 #include "../OrthancConfiguration.h" 47 #include "../OrthancConfiguration.h"
46 #include "../Search/DatabaseLookup.h" 48 #include "../Search/DatabaseLookup.h"
47 #include "../ServerContext.h" 49 #include "../ServerContext.h"
603 return; 605 return;
604 } 606 }
605 607
606 handler.Handle(call, decoded, dicom); 608 handler.Handle(call, decoded, dicom);
607 } 609 }
610
611
612 static void DefaultHandler(RestApiGetCall& call,
613 std::auto_ptr<ImageAccessor>& decoded,
614 ImageExtractionMode mode,
615 bool invert)
616 {
617 ImageToEncode image(decoded, mode, invert);
618
619 HttpContentNegociation negociation;
620 EncodePng png(image);
621 negociation.Register(MIME_PNG, png);
622
623 EncodeJpeg jpeg(image, call);
624 negociation.Register(MIME_JPEG, jpeg);
625
626 EncodePam pam(image);
627 negociation.Register(MIME_PAM, pam);
628
629 if (negociation.Apply(call.GetHttpHeaders()))
630 {
631 image.Answer(call.GetOutput());
632 }
633 }
608 }; 634 };
609 635
610 636
611 class GetImageHandler : public IDecodedFrameHandler 637 class GetImageHandler : public IDecodedFrameHandler
612 { 638 {
629 { 655 {
630 DicomImageInformation info(dicom); 656 DicomImageInformation info(dicom);
631 invert = (info.GetPhotometricInterpretation() == PhotometricInterpretation_Monochrome1); 657 invert = (info.GetPhotometricInterpretation() == PhotometricInterpretation_Monochrome1);
632 } 658 }
633 659
634 ImageToEncode image(decoded, mode_, invert); 660 DefaultHandler(call, decoded, mode_, invert);
635
636 HttpContentNegociation negociation;
637 EncodePng png(image);
638 negociation.Register(MIME_PNG, png);
639
640 EncodeJpeg jpeg(image, call);
641 negociation.Register(MIME_JPEG, jpeg);
642
643 EncodePam pam(image);
644 negociation.Register(MIME_PAM, pam);
645
646 if (negociation.Apply(call.GetHttpHeaders()))
647 {
648 image.Answer(call.GetOutput());
649 }
650 } 661 }
651 662
652 virtual bool RequiresDicomTags() const ORTHANC_OVERRIDE 663 virtual bool RequiresDicomTags() const ORTHANC_OVERRIDE
653 { 664 {
654 return mode_ == ImageExtractionMode_Preview; 665 return mode_ == ImageExtractionMode_Preview;
664 float& windowWidth, 675 float& windowWidth,
665 float& rescaleSlope, 676 float& rescaleSlope,
666 float& rescaleIntercept, 677 float& rescaleIntercept,
667 bool& invert) 678 bool& invert)
668 { 679 {
669 DicomImageInformation info(dicom);
670
671 windowWidth = static_cast<float>(1 << info.GetBitsStored());
672 windowCenter = windowWidth / 2.0f;
673 rescaleSlope = 1.0f;
674 rescaleIntercept = 0.0f;
675 invert = false;
676
677 if (dicom.HasTag(Orthanc::DICOM_TAG_WINDOW_CENTER) &&
678 dicom.HasTag(Orthanc::DICOM_TAG_WINDOW_WIDTH))
679 {
680 dicom.ParseFloat(windowCenter, Orthanc::DICOM_TAG_WINDOW_CENTER);
681 dicom.ParseFloat(windowWidth, Orthanc::DICOM_TAG_WINDOW_WIDTH);
682 }
683
684 if (dicom.HasTag(Orthanc::DICOM_TAG_RESCALE_SLOPE) &&
685 dicom.HasTag(Orthanc::DICOM_TAG_RESCALE_INTERCEPT))
686 {
687 dicom.ParseFloat(rescaleSlope, Orthanc::DICOM_TAG_RESCALE_SLOPE);
688 dicom.ParseFloat(rescaleIntercept, Orthanc::DICOM_TAG_RESCALE_INTERCEPT);
689 }
690
691 invert = (info.GetPhotometricInterpretation() == PhotometricInterpretation_Monochrome1);
692 } 680 }
693 681
694 public: 682 public:
695 virtual void Handle(RestApiGetCall& call, 683 virtual void Handle(RestApiGetCall& call,
696 std::auto_ptr<ImageAccessor>& decoded, 684 std::auto_ptr<ImageAccessor>& decoded,
697 const DicomMap& dicom) ORTHANC_OVERRIDE 685 const DicomMap& dicom) ORTHANC_OVERRIDE
698 { 686 {
699 // TODO 687 static const char* ARG_WINDOW_CENTER = "window-center";
688 static const char* ARG_WINDOW_WIDTH = "window-width";
689 static const char* ARG_MAX_WIDTH = "max-width";
690 static const char* ARG_MAX_HEIGHT = "max-height";
691 static const char* ARG_SMOOTH = "smooth";
692
693 DicomImageInformation info(dicom);
694
695 const bool invert = (info.GetPhotometricInterpretation() == PhotometricInterpretation_Monochrome1);
696
697 float rescaleSlope = 1.0f;
698 float rescaleIntercept = 0.0f;
699
700 if (dicom.HasTag(Orthanc::DICOM_TAG_RESCALE_SLOPE) &&
701 dicom.HasTag(Orthanc::DICOM_TAG_RESCALE_INTERCEPT))
702 {
703 dicom.ParseFloat(rescaleSlope, Orthanc::DICOM_TAG_RESCALE_SLOPE);
704 dicom.ParseFloat(rescaleIntercept, Orthanc::DICOM_TAG_RESCALE_INTERCEPT);
705 }
706
707 float windowWidth = static_cast<float>(1 << info.GetBitsStored());
708 float windowCenter = windowWidth / 2.0f;
709
710 if (dicom.HasTag(Orthanc::DICOM_TAG_WINDOW_CENTER) &&
711 dicom.HasTag(Orthanc::DICOM_TAG_WINDOW_WIDTH))
712 {
713 dicom.ParseFloat(windowCenter, Orthanc::DICOM_TAG_WINDOW_CENTER);
714 dicom.ParseFloat(windowWidth, Orthanc::DICOM_TAG_WINDOW_WIDTH);
715 }
716
717 if (call.HasArgument(ARG_WINDOW_WIDTH))
718 {
719 try
720 {
721 windowWidth = boost::lexical_cast<float>(call.GetArgument(ARG_WINDOW_WIDTH, ""));
722 }
723 catch (boost::bad_lexical_cast&)
724 {
725 throw OrthancException(ErrorCode_ParameterOutOfRange,
726 "Bad value for argument: " + std::string(ARG_WINDOW_WIDTH));
727 }
728 }
729
730 if (call.HasArgument(ARG_WINDOW_CENTER))
731 {
732 try
733 {
734 windowCenter = boost::lexical_cast<float>(call.GetArgument(ARG_WINDOW_CENTER, ""));
735 }
736 catch (boost::bad_lexical_cast&)
737 {
738 throw OrthancException(ErrorCode_ParameterOutOfRange,
739 "Bad value for argument: " + std::string(ARG_WINDOW_CENTER));
740 }
741 }
742
743 unsigned int maxWidth = 0;
744 unsigned int maxHeight = 0;
745
746 if (call.HasArgument(ARG_MAX_WIDTH))
747 {
748 try
749 {
750 int tmp = boost::lexical_cast<int>(call.GetArgument(ARG_MAX_WIDTH, ""));
751 if (tmp < 0)
752 {
753 throw OrthancException(ErrorCode_ParameterOutOfRange,
754 "Argument cannot be negative: " + std::string(ARG_MAX_WIDTH));
755 }
756 else
757 {
758 maxWidth = static_cast<unsigned int>(tmp);
759 }
760 }
761 catch (boost::bad_lexical_cast&)
762 {
763 throw OrthancException(ErrorCode_ParameterOutOfRange,
764 "Bad value for argument: " + std::string(ARG_MAX_WIDTH));
765 }
766 }
767
768 if (call.HasArgument(ARG_MAX_HEIGHT))
769 {
770 try
771 {
772 int tmp = boost::lexical_cast<int>(call.GetArgument(ARG_MAX_HEIGHT, ""));
773 if (tmp < 0)
774 {
775 throw OrthancException(ErrorCode_ParameterOutOfRange,
776 "Argument cannot be negative: " + std::string(ARG_MAX_HEIGHT));
777 }
778 else
779 {
780 maxHeight = static_cast<unsigned int>(tmp);
781 }
782 }
783 catch (boost::bad_lexical_cast&)
784 {
785 throw OrthancException(ErrorCode_ParameterOutOfRange,
786 "Bad value for argument: " + std::string(ARG_MAX_HEIGHT));
787 }
788 }
789
790 bool smooth = true;
791
792 if (call.HasArgument(ARG_SMOOTH))
793 {
794 std::string value = call.GetArgument(ARG_SMOOTH, "");
795 if (value == "0" ||
796 value == "false")
797 {
798 smooth = false;
799 }
800 else if (value == "1" ||
801 value == "true")
802 {
803 smooth = true;
804 }
805 else
806 {
807 throw OrthancException(ErrorCode_ParameterOutOfRange,
808 "Argument must be Boolean: " + std::string(ARG_SMOOTH));
809 }
810 }
811
812 if (decoded->GetFormat() == PixelFormat_RGB24)
813 {
814 if ((maxWidth == 0 &&
815 maxHeight == 0) ||
816 decoded->GetWidth() == 0 ||
817 decoded->GetHeight() == 0)
818 {
819 DefaultHandler(call, decoded, ImageExtractionMode_Preview, false);
820 }
821 else
822 {
823 float ratio = 1;
824
825 if (maxWidth != 0)
826 {
827 ratio = static_cast<float>(maxWidth) / static_cast<float>(decoded->GetWidth());
828 }
829
830 if (maxHeight != 0)
831 {
832 float ratioY = static_cast<float>(maxHeight) / static_cast<float>(decoded->GetHeight());
833 if (ratioY < ratio)
834 {
835 ratio = ratioY;
836 }
837 }
838
839 unsigned int width = boost::math::iround(ratio * static_cast<float>(decoded->GetWidth()));
840 unsigned int height = boost::math::iround(ratio * static_cast<float>(decoded->GetHeight()));
841
842 std::auto_ptr<ImageAccessor> rescaled(new Image(PixelFormat_RGB24, width, height, false));
843 if (smooth &&
844 ratio < 1)
845 {
846 ImageProcessing::SmoothGaussian5x5(*decoded);
847 }
848 ImageProcessing::Resize(*rescaled, *decoded);
849 DefaultHandler(call, rescaled, ImageExtractionMode_Preview, false);
850 }
851 }
700 } 852 }
701 853
702 virtual bool RequiresDicomTags() const ORTHANC_OVERRIDE 854 virtual bool RequiresDicomTags() const ORTHANC_OVERRIDE
703 { 855 {
704 return true; 856 return true;