comparison Framework/ImageToolbox.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 "ImageToolbox.h"
22
23 #include "Jpeg2000Reader.h"
24 #include "Jpeg2000Writer.h"
25
26 #include "Orthanc/Core/OrthancException.h"
27 #include "Orthanc/Core/Images/ImageProcessing.h"
28 #include "Orthanc/Core/Images/PngReader.h"
29 #include "Orthanc/Core/Images/PngWriter.h"
30 #include "Orthanc/Core/Images/JpegReader.h"
31 #include "Orthanc/Core/Images/JpegWriter.h"
32 #include "Orthanc/Core/Logging.h"
33
34 #include <string.h>
35 #include <memory>
36
37
38 namespace OrthancWSI
39 {
40 namespace ImageToolbox
41 {
42 Orthanc::ImageAccessor* Allocate(Orthanc::PixelFormat format,
43 unsigned int width,
44 unsigned int height)
45 {
46 return new Orthanc::Image(format, width, height, false);
47 }
48
49
50 void Embed(Orthanc::ImageAccessor& target,
51 const Orthanc::ImageAccessor& source,
52 unsigned int x,
53 unsigned int y)
54 {
55 if (target.GetFormat() != source.GetFormat())
56 {
57 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
58 }
59
60 if (x >= target.GetWidth() ||
61 y >= target.GetHeight())
62 {
63 return;
64 }
65
66 unsigned int h = std::min(source.GetHeight(), target.GetHeight() - y);
67 unsigned int w = std::min(source.GetWidth(), target.GetWidth() - x);
68
69 Orthanc::ImageAccessor targetRegion = target.GetRegion(x, y, w, h);
70 Orthanc::ImageAccessor sourceRegion = source.GetRegion(0, 0, w, h);
71 Orthanc::ImageProcessing::Copy(targetRegion, sourceRegion);
72 }
73
74
75
76 void Set(Orthanc::ImageAccessor& image,
77 uint8_t r,
78 uint8_t g,
79 uint8_t b)
80 {
81 if (image.GetWidth() == 0 ||
82 image.GetHeight() == 0)
83 {
84 return;
85 }
86
87 uint8_t grayscale = (2126 * static_cast<uint16_t>(r) +
88 7152 * static_cast<uint16_t>(g) +
89 0722 * static_cast<uint16_t>(b)) / 10000;
90
91 switch (image.GetFormat())
92 {
93 case Orthanc::PixelFormat_Grayscale8:
94 {
95 for (unsigned int y = 0; y < image.GetHeight(); y++)
96 {
97 memset(image.GetRow(y), grayscale, image.GetWidth());
98 }
99
100 break;
101 }
102
103 case Orthanc::PixelFormat_RGB24:
104 {
105 for (unsigned int y = 0; y < image.GetHeight(); y++)
106 {
107 uint8_t* p = reinterpret_cast<uint8_t*>(image.GetRow(y));
108 for (unsigned int x = 0; x < image.GetWidth(); x++, p += 3)
109 {
110 p[0] = r;
111 p[1] = g;
112 p[2] = b;
113 }
114 }
115
116 break;
117 }
118
119 default:
120 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
121 }
122 }
123
124
125 Orthanc::ImageAccessor* DecodeTile(const std::string& source,
126 ImageCompression compression)
127 {
128 switch (compression)
129 {
130 case ImageCompression_Png:
131 {
132 std::auto_ptr<Orthanc::PngReader> reader(new Orthanc::PngReader);
133 reader->ReadFromMemory(source);
134 return reader.release();
135 }
136
137 case ImageCompression_Jpeg:
138 {
139 std::auto_ptr<Orthanc::JpegReader> reader(new Orthanc::JpegReader);
140 reader->ReadFromMemory(source);
141 return reader.release();
142 }
143
144 case ImageCompression_Jpeg2000:
145 {
146 std::auto_ptr<Jpeg2000Reader> reader(new Jpeg2000Reader);
147 reader->ReadFromMemory(source);
148 return reader.release();
149 }
150
151 default:
152 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
153 }
154 }
155
156
157 void EncodeTile(std::string& target,
158 const Orthanc::ImageAccessor& source,
159 ImageCompression compression,
160 uint8_t quality)
161 {
162 if (compression == ImageCompression_None)
163 {
164 unsigned int pitch = GetBytesPerPixel(source.GetFormat()) * source.GetWidth();
165 target.resize(pitch * source.GetHeight());
166
167 for (unsigned int i = 0; i < source.GetHeight(); i++)
168 {
169 memcpy(&target[i * pitch], source.GetConstRow(i), pitch);
170 }
171 }
172 else
173 {
174 std::auto_ptr<Orthanc::IImageWriter> writer;
175
176 switch (compression)
177 {
178 case ImageCompression_Png:
179 writer.reset(new Orthanc::PngWriter);
180 break;
181
182 case ImageCompression_Jpeg:
183 writer.reset(new Orthanc::JpegWriter);
184 dynamic_cast<Orthanc::JpegWriter&>(*writer).SetQuality(quality);
185 break;
186
187 case ImageCompression_Jpeg2000:
188 writer.reset(new Jpeg2000Writer);
189 break;
190
191 default:
192 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
193 }
194
195 writer->WriteToMemory(target, source);
196 }
197 }
198
199
200 void ChangeTileCompression(std::string& target,
201 const std::string& source,
202 ImageCompression sourceCompression,
203 ImageCompression targetCompression,
204 uint8_t quality)
205 {
206 if (sourceCompression == targetCompression)
207 {
208 target = source;
209 }
210 else
211 {
212 std::auto_ptr<Orthanc::ImageAccessor> decoded(DecodeTile(source, sourceCompression));
213 EncodeTile(target, *decoded, targetCompression, quality);
214 }
215 }
216
217
218 static uint8_t GetPixelValue(const Orthanc::ImageAccessor& source,
219 unsigned int x,
220 unsigned int y,
221 unsigned int channel,
222 int offsetX,
223 int offsetY)
224 {
225 assert(channel < source.GetBytesPerPixel());
226 assert(source.GetFormat() == Orthanc::PixelFormat_Grayscale8 ||
227 source.GetFormat() == Orthanc::PixelFormat_RGB24 ||
228 source.GetFormat() == Orthanc::PixelFormat_RGBA32); // 16bpp is unsupported
229
230 if (static_cast<int>(x) + offsetX < 0)
231 {
232 x = 0;
233 }
234 else
235 {
236 x += offsetX;
237 if (x >= source.GetWidth())
238 {
239 x = source.GetWidth() - 1;
240 }
241 }
242
243 if (static_cast<int>(y) + offsetY < 0)
244 {
245 y = 0;
246 }
247 else
248 {
249 y += offsetY;
250 if (y >= source.GetHeight())
251 {
252 y = source.GetHeight() - 1;
253 }
254 }
255
256 return *(reinterpret_cast<const uint8_t*>(source.GetConstBuffer()) +
257 y * source.GetPitch() + x * source.GetBytesPerPixel() + channel);
258 }
259
260
261 static uint8_t SmoothPixelValue(const Orthanc::ImageAccessor& source,
262 unsigned int x,
263 unsigned int y,
264 unsigned int channel)
265 {
266 static const uint32_t kernel[5] = { 1, 4, 6, 4, 1 };
267 static const uint32_t normalization = 2 * (1 + 4 + 6 + 4 + 1);
268
269 uint32_t accumulator = 0;
270
271 // Horizontal smoothing
272 for (int offset = -2; offset <= 2; offset++)
273 {
274 accumulator += kernel[offset + 2] * GetPixelValue(source, x, y, channel, offset, 0);
275 }
276
277 // Vertical smoothing
278 for (int offset = -2; offset <= 2; offset++)
279 {
280 accumulator += kernel[offset + 2] * GetPixelValue(source, x, y, channel, 0, offset);
281 }
282
283 return static_cast<uint8_t>(accumulator / normalization);
284 }
285
286
287 Orthanc::ImageAccessor* Halve(const Orthanc::ImageAccessor& source,
288 bool smooth)
289 {
290 if (source.GetWidth() % 2 == 1 ||
291 source.GetHeight() % 2 == 1)
292 {
293 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
294 }
295
296 if (source.GetFormat() != Orthanc::PixelFormat_Grayscale8 &&
297 source.GetFormat() != Orthanc::PixelFormat_RGB24 &&
298 source.GetFormat() != Orthanc::PixelFormat_RGBA32) // 16bpp is not supported (*)
299 {
300 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
301 }
302
303 unsigned int channelsCount = source.GetBytesPerPixel(); // OK tx (*)
304
305 std::auto_ptr<Orthanc::ImageAccessor> result(Allocate(source.GetFormat(),
306 source.GetWidth() / 2,
307 source.GetHeight() / 2));
308
309 for (unsigned int y = 0; y < source.GetHeight() / 2; y++)
310 {
311 uint8_t* q = reinterpret_cast<uint8_t*>(result->GetRow(y));
312
313 for (unsigned int x = 0; x < source.GetWidth() / 2; x++, q += 3)
314 {
315 for (unsigned int c = 0; c < channelsCount; c++)
316 {
317 if (smooth)
318 {
319 q[c] = SmoothPixelValue(source, 2 * x, 2 * y, c);
320 }
321 else
322 {
323 q[c] = GetPixelValue(source, 2 * x, 2 * y, c, 0, 0);
324 }
325 }
326 }
327 }
328
329 return result.release();
330 }
331
332
333 Orthanc::ImageAccessor* Clone(const Orthanc::ImageAccessor& accessor)
334 {
335 std::auto_ptr<Orthanc::ImageAccessor> result(Allocate(accessor.GetFormat(),
336 accessor.GetWidth(),
337 accessor.GetHeight()));
338 Embed(*result, accessor, 0, 0);
339
340 return result.release();
341 }
342
343
344 Orthanc::ImageAccessor* Render(ITiledPyramid& pyramid,
345 unsigned int level)
346 {
347 std::auto_ptr<Orthanc::ImageAccessor> result(Allocate(pyramid.GetPixelFormat(),
348 pyramid.GetLevelWidth(level),
349 pyramid.GetLevelHeight(level)));
350
351 LOG(INFO) << "Rendering a tiled image of size " << result->GetWidth() << "x" << result->GetHeight();
352
353 for (unsigned int y = 0; y < result->GetHeight(); y += pyramid.GetTileHeight())
354 {
355 for (unsigned int x = 0; x < result->GetWidth(); x += pyramid.GetTileWidth())
356 {
357 std::auto_ptr<Orthanc::ImageAccessor> tile(pyramid.DecodeTile(level,
358 x / pyramid.GetTileWidth(),
359 y / pyramid.GetTileHeight()));
360 Embed(*result, *tile, x, y);
361 }
362 }
363
364 return result.release();
365 }
366 }
367 }