845
|
1 /**
|
|
2 * Orthanc - A Lightweight, RESTful DICOM Store
|
|
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
|
|
4 * Belgium
|
|
5 *
|
|
6 * This program is free software: you can redistribute it and/or
|
|
7 * modify it under the terms of the GNU General Public License as
|
|
8 * published by the Free Software Foundation, either version 3 of the
|
|
9 * License, or (at your option) any later version.
|
|
10 *
|
|
11 * In addition, as a special exception, the copyright holders of this
|
|
12 * program give permission to link the code of its release with the
|
|
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
|
|
14 * that use the same license as the "OpenSSL" library), and distribute
|
|
15 * the linked executables. You must obey the GNU General Public License
|
|
16 * in all respects for all of the code used other than "OpenSSL". If you
|
|
17 * modify file(s) with this exception, you may extend this exception to
|
|
18 * your version of the file(s), but you are not obligated to do so. If
|
|
19 * you do not wish to do so, delete this exception statement from your
|
|
20 * version. If you delete this exception statement from all source files
|
|
21 * in the program, then also delete it here.
|
|
22 *
|
|
23 * This program is distributed in the hope that it will be useful, but
|
|
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
26 * General Public License for more details.
|
|
27 *
|
|
28 * You should have received a copy of the GNU General Public License
|
|
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
30 **/
|
|
31
|
|
32
|
|
33 #include "../PrecompiledHeadersServer.h"
|
|
34 #include "DicomImageDecoder.h"
|
|
35
|
846
|
36
|
|
37 /*=========================================================================
|
|
38
|
|
39 This file is based on portions of the following project
|
|
40 (cf. function "DecodePsmctRle1()"):
|
|
41
|
|
42 Program: GDCM (Grassroots DICOM). A DICOM library
|
|
43 Module: http://gdcm.sourceforge.net/Copyright.html
|
|
44
|
|
45 Copyright (c) 2006-2011 Mathieu Malaterre
|
|
46 Copyright (c) 1993-2005 CREATIS
|
|
47 (CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image)
|
|
48 All rights reserved.
|
|
49
|
|
50 Redistribution and use in source and binary forms, with or without
|
|
51 modification, are permitted provided that the following conditions are met:
|
|
52
|
|
53 * Redistributions of source code must retain the above copyright notice,
|
|
54 this list of conditions and the following disclaimer.
|
|
55
|
|
56 * Redistributions in binary form must reproduce the above copyright notice,
|
|
57 this list of conditions and the following disclaimer in the documentation
|
|
58 and/or other materials provided with the distribution.
|
|
59
|
|
60 * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any
|
|
61 contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to
|
|
62 endorse or promote products derived from this software without specific
|
|
63 prior written permission.
|
|
64
|
|
65 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
|
|
66 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
67 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
68 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
|
|
69 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
70 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
71 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
72 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
73 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
74 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
75
|
|
76 =========================================================================*/
|
|
77
|
|
78
|
|
79
|
845
|
80 #include "../../Core/OrthancException.h"
|
|
81 #include "../ToDcmtkBridge.h"
|
|
82
|
|
83 #include <dcmtk/dcmjpls/djcodecd.h>
|
|
84 #include <dcmtk/dcmjpls/djcparam.h>
|
|
85 #include <dcmtk/dcmjpeg/djrplol.h>
|
|
86 #include <boost/lexical_cast.hpp>
|
|
87
|
|
88 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
|
|
89 #endif
|
|
90
|
|
91
|
|
92 namespace Orthanc
|
|
93 {
|
846
|
94 static const DicomTag DICOM_TAG_CONTENT(0x07a1, 0x100a);
|
|
95 static const DicomTag DICOM_TAG_COMPRESSION_TYPE(0x07a1, 0x1011);
|
|
96
|
|
97 bool DicomImageDecoder::IsPsmctRle1(DcmDataset& dataset)
|
|
98 {
|
|
99 DcmElement* e;
|
|
100 char* c;
|
|
101
|
|
102 // Check whether the DICOM instance contains an image encoded with
|
|
103 // the PMSCT_RLE1 scheme.
|
|
104 if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_COMPRESSION_TYPE), e).good() ||
|
|
105 e == NULL ||
|
|
106 !e->isaString() ||
|
|
107 !e->getString(c).good() ||
|
|
108 c == NULL ||
|
|
109 strcmp("PMSCT_RLE1", c))
|
|
110 {
|
|
111 return false;
|
|
112 }
|
|
113 else
|
|
114 {
|
|
115 return true;
|
|
116 }
|
|
117 }
|
|
118
|
|
119
|
|
120 bool DicomImageDecoder::DecodePsmctRle1(std::string& output,
|
|
121 DcmDataset& dataset)
|
|
122 {
|
|
123 // Check whether the DICOM instance contains an image encoded with
|
|
124 // the PMSCT_RLE1 scheme.
|
|
125 if (!IsPsmctRle1(dataset))
|
|
126 {
|
|
127 return false;
|
|
128 }
|
|
129
|
|
130 // OK, this is a custom RLE encoding from Philips. Get the pixel
|
|
131 // data from the appropriate private DICOM tag.
|
|
132 Uint8* pixData = NULL;
|
|
133 DcmElement* e;
|
|
134 if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_CONTENT), e).good() ||
|
|
135 e == NULL ||
|
|
136 e->getUint8Array(pixData) != EC_Normal)
|
|
137 {
|
|
138 return false;
|
|
139 }
|
|
140
|
|
141 // The "unsigned" below IS VERY IMPORTANT
|
|
142 const uint8_t* inbuffer = reinterpret_cast<const uint8_t*>(pixData);
|
|
143 const size_t length = e->getLength();
|
|
144
|
|
145 /**
|
|
146 * The code below is an adaptation of a sample code for GDCM by
|
|
147 * Mathieu Malaterre (under a BSD license).
|
|
148 * http://gdcm.sourceforge.net/html/rle2img_8cxx-example.html
|
|
149 **/
|
|
150
|
|
151 // RLE pass
|
|
152 std::vector<uint8_t> temp;
|
|
153 temp.reserve(length);
|
|
154 for (size_t i = 0; i < length; i++)
|
|
155 {
|
|
156 if (inbuffer[i] == 0xa5)
|
|
157 {
|
|
158 temp.push_back(inbuffer[i+2]);
|
|
159 for (uint8_t repeat = inbuffer[i + 1]; repeat != 0; repeat--)
|
|
160 {
|
|
161 temp.push_back(inbuffer[i+2]);
|
|
162 }
|
|
163 i += 2;
|
|
164 }
|
|
165 else
|
|
166 {
|
|
167 temp.push_back(inbuffer[i]);
|
|
168 }
|
|
169 }
|
|
170
|
|
171 // Delta encoding pass
|
|
172 uint16_t delta = 0;
|
|
173 output.clear();
|
|
174 output.reserve(temp.size());
|
|
175 for (size_t i = 0; i < temp.size(); i++)
|
|
176 {
|
|
177 uint16_t value;
|
|
178
|
|
179 if (temp[i] == 0x5a)
|
|
180 {
|
|
181 uint16_t v1 = temp[i + 1];
|
|
182 uint16_t v2 = temp[i + 2];
|
|
183 value = (v2 << 8) + v1;
|
|
184 i += 2;
|
|
185 }
|
|
186 else
|
|
187 {
|
|
188 value = delta + (int8_t) temp[i];
|
|
189 }
|
|
190
|
|
191 output.push_back(value & 0xff);
|
|
192 output.push_back(value >> 8);
|
|
193 delta = value;
|
|
194 }
|
|
195
|
|
196 if (output.size() % 2)
|
|
197 {
|
|
198 output.resize(output.size() - 1);
|
|
199 }
|
|
200
|
|
201 return true;
|
|
202 }
|
|
203
|
|
204
|
845
|
205 void DicomImageDecoder::SetupImageBuffer(ImageBuffer& target,
|
|
206 DcmDataset& dataset)
|
|
207 {
|
|
208 OFString value;
|
|
209
|
|
210 if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_COLUMNS), value).good())
|
|
211 {
|
|
212 throw OrthancException(ErrorCode_BadFileFormat);
|
|
213 }
|
|
214
|
|
215 unsigned int width = boost::lexical_cast<unsigned int>(value.c_str());
|
|
216
|
|
217 if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_ROWS), value).good())
|
|
218 {
|
|
219 throw OrthancException(ErrorCode_BadFileFormat);
|
|
220 }
|
|
221
|
|
222 unsigned int height = boost::lexical_cast<unsigned int>(value.c_str());
|
|
223
|
|
224 if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_BITS_STORED), value).good())
|
|
225 {
|
|
226 throw OrthancException(ErrorCode_BadFileFormat);
|
|
227 }
|
|
228
|
|
229 unsigned int bitsStored = boost::lexical_cast<unsigned int>(value.c_str());
|
|
230
|
|
231 if (!dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_REPRESENTATION), value).good())
|
|
232 {
|
|
233 throw OrthancException(ErrorCode_BadFileFormat);
|
|
234 }
|
|
235
|
|
236 bool isSigned = (boost::lexical_cast<unsigned int>(value.c_str()) != 0);
|
|
237
|
|
238 unsigned int samplesPerPixel = 1; // By default
|
|
239 if (dataset.findAndGetOFString(ToDcmtkBridge::Convert(DICOM_TAG_SAMPLES_PER_PIXEL), value).good())
|
|
240 {
|
|
241 samplesPerPixel = boost::lexical_cast<unsigned int>(value.c_str());
|
|
242 }
|
|
243
|
|
244 target.SetHeight(height);
|
|
245 target.SetWidth(width);
|
|
246
|
|
247 if (bitsStored == 8 && samplesPerPixel == 1 && !isSigned)
|
|
248 {
|
|
249 target.SetFormat(PixelFormat_Grayscale8);
|
|
250 }
|
|
251 else if (bitsStored == 8 && samplesPerPixel == 3 && !isSigned)
|
|
252 {
|
|
253 target.SetFormat(PixelFormat_RGB24);
|
|
254 }
|
|
255 else if (bitsStored == 16 && samplesPerPixel == 1 && !isSigned)
|
|
256 {
|
|
257 target.SetFormat(PixelFormat_Grayscale16);
|
|
258 }
|
|
259 else if (bitsStored == 16 && samplesPerPixel == 1 && isSigned)
|
|
260 {
|
|
261 target.SetFormat(PixelFormat_SignedGrayscale16);
|
|
262 }
|
|
263 else
|
|
264 {
|
|
265 throw OrthancException(ErrorCode_NotImplemented);
|
|
266 }
|
|
267 }
|
|
268
|
|
269
|
|
270 bool DicomImageDecoder::IsJpegLossless(const DcmDataset& dataset)
|
|
271 {
|
|
272 return (dataset.getOriginalXfer() == EXS_JPEGLSLossless ||
|
|
273 dataset.getOriginalXfer() == EXS_JPEGLSLossy);
|
|
274 }
|
|
275
|
|
276
|
|
277 #if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
|
|
278 void DicomImageDecoder::DecodeJpegLossless(ImageBuffer& target,
|
|
279 DcmDataset& dataset)
|
|
280 {
|
|
281 if (!IsJpegLossless(dataset))
|
|
282 {
|
|
283 throw OrthancException(ErrorCode_BadParameterType);
|
|
284 }
|
|
285
|
|
286 DcmElement *element = NULL;
|
|
287 if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), element).good())
|
|
288 {
|
|
289 throw OrthancException(ErrorCode_BadFileFormat);
|
|
290 }
|
|
291
|
|
292 DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element);
|
|
293 DcmPixelSequence* pixelSequence = NULL;
|
|
294 if (!pixelData.getEncapsulatedRepresentation
|
|
295 (dataset.getOriginalXfer(), NULL, pixelSequence).good())
|
|
296 {
|
|
297 throw OrthancException(ErrorCode_BadFileFormat);
|
|
298 }
|
|
299
|
|
300 SetupImageBuffer(target, dataset);
|
|
301
|
|
302 ImageAccessor accessor(target.GetAccessor());
|
|
303
|
|
304 /**
|
|
305 * The "DJLSLosslessDecoder" and "DJLSNearLosslessDecoder" in DCMTK
|
|
306 * are exactly the same, except for the "supportedTransferSyntax()"
|
|
307 * virtual function.
|
|
308 * http://support.dcmtk.org/docs/classDJLSDecoderBase.html
|
|
309 **/
|
|
310
|
|
311 DJLSLosslessDecoder decoder; DJLSCodecParameter parameters;
|
|
312 //DJLSNearLosslessDecoder decoder; DJLSCodecParameter parameters;
|
|
313
|
|
314 Uint32 startFragment = 0; // Default
|
|
315 OFString decompressedColorModel; // Out
|
|
316 DJ_RPLossless representationParameter;
|
|
317 OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, ¶meters,
|
|
318 &dataset, 0, startFragment, accessor.GetBuffer(),
|
|
319 accessor.GetSize(), decompressedColorModel);
|
|
320
|
|
321 if (!c.good())
|
|
322 {
|
|
323 throw OrthancException(ErrorCode_InternalError);
|
|
324 }
|
|
325 }
|
|
326 #endif
|
846
|
327
|
|
328
|
845
|
329 }
|