comparison OrthancFramework/Sources/DicomParsing/IDicomTranscoder.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/IDicomTranscoder.cpp@5fe8c6d3212e
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 "IDicomTranscoder.h"
36
37 #include "../OrthancException.h"
38 #include "FromDcmtkBridge.h"
39 #include "ParsedDicomFile.h"
40
41 #include <dcmtk/dcmdata/dcfilefo.h>
42 #include <dcmtk/dcmdata/dcdeftag.h>
43
44 namespace Orthanc
45 {
46 IDicomTranscoder::TranscodingType IDicomTranscoder::GetTranscodingType(DicomTransferSyntax target,
47 DicomTransferSyntax source)
48 {
49 if (target == source)
50 {
51 return TranscodingType_Lossless;
52 }
53 else if (target == DicomTransferSyntax_LittleEndianImplicit ||
54 target == DicomTransferSyntax_LittleEndianExplicit ||
55 target == DicomTransferSyntax_BigEndianExplicit ||
56 target == DicomTransferSyntax_DeflatedLittleEndianExplicit ||
57 target == DicomTransferSyntax_JPEGProcess14 ||
58 target == DicomTransferSyntax_JPEGProcess14SV1 ||
59 target == DicomTransferSyntax_JPEGLSLossless ||
60 target == DicomTransferSyntax_JPEG2000LosslessOnly ||
61 target == DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly)
62 {
63 return TranscodingType_Lossless;
64 }
65 else if (target == DicomTransferSyntax_JPEGProcess1 ||
66 target == DicomTransferSyntax_JPEGProcess2_4 ||
67 target == DicomTransferSyntax_JPEGLSLossy ||
68 target == DicomTransferSyntax_JPEG2000 ||
69 target == DicomTransferSyntax_JPEG2000Multicomponent)
70 {
71 return TranscodingType_Lossy;
72 }
73 else
74 {
75 return TranscodingType_Unknown;
76 }
77 }
78
79
80 std::string IDicomTranscoder::GetSopInstanceUid(DcmFileFormat& dicom)
81 {
82 if (dicom.getDataset() == NULL)
83 {
84 throw OrthancException(ErrorCode_InternalError);
85 }
86
87 DcmDataset& dataset = *dicom.getDataset();
88
89 const char* v = NULL;
90
91 if (dataset.findAndGetString(DCM_SOPInstanceUID, v).good() &&
92 v != NULL)
93 {
94 return std::string(v);
95 }
96 else
97 {
98 throw OrthancException(ErrorCode_BadFileFormat, "File without SOP instance UID");
99 }
100 }
101
102
103 void IDicomTranscoder::CheckTranscoding(IDicomTranscoder::DicomImage& transcoded,
104 DicomTransferSyntax sourceSyntax,
105 const std::string& sourceSopInstanceUid,
106 const std::set<DicomTransferSyntax>& allowedSyntaxes,
107 bool allowNewSopInstanceUid)
108 {
109 DcmFileFormat& parsed = transcoded.GetParsed();
110
111 if (parsed.getDataset() == NULL)
112 {
113 throw OrthancException(ErrorCode_InternalError);
114 }
115
116 std::string targetSopInstanceUid = GetSopInstanceUid(parsed);
117
118 if (parsed.getDataset()->tagExists(DCM_PixelData))
119 {
120 if (!allowNewSopInstanceUid && (targetSopInstanceUid != sourceSopInstanceUid))
121 {
122 throw OrthancException(ErrorCode_InternalError);
123 }
124 }
125 else
126 {
127 if (targetSopInstanceUid != sourceSopInstanceUid)
128 {
129 throw OrthancException(ErrorCode_InternalError,
130 "No pixel data: Transcoding must not change the SOP instance UID");
131 }
132 }
133
134 DicomTransferSyntax targetSyntax;
135 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(targetSyntax, parsed))
136 {
137 return; // Unknown transfer syntax, cannot do further test
138 }
139
140 if (allowedSyntaxes.find(sourceSyntax) != allowedSyntaxes.end())
141 {
142 // No transcoding should have happened
143 if (targetSopInstanceUid != sourceSopInstanceUid)
144 {
145 throw OrthancException(ErrorCode_InternalError);
146 }
147 }
148
149 if (allowedSyntaxes.find(targetSyntax) == allowedSyntaxes.end())
150 {
151 throw OrthancException(ErrorCode_InternalError, "An incorrect output transfer syntax was chosen");
152 }
153
154 if (parsed.getDataset()->tagExists(DCM_PixelData))
155 {
156 switch (GetTranscodingType(targetSyntax, sourceSyntax))
157 {
158 case TranscodingType_Lossy:
159 if (targetSopInstanceUid == sourceSopInstanceUid)
160 {
161 throw OrthancException(ErrorCode_InternalError);
162 }
163 break;
164
165 case TranscodingType_Lossless:
166 if (targetSopInstanceUid != sourceSopInstanceUid)
167 {
168 throw OrthancException(ErrorCode_InternalError);
169 }
170 break;
171
172 default:
173 break;
174 }
175 }
176 }
177
178
179 void IDicomTranscoder::DicomImage::Parse()
180 {
181 if (parsed_.get() != NULL)
182 {
183 // Already parsed
184 throw OrthancException(ErrorCode_BadSequenceOfCalls);
185 }
186 else if (buffer_.get() != NULL)
187 {
188 if (isExternalBuffer_)
189 {
190 throw OrthancException(ErrorCode_InternalError);
191 }
192 else
193 {
194 parsed_.reset(FromDcmtkBridge::LoadFromMemoryBuffer(
195 buffer_->empty() ? NULL : buffer_->c_str(), buffer_->size()));
196
197 if (parsed_.get() == NULL)
198 {
199 throw OrthancException(ErrorCode_BadFileFormat);
200 }
201 }
202 }
203 else if (isExternalBuffer_)
204 {
205 parsed_.reset(FromDcmtkBridge::LoadFromMemoryBuffer(externalBuffer_, externalSize_));
206
207 if (parsed_.get() == NULL)
208 {
209 throw OrthancException(ErrorCode_BadFileFormat);
210 }
211 }
212 else
213 {
214 // No buffer is available
215 throw OrthancException(ErrorCode_BadSequenceOfCalls);
216 }
217 }
218
219
220 void IDicomTranscoder::DicomImage::Serialize()
221 {
222 if (parsed_.get() == NULL ||
223 buffer_.get() != NULL ||
224 isExternalBuffer_)
225 {
226 throw OrthancException(ErrorCode_BadSequenceOfCalls);
227 }
228 else if (parsed_->getDataset() == NULL)
229 {
230 throw OrthancException(ErrorCode_InternalError);
231 }
232 else
233 {
234 buffer_.reset(new std::string);
235 FromDcmtkBridge::SaveToMemoryBuffer(*buffer_, *parsed_->getDataset());
236 }
237 }
238
239
240 IDicomTranscoder::DicomImage::DicomImage() :
241 isExternalBuffer_(false)
242 {
243 }
244
245
246 void IDicomTranscoder::DicomImage::Clear()
247 {
248 parsed_.reset(NULL);
249 buffer_.reset(NULL);
250 isExternalBuffer_ = false;
251 }
252
253
254 void IDicomTranscoder::DicomImage::AcquireParsed(ParsedDicomFile& parsed)
255 {
256 AcquireParsed(parsed.ReleaseDcmtkObject());
257 }
258
259
260 void IDicomTranscoder::DicomImage::AcquireParsed(DcmFileFormat* parsed)
261 {
262 if (parsed == NULL)
263 {
264 throw OrthancException(ErrorCode_NullPointer);
265 }
266 else if (parsed->getDataset() == NULL)
267 {
268 throw OrthancException(ErrorCode_InternalError);
269 }
270 else if (parsed_.get() != NULL)
271 {
272 throw OrthancException(ErrorCode_BadSequenceOfCalls);
273 }
274 else
275 {
276 parsed_.reset(parsed);
277 }
278 }
279
280
281 void IDicomTranscoder::DicomImage::AcquireParsed(DicomImage& other)
282 {
283 AcquireParsed(other.ReleaseParsed());
284 }
285
286
287 void IDicomTranscoder::DicomImage::AcquireBuffer(std::string& buffer /* will be swapped */)
288 {
289 if (buffer_.get() != NULL ||
290 isExternalBuffer_)
291 {
292 throw OrthancException(ErrorCode_BadSequenceOfCalls);
293 }
294 else
295 {
296 buffer_.reset(new std::string);
297 buffer_->swap(buffer);
298 }
299 }
300
301
302 void IDicomTranscoder::DicomImage::AcquireBuffer(DicomImage& other)
303 {
304 if (buffer_.get() != NULL ||
305 isExternalBuffer_)
306 {
307 throw OrthancException(ErrorCode_BadSequenceOfCalls);
308 }
309 else if (other.isExternalBuffer_)
310 {
311 assert(other.buffer_.get() == NULL);
312 isExternalBuffer_ = true;
313 externalBuffer_ = other.externalBuffer_;
314 externalSize_ = other.externalSize_;
315 }
316 else if (other.buffer_.get() != NULL)
317 {
318 buffer_.reset(other.buffer_.release());
319 }
320 else
321 {
322 buffer_.reset(NULL);
323 }
324 }
325
326
327 void IDicomTranscoder::DicomImage::SetExternalBuffer(const void* buffer,
328 size_t size)
329 {
330 if (buffer_.get() != NULL ||
331 isExternalBuffer_)
332 {
333 throw OrthancException(ErrorCode_BadSequenceOfCalls);
334 }
335 else
336 {
337 isExternalBuffer_ = true;
338 externalBuffer_ = buffer;
339 externalSize_ = size;
340 }
341 }
342
343
344 void IDicomTranscoder::DicomImage::SetExternalBuffer(const std::string& buffer)
345 {
346 SetExternalBuffer(buffer.empty() ? NULL : buffer.c_str(), buffer.size());
347 }
348
349
350 DcmFileFormat& IDicomTranscoder::DicomImage::GetParsed()
351 {
352 if (parsed_.get() != NULL)
353 {
354 return *parsed_;
355 }
356 else if (buffer_.get() != NULL ||
357 isExternalBuffer_)
358 {
359 Parse();
360 return *parsed_;
361 }
362 else
363 {
364 throw OrthancException(
365 ErrorCode_BadSequenceOfCalls,
366 "AcquireParsed(), AcquireBuffer() or SetExternalBuffer() should have been called");
367 }
368 }
369
370
371 DcmFileFormat* IDicomTranscoder::DicomImage::ReleaseParsed()
372 {
373 if (parsed_.get() != NULL)
374 {
375 buffer_.reset(NULL);
376 return parsed_.release();
377 }
378 else if (buffer_.get() != NULL ||
379 isExternalBuffer_)
380 {
381 Parse();
382 buffer_.reset(NULL);
383 return parsed_.release();
384 }
385 else
386 {
387 throw OrthancException(
388 ErrorCode_BadSequenceOfCalls,
389 "AcquireParsed(), AcquireBuffer() or SetExternalBuffer() should have been called");
390 }
391 }
392
393
394 ParsedDicomFile* IDicomTranscoder::DicomImage::ReleaseAsParsedDicomFile()
395 {
396 return ParsedDicomFile::AcquireDcmtkObject(ReleaseParsed());
397 }
398
399
400 const void* IDicomTranscoder::DicomImage::GetBufferData()
401 {
402 if (isExternalBuffer_)
403 {
404 assert(buffer_.get() == NULL);
405 return externalBuffer_;
406 }
407 else
408 {
409 if (buffer_.get() == NULL)
410 {
411 Serialize();
412 }
413
414 assert(buffer_.get() != NULL);
415 return buffer_->empty() ? NULL : buffer_->c_str();
416 }
417 }
418
419
420 size_t IDicomTranscoder::DicomImage::GetBufferSize()
421 {
422 if (isExternalBuffer_)
423 {
424 assert(buffer_.get() == NULL);
425 return externalSize_;
426 }
427 else
428 {
429 if (buffer_.get() == NULL)
430 {
431 Serialize();
432 }
433
434 assert(buffer_.get() != NULL);
435 return buffer_->size();
436 }
437 }
438 }