comparison Core/Images/ImageProcessing.cpp @ 1612:96582230ddcb

Core/ImageFormats folder renamed as Core/Images
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 04 Sep 2015 16:36:18 +0200
parents Core/ImageFormats/ImageProcessing.cpp@2dff2bdffdb8
children b1291df2f780
comparison
equal deleted inserted replaced
1611:5e9b2aac8b89 1612:96582230ddcb
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 (std::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 if (target.GetFormat() == PixelFormat_Grayscale8 &&
382 source.GetFormat() == PixelFormat_RGBA32)
383 {
384 for (unsigned int y = 0; y < source.GetHeight(); y++)
385 {
386 const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
387 uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
388 for (unsigned int x = 0; x < source.GetWidth(); x++, q++)
389 {
390 *q = static_cast<uint8_t>((2126 * static_cast<uint32_t>(p[0]) +
391 7152 * static_cast<uint32_t>(p[1]) +
392 0722 * static_cast<uint32_t>(p[2])) / 10000);
393 p += 4;
394 }
395 }
396
397 return;
398 }
399
400 if (target.GetFormat() == PixelFormat_RGB24 &&
401 source.GetFormat() == PixelFormat_RGBA32)
402 {
403 for (unsigned int y = 0; y < source.GetHeight(); y++)
404 {
405 const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
406 uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
407 for (unsigned int x = 0; x < source.GetWidth(); x++)
408 {
409 q[0] = p[0];
410 q[1] = p[1];
411 q[2] = p[2];
412 p += 4;
413 q += 3;
414 }
415 }
416
417 return;
418 }
419
420 if (target.GetFormat() == PixelFormat_RGBA32 &&
421 source.GetFormat() == PixelFormat_RGB24)
422 {
423 for (unsigned int y = 0; y < source.GetHeight(); y++)
424 {
425 const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
426 uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
427 for (unsigned int x = 0; x < source.GetWidth(); x++)
428 {
429 q[0] = p[0];
430 q[1] = p[1];
431 q[2] = p[2];
432 q[3] = 255; // Set the alpha channel to full opacity
433 p += 3;
434 q += 4;
435 }
436 }
437
438 return;
439 }
440
441 throw OrthancException(ErrorCode_NotImplemented);
442 }
443
444
445
446 void ImageProcessing::Set(ImageAccessor& image,
447 int64_t value)
448 {
449 switch (image.GetFormat())
450 {
451 case PixelFormat_Grayscale8:
452 SetInternal<uint8_t>(image, value);
453 return;
454
455 case PixelFormat_Grayscale16:
456 SetInternal<uint16_t>(image, value);
457 return;
458
459 case PixelFormat_SignedGrayscale16:
460 SetInternal<int16_t>(image, value);
461 return;
462
463 default:
464 throw OrthancException(ErrorCode_NotImplemented);
465 }
466 }
467
468
469 void ImageProcessing::ShiftRight(ImageAccessor& image,
470 unsigned int shift)
471 {
472 if (image.GetWidth() == 0 ||
473 image.GetHeight() == 0 ||
474 shift == 0)
475 {
476 // Nothing to do
477 return;
478 }
479
480 throw OrthancException(ErrorCode_NotImplemented);
481 }
482
483
484 void ImageProcessing::GetMinMaxValue(int64_t& minValue,
485 int64_t& maxValue,
486 const ImageAccessor& image)
487 {
488 switch (image.GetFormat())
489 {
490 case PixelFormat_Grayscale8:
491 {
492 uint8_t a, b;
493 GetMinMaxValueInternal<uint8_t>(a, b, image);
494 minValue = a;
495 maxValue = b;
496 break;
497 }
498
499 case PixelFormat_Grayscale16:
500 {
501 uint16_t a, b;
502 GetMinMaxValueInternal<uint16_t>(a, b, image);
503 minValue = a;
504 maxValue = b;
505 break;
506 }
507
508 case PixelFormat_SignedGrayscale16:
509 {
510 int16_t a, b;
511 GetMinMaxValueInternal<int16_t>(a, b, image);
512 minValue = a;
513 maxValue = b;
514 break;
515 }
516
517 default:
518 throw OrthancException(ErrorCode_NotImplemented);
519 }
520 }
521
522
523
524 void ImageProcessing::AddConstant(ImageAccessor& image,
525 int64_t value)
526 {
527 switch (image.GetFormat())
528 {
529 case PixelFormat_Grayscale8:
530 AddConstantInternal<uint8_t>(image, value);
531 return;
532
533 case PixelFormat_Grayscale16:
534 AddConstantInternal<uint16_t>(image, value);
535 return;
536
537 case PixelFormat_SignedGrayscale16:
538 AddConstantInternal<int16_t>(image, value);
539 return;
540
541 default:
542 throw OrthancException(ErrorCode_NotImplemented);
543 }
544 }
545
546
547 void ImageProcessing::MultiplyConstant(ImageAccessor& image,
548 float factor)
549 {
550 switch (image.GetFormat())
551 {
552 case PixelFormat_Grayscale8:
553 MultiplyConstantInternal<uint8_t>(image, factor);
554 return;
555
556 case PixelFormat_Grayscale16:
557 MultiplyConstantInternal<uint16_t>(image, factor);
558 return;
559
560 case PixelFormat_SignedGrayscale16:
561 MultiplyConstantInternal<int16_t>(image, factor);
562 return;
563
564 default:
565 throw OrthancException(ErrorCode_NotImplemented);
566 }
567 }
568
569
570 void ImageProcessing::ShiftScale(ImageAccessor& image,
571 float offset,
572 float scaling)
573 {
574 switch (image.GetFormat())
575 {
576 case PixelFormat_Grayscale8:
577 ShiftScaleInternal<uint8_t>(image, offset, scaling);
578 return;
579
580 case PixelFormat_Grayscale16:
581 ShiftScaleInternal<uint16_t>(image, offset, scaling);
582 return;
583
584 case PixelFormat_SignedGrayscale16:
585 ShiftScaleInternal<int16_t>(image, offset, scaling);
586 return;
587
588 default:
589 throw OrthancException(ErrorCode_NotImplemented);
590 }
591 }
592 }