Mercurial > hg > orthanc
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; |