comparison OrthancFramework/Sources/DicomFormat/DicomImageInformation.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/DicomFormat/DicomImageInformation.cpp@2a170a8f1faf
children eab8010c05fc
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
36 #ifndef NOMINMAX
37 #define NOMINMAX
38 #endif
39
40 #include "DicomImageInformation.h"
41
42 #include "../Compatibility.h"
43 #include "../OrthancException.h"
44 #include "../Toolbox.h"
45 #include <boost/lexical_cast.hpp>
46 #include <limits>
47 #include <cassert>
48 #include <stdio.h>
49 #include <memory>
50
51 namespace Orthanc
52 {
53 DicomImageInformation::DicomImageInformation(const DicomMap& values)
54 {
55 unsigned int pixelRepresentation;
56 unsigned int planarConfiguration = 0;
57
58 try
59 {
60 std::string p = values.GetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION).GetContent();
61 Toolbox::ToUpperCase(p);
62
63 if (p == "RGB")
64 {
65 photometric_ = PhotometricInterpretation_RGB;
66 }
67 else if (p == "MONOCHROME1")
68 {
69 photometric_ = PhotometricInterpretation_Monochrome1;
70 }
71 else if (p == "MONOCHROME2")
72 {
73 photometric_ = PhotometricInterpretation_Monochrome2;
74 }
75 else if (p == "PALETTE COLOR")
76 {
77 photometric_ = PhotometricInterpretation_Palette;
78 }
79 else if (p == "HSV")
80 {
81 photometric_ = PhotometricInterpretation_HSV;
82 }
83 else if (p == "ARGB")
84 {
85 photometric_ = PhotometricInterpretation_ARGB;
86 }
87 else if (p == "CMYK")
88 {
89 photometric_ = PhotometricInterpretation_CMYK;
90 }
91 else if (p == "YBR_FULL")
92 {
93 photometric_ = PhotometricInterpretation_YBRFull;
94 }
95 else if (p == "YBR_FULL_422")
96 {
97 photometric_ = PhotometricInterpretation_YBRFull422;
98 }
99 else if (p == "YBR_PARTIAL_420")
100 {
101 photometric_ = PhotometricInterpretation_YBRPartial420;
102 }
103 else if (p == "YBR_PARTIAL_422")
104 {
105 photometric_ = PhotometricInterpretation_YBRPartial422;
106 }
107 else if (p == "YBR_ICT")
108 {
109 photometric_ = PhotometricInterpretation_YBR_ICT;
110 }
111 else if (p == "YBR_RCT")
112 {
113 photometric_ = PhotometricInterpretation_YBR_RCT;
114 }
115 else
116 {
117 photometric_ = PhotometricInterpretation_Unknown;
118 }
119
120 values.GetValue(DICOM_TAG_COLUMNS).ParseFirstUnsignedInteger(width_); // in some US images, we've seen tag values of "800\0"; that's why we parse the 'first' value
121 values.GetValue(DICOM_TAG_ROWS).ParseFirstUnsignedInteger(height_);
122
123 bitsAllocated_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_ALLOCATED).GetContent());
124
125 try
126 {
127 samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).GetContent());
128 }
129 catch (OrthancException&)
130 {
131 samplesPerPixel_ = 1; // Assume 1 color channel
132 }
133
134 try
135 {
136 bitsStored_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).GetContent());
137 }
138 catch (OrthancException&)
139 {
140 bitsStored_ = bitsAllocated_;
141 }
142
143 try
144 {
145 highBit_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_HIGH_BIT).GetContent());
146 }
147 catch (OrthancException&)
148 {
149 highBit_ = bitsStored_ - 1;
150 }
151
152 try
153 {
154 pixelRepresentation = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).GetContent());
155 }
156 catch (OrthancException&)
157 {
158 pixelRepresentation = 0; // Assume unsigned pixels
159 }
160
161 if (samplesPerPixel_ > 1)
162 {
163 // The "Planar Configuration" is only set when "Samples per Pixels" is greater than 1
164 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.7.6.3.1.3
165 try
166 {
167 planarConfiguration = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).GetContent());
168 }
169 catch (OrthancException&)
170 {
171 planarConfiguration = 0; // Assume interleaved color channels
172 }
173 }
174 }
175 catch (boost::bad_lexical_cast&)
176 {
177 throw OrthancException(ErrorCode_NotImplemented);
178 }
179 catch (OrthancException&)
180 {
181 throw OrthancException(ErrorCode_NotImplemented);
182 }
183
184 if (values.HasTag(DICOM_TAG_NUMBER_OF_FRAMES))
185 {
186 try
187 {
188 numberOfFrames_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).GetContent());
189 }
190 catch (boost::bad_lexical_cast&)
191 {
192 throw OrthancException(ErrorCode_NotImplemented);
193 }
194 }
195 else
196 {
197 numberOfFrames_ = 1;
198 }
199
200 if (bitsAllocated_ != 8 && bitsAllocated_ != 16 &&
201 bitsAllocated_ != 24 && bitsAllocated_ != 32)
202 {
203 throw OrthancException(ErrorCode_IncompatibleImageFormat, "Image not supported: " + boost::lexical_cast<std::string>(bitsAllocated_) + " bits allocated");
204 }
205 else if (numberOfFrames_ == 0)
206 {
207 throw OrthancException(ErrorCode_IncompatibleImageFormat, "Image not supported (no frames)");
208 }
209 else if (planarConfiguration != 0 && planarConfiguration != 1)
210 {
211 throw OrthancException(ErrorCode_IncompatibleImageFormat, "Image not supported: planar configuration is " + boost::lexical_cast<std::string>(planarConfiguration));
212 }
213
214 if (samplesPerPixel_ == 0)
215 {
216 throw OrthancException(ErrorCode_IncompatibleImageFormat, "Image not supported: samples per pixel is 0");
217 }
218
219 bytesPerValue_ = bitsAllocated_ / 8;
220
221 isPlanar_ = (planarConfiguration != 0 ? true : false);
222 isSigned_ = (pixelRepresentation != 0 ? true : false);
223 }
224
225 DicomImageInformation* DicomImageInformation::Clone() const
226 {
227 std::unique_ptr<DicomImageInformation> target(new DicomImageInformation);
228 target->width_ = width_;
229 target->height_ = height_;
230 target->samplesPerPixel_ = samplesPerPixel_;
231 target->numberOfFrames_ = numberOfFrames_;
232 target->isPlanar_ = isPlanar_;
233 target->isSigned_ = isSigned_;
234 target->bytesPerValue_ = bytesPerValue_;
235 target->bitsAllocated_ = bitsAllocated_;
236 target->bitsStored_ = bitsStored_;
237 target->highBit_ = highBit_;
238 target->photometric_ = photometric_;
239
240 return target.release();
241 }
242
243 bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format,
244 bool ignorePhotometricInterpretation) const
245 {
246 if (photometric_ == PhotometricInterpretation_Palette)
247 {
248 if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned())
249 {
250 format = PixelFormat_RGB24;
251 return true;
252 }
253
254 if (GetBitsStored() == 16 && GetChannelCount() == 1 && !IsSigned())
255 {
256 format = PixelFormat_RGB48;
257 return true;
258 }
259 }
260
261 if (ignorePhotometricInterpretation ||
262 photometric_ == PhotometricInterpretation_Monochrome1 ||
263 photometric_ == PhotometricInterpretation_Monochrome2)
264 {
265 if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned())
266 {
267 format = PixelFormat_Grayscale8;
268 return true;
269 }
270
271 if (GetBitsAllocated() == 16 && GetChannelCount() == 1 && !IsSigned())
272 {
273 format = PixelFormat_Grayscale16;
274 return true;
275 }
276
277 if (GetBitsAllocated() == 16 && GetChannelCount() == 1 && IsSigned())
278 {
279 format = PixelFormat_SignedGrayscale16;
280 return true;
281 }
282
283 if (GetBitsAllocated() == 32 && GetChannelCount() == 1 && !IsSigned())
284 {
285 format = PixelFormat_Grayscale32;
286 return true;
287 }
288 }
289
290 if (GetBitsStored() == 8 &&
291 GetChannelCount() == 3 &&
292 !IsSigned() &&
293 (ignorePhotometricInterpretation || photometric_ == PhotometricInterpretation_RGB))
294 {
295 format = PixelFormat_RGB24;
296 return true;
297 }
298
299 return false;
300 }
301
302
303 size_t DicomImageInformation::GetFrameSize() const
304 {
305 return (GetHeight() *
306 GetWidth() *
307 GetBytesPerValue() *
308 GetChannelCount());
309 }
310 }