Mercurial > hg > orthanc-wsi
annotate Framework/Jpeg2000Reader.cpp @ 334:f011fc199b6a default tip
back to mainline
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 18 Oct 2024 15:44:18 +0200 |
parents | 0683312e21ba |
children |
rev | line source |
---|---|
0 | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
312
0683312e21ba
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
309
diff
changeset
|
5 * Copyright (C) 2017-2023 Osimis S.A., Belgium |
0683312e21ba
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
309
diff
changeset
|
6 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium |
309
7020852a8fa9
updated year to 2024
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
254
diff
changeset
|
7 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
0 | 8 * |
9 * This program is free software: you can redistribute it and/or | |
10 * modify it under the terms of the GNU Affero General Public License | |
11 * as published by the Free Software Foundation, either version 3 of | |
12 * the License, or (at your option) any later version. | |
13 * | |
14 * This program is distributed in the hope that it will be useful, but | |
15 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 * Affero General Public License for more details. | |
18 * | |
19 * You should have received a copy of the GNU Affero General Public License | |
20 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
21 **/ | |
22 | |
23 | |
16
7a88c614be04
preparing for precompiled headers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
24 #include "PrecompiledHeadersWSI.h" |
0 | 25 #include "Jpeg2000Reader.h" |
26 | |
199
a1c265cb2174
replacing deprecated std::auto_ptr by std::unique_ptr
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
192
diff
changeset
|
27 #include "ImageToolbox.h" |
a1c265cb2174
replacing deprecated std::auto_ptr by std::unique_ptr
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
192
diff
changeset
|
28 |
a1c265cb2174
replacing deprecated std::auto_ptr by std::unique_ptr
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
192
diff
changeset
|
29 #include <Compatibility.h> // For std::unique_ptr |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
30 #include <Images/ImageProcessing.h> |
192 | 31 #include <OrthancException.h> |
32 #include <SystemToolbox.h> | |
0 | 33 |
34 #include <cassert> | |
35 #include <string.h> | |
36 #include <openjpeg.h> | |
37 | |
38 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
39 # define OPJ_CLRSPC_GRAY CLRSPC_GRAY | |
40 # define OPJ_CLRSPC_SRGB CLRSPC_SRGB | |
41 # define OPJ_CODEC_J2K CODEC_J2K | |
42 # define OPJ_CODEC_JP2 CODEC_JP2 | |
43 typedef opj_dinfo_t opj_codec_t; | |
44 typedef opj_cio_t opj_stream_t; | |
45 #elif ORTHANC_OPENJPEG_MAJOR_VERSION == 2 | |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
46 // OK, no need for compatibility macros |
0 | 47 #else |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
48 # error Unsupported version of OpenJpeg |
0 | 49 #endif |
50 | |
51 | |
52 namespace OrthancWSI | |
53 { | |
54 namespace | |
55 { | |
56 // Check out opj_dparameters_t::decod_format | |
57 enum InputFormat | |
58 { | |
59 InputFormat_J2K = 0, | |
60 InputFormat_JP2 = 1, | |
61 InputFormat_JPT = 2 | |
62 }; | |
63 | |
64 enum OutputFormat | |
65 { | |
66 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
67 OutputFormat_PGX = 1 | |
68 #else | |
69 OutputFormat_PGX = 11 | |
70 #endif | |
71 }; | |
72 | |
73 class OpenJpegDecoder : public boost::noncopyable | |
74 { | |
75 private: | |
76 opj_dparameters_t parameters_; | |
77 opj_codec_t* dinfo_; | |
78 | |
79 void SetupParameters(InputFormat format) | |
80 { | |
81 opj_set_default_decoder_parameters(¶meters_); | |
82 | |
83 parameters_.decod_format = format; | |
84 parameters_.cod_format = OutputFormat_PGX; | |
85 | |
86 #if OPENJPEG_MAJOR_VERSION == 1 | |
87 parameters_.cp_layer = 0; | |
88 parameters_.cp_reduce = 0; | |
89 #endif | |
90 } | |
91 | |
92 void Finalize() | |
93 { | |
94 if (dinfo_ != NULL) | |
95 { | |
96 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
97 opj_destroy_decompress(dinfo_); | |
98 #else | |
99 opj_destroy_codec(dinfo_); | |
100 #endif | |
101 dinfo_ = NULL; | |
102 } | |
103 } | |
104 | |
105 public: | |
145 | 106 explicit OpenJpegDecoder(Jpeg2000Format format) : |
107 dinfo_(NULL) | |
0 | 108 { |
109 switch (format) | |
110 { | |
111 case Jpeg2000Format_J2K: | |
112 SetupParameters(InputFormat_J2K); | |
113 dinfo_ = opj_create_decompress(OPJ_CODEC_J2K); | |
114 break; | |
115 | |
116 case Jpeg2000Format_JP2: | |
117 SetupParameters(InputFormat_JP2); | |
118 dinfo_ = opj_create_decompress(OPJ_CODEC_JP2); | |
119 break; | |
120 | |
121 default: | |
122 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
123 } | |
124 | |
125 if (!dinfo_) | |
126 { | |
127 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
128 } | |
129 | |
130 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
131 opj_setup_decoder(dinfo_, ¶meters_); | |
132 #else | |
133 if (!opj_setup_decoder(dinfo_, ¶meters_)) | |
134 { | |
135 Finalize(); | |
136 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
137 } | |
138 #endif | |
139 } | |
140 | |
141 ~OpenJpegDecoder() | |
142 { | |
143 Finalize(); | |
144 } | |
145 | |
146 opj_codec_t* GetObject() | |
147 { | |
148 return dinfo_; | |
149 } | |
150 | |
151 const opj_dparameters_t& GetParameters() const | |
152 { | |
153 return parameters_; | |
154 } | |
155 }; | |
156 | |
157 | |
158 class OpenJpegInput : public boost::noncopyable | |
159 { | |
160 private: | |
161 opj_stream_t* cio_; | |
162 | |
163 const uint8_t* buffer_; | |
164 size_t size_; | |
165 size_t position_; | |
166 | |
167 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 2 | |
168 static void Free(void *userData) | |
169 { | |
170 } | |
171 | |
172 static OPJ_SIZE_T Read(void *target, | |
173 OPJ_SIZE_T size, | |
174 void *userData) | |
175 { | |
176 OpenJpegInput& that = *reinterpret_cast<OpenJpegInput*>(userData); | |
179
f80631f632ad
cppcheck, upgrade to Orthanc framework 1.7.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
175
diff
changeset
|
177 assert(that.position_ <= that.size_); |
0 | 178 assert(size >= 0); |
179 | |
180 if (that.position_ == that.size_) | |
181 { | |
182 // End of file | |
183 return -1; | |
184 } | |
185 else | |
186 { | |
187 if (that.position_ + size > that.size_) | |
188 { | |
189 size = that.size_ - that.position_; | |
190 } | |
191 | |
192 if (size > 0) | |
193 { | |
194 memcpy(target, that.buffer_ + that.position_, size); | |
195 } | |
196 | |
197 that.position_ += size; | |
198 return size; | |
199 } | |
200 } | |
201 | |
202 static OPJ_OFF_T Skip(OPJ_OFF_T skip, | |
203 void *userData) | |
204 { | |
205 assert(skip >= 0); | |
206 OpenJpegInput& that = *reinterpret_cast<OpenJpegInput*>(userData); | |
207 | |
208 if (that.position_ == that.size_) | |
209 { | |
210 // End of file | |
211 return -1; | |
212 } | |
213 else if (that.position_ + skip > that.size_) | |
214 { | |
215 size_t offset = that.size_ - that.position_; | |
216 that.position_ = that.size_; | |
217 return offset; | |
218 } | |
219 else | |
220 { | |
221 that.position_ += skip; | |
222 return skip; | |
223 } | |
224 } | |
225 | |
226 static OPJ_BOOL Seek(OPJ_OFF_T position, | |
227 void *userData) | |
228 { | |
229 assert(position >= 0); | |
230 OpenJpegInput& that = *reinterpret_cast<OpenJpegInput*>(userData); | |
231 | |
232 if (static_cast<size_t>(position) > that.size_) | |
233 { | |
234 that.position_ = that.size_; | |
235 return false; | |
236 } | |
237 else | |
238 { | |
239 that.position_ = position; | |
240 return true; | |
241 } | |
242 } | |
243 #endif | |
244 | |
245 public: | |
246 OpenJpegInput(OpenJpegDecoder& decoder, | |
247 const void* buffer, | |
248 size_t size) : | |
249 buffer_(reinterpret_cast<const uint8_t*>(buffer)), | |
250 size_(size), | |
251 position_(0) | |
252 { | |
253 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
254 cio_ = opj_cio_open(reinterpret_cast<opj_common_ptr>(decoder.GetObject()), | |
255 reinterpret_cast<unsigned char*>(const_cast<void*>(buffer)), | |
256 size); | |
257 #else | |
258 cio_ = opj_stream_create(size_, 1 /* input stream */); | |
259 if (!cio_) | |
260 { | |
261 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
262 } | |
263 | |
264 // http://openjpeg.narkive.com/zHqG2fMe/opj-stream-set-user-data-length | |
265 // "I'd suggest to precise in the documentation that the skip | |
266 // and read callback functions should return -1 on end of | |
267 // stream, and the seek callback function should return false | |
268 // on end of stream." | |
269 | |
270 opj_stream_set_user_data(cio_, this, Free); | |
271 opj_stream_set_user_data_length(cio_, size); | |
272 opj_stream_set_read_function(cio_, Read); | |
273 opj_stream_set_skip_function(cio_, Skip); | |
274 opj_stream_set_seek_function(cio_, Seek); | |
275 #endif | |
276 } | |
277 | |
278 ~OpenJpegInput() | |
279 { | |
280 if (cio_) | |
281 { | |
282 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
283 opj_cio_close(cio_); | |
284 #else | |
285 opj_stream_destroy(cio_); | |
286 #endif | |
287 cio_ = NULL; | |
288 } | |
289 } | |
290 | |
291 opj_stream_t* GetObject() | |
292 { | |
293 return cio_; | |
294 } | |
295 }; | |
296 | |
297 | |
298 class OpenJpegImage | |
299 { | |
300 private: | |
301 opj_image_t* image_; | |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
302 |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
303 Orthanc::ImageAccessor* ExtractChannel(unsigned int channel) |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
304 { |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
305 const unsigned int width = image_->comps[channel].w; |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
306 const unsigned int height = image_->comps[channel].h; |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
307 |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
308 std::unique_ptr<Orthanc::ImageAccessor> target( |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
309 new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); |
0 | 310 |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
311 const int32_t* q = image_->comps[channel].data; |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
312 assert(q != NULL); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
313 |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
314 for (unsigned int y = 0; y < height; y++) |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
315 { |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
316 uint8_t *p = reinterpret_cast<uint8_t*>(target->GetRow(y)); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
317 |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
318 for (unsigned int x = 0; x < width; x++, p++, q++) |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
319 { |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
320 *p = *q; |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
321 } |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
322 } |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
323 |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
324 return target.release(); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
325 } |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
326 |
0 | 327 void CopyChannel(Orthanc::ImageAccessor& target, |
328 unsigned int channel, | |
329 unsigned int targetIncrement) | |
330 { | |
209 | 331 const unsigned int width = target.GetWidth(); |
332 const unsigned int height = target.GetHeight(); | |
333 | |
227 | 334 if (width == static_cast<unsigned int>(image_->comps[channel].w) && |
335 height == static_cast<unsigned int>(image_->comps[channel].h)) | |
0 | 336 { |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
337 const int32_t* q = image_->comps[channel].data; |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
338 assert(q != NULL); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
339 |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
340 for (unsigned int y = 0; y < height; y++) |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
341 { |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
342 uint8_t *p = reinterpret_cast<uint8_t*>(target.GetRow(y)) + channel; |
0 | 343 |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
344 for (unsigned int x = 0; x < width; x++, p += targetIncrement) |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
345 { |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
346 *p = *q; |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
347 q++; |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
348 } |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
349 } |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
350 } |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
351 else |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
352 { |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
353 // This component is subsampled |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
354 std::unique_ptr<Orthanc::ImageAccessor> source(ExtractChannel(channel)); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
355 Orthanc::Image resized(Orthanc::PixelFormat_Grayscale8, width, height, false); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
356 Orthanc::ImageProcessing::Resize(resized, *source); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
357 |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
358 assert(resized.GetWidth() == target.GetWidth() && |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
359 resized.GetHeight() == target.GetHeight() && |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
360 resized.GetFormat() == Orthanc::PixelFormat_Grayscale8 && |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
361 source->GetFormat() == Orthanc::PixelFormat_Grayscale8); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
362 |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
363 for (unsigned int y = 0; y < height; y++) |
0 | 364 { |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
365 const uint8_t *q = reinterpret_cast<const uint8_t*>(resized.GetConstRow(y)); |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
366 uint8_t *p = reinterpret_cast<uint8_t*>(target.GetRow(y)) + channel; |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
367 |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
368 for (unsigned int x = 0; x < width; x++, p += targetIncrement) |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
369 { |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
370 *p = *q; |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
371 q++; |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
372 } |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
373 } |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
374 } |
0 | 375 } |
376 | |
377 public: | |
378 OpenJpegImage(OpenJpegDecoder& decoder, | |
379 OpenJpegInput& input) : | |
380 image_(NULL) | |
381 { | |
382 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1 | |
383 image_ = opj_decode(decoder.GetObject(), input.GetObject()); | |
384 if (image_ == NULL) | |
385 { | |
386 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
387 } | |
388 #else | |
389 if (!opj_read_header(input.GetObject(), decoder.GetObject(), &image_) || | |
390 image_ == NULL) | |
391 { | |
392 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
393 } | |
394 | |
395 if (!opj_set_decode_area(decoder.GetObject(), image_, | |
396 static_cast<int32_t>(decoder.GetParameters().DA_x0), | |
397 static_cast<int32_t>(decoder.GetParameters().DA_y0), | |
398 static_cast<int32_t>(decoder.GetParameters().DA_x1), | |
399 static_cast<int32_t>(decoder.GetParameters().DA_y1)) || // Decode the whole image | |
400 !opj_decode(decoder.GetObject(), input.GetObject(), image_) || | |
401 !opj_end_decompress(decoder.GetObject(), input.GetObject())) | |
402 { | |
403 opj_image_destroy(image_); | |
404 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
405 } | |
406 #endif | |
407 } | |
408 | |
409 ~OpenJpegImage() | |
410 { | |
411 if (image_ != NULL) | |
412 { | |
413 opj_image_destroy(image_); | |
414 image_ = NULL; | |
415 } | |
416 } | |
417 | |
418 Orthanc::ImageAccessor* ProvideImage() | |
419 { | |
420 if (image_->x1 < 0 || | |
421 image_->y1 < 0) | |
422 { | |
423 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
424 } | |
425 | |
426 if (image_->x0 != 0 || | |
427 image_->y0 != 0) | |
428 { | |
429 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
430 } | |
431 | |
432 for (unsigned int c = 0; c < static_cast<unsigned int>(image_->numcomps); c++) | |
433 { | |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
434 if (image_->comps[c].x0 != 0 || |
0 | 435 image_->comps[c].y0 != 0 || |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
436 image_->comps[c].dx * image_->comps[c].w != image_->x1 || |
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
437 image_->comps[c].dy * image_->comps[c].h != image_->y1 || |
0 | 438 image_->comps[c].prec != 8 || |
439 image_->comps[c].sgnd != 0) | |
440 { | |
441 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
442 } | |
443 } | |
444 | |
445 unsigned int width = static_cast<unsigned int>(image_->x1); | |
446 unsigned int height = static_cast<unsigned int>(image_->y1); | |
447 | |
448 Orthanc::PixelFormat format; | |
124
24bd36849c56
Support more colorspaces with JPEG2k
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
115
diff
changeset
|
449 if (image_->numcomps == 1 /*&& image_->color_space != OPJ_CLRSPC_GRAY*/) |
0 | 450 { |
451 format = Orthanc::PixelFormat_Grayscale8; | |
452 } | |
124
24bd36849c56
Support more colorspaces with JPEG2k
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
115
diff
changeset
|
453 else if (image_->numcomps == 3 /*&& image_->color_space == OPJ_CLRSPC_SRGB*/) |
0 | 454 { |
455 format = Orthanc::PixelFormat_RGB24; | |
456 } | |
457 else | |
458 { | |
459 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
460 } | |
461 | |
199
a1c265cb2174
replacing deprecated std::auto_ptr by std::unique_ptr
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
192
diff
changeset
|
462 std::unique_ptr<Orthanc::ImageAccessor> image(ImageToolbox::Allocate(format, width, height)); |
0 | 463 |
464 switch (format) | |
465 { | |
466 case Orthanc::PixelFormat_Grayscale8: | |
467 { | |
468 CopyChannel(*image, 0, 1); | |
469 break; | |
470 } | |
471 | |
472 case Orthanc::PixelFormat_RGB24: | |
473 { | |
474 CopyChannel(*image, 0, 3); | |
475 CopyChannel(*image, 1, 3); | |
476 CopyChannel(*image, 2, 3); | |
477 break; | |
478 } | |
479 | |
480 default: | |
481 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
482 } | |
483 | |
484 return image.release(); | |
485 } | |
486 }; | |
487 } | |
488 | |
489 | |
490 void Jpeg2000Reader::ReadFromMemory(const void* buffer, | |
491 size_t size) | |
492 { | |
493 OpenJpegDecoder decoder(DetectFormatFromMemory(buffer, size)); | |
494 OpenJpegInput input(decoder, buffer, size); | |
495 OpenJpegImage image(decoder, input); | |
496 | |
497 image_.reset(image.ProvideImage()); | |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
498 AssignWritable(image_->GetFormat(), |
0 | 499 image_->GetWidth(), |
500 image_->GetHeight(), | |
501 image_->GetPitch(), | |
217
20bc074ec19a
Viewer can display DICOM pyramids whose tile sizes vary across levels
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
214
diff
changeset
|
502 image_->GetBuffer()); |
0 | 503 } |
504 | |
505 | |
506 void Jpeg2000Reader::ReadFromMemory(const std::string& buffer) | |
507 { | |
508 if (buffer.empty()) | |
509 { | |
510 ReadFromMemory(NULL, 0); | |
511 } | |
512 else | |
513 { | |
514 ReadFromMemory(buffer.c_str(), buffer.size()); | |
515 } | |
516 } | |
517 | |
518 void Jpeg2000Reader::ReadFromFile(const std::string& filename) | |
519 { | |
520 // TODO Use opj_stream_create_file_stream() ? | |
521 | |
522 std::string content; | |
43 | 523 Orthanc::SystemToolbox::ReadFile(content, filename); |
0 | 524 } |
525 | |
526 | |
527 Jpeg2000Format Jpeg2000Reader::DetectFormatFromMemory(const void* buffer, | |
528 size_t size) | |
529 { | |
530 static const char JP2_RFC3745_HEADER[] = "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"; | |
531 static const char JP2_HEADER[] = "\x0d\x0a\x87\x0a"; | |
532 static const char J2K_HEADER[] = "\xff\x4f\xff\x51"; | |
533 | |
534 if (size < sizeof(JP2_RFC3745_HEADER) - 1) | |
535 { | |
536 return Jpeg2000Format_Unknown; | |
537 } | |
538 | |
539 if (memcmp(buffer, JP2_RFC3745_HEADER, sizeof(JP2_RFC3745_HEADER) - 1) == 0 || | |
540 memcmp(buffer, JP2_HEADER, sizeof(JP2_HEADER) - 1) == 0) | |
541 { | |
542 return Jpeg2000Format_JP2; | |
543 } | |
544 else if (memcmp(buffer, J2K_HEADER, sizeof(J2K_HEADER) - 1) == 0) | |
545 { | |
546 return Jpeg2000Format_J2K; | |
547 } | |
548 | |
549 return Jpeg2000Format_Unknown; | |
550 } | |
551 } |