comparison Framework/Jpeg2000Writer.cpp @ 0:4a7a53257c7d

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 22 Oct 2016 21:48:33 +0200
parents
children 7a88c614be04
comparison
equal deleted inserted replaced
-1:000000000000 0:4a7a53257c7d
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 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU Affero General Public License
8 * as published by the Free Software Foundation, either version 3 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
20
21 #include "Jpeg2000Writer.h"
22
23 #include "Orthanc/Core/ChunkedBuffer.h"
24 #include "Orthanc/Core/OrthancException.h"
25
26 #include <openjpeg.h>
27 #include <string.h>
28 #include <vector>
29
30 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
31 # define OPJ_CLRSPC_GRAY CLRSPC_GRAY
32 # define OPJ_CLRSPC_SRGB CLRSPC_SRGB
33 # define OPJ_CODEC_J2K CODEC_J2K
34 typedef opj_cinfo_t opj_codec_t;
35 typedef opj_cio_t opj_stream_t;
36 #elif ORTHANC_OPENJPEG_MAJOR_VERSION == 2
37 #else
38 #error Unsupported version of OpenJpeg
39 #endif
40
41 namespace OrthancWSI
42 {
43 namespace
44 {
45 class OpenJpegImage : public boost::noncopyable
46 {
47 private:
48 std::vector<opj_image_cmptparm_t> components_;
49 COLOR_SPACE colorspace_;
50 opj_image_t* image_;
51
52 void SetupComponents(unsigned int width,
53 unsigned int height,
54 Orthanc::PixelFormat format)
55 {
56 switch (format)
57 {
58 case Orthanc::PixelFormat_Grayscale8:
59 colorspace_ = OPJ_CLRSPC_GRAY;
60 components_.resize(1);
61 break;
62
63 case Orthanc::PixelFormat_RGB24:
64 colorspace_ = OPJ_CLRSPC_SRGB;
65 components_.resize(3);
66 break;
67
68 default:
69 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
70 }
71
72 for (size_t i = 0; i < components_.size(); i++)
73 {
74 memset(&components_[i], 0, sizeof(opj_image_cmptparm_t));
75 components_[i].dx = 1;
76 components_[i].dy = 1;
77 components_[i].x0 = 0;
78 components_[i].y0 = 0;
79 components_[i].w = width;
80 components_[i].h = height;
81 components_[i].prec = 8;
82 components_[i].bpp = 8;
83 components_[i].sgnd = 0;
84 }
85 }
86
87 void CopyRGB24(unsigned int width,
88 unsigned int height,
89 unsigned int pitch,
90 const void* buffer)
91 {
92 int32_t* r = image_->comps[0].data;
93 int32_t* g = image_->comps[1].data;
94 int32_t* b = image_->comps[2].data;
95
96 for (unsigned int y = 0; y < height; y++)
97 {
98 const uint8_t *p = reinterpret_cast<const uint8_t*>(buffer) + y * pitch;
99
100 for (unsigned int x = 0; x < width; x++)
101 {
102 *r = p[0];
103 *g = p[1];
104 *b = p[2];
105 p += 3;
106 r++;
107 g++;
108 b++;
109 }
110 }
111 }
112
113 void CopyGrayscale8(unsigned int width,
114 unsigned int height,
115 unsigned int pitch,
116 const void* buffer)
117 {
118 int32_t* q = image_->comps[0].data;
119
120 for (unsigned int y = 0; y < height; y++)
121 {
122 const uint8_t *p = reinterpret_cast<const uint8_t*>(buffer) + y * pitch;
123
124 for (unsigned int x = 0; x < width; x++)
125 {
126 *q = *p;
127 p++;
128 q++;
129 }
130 }
131 }
132
133 public:
134 OpenJpegImage(unsigned int width,
135 unsigned int height,
136 unsigned int pitch,
137 Orthanc::PixelFormat format,
138 const void* buffer) : image_(NULL)
139 {
140 SetupComponents(width, height, format);
141
142 image_ = opj_image_create(components_.size(), &components_[0], colorspace_);
143 if (!image_)
144 {
145 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
146 }
147
148 image_->x0 = 0;
149 image_->y0 = 0;
150 image_->x1 = width;
151 image_->y1 = height;
152
153 switch (format)
154 {
155 case Orthanc::PixelFormat_Grayscale8:
156 CopyGrayscale8(width, height, pitch, buffer);
157 break;
158
159 case Orthanc::PixelFormat_RGB24:
160 CopyRGB24(width, height, pitch, buffer);
161 break;
162
163 default:
164 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
165 }
166 }
167
168
169 ~OpenJpegImage()
170 {
171 if (image_)
172 {
173 opj_image_destroy(image_);
174 image_ = NULL;
175 }
176 }
177
178
179 opj_image_t* GetObject()
180 {
181 return image_;
182 }
183 };
184
185
186 class OpenJpegEncoder : public boost::noncopyable
187 {
188 private:
189 opj_codec_t* cinfo_;
190
191 public:
192 OpenJpegEncoder(opj_cparameters_t& parameters,
193 OpenJpegImage& image) : cinfo_(NULL)
194 {
195 cinfo_ = opj_create_compress(OPJ_CODEC_J2K);
196 if (!cinfo_)
197 {
198 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
199 }
200
201 opj_setup_encoder(cinfo_, &parameters, image.GetObject());
202 }
203
204 ~OpenJpegEncoder()
205 {
206 if (cinfo_ != NULL)
207 {
208 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
209 opj_destroy_compress(cinfo_);
210 #else
211 opj_destroy_codec(cinfo_);
212 #endif
213 cinfo_ = NULL;
214 }
215 }
216
217 opj_codec_t* GetObject()
218 {
219 return cinfo_;
220 }
221 };
222
223
224 class OpenJpegOutput : public boost::noncopyable
225 {
226 private:
227 opj_stream_t* cio_;
228
229 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 2
230 Orthanc::ChunkedBuffer buffer_;
231
232 static void Free(void *userData)
233 {
234 }
235
236 static OPJ_SIZE_T Write(void *buffer,
237 OPJ_SIZE_T size,
238 void *userData)
239 {
240 OpenJpegOutput* that = reinterpret_cast<OpenJpegOutput*>(userData);
241 that->buffer_.AddChunk(reinterpret_cast<const char*>(buffer), size);
242 return size;
243 }
244 #endif
245
246 public:
247 OpenJpegOutput(OpenJpegEncoder& encoder) : cio_(NULL)
248 {
249 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
250 cio_ = opj_cio_open(reinterpret_cast<opj_common_ptr>(encoder.GetObject()), NULL, 0);
251 #else
252 cio_ = opj_stream_default_create(0 /* output stream */);
253 if (!cio_)
254 {
255 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
256 }
257
258 opj_stream_set_user_data(cio_, this, Free);
259 opj_stream_set_write_function(cio_, Write);
260 #endif
261 }
262
263 ~OpenJpegOutput()
264 {
265 if (cio_)
266 {
267 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
268 opj_cio_close(cio_);
269 #else
270 opj_stream_destroy(cio_);
271 #endif
272 cio_ = NULL;
273 }
274 }
275
276 opj_stream_t* GetObject()
277 {
278 return cio_;
279 }
280
281 void Flatten(std::string& target)
282 {
283 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
284 target.assign(reinterpret_cast<const char*>(cio_->buffer), cio_tell(cio_));
285 #else
286 buffer_.Flatten(target);
287 #endif
288 }
289 };
290 }
291
292
293 static void SetupParameters(opj_cparameters_t& parameters,
294 Orthanc::PixelFormat format,
295 bool isLossless)
296 {
297 opj_set_default_encoder_parameters(&parameters);
298 parameters.cp_disto_alloc = 1;
299
300 if (isLossless)
301 {
302 parameters.tcp_numlayers = 1;
303 parameters.tcp_rates[0] = 0;
304 }
305 else
306 {
307 parameters.tcp_numlayers = 5;
308 parameters.tcp_rates[0] = 1920;
309 parameters.tcp_rates[1] = 480;
310 parameters.tcp_rates[2] = 120;
311 parameters.tcp_rates[3] = 30;
312 parameters.tcp_rates[4] = 10;
313 parameters.irreversible = 1;
314
315 if (format == Orthanc::PixelFormat_RGB24 ||
316 format == Orthanc::PixelFormat_RGBA32)
317 {
318 // This must be set to 1 if the number of color channels is >= 3
319 parameters.tcp_mct = 1;
320 }
321 }
322
323 parameters.cp_comment = const_cast<char*>("");
324 }
325
326
327 void Jpeg2000Writer::WriteToMemoryInternal(std::string& compressed,
328 unsigned int width,
329 unsigned int height,
330 unsigned int pitch,
331 Orthanc::PixelFormat format,
332 const void* buffer)
333 {
334 if (format != Orthanc::PixelFormat_Grayscale8 &&
335 format != Orthanc::PixelFormat_RGB24 &&
336 format != Orthanc::PixelFormat_RGBA32)
337 {
338 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
339 }
340
341 opj_cparameters_t parameters;
342 SetupParameters(parameters, format, isLossless_);
343
344 OpenJpegImage image(width, height, pitch, format, buffer);
345 OpenJpegEncoder encoder(parameters, image);
346 OpenJpegOutput output(encoder);
347
348 #if ORTHANC_OPENJPEG_MAJOR_VERSION == 1
349 if (!opj_encode(encoder.GetObject(), output.GetObject(), image.GetObject(), parameters.index))
350 {
351 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
352 }
353 #else
354 if (!opj_start_compress(encoder.GetObject(), image.GetObject(), output.GetObject()) ||
355 !opj_encode(encoder.GetObject(), output.GetObject()) ||
356 !opj_end_compress(encoder.GetObject(), output.GetObject()))
357 {
358 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
359 }
360 #endif
361
362 output.Flatten(compressed);
363 }
364 }