Mercurial > hg > orthanc
comparison UnitTestsSources/FromDcmtkTests.cpp @ 3884:83061cdc7703 transcoding
moving old tests to the graveyard
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 05 May 2020 14:37:29 +0200 |
parents | 795c9ca5eb91 |
children | e23026566536 |
comparison
equal
deleted
inserted
replaced
3883:795c9ca5eb91 | 3884:83061cdc7703 |
---|---|
1922 | 1922 |
1923 | 1923 |
1924 | 1924 |
1925 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 | 1925 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 |
1926 | 1926 |
1927 #include "../Core/DicomParsing/Internals/DicomFrameIndex.h" | 1927 #include "../Core/DicomNetworking/DicomStoreUserConnection.h" |
1928 | 1928 |
1929 #include <dcmtk/dcmdata/dcostrmb.h> | |
1930 #include <dcmtk/dcmdata/dcpixel.h> | |
1931 #include <dcmtk/dcmdata/dcpxitem.h> | |
1932 #include <dcmtk/dcmjpeg/djrploss.h> // for DJ_RPLossy | 1929 #include <dcmtk/dcmjpeg/djrploss.h> // for DJ_RPLossy |
1933 #include <dcmtk/dcmjpeg/djrplol.h> // for DJ_RPLossless | 1930 #include <dcmtk/dcmjpeg/djrplol.h> // for DJ_RPLossless |
1934 | 1931 |
1935 | 1932 |
1936 #if !defined(ORTHANC_ENABLE_DCMTK_JPEG) | 1933 #if !defined(ORTHANC_ENABLE_DCMTK_JPEG) |
1939 | 1936 |
1940 #if !defined(ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS) | 1937 #if !defined(ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS) |
1941 # error Macro ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS must be defined | 1938 # error Macro ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS must be defined |
1942 #endif | 1939 #endif |
1943 | 1940 |
1944 | |
1945 | |
1946 namespace Orthanc | |
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.getCurrentXfer(); | |
2095 if (xfer == EXS_Unknown) | |
2096 { | |
2097 dataset.updateOriginalXfer(); | |
2098 xfer = dataset.getCurrentXfer(); | |
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 | |
2255 class IDicomTranscoder1 : public boost::noncopyable | |
2256 { | |
2257 public: | |
2258 virtual ~IDicomTranscoder1() | |
2259 { | |
2260 } | |
2261 | |
2262 virtual DcmFileFormat& GetDicom() = 0; | |
2263 | |
2264 virtual DicomTransferSyntax GetTransferSyntax() = 0; | |
2265 | |
2266 virtual std::string GetSopClassUid() = 0; | |
2267 | |
2268 virtual std::string GetSopInstanceUid() = 0; | |
2269 | |
2270 virtual unsigned int GetFramesCount() = 0; | |
2271 | |
2272 virtual ImageAccessor* DecodeFrame(unsigned int frame) = 0; | |
2273 | |
2274 virtual void GetCompressedFrame(std::string& target, | |
2275 unsigned int frame) = 0; | |
2276 | |
2277 // NB: Transcoding can change the value of "GetSopInstanceUid()" | |
2278 // and "GetTransferSyntax()" if lossy compression is applied | |
2279 virtual bool Transcode(std::string& target, | |
2280 DicomTransferSyntax syntax, | |
2281 bool allowNewSopInstanceUid) = 0; | |
2282 | |
2283 virtual void WriteToMemoryBuffer(std::string& target) = 0; | |
2284 }; | |
2285 | |
2286 | |
2287 class DcmtkTranscoder2 : public IDicomTranscoder1 | |
2288 { | |
2289 private: | |
2290 std::unique_ptr<DcmFileFormat> dicom_; | |
2291 std::unique_ptr<DicomFrameIndex> index_; | |
2292 DicomTransferSyntax transferSyntax_; | |
2293 std::string sopClassUid_; | |
2294 std::string sopInstanceUid_; | |
2295 uint16_t bitsStored_; | |
2296 unsigned int lossyQuality_; | |
2297 | |
2298 static std::string GetStringTag(DcmDataset& dataset, | |
2299 const DcmTagKey& tag) | |
2300 { | |
2301 const char* value = NULL; | |
2302 | |
2303 if (!dataset.findAndGetString(tag, value).good() || | |
2304 value == NULL) | |
2305 { | |
2306 throw OrthancException(ErrorCode_BadFileFormat, | |
2307 "Missing SOP class/instance UID in DICOM instance"); | |
2308 } | |
2309 else | |
2310 { | |
2311 return std::string(value); | |
2312 } | |
2313 } | |
2314 | |
2315 void Setup(DcmFileFormat* dicom) | |
2316 { | |
2317 lossyQuality_ = 90; | |
2318 | |
2319 dicom_.reset(dicom); | |
2320 | |
2321 if (dicom == NULL || | |
2322 dicom_->getDataset() == NULL) | |
2323 { | |
2324 throw OrthancException(ErrorCode_NullPointer); | |
2325 } | |
2326 | |
2327 DcmDataset& dataset = *dicom_->getDataset(); | |
2328 index_.reset(new DicomFrameIndex(dataset)); | |
2329 | |
2330 E_TransferSyntax xfer = dataset.getCurrentXfer(); | |
2331 if (xfer == EXS_Unknown) | |
2332 { | |
2333 dataset.updateOriginalXfer(); | |
2334 xfer = dataset.getCurrentXfer(); | |
2335 if (xfer == EXS_Unknown) | |
2336 { | |
2337 throw OrthancException(ErrorCode_BadFileFormat, | |
2338 "Cannot determine the transfer syntax of the DICOM instance"); | |
2339 } | |
2340 } | |
2341 | |
2342 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transferSyntax_, xfer)) | |
2343 { | |
2344 throw OrthancException( | |
2345 ErrorCode_BadFileFormat, | |
2346 "Unsupported transfer syntax: " + boost::lexical_cast<std::string>(xfer)); | |
2347 } | |
2348 | |
2349 if (!dataset.findAndGetUint16(DCM_BitsStored, bitsStored_).good()) | |
2350 { | |
2351 throw OrthancException(ErrorCode_BadFileFormat, | |
2352 "Missing \"Bits Stored\" tag in DICOM instance"); | |
2353 } | |
2354 | |
2355 sopClassUid_ = GetStringTag(dataset, DCM_SOPClassUID); | |
2356 sopInstanceUid_ = GetStringTag(dataset, DCM_SOPInstanceUID); | |
2357 } | |
2358 | |
2359 public: | |
2360 DcmtkTranscoder2(DcmFileFormat* dicom) // Takes ownership | |
2361 { | |
2362 Setup(dicom); | |
2363 } | |
2364 | |
2365 DcmtkTranscoder2(const void* dicom, | |
2366 size_t size) | |
2367 { | |
2368 Setup(FromDcmtkBridge::LoadFromMemoryBuffer(dicom, size)); | |
2369 } | |
2370 | |
2371 void SetLossyQuality(unsigned int quality) | |
2372 { | |
2373 if (quality <= 0 || | |
2374 quality > 100) | |
2375 { | |
2376 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
2377 } | |
2378 else | |
2379 { | |
2380 lossyQuality_ = quality; | |
2381 } | |
2382 } | |
2383 | |
2384 unsigned int GetLossyQuality() const | |
2385 { | |
2386 return lossyQuality_; | |
2387 } | |
2388 | |
2389 unsigned int GetBitsStored() const | |
2390 { | |
2391 return bitsStored_; | |
2392 } | |
2393 | |
2394 virtual DcmFileFormat& GetDicom() | |
2395 { | |
2396 assert(dicom_ != NULL); | |
2397 return *dicom_; | |
2398 } | |
2399 | |
2400 virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE | |
2401 { | |
2402 return transferSyntax_; | |
2403 } | |
2404 | |
2405 virtual std::string GetSopClassUid() ORTHANC_OVERRIDE | |
2406 { | |
2407 return sopClassUid_; | |
2408 } | |
2409 | |
2410 virtual std::string GetSopInstanceUid() ORTHANC_OVERRIDE | |
2411 { | |
2412 return sopInstanceUid_; | |
2413 } | |
2414 | |
2415 virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE | |
2416 { | |
2417 return index_->GetFramesCount(); | |
2418 } | |
2419 | |
2420 virtual void WriteToMemoryBuffer(std::string& target) ORTHANC_OVERRIDE | |
2421 { | |
2422 if (!FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_)) | |
2423 { | |
2424 throw OrthancException(ErrorCode_InternalError, | |
2425 "Cannot write the DICOM instance to a memory buffer"); | |
2426 } | |
2427 } | |
2428 | |
2429 virtual ImageAccessor* DecodeFrame(unsigned int frame) ORTHANC_OVERRIDE | |
2430 { | |
2431 assert(dicom_->getDataset() != NULL); | |
2432 return DicomImageDecoder::Decode(*dicom_->getDataset(), frame); | |
2433 } | |
2434 | |
2435 virtual void GetCompressedFrame(std::string& target, | |
2436 unsigned int frame) ORTHANC_OVERRIDE | |
2437 { | |
2438 index_->GetRawFrame(target, frame); | |
2439 } | |
2440 | |
2441 virtual bool Transcode(std::string& target, | |
2442 DicomTransferSyntax syntax, | |
2443 bool allowNewSopInstanceUid) ORTHANC_OVERRIDE | |
2444 { | |
2445 assert(dicom_ != NULL && | |
2446 dicom_->getDataset() != NULL); | |
2447 | |
2448 if (syntax == GetTransferSyntax()) | |
2449 { | |
2450 printf("NO TRANSCODING\n"); | |
2451 | |
2452 // No change in the transfer syntax => simply serialize the current dataset | |
2453 WriteToMemoryBuffer(target); | |
2454 return true; | |
2455 } | |
2456 | |
2457 printf(">> %d\n", bitsStored_); | |
2458 | |
2459 if (syntax == DicomTransferSyntax_LittleEndianImplicit && | |
2460 FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && | |
2461 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2462 { | |
2463 transferSyntax_ = DicomTransferSyntax_LittleEndianImplicit; | |
2464 return true; | |
2465 } | |
2466 | |
2467 if (syntax == DicomTransferSyntax_LittleEndianExplicit && | |
2468 FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && | |
2469 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2470 { | |
2471 transferSyntax_ = DicomTransferSyntax_LittleEndianExplicit; | |
2472 return true; | |
2473 } | |
2474 | |
2475 if (syntax == DicomTransferSyntax_BigEndianExplicit && | |
2476 FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && | |
2477 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2478 { | |
2479 transferSyntax_ = DicomTransferSyntax_BigEndianExplicit; | |
2480 return true; | |
2481 } | |
2482 | |
2483 if (syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit && | |
2484 FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && | |
2485 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2486 { | |
2487 transferSyntax_ = DicomTransferSyntax_DeflatedLittleEndianExplicit; | |
2488 return true; | |
2489 } | |
2490 | |
2491 #if ORTHANC_ENABLE_JPEG == 1 | |
2492 if (syntax == DicomTransferSyntax_JPEGProcess1 && | |
2493 allowNewSopInstanceUid && | |
2494 GetBitsStored() == 8) | |
2495 { | |
2496 DJ_RPLossy rpLossy(lossyQuality_); | |
2497 | |
2498 if (FromDcmtkBridge::Transcode(*dicom_, syntax, &rpLossy) && | |
2499 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2500 { | |
2501 transferSyntax_ = DicomTransferSyntax_JPEGProcess1; | |
2502 sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID); | |
2503 return true; | |
2504 } | |
2505 } | |
2506 #endif | |
2507 | |
2508 #if ORTHANC_ENABLE_JPEG == 1 | |
2509 if (syntax == DicomTransferSyntax_JPEGProcess2_4 && | |
2510 allowNewSopInstanceUid && | |
2511 GetBitsStored() <= 12) | |
2512 { | |
2513 DJ_RPLossy rpLossy(lossyQuality_); | |
2514 if (FromDcmtkBridge::Transcode(*dicom_, syntax, &rpLossy) && | |
2515 FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) | |
2516 { | |
2517 transferSyntax_ = DicomTransferSyntax_JPEGProcess2_4; | |
2518 sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID); | |
2519 return true; | |
2520 } | |
2521 } | |
2522 #endif | |
2523 | |
2524 return false; | |
2525 } | |
2526 }; | |
2527 } | |
2528 | |
2529 | |
2530 | |
2531 | |
2532 #include <boost/filesystem.hpp> | |
2533 | |
2534 | |
2535 static void TestFile(const std::string& path) | |
2536 { | |
2537 static unsigned int count = 0; | |
2538 count++; | |
2539 | |
2540 | |
2541 printf("** %s\n", path.c_str()); | |
2542 | |
2543 std::string s; | |
2544 SystemToolbox::ReadFile(s, path); | |
2545 | |
2546 Orthanc::DcmtkTranscoder2 transcoder(s.c_str(), s.size()); | |
2547 | |
2548 /*if (transcoder.GetBitsStored() != 8) // TODO | |
2549 return; */ | |
2550 | |
2551 { | |
2552 char buf[1024]; | |
2553 sprintf(buf, "/tmp/source-%06d.dcm", count); | |
2554 printf(">> %s\n", buf); | |
2555 Orthanc::SystemToolbox::WriteFile(s, buf); | |
2556 } | |
2557 | |
2558 printf("[%s] [%s] [%s] %d %d\n", GetTransferSyntaxUid(transcoder.GetTransferSyntax()), | |
2559 transcoder.GetSopClassUid().c_str(), transcoder.GetSopInstanceUid().c_str(), | |
2560 transcoder.GetFramesCount(), transcoder.GetTransferSyntax()); | |
2561 | |
2562 for (size_t i = 0; i < transcoder.GetFramesCount(); i++) | |
2563 { | |
2564 std::string f; | |
2565 transcoder.GetCompressedFrame(f, i); | |
2566 | |
2567 if (i == 0) | |
2568 { | |
2569 char buf[1024]; | |
2570 sprintf(buf, "/tmp/frame-%06d.raw", count); | |
2571 printf(">> %s\n", buf); | |
2572 Orthanc::SystemToolbox::WriteFile(f, buf); | |
2573 } | |
2574 } | |
2575 | |
2576 { | |
2577 std::string t; | |
2578 transcoder.WriteToMemoryBuffer(t); | |
2579 | |
2580 Orthanc::DcmtkTranscoder2 transcoder2(t.c_str(), t.size()); | |
2581 printf(">> %d %d ; %lu bytes\n", transcoder.GetTransferSyntax(), transcoder2.GetTransferSyntax(), t.size()); | |
2582 } | |
2583 | |
2584 { | |
2585 std::string a = transcoder.GetSopInstanceUid(); | |
2586 DicomTransferSyntax b = transcoder.GetTransferSyntax(); | |
2587 | |
2588 DicomTransferSyntax syntax = DicomTransferSyntax_JPEGProcess2_4; | |
2589 //DicomTransferSyntax syntax = DicomTransferSyntax_LittleEndianExplicit; | |
2590 | |
2591 std::string t; | |
2592 bool ok = transcoder.Transcode(t, syntax, true); | |
2593 printf("Transcoding: %d\n", ok); | |
2594 | |
2595 if (ok) | |
2596 { | |
2597 printf("[%s] => [%s]\n", a.c_str(), transcoder.GetSopInstanceUid().c_str()); | |
2598 printf("[%s] => [%s]\n", GetTransferSyntaxUid(b), | |
2599 GetTransferSyntaxUid(transcoder.GetTransferSyntax())); | |
2600 | |
2601 { | |
2602 char buf[1024]; | |
2603 sprintf(buf, "/tmp/transcoded-%06d.dcm", count); | |
2604 printf(">> %s\n", buf); | |
2605 Orthanc::SystemToolbox::WriteFile(t, buf); | |
2606 } | |
2607 | |
2608 Orthanc::DcmtkTranscoder2 transcoder2(t.c_str(), t.size()); | |
2609 printf(" => transcoded transfer syntax %d ; %lu bytes\n", transcoder2.GetTransferSyntax(), t.size()); | |
2610 } | |
2611 } | |
2612 | |
2613 printf("\n"); | |
2614 } | |
2615 | |
2616 TEST(Toto, DISABLED_Transcode) | |
2617 { | |
2618 //OFLog::configure(OFLogger::DEBUG_LOG_LEVEL); | |
2619 | |
2620 if (1) | |
2621 { | |
2622 const char* const PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes"; | |
2623 | |
2624 for (boost::filesystem::directory_iterator it(PATH); | |
2625 it != boost::filesystem::directory_iterator(); ++it) | |
2626 { | |
2627 if (boost::filesystem::is_regular_file(it->status())) | |
2628 { | |
2629 TestFile(it->path().string()); | |
2630 } | |
2631 } | |
2632 } | |
2633 | |
2634 if (0) | |
2635 { | |
2636 TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Multiframe.dcm"); | |
2637 TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Issue44/Monochrome1-Jpeg.dcm"); | |
2638 } | |
2639 | |
2640 if (0) | |
2641 { | |
2642 TestFile("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.1.dcm"); | |
2643 } | |
2644 } | |
2645 | |
2646 | |
2647 TEST(Toto, DISABLED_Transcode2) | |
2648 { | |
2649 for (int i = 0; i <= DicomTransferSyntax_XML; i++) | |
2650 { | |
2651 DicomTransferSyntax a = (DicomTransferSyntax) i; | |
2652 | |
2653 std::string path = ("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/" + | |
2654 std::string(GetTransferSyntaxUid(a)) + ".dcm"); | |
2655 if (Orthanc::SystemToolbox::IsRegularFile(path)) | |
2656 { | |
2657 printf("\n======= %s\n", GetTransferSyntaxUid(a)); | |
2658 | |
2659 std::string source; | |
2660 Orthanc::SystemToolbox::ReadFile(source, path); | |
2661 | |
2662 DcmtkImageReader reader; | |
2663 | |
2664 { | |
2665 std::unique_ptr<IParsedDicomImage> image( | |
2666 reader.Read(source.c_str(), source.size())); | |
2667 ASSERT_TRUE(image.get() != NULL); | |
2668 ASSERT_EQ(a, image->GetTransferSyntax()); | |
2669 | |
2670 std::string target; | |
2671 image->WriteToMemoryBuffer(target); | |
2672 } | |
2673 | |
2674 for (int j = 0; j <= DicomTransferSyntax_XML; j++) | |
2675 { | |
2676 DicomTransferSyntax b = (DicomTransferSyntax) j; | |
2677 //if (a == b) continue; | |
2678 | |
2679 std::unique_ptr<IParsedDicomImage> image( | |
2680 reader.Transcode(source.c_str(), source.size(), b, true)); | |
2681 if (image.get() != NULL) | |
2682 { | |
2683 printf("[%s] -> [%s]\n", GetTransferSyntaxUid(a), GetTransferSyntaxUid(b)); | |
2684 | |
2685 std::string target; | |
2686 image->WriteToMemoryBuffer(target); | |
2687 | |
2688 char buf[1024]; | |
2689 sprintf(buf, "/tmp/%s-%s.dcm", GetTransferSyntaxUid(a), GetTransferSyntaxUid(b)); | |
2690 | |
2691 SystemToolbox::WriteFile(target, buf); | |
2692 } | |
2693 else if (a != DicomTransferSyntax_JPEG2000 && | |
2694 a != DicomTransferSyntax_JPEG2000LosslessOnly) | |
2695 { | |
2696 ASSERT_TRUE(b != DicomTransferSyntax_LittleEndianImplicit && | |
2697 b != DicomTransferSyntax_LittleEndianExplicit && | |
2698 b != DicomTransferSyntax_BigEndianExplicit && | |
2699 b != DicomTransferSyntax_DeflatedLittleEndianExplicit); | |
2700 } | |
2701 } | |
2702 } | |
2703 } | |
2704 } | |
2705 | |
2706 | |
2707 #include "../Core/DicomNetworking/DicomAssociation.h" | |
2708 #include "../Core/DicomNetworking/DicomControlUserConnection.h" | |
2709 #include "../Core/DicomNetworking/DicomStoreUserConnection.h" | |
2710 | |
2711 TEST(Toto, DISABLED_DicomAssociation) | |
2712 { | |
2713 DicomAssociationParameters params; | |
2714 params.SetLocalApplicationEntityTitle("ORTHANC"); | |
2715 params.SetRemoteApplicationEntityTitle("PACS"); | |
2716 params.SetRemotePort(2001); | |
2717 | |
2718 #if 0 | |
2719 DicomAssociation assoc; | |
2720 assoc.ProposeGenericPresentationContext(UID_StorageCommitmentPushModelSOPClass); | |
2721 assoc.ProposeGenericPresentationContext(UID_VerificationSOPClass); | |
2722 assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage, | |
2723 DicomTransferSyntax_JPEGProcess1); | |
2724 assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage, | |
2725 DicomTransferSyntax_JPEGProcess2_4); | |
2726 assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage, | |
2727 DicomTransferSyntax_JPEG2000); | |
2728 | |
2729 assoc.Open(params); | |
2730 | |
2731 int presID = ASC_findAcceptedPresentationContextID(&assoc.GetDcmtkAssociation(), UID_ComputedRadiographyImageStorage); | |
2732 printf(">> %d\n", presID); | |
2733 | |
2734 std::map<DicomTransferSyntax, uint8_t> pc; | |
2735 printf(">> %d\n", assoc.LookupAcceptedPresentationContext(pc, UID_ComputedRadiographyImageStorage)); | |
2736 | |
2737 for (std::map<DicomTransferSyntax, uint8_t>::const_iterator | |
2738 it = pc.begin(); it != pc.end(); ++it) | |
2739 { | |
2740 printf("[%s] => %d\n", GetTransferSyntaxUid(it->first), it->second); | |
2741 } | |
2742 #else | |
2743 { | |
2744 DicomControlUserConnection assoc(params); | |
2745 | |
2746 try | |
2747 { | |
2748 printf(">> %d\n", assoc.Echo()); | |
2749 } | |
2750 catch (OrthancException&) | |
2751 { | |
2752 } | |
2753 } | |
2754 | |
2755 params.SetRemoteApplicationEntityTitle("PACS"); | |
2756 params.SetRemotePort(2000); | |
2757 | |
2758 { | |
2759 DicomControlUserConnection assoc(params); | |
2760 printf(">> %d\n", assoc.Echo()); | |
2761 } | |
2762 | |
2763 #endif | |
2764 } | |
2765 | |
2766 static void TestTranscode(DicomStoreUserConnection& scu, | |
2767 const std::string& sopClassUid, | |
2768 DicomTransferSyntax transferSyntax) | |
2769 { | |
2770 std::set<DicomTransferSyntax> accepted; | |
2771 | |
2772 scu.LookupTranscoding(accepted, sopClassUid, transferSyntax); | |
2773 if (accepted.empty()) | |
2774 { | |
2775 throw OrthancException(ErrorCode_NetworkProtocol, | |
2776 "The SOP class is not supported by the remote modality"); | |
2777 } | |
2778 | |
2779 { | |
2780 unsigned int count = 0; | |
2781 for (std::set<DicomTransferSyntax>::const_iterator | |
2782 it = accepted.begin(); it != accepted.end(); ++it) | |
2783 { | |
2784 LOG(INFO) << "available for transcoding " << (count++) << ": " << sopClassUid | |
2785 << " / " << GetTransferSyntaxUid(*it); | |
2786 } | |
2787 } | |
2788 | |
2789 if (accepted.find(transferSyntax) != accepted.end()) | |
2790 { | |
2791 printf("**** OK, without transcoding !! [%s]\n", GetTransferSyntaxUid(transferSyntax)); | |
2792 } | |
2793 else | |
2794 { | |
2795 // Transcoding - only in Orthanc >= 1.7.0 | |
2796 | |
2797 const DicomTransferSyntax uncompressed[] = { | |
2798 DicomTransferSyntax_LittleEndianImplicit, // Default transfer syntax | |
2799 DicomTransferSyntax_LittleEndianExplicit, | |
2800 DicomTransferSyntax_BigEndianExplicit | |
2801 }; | |
2802 | |
2803 bool found = false; | |
2804 for (size_t i = 0; i < 3; i++) | |
2805 { | |
2806 if (accepted.find(uncompressed[i]) != accepted.end()) | |
2807 { | |
2808 printf("**** TRANSCODING to %s\n", GetTransferSyntaxUid(uncompressed[i])); | |
2809 found = true; | |
2810 break; | |
2811 } | |
2812 } | |
2813 | |
2814 if (!found) | |
2815 { | |
2816 printf("**** KO KO KO\n"); | |
2817 } | |
2818 } | |
2819 } | |
2820 | |
2821 | |
2822 TEST(Toto, DISABLED_Store) | |
2823 { | |
2824 DicomAssociationParameters params; | |
2825 params.SetLocalApplicationEntityTitle("ORTHANC"); | |
2826 params.SetRemoteApplicationEntityTitle("STORESCP"); | |
2827 params.SetRemotePort(2000); | |
2828 | |
2829 DicomStoreUserConnection assoc(params); | |
2830 assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess1); | |
2831 assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess2_4); | |
2832 //assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit); | |
2833 | |
2834 //assoc.SetUncompressedSyntaxesProposed(false); // Necessary for transcoding | |
2835 assoc.SetCommonClassesProposed(false); | |
2836 assoc.SetRetiredBigEndianProposed(true); | |
2837 TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit); | |
2838 TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000); | |
2839 TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000); | |
2840 } | |
2841 | |
2842 | |
2843 TEST(Toto, DISABLED_Store2) | |
2844 { | |
2845 DicomAssociationParameters params; | |
2846 params.SetLocalApplicationEntityTitle("ORTHANC"); | |
2847 params.SetRemoteApplicationEntityTitle("STORESCP"); | |
2848 params.SetRemotePort(2000); | |
2849 | |
2850 DicomStoreUserConnection assoc(params); | |
2851 //assoc.SetCommonClassesProposed(false); | |
2852 assoc.SetRetiredBigEndianProposed(true); | |
2853 | |
2854 std::string s; | |
2855 Orthanc::SystemToolbox::ReadFile(s, "/tmp/i/" + std::string(GetTransferSyntaxUid(DicomTransferSyntax_BigEndianExplicit)) +".dcm"); | |
2856 | |
2857 std::string c, i; | |
2858 assoc.Store(c, i, s.c_str(), s.size()); | |
2859 printf("[%s] [%s]\n", c.c_str(), i.c_str()); | |
2860 } | |
2861 | 1941 |
2862 | 1942 |
2863 namespace Orthanc | 1943 namespace Orthanc |
2864 { | 1944 { |
2865 class IDicomTranscoder : public boost::noncopyable | 1945 class IDicomTranscoder : public boost::noncopyable |
3165 DicomStoreUserConnection scu(p); | 2245 DicomStoreUserConnection scu(p); |
3166 scu.SetCommonClassesProposed(false); | 2246 scu.SetCommonClassesProposed(false); |
3167 scu.SetRetiredBigEndianProposed(true); | 2247 scu.SetRetiredBigEndianProposed(true); |
3168 | 2248 |
3169 std::string c, i; | 2249 std::string c, i; |
3170 IDicomTranscoder::Store(c, i, scu, transcoder, source.c_str(), source.size()); | 2250 try |
2251 { | |
2252 IDicomTranscoder::Store(c, i, scu, transcoder, source.c_str(), source.size()); | |
2253 } | |
2254 catch (OrthancException& e) | |
2255 { | |
2256 if (e.GetErrorCode() == ErrorCode_NotImplemented) | |
2257 { | |
2258 LOG(ERROR) << "cannot transcode " << GetTransferSyntaxUid(a); | |
2259 } | |
2260 else | |
2261 { | |
2262 throw e; | |
2263 } | |
2264 } | |
3171 } | 2265 } |
3172 } | 2266 } |
3173 } | 2267 } |
3174 | 2268 |
3175 | 2269 |