Mercurial > hg > orthanc
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" |