comparison Orthanc/ImageFormats/ImageProcessing.cpp @ 0:02f7a0400a91

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 25 Feb 2015 13:45:35 +0100
parents
children 7a0af291cc90
comparison
equal deleted inserted replaced
-1:000000000000 0:02f7a0400a91
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2015 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 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 "../PrecompiledHeaders.h"
34 #include "ImageProcessing.h"
35
36 #include "../OrthancException.h"
37
38 #include <boost/math/special_functions/round.hpp>
39
40 #include <cassert>
41 #include <string.h>
42 #include <limits>
43 #include <stdint.h>
44
45 namespace Orthanc
46 {
47 template <typename TargetType, typename SourceType>
48 static void ConvertInternal(ImageAccessor& target,
49 const ImageAccessor& source)
50 {
51 const TargetType minValue = std::numeric_limits<TargetType>::min();
52 const TargetType maxValue = std::numeric_limits<TargetType>::max();
53
54 for (unsigned int y = 0; y < source.GetHeight(); y++)
55 {
56 TargetType* t = reinterpret_cast<TargetType*>(target.GetRow(y));
57 const SourceType* s = reinterpret_cast<const SourceType*>(source.GetConstRow(y));
58
59 for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s++)
60 {
61 if (static_cast<int32_t>(*s) < static_cast<int32_t>(minValue))
62 {
63 *t = minValue;
64 }
65 else if (static_cast<int32_t>(*s) > static_cast<int32_t>(maxValue))
66 {
67 *t = maxValue;
68 }
69 else
70 {
71 *t = static_cast<TargetType>(*s);
72 }
73 }
74 }
75 }
76
77
78 template <typename TargetType>
79 static void ConvertColorToGrayscale(ImageAccessor& target,
80 const ImageAccessor& source)
81 {
82 assert(source.GetFormat() == PixelFormat_RGB24);
83
84 const TargetType minValue = std::numeric_limits<TargetType>::min();
85 const TargetType maxValue = std::numeric_limits<TargetType>::max();
86
87 for (unsigned int y = 0; y < source.GetHeight(); y++)
88 {
89 TargetType* t = reinterpret_cast<TargetType*>(target.GetRow(y));
90 const uint8_t* s = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
91
92 for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s += 3)
93 {
94 // Y = 0.2126 R + 0.7152 G + 0.0722 B
95 int32_t v = (2126 * static_cast<int32_t>(s[0]) +
96 7152 * static_cast<int32_t>(s[1]) +
97 0722 * static_cast<int32_t>(s[2])) / 1000;
98
99 if (static_cast<int32_t>(v) < static_cast<int32_t>(minValue))
100 {
101 *t = minValue;
102 }
103 else if (static_cast<int32_t>(v) > static_cast<int32_t>(maxValue))
104 {
105 *t = maxValue;
106 }
107 else
108 {
109 *t = static_cast<TargetType>(v);
110 }
111 }
112 }
113 }
114
115
116 template <typename PixelType>
117 static void SetInternal(ImageAccessor& image,
118 int64_t constant)
119 {
120 for (unsigned int y = 0; y < image.GetHeight(); y++)
121 {
122 PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
123
124 for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
125 {
126 *p = static_cast<PixelType>(constant);
127 }
128 }
129 }
130
131
132 template <typename PixelType>
133 static void GetMinMaxValueInternal(PixelType& minValue,
134 PixelType& maxValue,
135 const ImageAccessor& source)
136 {
137 // Deal with the special case of empty image
138 if (source.GetWidth() == 0 ||
139 source.GetHeight() == 0)
140 {
141 minValue = 0;
142 maxValue = 0;
143 return;
144 }
145
146 minValue = std::numeric_limits<PixelType>::max();
147 maxValue = std::numeric_limits<PixelType>::min();
148
149 for (unsigned int y = 0; y < source.GetHeight(); y++)
150 {
151 const PixelType* p = reinterpret_cast<const PixelType*>(source.GetConstRow(y));
152
153 for (unsigned int x = 0; x < source.GetWidth(); x++, p++)
154 {
155 if (*p < minValue)
156 {
157 minValue = *p;
158 }
159
160 if (*p > maxValue)
161 {
162 maxValue = *p;
163 }
164 }
165 }
166 }
167
168
169
170 template <typename PixelType>
171 static void AddConstantInternal(ImageAccessor& image,
172 int64_t constant)
173 {
174 if (constant == 0)
175 {
176 return;
177 }
178
179 const int64_t minValue = std::numeric_limits<PixelType>::min();
180 const int64_t maxValue = std::numeric_limits<PixelType>::max();
181
182 for (unsigned int y = 0; y < image.GetHeight(); y++)
183 {
184 PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
185
186 for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
187 {
188 int64_t v = static_cast<int64_t>(*p) + constant;
189
190 if (v > maxValue)
191 {
192 *p = std::numeric_limits<PixelType>::max();
193 }
194 else if (v < minValue)
195 {
196 *p = std::numeric_limits<PixelType>::min();
197 }
198 else
199 {
200 *p = static_cast<PixelType>(v);
201 }
202 }
203 }
204 }
205
206
207
208 template <typename PixelType>
209 void MultiplyConstantInternal(ImageAccessor& image,
210 float factor)
211 {
212 if (abs(factor - 1.0f) <= std::numeric_limits<float>::epsilon())
213 {
214 return;
215 }
216
217 const int64_t minValue = std::numeric_limits<PixelType>::min();
218 const int64_t maxValue = std::numeric_limits<PixelType>::max();
219
220 for (unsigned int y = 0; y < image.GetHeight(); y++)
221 {
222 PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
223
224 for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
225 {
226 int64_t v = boost::math::llround(static_cast<float>(*p) * factor);
227
228 if (v > maxValue)
229 {
230 *p = std::numeric_limits<PixelType>::max();
231 }
232 else if (v < minValue)
233 {
234 *p = std::numeric_limits<PixelType>::min();
235 }
236 else
237 {
238 *p = static_cast<PixelType>(v);
239 }
240 }
241 }
242 }
243
244
245 template <typename PixelType>
246 void ShiftScaleInternal(ImageAccessor& image,
247 float offset,
248 float scaling)
249 {
250 const float minValue = static_cast<float>(std::numeric_limits<PixelType>::min());
251 const float maxValue = static_cast<float>(std::numeric_limits<PixelType>::max());
252
253 for (unsigned int y = 0; y < image.GetHeight(); y++)
254 {
255 PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
256
257 for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
258 {
259 float v = (static_cast<float>(*p) + offset) * scaling;
260
261 if (v > maxValue)
262 {
263 *p = std::numeric_limits<PixelType>::max();
264 }
265 else if (v < minValue)
266 {
267 *p = std::numeric_limits<PixelType>::min();
268 }
269 else
270 {
271 *p = static_cast<PixelType>(boost::math::iround(v));
272 }
273 }
274 }
275 }
276
277
278 void ImageProcessing::Copy(ImageAccessor& target,
279 const ImageAccessor& source)
280 {
281 if (target.GetWidth() != source.GetWidth() ||
282 target.GetHeight() != source.GetHeight())
283 {
284 throw OrthancException(ErrorCode_IncompatibleImageSize);
285 }
286
287 if (target.GetFormat() != source.GetFormat())
288 {
289 throw OrthancException(ErrorCode_IncompatibleImageFormat);
290 }
291
292 unsigned int lineSize = GetBytesPerPixel(source.GetFormat()) * source.GetWidth();
293
294 assert(source.GetPitch() >= lineSize && target.GetPitch() >= lineSize);
295
296 for (unsigned int y = 0; y < source.GetHeight(); y++)
297 {
298 memcpy(target.GetRow(y), source.GetConstRow(y), lineSize);
299 }
300 }
301
302
303 void ImageProcessing::Convert(ImageAccessor& target,
304 const ImageAccessor& source)
305 {
306 if (target.GetWidth() != source.GetWidth() ||
307 target.GetHeight() != source.GetHeight())
308 {
309 throw OrthancException(ErrorCode_IncompatibleImageSize);
310 }
311
312 if (source.GetFormat() == target.GetFormat())
313 {
314 Copy(target, source);
315 return;
316 }
317
318 if (target.GetFormat() == PixelFormat_Grayscale16 &&
319 source.GetFormat() == PixelFormat_Grayscale8)
320 {
321 ConvertInternal<uint16_t, uint8_t>(target, source);
322 return;
323 }
324
325 if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
326 source.GetFormat() == PixelFormat_Grayscale8)
327 {
328 ConvertInternal<int16_t, uint8_t>(target, source);
329 return;
330 }
331
332 if (target.GetFormat() == PixelFormat_Grayscale8 &&
333 source.GetFormat() == PixelFormat_Grayscale16)
334 {
335 ConvertInternal<uint8_t, uint16_t>(target, source);
336 return;
337 }
338
339 if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
340 source.GetFormat() == PixelFormat_Grayscale16)
341 {
342 ConvertInternal<int16_t, uint16_t>(target, source);
343 return;
344 }
345
346 if (target.GetFormat() == PixelFormat_Grayscale8 &&
347 source.GetFormat() == PixelFormat_SignedGrayscale16)
348 {
349 ConvertInternal<uint8_t, int16_t>(target, source);
350 return;
351 }
352
353 if (target.GetFormat() == PixelFormat_Grayscale16 &&
354 source.GetFormat() == PixelFormat_SignedGrayscale16)
355 {
356 ConvertInternal<uint16_t, int16_t>(target, source);
357 return;
358 }
359
360 if (target.GetFormat() == PixelFormat_Grayscale8 &&
361 source.GetFormat() == PixelFormat_RGB24)
362 {
363 ConvertColorToGrayscale<uint8_t>(target, source);
364 return;
365 }
366
367 if (target.GetFormat() == PixelFormat_Grayscale16 &&
368 source.GetFormat() == PixelFormat_RGB24)
369 {
370 ConvertColorToGrayscale<uint16_t>(target, source);
371 return;
372 }
373
374 if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
375 source.GetFormat() == PixelFormat_RGB24)
376 {
377 ConvertColorToGrayscale<int16_t>(target, source);
378 return;
379 }
380
381 throw OrthancException(ErrorCode_NotImplemented);
382 }
383
384
385
386 void ImageProcessing::Set(ImageAccessor& image,
387 int64_t value)
388 {
389 switch (image.GetFormat())
390 {
391 case PixelFormat_Grayscale8:
392 SetInternal<uint8_t>(image, value);
393 return;
394
395 case PixelFormat_Grayscale16:
396 SetInternal<uint16_t>(image, value);
397 return;
398
399 case PixelFormat_SignedGrayscale16:
400 SetInternal<int16_t>(image, value);
401 return;
402
403 default:
404 throw OrthancException(ErrorCode_NotImplemented);
405 }
406 }
407
408
409 void ImageProcessing::ShiftRight(ImageAccessor& image,
410 unsigned int shift)
411 {
412 if (image.GetWidth() == 0 ||
413 image.GetHeight() == 0 ||
414 shift == 0)
415 {
416 // Nothing to do
417 return;
418 }
419
420 throw OrthancException(ErrorCode_NotImplemented);
421 }
422
423
424 void ImageProcessing::GetMinMaxValue(int64_t& minValue,
425 int64_t& maxValue,
426 const ImageAccessor& image)
427 {
428 switch (image.GetFormat())
429 {
430 case PixelFormat_Grayscale8:
431 {
432 uint8_t a, b;
433 GetMinMaxValueInternal<uint8_t>(a, b, image);
434 minValue = a;
435 maxValue = b;
436 break;
437 }
438
439 case PixelFormat_Grayscale16:
440 {
441 uint16_t a, b;
442 GetMinMaxValueInternal<uint16_t>(a, b, image);
443 minValue = a;
444 maxValue = b;
445 break;
446 }
447
448 case PixelFormat_SignedGrayscale16:
449 {
450 int16_t a, b;
451 GetMinMaxValueInternal<int16_t>(a, b, image);
452 minValue = a;
453 maxValue = b;
454 break;
455 }
456
457 default:
458 throw OrthancException(ErrorCode_NotImplemented);
459 }
460 }
461
462
463
464 void ImageProcessing::AddConstant(ImageAccessor& image,
465 int64_t value)
466 {
467 switch (image.GetFormat())
468 {
469 case PixelFormat_Grayscale8:
470 AddConstantInternal<uint8_t>(image, value);
471 return;
472
473 case PixelFormat_Grayscale16:
474 AddConstantInternal<uint16_t>(image, value);
475 return;
476
477 case PixelFormat_SignedGrayscale16:
478 AddConstantInternal<int16_t>(image, value);
479 return;
480
481 default:
482 throw OrthancException(ErrorCode_NotImplemented);
483 }
484 }
485
486
487 void ImageProcessing::MultiplyConstant(ImageAccessor& image,
488 float factor)
489 {
490 switch (image.GetFormat())
491 {
492 case PixelFormat_Grayscale8:
493 MultiplyConstantInternal<uint8_t>(image, factor);
494 return;
495
496 case PixelFormat_Grayscale16:
497 MultiplyConstantInternal<uint16_t>(image, factor);
498 return;
499
500 case PixelFormat_SignedGrayscale16:
501 MultiplyConstantInternal<int16_t>(image, factor);
502 return;
503
504 default:
505 throw OrthancException(ErrorCode_NotImplemented);
506 }
507 }
508
509
510 void ImageProcessing::ShiftScale(ImageAccessor& image,
511 float offset,
512 float scaling)
513 {
514 switch (image.GetFormat())
515 {
516 case PixelFormat_Grayscale8:
517 ShiftScaleInternal<uint8_t>(image, offset, scaling);
518 return;
519
520 case PixelFormat_Grayscale16:
521 ShiftScaleInternal<uint16_t>(image, offset, scaling);
522 return;
523
524 case PixelFormat_SignedGrayscale16:
525 ShiftScaleInternal<int16_t>(image, offset, scaling);
526 return;
527
528 default:
529 throw OrthancException(ErrorCode_NotImplemented);
530 }
531 }
532 }