Mercurial > hg > orthanc
comparison UnitTestsSources/FromDcmtkTests.cpp @ 3872:b40dfa6dc8da transcoding
working on a simpler abstraction for transcoding
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 30 Apr 2020 14:39:28 +0200 |
parents | 5797d184de67 |
children | cdd0cb5ec4e4 |
comparison
equal
deleted
inserted
replaced
3871:5797d184de67 | 3872:b40dfa6dc8da |
---|---|
1943 | 1943 |
1944 | 1944 |
1945 | 1945 |
1946 namespace Orthanc | 1946 namespace Orthanc |
1947 { | 1947 { |
1948 class IParsedDicomImage : public boost::noncopyable | |
1949 { | |
1950 public: | |
1951 virtual ~IParsedDicomImage() | |
1952 { | |
1953 } | |
1954 | |
1955 virtual DicomTransferSyntax GetTransferSyntax() = 0; | |
1956 | |
1957 virtual std::string GetSopClassUid() = 0; | |
1958 | |
1959 virtual std::string GetSopInstanceUid() = 0; | |
1960 | |
1961 virtual unsigned int GetFramesCount() = 0; | |
1962 | |
1963 // Can return NULL, for compressed transfer syntaxes | |
1964 virtual ImageAccessor* GetUncompressedFrame(unsigned int frame) = 0; | |
1965 | |
1966 virtual void GetCompressedFrame(std::string& target, | |
1967 unsigned int frame) = 0; | |
1968 | |
1969 virtual void WriteToMemoryBuffer(std::string& target) = 0; | |
1970 }; | |
1971 | |
1972 | |
1973 class IDicomImageReader : public boost::noncopyable | |
1974 { | |
1975 public: | |
1976 virtual ~IDicomImageReader() | |
1977 { | |
1978 } | |
1979 | |
1980 virtual IParsedDicomImage* Read(const void* data, | |
1981 size_t size) = 0; | |
1982 | |
1983 virtual IParsedDicomImage* Transcode(const void* data, | |
1984 size_t size, | |
1985 DicomTransferSyntax syntax, | |
1986 bool allowNewSopInstanceUid) = 0; | |
1987 }; | |
1988 | |
1989 | |
1990 class DcmtkImageReader : public IDicomImageReader | |
1991 { | |
1992 private: | |
1993 class Image : public IParsedDicomImage | |
1994 { | |
1995 private: | |
1996 std::unique_ptr<DcmFileFormat> dicom_; | |
1997 std::unique_ptr<DicomFrameIndex> index_; | |
1998 DicomTransferSyntax transferSyntax_; | |
1999 std::string sopClassUid_; | |
2000 std::string sopInstanceUid_; | |
2001 | |
2002 static std::string GetStringTag(DcmDataset& dataset, | |
2003 const DcmTagKey& tag) | |
2004 { | |
2005 const char* value = NULL; | |
2006 | |
2007 if (!dataset.findAndGetString(tag, value).good() || | |
2008 value == NULL) | |
2009 { | |
2010 throw OrthancException(ErrorCode_BadFileFormat, | |
2011 "Missing SOP class/instance UID in DICOM instance"); | |
2012 } | |
2013 else | |
2014 { | |
2015 return std::string(value); | |
2016 } | |
2017 } | |
2018 | |
2019 public: | |
2020 Image(DcmFileFormat* dicom, | |
2021 DicomTransferSyntax syntax) : | |
2022 dicom_(dicom), | |
2023 transferSyntax_(syntax) | |
2024 { | |
2025 if (dicom == NULL || | |
2026 dicom_->getDataset() == NULL) | |
2027 { | |
2028 throw OrthancException(ErrorCode_NullPointer); | |
2029 } | |
2030 | |
2031 DcmDataset& dataset = *dicom_->getDataset(); | |
2032 index_.reset(new DicomFrameIndex(dataset)); | |
2033 | |
2034 sopClassUid_ = GetStringTag(dataset, DCM_SOPClassUID); | |
2035 sopInstanceUid_ = GetStringTag(dataset, DCM_SOPInstanceUID); | |
2036 } | |
2037 | |
2038 virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE | |
2039 { | |
2040 return transferSyntax_; | |
2041 } | |
2042 | |
2043 virtual std::string GetSopClassUid() ORTHANC_OVERRIDE | |
2044 { | |
2045 return sopClassUid_; | |
2046 } | |
2047 | |
2048 virtual std::string GetSopInstanceUid() ORTHANC_OVERRIDE | |
2049 { | |
2050 return sopInstanceUid_; | |
2051 } | |
2052 | |
2053 virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE | |
2054 { | |
2055 return index_->GetFramesCount(); | |
2056 } | |
2057 | |
2058 virtual void WriteToMemoryBuffer(std::string& target) ORTHANC_OVERRIDE | |
2059 { | |
2060 assert(dicom_.get() != NULL); | |
2061 if (!FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, transferSyntax_)) | |
2062 { | |
2063 throw OrthancException(ErrorCode_InternalError, | |
2064 "Cannot write the DICOM instance to a memory buffer"); | |
2065 } | |
2066 } | |
2067 | |
2068 virtual ImageAccessor* GetUncompressedFrame(unsigned int frame) ORTHANC_OVERRIDE | |
2069 { | |
2070 assert(dicom_.get() != NULL && | |
2071 dicom_->getDataset() != NULL); | |
2072 return DicomImageDecoder::Decode(*dicom_->getDataset(), frame); | |
2073 } | |
2074 | |
2075 virtual void GetCompressedFrame(std::string& target, | |
2076 unsigned int frame) ORTHANC_OVERRIDE | |
2077 { | |
2078 assert(index_.get() != NULL); | |
2079 index_->GetRawFrame(target, frame); | |
2080 } | |
2081 }; | |
2082 | |
2083 unsigned int lossyQuality_; | |
2084 | |
2085 static DicomTransferSyntax DetectTransferSyntax(DcmFileFormat& dicom) | |
2086 { | |
2087 if (dicom.getDataset() == NULL) | |
2088 { | |
2089 throw OrthancException(ErrorCode_InternalError); | |
2090 } | |
2091 | |
2092 DcmDataset& dataset = *dicom.getDataset(); | |
2093 | |
2094 E_TransferSyntax xfer = dataset.getOriginalXfer(); | |
2095 if (xfer == EXS_Unknown) | |
2096 { | |
2097 dataset.updateOriginalXfer(); | |
2098 xfer = dataset.getOriginalXfer(); | |
2099 if (xfer == EXS_Unknown) | |
2100 { | |
2101 throw OrthancException(ErrorCode_BadFileFormat, | |
2102 "Cannot determine the transfer syntax of the DICOM instance"); | |
2103 } | |
2104 } | |
2105 | |
2106 DicomTransferSyntax syntax; | |
2107 if (FromDcmtkBridge::LookupOrthancTransferSyntax(syntax, xfer)) | |
2108 { | |
2109 return syntax; | |
2110 } | |
2111 else | |
2112 { | |
2113 throw OrthancException( | |
2114 ErrorCode_BadFileFormat, | |
2115 "Unsupported transfer syntax: " + boost::lexical_cast<std::string>(xfer)); | |
2116 } | |
2117 } | |
2118 | |
2119 | |
2120 static uint16_t GetBitsStored(DcmFileFormat& dicom) | |
2121 { | |
2122 if (dicom.getDataset() == NULL) | |
2123 { | |
2124 throw OrthancException(ErrorCode_InternalError); | |
2125 } | |
2126 | |
2127 uint16_t bitsStored; | |
2128 if (dicom.getDataset()->findAndGetUint16(DCM_BitsStored, bitsStored).good()) | |
2129 { | |
2130 return bitsStored; | |
2131 } | |
2132 else | |
2133 { | |
2134 throw OrthancException(ErrorCode_BadFileFormat, | |
2135 "Missing \"Bits Stored\" tag in DICOM instance"); | |
2136 } | |
2137 } | |
2138 | |
2139 | |
2140 public: | |
2141 DcmtkImageReader() : | |
2142 lossyQuality_(90) | |
2143 { | |
2144 } | |
2145 | |
2146 void SetLossyQuality(unsigned int quality) | |
2147 { | |
2148 if (quality <= 0 || | |
2149 quality > 100) | |
2150 { | |
2151 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
2152 } | |
2153 else | |
2154 { | |
2155 lossyQuality_ = quality; | |
2156 } | |
2157 } | |
2158 | |
2159 unsigned int GetLossyQuality() const | |
2160 { | |
2161 return lossyQuality_; | |
2162 } | |
2163 | |
2164 virtual IParsedDicomImage* Read(const void* data, | |
2165 size_t size) | |
2166 { | |
2167 std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(data, size)); | |
2168 if (dicom.get() == NULL) | |
2169 { | |
2170 throw OrthancException(ErrorCode_BadFileFormat); | |
2171 } | |
2172 | |
2173 DicomTransferSyntax transferSyntax = DetectTransferSyntax(*dicom); | |
2174 | |
2175 return new Image(dicom.release(), transferSyntax); | |
2176 } | |
2177 | |
2178 virtual IParsedDicomImage* Transcode(const void* data, | |
2179 size_t size, | |
2180 DicomTransferSyntax syntax, | |
2181 bool allowNewSopInstanceUid) | |
2182 { | |
2183 std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(data, size)); | |
2184 if (dicom.get() == NULL) | |
2185 { | |
2186 throw OrthancException(ErrorCode_BadFileFormat); | |
2187 } | |
2188 | |
2189 const uint16_t bitsStored = GetBitsStored(*dicom); | |
2190 | |
2191 if (syntax == DetectTransferSyntax(*dicom)) | |
2192 { | |
2193 // No transcoding is needed | |
2194 return new Image(dicom.release(), syntax); | |
2195 } | |
2196 | |
2197 if (syntax == DicomTransferSyntax_LittleEndianImplicit && | |
2198 FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_LittleEndianImplicit, NULL)) | |
2199 { | |
2200 return new Image(dicom.release(), syntax); | |
2201 } | |
2202 | |
2203 if (syntax == DicomTransferSyntax_LittleEndianExplicit && | |
2204 FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_LittleEndianExplicit, NULL)) | |
2205 { | |
2206 return new Image(dicom.release(), syntax); | |
2207 } | |
2208 | |
2209 if (syntax == DicomTransferSyntax_BigEndianExplicit && | |
2210 FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_BigEndianExplicit, NULL)) | |
2211 { | |
2212 return new Image(dicom.release(), syntax); | |
2213 } | |
2214 | |
2215 if (syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit && | |
2216 FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_DeflatedLittleEndianExplicit, NULL)) | |
2217 { | |
2218 return new Image(dicom.release(), syntax); | |
2219 } | |
2220 | |
2221 #if ORTHANC_ENABLE_JPEG == 1 | |
2222 if (syntax == DicomTransferSyntax_JPEGProcess1 && | |
2223 allowNewSopInstanceUid && | |
2224 bitsStored == 8) | |
2225 { | |
2226 DJ_RPLossy rpLossy(lossyQuality_); | |
2227 | |
2228 if (FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_JPEGProcess1, &rpLossy)) | |
2229 { | |
2230 return new Image(dicom.release(), syntax); | |
2231 } | |
2232 } | |
2233 #endif | |
2234 | |
2235 #if ORTHANC_ENABLE_JPEG == 1 | |
2236 if (syntax == DicomTransferSyntax_JPEGProcess2_4 && | |
2237 allowNewSopInstanceUid && | |
2238 bitsStored <= 12) | |
2239 { | |
2240 DJ_RPLossy rpLossy(lossyQuality_); | |
2241 if (FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_JPEGProcess2_4, &rpLossy)) | |
2242 { | |
2243 return new Image(dicom.release(), syntax); | |
2244 } | |
2245 } | |
2246 #endif | |
2247 | |
2248 //LOG(INFO) << "Unable to transcode DICOM image using the built-in reader"; | |
2249 return NULL; | |
2250 } | |
2251 }; | |
2252 | |
2253 | |
2254 | |
1948 class IDicomTranscoder : public boost::noncopyable | 2255 class IDicomTranscoder : public boost::noncopyable |
1949 { | 2256 { |
1950 public: | 2257 public: |
1951 virtual ~IDicomTranscoder() | 2258 virtual ~IDicomTranscoder() |
1952 { | 2259 { |
2148 } | 2455 } |
2149 | 2456 |
2150 printf(">> %d\n", bitsStored_); | 2457 printf(">> %d\n", bitsStored_); |
2151 | 2458 |
2152 if (syntax == DicomTransferSyntax_LittleEndianImplicit && | 2459 if (syntax == DicomTransferSyntax_LittleEndianImplicit && |
2153 FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_LittleEndianImplicit, NULL)) | 2460 FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && |
2461 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2154 { | 2462 { |
2155 transferSyntax_ = DicomTransferSyntax_LittleEndianImplicit; | 2463 transferSyntax_ = DicomTransferSyntax_LittleEndianImplicit; |
2156 return true; | 2464 return true; |
2157 } | 2465 } |
2158 | 2466 |
2159 if (syntax == DicomTransferSyntax_LittleEndianExplicit && | 2467 if (syntax == DicomTransferSyntax_LittleEndianExplicit && |
2160 FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_LittleEndianExplicit, NULL)) | 2468 FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && |
2469 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2161 { | 2470 { |
2162 transferSyntax_ = DicomTransferSyntax_LittleEndianExplicit; | 2471 transferSyntax_ = DicomTransferSyntax_LittleEndianExplicit; |
2163 return true; | 2472 return true; |
2164 } | 2473 } |
2165 | 2474 |
2166 if (syntax == DicomTransferSyntax_BigEndianExplicit && | 2475 if (syntax == DicomTransferSyntax_BigEndianExplicit && |
2167 FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_BigEndianExplicit, NULL)) | 2476 FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && |
2477 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2168 { | 2478 { |
2169 transferSyntax_ = DicomTransferSyntax_BigEndianExplicit; | 2479 transferSyntax_ = DicomTransferSyntax_BigEndianExplicit; |
2170 return true; | 2480 return true; |
2171 } | 2481 } |
2172 | 2482 |
2173 if (syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit && | 2483 if (syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit && |
2174 FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_DeflatedLittleEndianExplicit, NULL)) | 2484 FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && |
2485 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2175 { | 2486 { |
2176 transferSyntax_ = DicomTransferSyntax_DeflatedLittleEndianExplicit; | 2487 transferSyntax_ = DicomTransferSyntax_DeflatedLittleEndianExplicit; |
2177 return true; | 2488 return true; |
2178 } | 2489 } |
2179 | 2490 |
2182 allowNewSopInstanceUid && | 2493 allowNewSopInstanceUid && |
2183 GetBitsStored() == 8) | 2494 GetBitsStored() == 8) |
2184 { | 2495 { |
2185 DJ_RPLossy rpLossy(lossyQuality_); | 2496 DJ_RPLossy rpLossy(lossyQuality_); |
2186 | 2497 |
2187 if (FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_JPEGProcess1, &rpLossy)) | 2498 if (FromDcmtkBridge::Transcode(*dicom_, syntax, &rpLossy) && |
2499 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2188 { | 2500 { |
2189 transferSyntax_ = DicomTransferSyntax_JPEGProcess1; | 2501 transferSyntax_ = DicomTransferSyntax_JPEGProcess1; |
2190 sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID); | 2502 sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID); |
2191 return true; | 2503 return true; |
2192 } | 2504 } |
2197 if (syntax == DicomTransferSyntax_JPEGProcess2_4 && | 2509 if (syntax == DicomTransferSyntax_JPEGProcess2_4 && |
2198 allowNewSopInstanceUid && | 2510 allowNewSopInstanceUid && |
2199 GetBitsStored() <= 12) | 2511 GetBitsStored() <= 12) |
2200 { | 2512 { |
2201 DJ_RPLossy rpLossy(lossyQuality_); | 2513 DJ_RPLossy rpLossy(lossyQuality_); |
2202 if (FromDcmtkBridge::Transcode(target, *dicom_, DicomTransferSyntax_JPEGProcess2_4, &rpLossy)) | 2514 if (FromDcmtkBridge::Transcode(*dicom_, syntax, &rpLossy) && |
2515 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2203 { | 2516 { |
2204 transferSyntax_ = DicomTransferSyntax_JPEGProcess2_4; | 2517 transferSyntax_ = DicomTransferSyntax_JPEGProcess2_4; |
2205 sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID); | 2518 sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID); |
2206 return true; | 2519 return true; |
2207 } | 2520 } |
2368 } | 2681 } |
2369 | 2682 |
2370 printf("\n"); | 2683 printf("\n"); |
2371 } | 2684 } |
2372 | 2685 |
2373 TEST(Toto, Transcode) | 2686 TEST(Toto, DISABLED_Transcode) |
2374 { | 2687 { |
2375 //OFLog::configure(OFLogger::DEBUG_LOG_LEVEL); | 2688 //OFLog::configure(OFLogger::DEBUG_LOG_LEVEL); |
2376 | 2689 |
2377 if (0) | 2690 if (0) |
2378 { | 2691 { |
2437 TestFile("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.1.dcm"); | 2750 TestFile("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.1.dcm"); |
2438 } | 2751 } |
2439 } | 2752 } |
2440 | 2753 |
2441 | 2754 |
2755 TEST(Toto, DISABLED_Transcode2) | |
2756 { | |
2757 for (int i = 0; i <= DicomTransferSyntax_XML; i++) | |
2758 { | |
2759 DicomTransferSyntax a = (DicomTransferSyntax) i; | |
2760 | |
2761 std::string path = ("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/" + | |
2762 std::string(GetTransferSyntaxUid(a)) + ".dcm"); | |
2763 if (Orthanc::SystemToolbox::IsRegularFile(path)) | |
2764 { | |
2765 printf("\n======= %s\n", GetTransferSyntaxUid(a)); | |
2766 | |
2767 std::string source; | |
2768 Orthanc::SystemToolbox::ReadFile(source, path); | |
2769 | |
2770 DcmtkImageReader reader; | |
2771 | |
2772 { | |
2773 std::unique_ptr<IParsedDicomImage> image( | |
2774 reader.Read(source.c_str(), source.size())); | |
2775 ASSERT_TRUE(image.get() != NULL); | |
2776 ASSERT_EQ(a, image->GetTransferSyntax()); | |
2777 | |
2778 std::string target; | |
2779 image->WriteToMemoryBuffer(target); | |
2780 } | |
2781 | |
2782 for (int j = 0; j <= DicomTransferSyntax_XML; j++) | |
2783 { | |
2784 DicomTransferSyntax b = (DicomTransferSyntax) j; | |
2785 //if (a == b) continue; | |
2786 | |
2787 std::unique_ptr<IParsedDicomImage> image( | |
2788 reader.Transcode(source.c_str(), source.size(), b, true)); | |
2789 if (image.get() != NULL) | |
2790 { | |
2791 printf("[%s] -> [%s]\n", GetTransferSyntaxUid(a), GetTransferSyntaxUid(b)); | |
2792 | |
2793 std::string target; | |
2794 image->WriteToMemoryBuffer(target); | |
2795 | |
2796 char buf[1024]; | |
2797 sprintf(buf, "/tmp/%s-%s.dcm", GetTransferSyntaxUid(a), GetTransferSyntaxUid(b)); | |
2798 | |
2799 SystemToolbox::WriteFile(target, buf); | |
2800 } | |
2801 else if (a != DicomTransferSyntax_JPEG2000 && | |
2802 a != DicomTransferSyntax_JPEG2000LosslessOnly) | |
2803 { | |
2804 ASSERT_TRUE(b != DicomTransferSyntax_LittleEndianImplicit && | |
2805 b != DicomTransferSyntax_LittleEndianExplicit && | |
2806 b != DicomTransferSyntax_BigEndianExplicit && | |
2807 b != DicomTransferSyntax_DeflatedLittleEndianExplicit); | |
2808 } | |
2809 } | |
2810 } | |
2811 } | |
2812 } | |
2813 | |
2442 | 2814 |
2443 #include "../Core/DicomNetworking/DicomAssociation.h" | 2815 #include "../Core/DicomNetworking/DicomAssociation.h" |
2444 #include "../Core/DicomNetworking/DicomControlUserConnection.h" | 2816 #include "../Core/DicomNetworking/DicomControlUserConnection.h" |
2445 #include "../Core/DicomNetworking/DicomStoreUserConnection.h" | 2817 #include "../Core/DicomNetworking/DicomStoreUserConnection.h" |
2446 | 2818 |