Mercurial > hg > orthanc
comparison OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp @ 4777:3b78ba359db3
Support detection of windowing and rescale in Philips multiframe images
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 30 Aug 2021 11:41:05 +0200 |
parents | 9da6ca57a977 |
children | 61da49321754 434843934307 ae643f664628 |
comparison
equal
deleted
inserted
replaced
4776:79d4e155592b | 4777:3b78ba359db3 |
---|---|
664 } | 664 } |
665 | 665 |
666 // "dicom" is non-NULL iff. "RequiresDicomTags() == true" | 666 // "dicom" is non-NULL iff. "RequiresDicomTags() == true" |
667 virtual void Handle(RestApiGetCall& call, | 667 virtual void Handle(RestApiGetCall& call, |
668 std::unique_ptr<ImageAccessor>& decoded, | 668 std::unique_ptr<ImageAccessor>& decoded, |
669 const ParsedDicomFile* dicom) = 0; | 669 const ParsedDicomFile* dicom, |
670 unsigned int frame) = 0; | |
670 | 671 |
671 virtual bool RequiresDicomTags() const = 0; | 672 virtual bool RequiresDicomTags() const = 0; |
672 | 673 |
673 static void Apply(RestApiGetCall& call, | 674 static void Apply(RestApiGetCall& call, |
674 IDecodedFrameHandler& handler, | 675 IDecodedFrameHandler& handler, |
797 * Retrieve a summary of the DICOM tags, which is | 798 * Retrieve a summary of the DICOM tags, which is |
798 * necessary to deal with MONOCHROME1 photometric | 799 * necessary to deal with MONOCHROME1 photometric |
799 * interpretation, and with windowing parameters. | 800 * interpretation, and with windowing parameters. |
800 **/ | 801 **/ |
801 ServerContext::DicomCacheLocker locker(context, publicId); | 802 ServerContext::DicomCacheLocker locker(context, publicId); |
802 handler.Handle(call, decoded, &locker.GetDicom()); | 803 handler.Handle(call, decoded, &locker.GetDicom(), frame); |
803 } | 804 } |
804 else | 805 else |
805 { | 806 { |
806 handler.Handle(call, decoded, NULL); | 807 handler.Handle(call, decoded, NULL, frame); |
807 } | 808 } |
808 } | 809 } |
809 catch (OrthancException& e) | 810 catch (OrthancException& e) |
810 { | 811 { |
811 if (e.GetErrorCode() == ErrorCode_ParameterOutOfRange || | 812 if (e.GetErrorCode() == ErrorCode_ParameterOutOfRange || |
866 { | 867 { |
867 } | 868 } |
868 | 869 |
869 virtual void Handle(RestApiGetCall& call, | 870 virtual void Handle(RestApiGetCall& call, |
870 std::unique_ptr<ImageAccessor>& decoded, | 871 std::unique_ptr<ImageAccessor>& decoded, |
871 const ParsedDicomFile* dicom) ORTHANC_OVERRIDE | 872 const ParsedDicomFile* dicom, |
873 unsigned int frame) ORTHANC_OVERRIDE | |
872 { | 874 { |
873 bool invert = false; | 875 bool invert = false; |
874 | 876 |
875 if (mode_ == ImageExtractionMode_Preview) | 877 if (mode_ == ImageExtractionMode_Preview) |
876 { | 878 { |
897 | 899 |
898 | 900 |
899 class RenderedFrameHandler : public IDecodedFrameHandler | 901 class RenderedFrameHandler : public IDecodedFrameHandler |
900 { | 902 { |
901 private: | 903 private: |
902 static void GetDicomParameters(bool& invert, | 904 static void GetUserArguments(double& windowWidth /* inout */, |
903 float& rescaleSlope, | 905 double& windowCenter /* inout */, |
904 float& rescaleIntercept, | |
905 float& windowWidth, | |
906 float& windowCenter, | |
907 const ParsedDicomFile& dicom) | |
908 { | |
909 DicomMap tags; | |
910 OrthancConfiguration::DefaultExtractDicomSummary(tags, dicom); | |
911 | |
912 DicomImageInformation info(tags); | |
913 | |
914 invert = (info.GetPhotometricInterpretation() == PhotometricInterpretation_Monochrome1); | |
915 | |
916 rescaleSlope = 1.0f; | |
917 rescaleIntercept = 0.0f; | |
918 | |
919 if (dicom.HasTag(Orthanc::DICOM_TAG_RESCALE_SLOPE) && | |
920 dicom.HasTag(Orthanc::DICOM_TAG_RESCALE_INTERCEPT)) | |
921 { | |
922 tags.ParseFloat(rescaleSlope, Orthanc::DICOM_TAG_RESCALE_SLOPE); | |
923 tags.ParseFloat(rescaleIntercept, Orthanc::DICOM_TAG_RESCALE_INTERCEPT); | |
924 } | |
925 | |
926 windowWidth = static_cast<float>(1 << info.GetBitsStored()) * rescaleSlope; | |
927 windowCenter = windowWidth / 2.0f + rescaleIntercept; | |
928 | |
929 if (tags.HasTag(Orthanc::DICOM_TAG_WINDOW_CENTER) && | |
930 tags.HasTag(Orthanc::DICOM_TAG_WINDOW_WIDTH)) | |
931 { | |
932 tags.ParseFirstFloat(windowCenter, Orthanc::DICOM_TAG_WINDOW_CENTER); | |
933 tags.ParseFirstFloat(windowWidth, Orthanc::DICOM_TAG_WINDOW_WIDTH); | |
934 } | |
935 } | |
936 | |
937 static void GetUserArguments(float& windowWidth /* inout */, | |
938 float& windowCenter /* inout */, | |
939 unsigned int& argWidth, | 906 unsigned int& argWidth, |
940 unsigned int& argHeight, | 907 unsigned int& argHeight, |
941 bool& smooth, | 908 bool& smooth, |
942 const RestApiGetCall& call) | 909 const RestApiGetCall& call) |
943 { | 910 { |
945 static const char* ARG_WINDOW_WIDTH = "window-width"; | 912 static const char* ARG_WINDOW_WIDTH = "window-width"; |
946 static const char* ARG_WIDTH = "width"; | 913 static const char* ARG_WIDTH = "width"; |
947 static const char* ARG_HEIGHT = "height"; | 914 static const char* ARG_HEIGHT = "height"; |
948 static const char* ARG_SMOOTH = "smooth"; | 915 static const char* ARG_SMOOTH = "smooth"; |
949 | 916 |
950 if (call.HasArgument(ARG_WINDOW_WIDTH)) | 917 if (call.HasArgument(ARG_WINDOW_WIDTH) && |
951 { | 918 !SerializationToolbox::ParseDouble(windowWidth, call.GetArgument(ARG_WINDOW_WIDTH, ""))) |
952 try | 919 { |
953 { | 920 throw OrthancException(ErrorCode_ParameterOutOfRange, |
954 windowWidth = boost::lexical_cast<float>(call.GetArgument(ARG_WINDOW_WIDTH, "")); | 921 "Bad value for argument: " + std::string(ARG_WINDOW_WIDTH)); |
955 } | 922 } |
956 catch (boost::bad_lexical_cast&) | 923 |
957 { | 924 if (call.HasArgument(ARG_WINDOW_CENTER) && |
958 throw OrthancException(ErrorCode_ParameterOutOfRange, | 925 !SerializationToolbox::ParseDouble(windowCenter, call.GetArgument(ARG_WINDOW_CENTER, ""))) |
959 "Bad value for argument: " + std::string(ARG_WINDOW_WIDTH)); | 926 { |
960 } | 927 throw OrthancException(ErrorCode_ParameterOutOfRange, |
961 } | 928 "Bad value for argument: " + std::string(ARG_WINDOW_CENTER)); |
962 | |
963 if (call.HasArgument(ARG_WINDOW_CENTER)) | |
964 { | |
965 try | |
966 { | |
967 windowCenter = boost::lexical_cast<float>(call.GetArgument(ARG_WINDOW_CENTER, "")); | |
968 } | |
969 catch (boost::bad_lexical_cast&) | |
970 { | |
971 throw OrthancException(ErrorCode_ParameterOutOfRange, | |
972 "Bad value for argument: " + std::string(ARG_WINDOW_CENTER)); | |
973 } | |
974 } | 929 } |
975 | 930 |
976 argWidth = 0; | 931 argWidth = 0; |
977 argHeight = 0; | 932 argHeight = 0; |
978 | 933 |
1030 | 985 |
1031 | 986 |
1032 public: | 987 public: |
1033 virtual void Handle(RestApiGetCall& call, | 988 virtual void Handle(RestApiGetCall& call, |
1034 std::unique_ptr<ImageAccessor>& decoded, | 989 std::unique_ptr<ImageAccessor>& decoded, |
1035 const ParsedDicomFile* dicom) ORTHANC_OVERRIDE | 990 const ParsedDicomFile* dicom, |
991 unsigned int frame) ORTHANC_OVERRIDE | |
1036 { | 992 { |
1037 if (dicom == NULL) | 993 if (dicom == NULL) |
1038 { | 994 { |
1039 throw OrthancException(ErrorCode_InternalError); | 995 throw OrthancException(ErrorCode_InternalError); |
1040 } | 996 } |
1041 | 997 |
1042 bool invert; | 998 PhotometricInterpretation photometric; |
1043 float rescaleSlope, rescaleIntercept, windowWidth, windowCenter; | 999 const bool invert = (dicom->LookupPhotometricInterpretation(photometric) && |
1044 GetDicomParameters(invert, rescaleSlope, rescaleIntercept, windowWidth, windowCenter, *dicom); | 1000 photometric == PhotometricInterpretation_Monochrome1); |
1045 | 1001 |
1002 double rescaleIntercept, rescaleSlope, windowCenter, windowWidth; | |
1003 dicom->GetRescale(rescaleIntercept, rescaleSlope, frame); | |
1004 dicom->GetDefaultWindowing(windowCenter, windowWidth, frame); | |
1005 | |
1046 unsigned int argWidth, argHeight; | 1006 unsigned int argWidth, argHeight; |
1047 bool smooth; | 1007 bool smooth; |
1048 GetUserArguments(windowWidth, windowCenter, argWidth, argHeight, smooth, call); | 1008 GetUserArguments(windowWidth, windowCenter, argWidth, argHeight, smooth, call); |
1049 | 1009 |
1050 unsigned int targetWidth = decoded->GetWidth(); | 1010 unsigned int targetWidth = decoded->GetWidth(); |
1110 if (windowWidth <= 1.0f) | 1070 if (windowWidth <= 1.0f) |
1111 { | 1071 { |
1112 windowWidth = 1; | 1072 windowWidth = 1; |
1113 } | 1073 } |
1114 | 1074 |
1115 if (std::abs(rescaleSlope) <= 0.1f) | 1075 if (std::abs(rescaleSlope) <= 0.1) |
1116 { | 1076 { |
1117 rescaleSlope = 0.1f; | 1077 rescaleSlope = 0.1; |
1118 } | 1078 } |
1119 | 1079 |
1120 const float scaling = 255.0f * rescaleSlope / windowWidth; | 1080 const double scaling = 255.0 * rescaleSlope / windowWidth; |
1121 const float offset = (rescaleIntercept - windowCenter + windowWidth / 2.0f) / rescaleSlope; | 1081 const double offset = (rescaleIntercept - windowCenter + windowWidth / 2.0) / rescaleSlope; |
1122 | 1082 |
1123 std::unique_ptr<ImageAccessor> rescaled(new Image(PixelFormat_Grayscale8, decoded->GetWidth(), decoded->GetHeight(), false)); | 1083 std::unique_ptr<ImageAccessor> rescaled(new Image(PixelFormat_Grayscale8, decoded->GetWidth(), decoded->GetHeight(), false)); |
1124 ImageProcessing::ShiftScale(*rescaled, converted, offset, scaling, false); | 1084 ImageProcessing::ShiftScale(*rescaled, converted, static_cast<float>(offset), static_cast<float>(scaling), false); |
1125 | 1085 |
1126 if (targetWidth == decoded->GetWidth() && | 1086 if (targetWidth == decoded->GetWidth() && |
1127 targetHeight == decoded->GetHeight()) | 1087 targetHeight == decoded->GetHeight()) |
1128 { | 1088 { |
1129 DefaultHandler(call, rescaled, ImageExtractionMode_UInt8, invert); | 1089 DefaultHandler(call, rescaled, ImageExtractionMode_UInt8, invert); |