comparison Core/DicomParsing/FromDcmtkBridge.cpp @ 3956:6e14f2da7c7e

integration transcoding->mainline
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 20 May 2020 16:42:44 +0200
parents 74eeadf5d51d
children 884b55ce01f6
comparison
equal deleted inserted replaced
3892:fe0e4ef52a72 3956:6e14f2da7c7e
119 # include <dcmtk/dcmjpls/djencode.h> 119 # include <dcmtk/dcmjpls/djencode.h>
120 # endif 120 # endif
121 #endif 121 #endif
122 122
123 123
124 #include <dcmtk/dcmdata/dcrledrg.h>
125 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
126 # include <dcmtk/dcmdata/dcrleerg.h>
127 # include <dcmtk/dcmimage/diregist.h> // include to support color images
128 #endif
129
130
124 namespace Orthanc 131 namespace Orthanc
125 { 132 {
126 static bool IsBinaryTag(const DcmTag& key) 133 static bool IsBinaryTag(const DcmTag& key)
127 { 134 {
128 return (key.isUnknownVR() || 135 return (key.isUnknownVR() ||
1196 default: 1203 default:
1197 throw OrthancException(ErrorCode_ParameterOutOfRange); 1204 throw OrthancException(ErrorCode_ParameterOutOfRange);
1198 } 1205 }
1199 } 1206 }
1200 1207
1208
1209
1210 static bool SaveToMemoryBufferInternal(std::string& buffer,
1211 DcmFileFormat& dicom,
1212 E_TransferSyntax xfer)
1213 {
1214 E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength;
1215
1216 // Create a memory buffer with the proper size
1217 {
1218 const uint32_t estimatedSize = dicom.calcElementLength(xfer, encodingType); // (*)
1219 buffer.resize(estimatedSize);
1220 }
1221
1222 DcmOutputBufferStream ob(&buffer[0], buffer.size());
1223
1224 // Fill the memory buffer with the meta-header and the dataset
1225 dicom.transferInit();
1226 OFCondition c = dicom.write(ob, xfer, encodingType, NULL,
1227 /*opt_groupLength*/ EGL_recalcGL,
1228 /*opt_paddingType*/ EPD_noChange,
1229 /*padlen*/ 0, /*subPadlen*/ 0, /*instanceLength*/ 0,
1230 EWM_updateMeta /* creates new SOP instance UID on lossy */);
1231 dicom.transferEnd();
1232
1233 if (c.good())
1234 {
1235 // The DICOM file is successfully written, truncate the target
1236 // buffer if its size was overestimated by (*)
1237 ob.flush();
1238
1239 size_t effectiveSize = static_cast<size_t>(ob.tell());
1240 if (effectiveSize < buffer.size())
1241 {
1242 buffer.resize(effectiveSize);
1243 }
1244
1245 return true;
1246 }
1247 else
1248 {
1249 // Error
1250 buffer.clear();
1251 return false;
1252 }
1253 }
1254
1255
1201 bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer, 1256 bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer,
1202 DcmDataset& dataSet) 1257 DcmDataset& dataSet)
1203 { 1258 {
1204 // Determine the transfer syntax which shall be used to write the 1259 // Determine the transfer syntax which shall be used to write the
1205 // information to the file. We always switch to the Little Endian 1260 // information to the file. If not possible, switch to the Little
1206 // syntax, with explicit length. 1261 // Endian syntax, with explicit length.
1207 1262
1208 // http://support.dcmtk.org/docs/dcxfer_8h-source.html 1263 // http://support.dcmtk.org/docs/dcxfer_8h-source.html
1209 1264
1210 1265
1211 /** 1266 /**
1212 * Note that up to Orthanc 0.7.1 (inclusive), the 1267 * Note that up to Orthanc 0.7.1 (inclusive), the
1213 * "EXS_LittleEndianExplicit" was always used to save the DICOM 1268 * "EXS_LittleEndianExplicit" was always used to save the DICOM
1214 * dataset into memory. We now keep the original transfer syntax 1269 * dataset into memory. We now keep the original transfer syntax
1215 * (if available). 1270 * (if available).
1216 **/ 1271 **/
1217 E_TransferSyntax xfer = dataSet.getOriginalXfer(); 1272 E_TransferSyntax xfer = dataSet.getCurrentXfer();
1218 if (xfer == EXS_Unknown) 1273 if (xfer == EXS_Unknown)
1219 { 1274 {
1220 // No information about the original transfer syntax: This is 1275 // No information about the original transfer syntax: This is
1221 // most probably a DICOM dataset that was read from memory. 1276 // most probably a DICOM dataset that was read from memory.
1222 xfer = EXS_LittleEndianExplicit; 1277 xfer = EXS_LittleEndianExplicit;
1223 } 1278 }
1224
1225 E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength;
1226 1279
1227 // Create the meta-header information 1280 // Create the meta-header information
1228 DcmFileFormat ff(&dataSet); 1281 DcmFileFormat ff(&dataSet);
1229 ff.validateMetaInfo(xfer); 1282 ff.validateMetaInfo(xfer);
1230 ff.removeInvalidGroups(); 1283 ff.removeInvalidGroups();
1231 1284
1232 // Create a memory buffer with the proper size 1285 return SaveToMemoryBufferInternal(buffer, ff, xfer);
1233 { 1286 }
1234 const uint32_t estimatedSize = ff.calcElementLength(xfer, encodingType); // (*) 1287
1235 buffer.resize(estimatedSize); 1288
1236 } 1289 bool FromDcmtkBridge::Transcode(DcmFileFormat& dicom,
1237 1290 DicomTransferSyntax syntax,
1238 DcmOutputBufferStream ob(&buffer[0], buffer.size()); 1291 const DcmRepresentationParameter* representation)
1239 1292 {
1240 // Fill the memory buffer with the meta-header and the dataset 1293 E_TransferSyntax xfer;
1241 ff.transferInit(); 1294 if (!LookupDcmtkTransferSyntax(xfer, syntax))
1242 OFCondition c = ff.write(ob, xfer, encodingType, NULL, 1295 {
1243 /*opt_groupLength*/ EGL_recalcGL, 1296 throw OrthancException(ErrorCode_InternalError);
1244 /*opt_paddingType*/ EPD_withoutPadding);
1245 ff.transferEnd();
1246
1247 if (c.good())
1248 {
1249 // The DICOM file is successfully written, truncate the target
1250 // buffer if its size was overestimated by (*)
1251 ob.flush();
1252
1253 size_t effectiveSize = static_cast<size_t>(ob.tell());
1254 if (effectiveSize < buffer.size())
1255 {
1256 buffer.resize(effectiveSize);
1257 }
1258
1259 return true;
1260 } 1297 }
1261 else 1298 else
1262 { 1299 {
1263 // Error 1300 DicomTransferSyntax sourceSyntax;
1264 buffer.clear(); 1301 bool known = LookupOrthancTransferSyntax(sourceSyntax, dicom);
1265 return false; 1302
1303 if (!dicom.chooseRepresentation(xfer, representation).good() ||
1304 !dicom.canWriteXfer(xfer) ||
1305 !dicom.validateMetaInfo(xfer, EWM_updateMeta).good())
1306 {
1307 return false;
1308 }
1309 else
1310 {
1311 dicom.removeInvalidGroups();
1312
1313 if (known)
1314 {
1315 LOG(INFO) << "Transcoded an image from transfer syntax "
1316 << GetTransferSyntaxUid(sourceSyntax) << " to "
1317 << GetTransferSyntaxUid(syntax);
1318 }
1319 else
1320 {
1321 LOG(INFO) << "Transcoded an image from unknown transfer syntax to "
1322 << GetTransferSyntaxUid(syntax);
1323 }
1324
1325 return true;
1326 }
1266 } 1327 }
1267 } 1328 }
1268 1329
1269 1330
1270 ValueRepresentation FromDcmtkBridge::LookupValueRepresentation(const DicomTag& tag) 1331 ValueRepresentation FromDcmtkBridge::LookupValueRepresentation(const DicomTag& tag)
1657 case Json::arrayValue: 1718 case Json::arrayValue:
1658 { 1719 {
1659 DcmTag key(tag.GetGroup(), tag.GetElement()); 1720 DcmTag key(tag.GetGroup(), tag.GetElement());
1660 if (key.getEVR() != EVR_SQ) 1721 if (key.getEVR() != EVR_SQ)
1661 { 1722 {
1662 throw OrthancException(ErrorCode_BadParameterType); 1723 throw OrthancException(ErrorCode_BadParameterType, "Bad Parameter type for tag " + tag.Format());
1663 } 1724 }
1664 1725
1665 DcmSequenceOfItems* sequence = new DcmSequenceOfItems(key); 1726 DcmSequenceOfItems* sequence = new DcmSequenceOfItems(key);
1666 element.reset(sequence); 1727 element.reset(sequence);
1667 1728
1701 1762
1702 break; 1763 break;
1703 } 1764 }
1704 1765
1705 default: 1766 default:
1706 throw OrthancException(ErrorCode_BadParameterType); 1767 throw OrthancException(ErrorCode_BadParameterType, "Bad Parameter type for tag " + tag.Format());
1707 } 1768 }
1708 1769
1709 return element.release(); 1770 return element.release();
1710 } 1771 }
1711 1772
1717 { 1778 {
1718 throw OrthancException(ErrorCode_BadFileFormat); 1779 throw OrthancException(ErrorCode_BadFileFormat);
1719 } 1780 }
1720 1781
1721 DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element); 1782 DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element);
1783
1784 E_TransferSyntax repType;
1785 const DcmRepresentationParameter *repParam = NULL;
1786 pixelData.getCurrentRepresentationKey(repType, repParam);
1787
1722 DcmPixelSequence* pixelSequence = NULL; 1788 DcmPixelSequence* pixelSequence = NULL;
1723 if (!pixelData.getEncapsulatedRepresentation 1789 if (!pixelData.getEncapsulatedRepresentation(repType, repParam, pixelSequence).good())
1724 (dataset.getOriginalXfer(), NULL, pixelSequence).good())
1725 { 1790 {
1726 return NULL; 1791 return NULL;
1727 } 1792 }
1728 else 1793 else
1729 { 1794 {
1972 } 2037 }
1973 } 2038 }
1974 } 2039 }
1975 2040
1976 2041
1977 bool FromDcmtkBridge::LookupTransferSyntax(std::string& result,
1978 DcmFileFormat& dicom)
1979 {
1980 const char* value = NULL;
1981
1982 if (dicom.getMetaInfo() != NULL &&
1983 dicom.getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, value).good() &&
1984 value != NULL)
1985 {
1986 result.assign(value);
1987 return true;
1988 }
1989 else
1990 {
1991 return false;
1992 }
1993 }
1994
1995
1996 #if ORTHANC_ENABLE_LUA == 1 2042 #if ORTHANC_ENABLE_LUA == 1
1997 void FromDcmtkBridge::ExecuteToDicom(DicomMap& target, 2043 void FromDcmtkBridge::ExecuteToDicom(DicomMap& target,
1998 LuaFunctionCall& call) 2044 LuaFunctionCall& call)
1999 { 2045 {
2000 Json::Value output; 2046 Json::Value output;
2071 DJDecoderRegistration::registerCodecs(); 2117 DJDecoderRegistration::registerCodecs();
2072 # if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 2118 # if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
2073 DJEncoderRegistration::registerCodecs(); 2119 DJEncoderRegistration::registerCodecs();
2074 # endif 2120 # endif
2075 #endif 2121 #endif
2122
2123 LOG(INFO) << "Registering RLE codecs in DCMTK";
2124 DcmRLEDecoderRegistration::registerCodecs();
2125 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
2126 DcmRLEEncoderRegistration::registerCodecs();
2127 #endif
2076 } 2128 }
2077 2129
2078 2130
2079 void FromDcmtkBridge::FinalizeCodecs() 2131 void FromDcmtkBridge::FinalizeCodecs()
2080 { 2132 {
2090 // Unregister JPEG codecs 2142 // Unregister JPEG codecs
2091 DJDecoderRegistration::cleanup(); 2143 DJDecoderRegistration::cleanup();
2092 # if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 2144 # if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
2093 DJEncoderRegistration::cleanup(); 2145 DJEncoderRegistration::cleanup();
2094 # endif 2146 # endif
2147 #endif
2148
2149 DcmRLEDecoderRegistration::cleanup();
2150 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
2151 DcmRLEEncoderRegistration::cleanup();
2095 #endif 2152 #endif
2096 } 2153 }
2097 2154
2098 2155
2099 2156
2576 std::vector<size_t> parentIndexes; 2633 std::vector<size_t> parentIndexes;
2577 bool hasCodeExtensions; 2634 bool hasCodeExtensions;
2578 Encoding encoding = DetectEncoding(hasCodeExtensions, dataset, defaultEncoding); 2635 Encoding encoding = DetectEncoding(hasCodeExtensions, dataset, defaultEncoding);
2579 ApplyVisitorToDataset(dataset, visitor, parentTags, parentIndexes, encoding, hasCodeExtensions); 2636 ApplyVisitorToDataset(dataset, visitor, parentTags, parentIndexes, encoding, hasCodeExtensions);
2580 } 2637 }
2638
2639
2640
2641 bool FromDcmtkBridge::LookupOrthancTransferSyntax(DicomTransferSyntax& target,
2642 DcmFileFormat& dicom)
2643 {
2644 if (dicom.getDataset() == NULL)
2645 {
2646 throw OrthancException(ErrorCode_InternalError);
2647 }
2648
2649 DcmDataset& dataset = *dicom.getDataset();
2650
2651 E_TransferSyntax xfer = dataset.getCurrentXfer();
2652 if (xfer == EXS_Unknown)
2653 {
2654 dataset.updateOriginalXfer();
2655 xfer = dataset.getOriginalXfer();
2656 if (xfer == EXS_Unknown)
2657 {
2658 throw OrthancException(ErrorCode_BadFileFormat,
2659 "Cannot determine the transfer syntax of the DICOM instance");
2660 }
2661 }
2662
2663 return FromDcmtkBridge::LookupOrthancTransferSyntax(target, xfer);
2664 }
2581 } 2665 }
2582 2666
2583 2667
2584 #include "./FromDcmtkBridge_TransferSyntaxes.impl.h" 2668 #include "./FromDcmtkBridge_TransferSyntaxes.impl.h"