comparison OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents Core/DicomParsing/DcmtkTranscoder.cpp@31252a887f0b
children bf7b9edf6b81
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34 #include "../PrecompiledHeaders.h"
35 #include "DcmtkTranscoder.h"
36
37
38 #if !defined(ORTHANC_ENABLE_DCMTK_JPEG)
39 # error Macro ORTHANC_ENABLE_DCMTK_JPEG must be defined
40 #endif
41
42 #if !defined(ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS)
43 # error Macro ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS must be defined
44 #endif
45
46
47 #include "FromDcmtkBridge.h"
48 #include "../OrthancException.h"
49
50 #include <dcmtk/dcmdata/dcdeftag.h>
51 #include <dcmtk/dcmjpeg/djrploss.h> // for DJ_RPLossy
52 #include <dcmtk/dcmjpeg/djrplol.h> // for DJ_RPLossless
53 #include <dcmtk/dcmjpls/djrparam.h> // for DJLSRepresentationParameter
54
55
56 namespace Orthanc
57 {
58 static bool GetBitsStored(uint16_t& bitsStored,
59 DcmDataset& dataset)
60 {
61 return dataset.findAndGetUint16(DCM_BitsStored, bitsStored).good();
62 }
63
64
65 void DcmtkTranscoder::SetLossyQuality(unsigned int quality)
66 {
67 if (quality <= 0 ||
68 quality > 100)
69 {
70 throw OrthancException(
71 ErrorCode_ParameterOutOfRange,
72 "The quality for lossy transcoding must be an integer between 1 and 100, received: " +
73 boost::lexical_cast<std::string>(quality));
74 }
75 else
76 {
77 LOG(INFO) << "Quality for lossy transcoding using DCMTK is set to: " << quality;
78 lossyQuality_ = quality;
79 }
80 }
81
82
83 bool DcmtkTranscoder::InplaceTranscode(DicomTransferSyntax& selectedSyntax /* out */,
84 DcmFileFormat& dicom,
85 const std::set<DicomTransferSyntax>& allowedSyntaxes,
86 bool allowNewSopInstanceUid)
87 {
88 if (dicom.getDataset() == NULL)
89 {
90 throw OrthancException(ErrorCode_InternalError);
91 }
92
93 DicomTransferSyntax syntax;
94 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(syntax, dicom))
95 {
96 throw OrthancException(ErrorCode_BadFileFormat,
97 "Cannot determine the transfer syntax");
98 }
99
100 uint16_t bitsStored;
101 bool hasBitsStored = GetBitsStored(bitsStored, *dicom.getDataset());
102
103 std::string sourceSopInstanceUid = IDicomTranscoder::GetSopInstanceUid(dicom);
104
105 if (allowedSyntaxes.find(syntax) != allowedSyntaxes.end())
106 {
107 // No transcoding is needed
108 return true;
109 }
110
111 if (allowedSyntaxes.find(DicomTransferSyntax_LittleEndianImplicit) != allowedSyntaxes.end() &&
112 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_LittleEndianImplicit, NULL))
113 {
114 selectedSyntax = DicomTransferSyntax_LittleEndianImplicit;
115 return true;
116 }
117
118 if (allowedSyntaxes.find(DicomTransferSyntax_LittleEndianExplicit) != allowedSyntaxes.end() &&
119 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_LittleEndianExplicit, NULL))
120 {
121 selectedSyntax = DicomTransferSyntax_LittleEndianExplicit;
122 return true;
123 }
124
125 if (allowedSyntaxes.find(DicomTransferSyntax_BigEndianExplicit) != allowedSyntaxes.end() &&
126 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_BigEndianExplicit, NULL))
127 {
128 selectedSyntax = DicomTransferSyntax_BigEndianExplicit;
129 return true;
130 }
131
132 if (allowedSyntaxes.find(DicomTransferSyntax_DeflatedLittleEndianExplicit) != allowedSyntaxes.end() &&
133 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_DeflatedLittleEndianExplicit, NULL))
134 {
135 selectedSyntax = DicomTransferSyntax_DeflatedLittleEndianExplicit;
136 return true;
137 }
138
139 #if ORTHANC_ENABLE_DCMTK_JPEG == 1
140 if (allowedSyntaxes.find(DicomTransferSyntax_JPEGProcess1) != allowedSyntaxes.end() &&
141 allowNewSopInstanceUid &&
142 (!hasBitsStored || bitsStored == 8))
143 {
144 // Check out "dcmjpeg/apps/dcmcjpeg.cc"
145 DJ_RPLossy parameters(lossyQuality_);
146
147 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess1, &parameters))
148 {
149 selectedSyntax = DicomTransferSyntax_JPEGProcess1;
150 return true;
151 }
152 }
153 #endif
154
155 #if ORTHANC_ENABLE_DCMTK_JPEG == 1
156 if (allowedSyntaxes.find(DicomTransferSyntax_JPEGProcess2_4) != allowedSyntaxes.end() &&
157 allowNewSopInstanceUid &&
158 (!hasBitsStored || bitsStored <= 12))
159 {
160 // Check out "dcmjpeg/apps/dcmcjpeg.cc"
161 DJ_RPLossy parameters(lossyQuality_);
162 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess2_4, &parameters))
163 {
164 selectedSyntax = DicomTransferSyntax_JPEGProcess2_4;
165 return true;
166 }
167 }
168 #endif
169
170 #if ORTHANC_ENABLE_DCMTK_JPEG == 1
171 if (allowedSyntaxes.find(DicomTransferSyntax_JPEGProcess14) != allowedSyntaxes.end())
172 {
173 // Check out "dcmjpeg/apps/dcmcjpeg.cc"
174 DJ_RPLossless parameters(6 /* opt_selection_value */,
175 0 /* opt_point_transform */);
176 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess14, &parameters))
177 {
178 selectedSyntax = DicomTransferSyntax_JPEGProcess14;
179 return true;
180 }
181 }
182 #endif
183
184 #if ORTHANC_ENABLE_DCMTK_JPEG == 1
185 if (allowedSyntaxes.find(DicomTransferSyntax_JPEGProcess14SV1) != allowedSyntaxes.end())
186 {
187 // Check out "dcmjpeg/apps/dcmcjpeg.cc"
188 DJ_RPLossless parameters(6 /* opt_selection_value */,
189 0 /* opt_point_transform */);
190 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess14SV1, &parameters))
191 {
192 selectedSyntax = DicomTransferSyntax_JPEGProcess14SV1;
193 return true;
194 }
195 }
196 #endif
197
198 #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1
199 if (allowedSyntaxes.find(DicomTransferSyntax_JPEGLSLossless) != allowedSyntaxes.end())
200 {
201 // Check out "dcmjpls/apps/dcmcjpls.cc"
202 DJLSRepresentationParameter parameters(2 /* opt_nearlossless_deviation */,
203 OFTrue /* opt_useLosslessProcess */);
204
205 /**
206 * WARNING: This call results in a segmentation fault if using
207 * the DCMTK package 3.6.2 from Ubuntu 18.04.
208 **/
209 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGLSLossless, &parameters))
210 {
211 selectedSyntax = DicomTransferSyntax_JPEGLSLossless;
212 return true;
213 }
214 }
215 #endif
216
217 #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1
218 if (allowNewSopInstanceUid &&
219 allowedSyntaxes.find(DicomTransferSyntax_JPEGLSLossy) != allowedSyntaxes.end())
220 {
221 // Check out "dcmjpls/apps/dcmcjpls.cc"
222 DJLSRepresentationParameter parameters(2 /* opt_nearlossless_deviation */,
223 OFFalse /* opt_useLosslessProcess */);
224
225 /**
226 * WARNING: This call results in a segmentation fault if using
227 * the DCMTK package 3.6.2 from Ubuntu 18.04.
228 **/
229 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGLSLossy, &parameters))
230 {
231 selectedSyntax = DicomTransferSyntax_JPEGLSLossy;
232 return true;
233 }
234 }
235 #endif
236
237 return false;
238 }
239
240
241 bool DcmtkTranscoder::IsSupported(DicomTransferSyntax syntax)
242 {
243 if (syntax == DicomTransferSyntax_LittleEndianImplicit ||
244 syntax == DicomTransferSyntax_LittleEndianExplicit ||
245 syntax == DicomTransferSyntax_BigEndianExplicit ||
246 syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit)
247 {
248 return true;
249 }
250
251 #if ORTHANC_ENABLE_DCMTK_JPEG == 1
252 if (syntax == DicomTransferSyntax_JPEGProcess1 ||
253 syntax == DicomTransferSyntax_JPEGProcess2_4 ||
254 syntax == DicomTransferSyntax_JPEGProcess14 ||
255 syntax == DicomTransferSyntax_JPEGProcess14SV1)
256 {
257 return true;
258 }
259 #endif
260
261 #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1
262 if (syntax == DicomTransferSyntax_JPEGLSLossless ||
263 syntax == DicomTransferSyntax_JPEGLSLossy)
264 {
265 return true;
266 }
267 #endif
268
269 return false;
270 }
271
272
273 bool DcmtkTranscoder::Transcode(DicomImage& target,
274 DicomImage& source /* in, "GetParsed()" possibly modified */,
275 const std::set<DicomTransferSyntax>& allowedSyntaxes,
276 bool allowNewSopInstanceUid)
277 {
278 target.Clear();
279
280 DicomTransferSyntax sourceSyntax;
281 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(sourceSyntax, source.GetParsed()))
282 {
283 LOG(ERROR) << "Unsupport transfer syntax for transcoding";
284 return false;
285 }
286
287 {
288 std::string s;
289 for (std::set<DicomTransferSyntax>::const_iterator
290 it = allowedSyntaxes.begin(); it != allowedSyntaxes.end(); ++it)
291 {
292 if (!s.empty())
293 {
294 s += ", ";
295 }
296
297 s += GetTransferSyntaxUid(*it);
298 }
299
300 if (s.empty())
301 {
302 s = "<none>";
303 }
304
305 LOG(INFO) << "DCMTK transcoding from " << GetTransferSyntaxUid(sourceSyntax)
306 << " to one of: " << s;
307 }
308
309 #if !defined(NDEBUG)
310 const std::string sourceSopInstanceUid = GetSopInstanceUid(source.GetParsed());
311 #endif
312
313 DicomTransferSyntax targetSyntax;
314 if (allowedSyntaxes.find(sourceSyntax) != allowedSyntaxes.end())
315 {
316 // No transcoding is needed
317 target.AcquireParsed(source);
318 target.AcquireBuffer(source);
319 return true;
320 }
321 else if (InplaceTranscode(targetSyntax, source.GetParsed(),
322 allowedSyntaxes, allowNewSopInstanceUid))
323 {
324 // Sanity check
325 DicomTransferSyntax targetSyntax2;
326 if (FromDcmtkBridge::LookupOrthancTransferSyntax(targetSyntax2, source.GetParsed()) &&
327 targetSyntax == targetSyntax2 &&
328 allowedSyntaxes.find(targetSyntax2) != allowedSyntaxes.end())
329 {
330 target.AcquireParsed(source);
331 source.Clear();
332
333 #if !defined(NDEBUG)
334 // Only run the sanity check in debug mode
335 CheckTranscoding(target, sourceSyntax, sourceSopInstanceUid,
336 allowedSyntaxes, allowNewSopInstanceUid);
337 #endif
338
339 return true;
340 }
341 else
342 {
343 throw OrthancException(ErrorCode_InternalError);
344 }
345 }
346 else
347 {
348 // Cannot transcode
349 return false;
350 }
351 }
352 }