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