1 /*
2 * This file is part of RawTherapee.
3 *
4 * Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
5 *
6 * RawTherapee is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * RawTherapee is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
18 */
19 #pragma once
20
21 #include <vector>
22
23 #include <glibmm/ustring.h>
24 #include <lcms2.h>
25
26 #include "alignedbuffer.h"
27 #include "coord2d.h"
28 #include "imagedimensions.h"
29 #include "LUT.h"
30 #include "rt_math.h"
31
32 #include "../rtgui/threadutils.h"
33
34 #define TR_NONE 0
35 #define TR_R90 1
36 #define TR_R180 2
37 #define TR_R270 3
38 #define TR_VFLIP 4
39 #define TR_HFLIP 8
40 #define TR_ROT 3
41
42 #define CHECK_BOUNDS 0
43
44 namespace rtengine
45 {
46
47 namespace procparams
48 {
49
50 struct CoarseTransformParams;
51
52 }
53
54 class ProgressListener;
55 class Color;
56
57 extern const char sImage8[];
58 extern const char sImage16[];
59 extern const char sImagefloat[];
60
61 int getCoarseBitMask(const procparams::CoarseTransformParams& coarse);
62 const LUTf& getigammatab();
63
64 enum TypeInterpolation { TI_Nearest, TI_Bilinear };
65
66 // --------------------------------------------------------------------
67 // Generic classes
68 // --------------------------------------------------------------------
69
70 class ImageDatas : virtual public ImageDimensions
71 {
72 public:
73 template<class S, class D>
convertTo(S src,D & dst)74 void convertTo(S src, D& dst) const
75 {
76 dst = src;
77 }
78
79 // parameters that will never be used, replaced by the subclasses r, g and b parameters!
80 // they are still necessary to implement operator() in this parent class
~ImageDatas()81 virtual ~ImageDatas() {}
allocate(int W,int H)82 virtual void allocate (int W, int H) {}
rotate(int deg)83 virtual void rotate (int deg) {}
84 // free the memory allocated for the image data without deleting the object.
flushData()85 virtual void flushData ()
86 {
87 allocate(0, 0);
88 }
89
hflip()90 virtual void hflip () {}
vflip()91 virtual void vflip () {}
92
93 // Read the raw dump of the data
readData(FILE * fh)94 void readData (FILE *fh) {}
95 // Write a raw dump of the data
writeData(FILE * fh)96 void writeData (FILE *fh) const {}
97
normalizeInt(int srcMinVal,int srcMaxVal)98 virtual void normalizeInt (int srcMinVal, int srcMaxVal) {};
normalizeFloat(float srcMinVal,float srcMaxVal)99 virtual void normalizeFloat (float srcMinVal, float srcMaxVal) {};
computeHistogramAutoWB(double & avg_r,double & avg_g,double & avg_b,int & n,LUTu & histogram,int compression)100 virtual void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, int compression) const {}
getSpotWBData(double & reds,double & greens,double & blues,int & rn,int & gn,int & bn,std::vector<Coord2D> & red,std::vector<Coord2D> & green,std::vector<Coord2D> & blue,int tran)101 virtual void getSpotWBData (double &reds, double &greens, double &blues, int &rn, int &gn, int &bn,
102 std::vector<Coord2D> &red, std::vector<Coord2D> &green, std::vector<Coord2D> &blue,
103 int tran) const {}
getAutoWBMultipliers(double & rm,double & gm,double & bm)104 virtual void getAutoWBMultipliers (double &rm, double &gm, double &bm) const
105 {
106 rm = gm = bm = 1.0;
107 }
108
109 };
110
111 template<>
convertTo(unsigned short src,unsigned char & dst)112 inline void ImageDatas::convertTo(unsigned short src, unsigned char& dst) const
113 {
114 dst = uint16ToUint8Rounded(src);
115 }
116 template<>
convertTo(unsigned char src,int & dst)117 inline void ImageDatas::convertTo(unsigned char src, int& dst) const
118 {
119 dst = src * 257;
120 }
121 template<>
convertTo(unsigned char src,unsigned short & dst)122 inline void ImageDatas::convertTo(unsigned char src, unsigned short& dst) const
123 {
124 dst = src * 257;
125 }
126 template<>
convertTo(float src,unsigned char & dst)127 inline void ImageDatas::convertTo(float src, unsigned char& dst) const
128 {
129 dst = uint16ToUint8Rounded(CLIP(src));
130 }
131 template<>
convertTo(unsigned char src,float & dst)132 inline void ImageDatas::convertTo(unsigned char src, float& dst) const
133 {
134 dst = src * 257;
135 }
136 template<>
convertTo(float src,float & dst)137 inline void ImageDatas::convertTo(float src, float& dst) const
138 {
139 dst = std::isnan(src) ? 0.f : src;
140 }
141
142 // --------------------------------------------------------------------
143 // Planar order classes
144 // --------------------------------------------------------------------
145
146 template <class T>
147 class PlanarPtr
148 {
149 protected:
150 AlignedBuffer<T*> ab;
151 public:
152 #if CHECK_BOUNDS
153 size_t width_, height_;
154 #endif
155 T** ptrs;
156
157 #if CHECK_BOUNDS
PlanarPtr()158 PlanarPtr() : width_(0), height_(0), ptrs (NULL) {}
159 #else
PlanarPtr()160 PlanarPtr() : ptrs (nullptr) {}
161 #endif
162
resize(size_t newSize)163 bool resize(size_t newSize)
164 {
165 if (ab.resize(newSize)) {
166 ptrs = ab.data;
167 return true;
168 } else {
169 ptrs = nullptr;
170 return false;
171 }
172 }
swap(PlanarPtr<T> & other)173 void swap (PlanarPtr<T> &other)
174 {
175 ab.swap(other.ab);
176 T** tmpsPtrs = other.ptrs;
177 other.ptrs = ptrs;
178 ptrs = tmpsPtrs;
179
180 #if CHECK_BOUNDS
181 size_t tmp = other.width_;
182 other.width_ = width_;
183 width_ = tmp;
184 tmp = other.height_;
185 other.height_ = height_;
186 height_ = tmp;
187 #endif
188 }
189
operator()190 T*& operator() (size_t row)
191 {
192 #if CHECK_BOUNDS
193 assert (row < height_);
194 #endif
195 return ptrs[row];
196 }
197 // Will send back the start of a row, starting with a red, green or blue value
operator()198 T* operator() (size_t row) const
199 {
200 #if CHECK_BOUNDS
201 assert (row < height_);
202 #endif
203 return ptrs[row];
204 }
205 // Will send back a value at a given row, col position
operator()206 T& operator() (size_t row, size_t col)
207 {
208 #if CHECK_BOUNDS
209 assert (row < height_ && col < width_);
210 #endif
211 return ptrs[row][col];
212 }
operator()213 const T operator() (size_t row, size_t col) const
214 {
215 #if CHECK_BOUNDS
216 assert (row < height_ && col < width_);
217 #endif
218 return ptrs[row][col];
219 }
220 };
221
222 template <class T>
223 class PlanarWhateverData : virtual public ImageDatas
224 {
225
226 private:
227 AlignedBuffer<T> abData;
228
229 size_t rowstride; // Plan size, in bytes (all padding bytes included)
230
231 public:
232 T* data;
233 PlanarPtr<T> v; // v stands for "value", whatever it represent
234
PlanarWhateverData()235 PlanarWhateverData() : rowstride(0), data (nullptr) {}
PlanarWhateverData(int w,int h)236 PlanarWhateverData(int w, int h) : rowstride(0), data (nullptr)
237 {
238 allocate(w, h);
239 }
240
241 // Send back the row stride. WARNING: unit = byte, not element!
getRowStride()242 size_t getRowStride () const
243 {
244 return rowstride;
245 }
246
swap(PlanarWhateverData<T> & other)247 void swap(PlanarWhateverData<T> &other)
248 {
249 abData.swap(other.abData);
250 v.swap(other.v);
251 T* tmpData = other.data;
252 other.data = data;
253 data = tmpData;
254 int tmpWidth = other.width;
255 other.width = width;
256 width = tmpWidth;
257 int tmpHeight = other.height;
258 other.height = height;
259 height = tmpHeight;
260 #if CHECK_BOUNDS
261 v.width_ = width;
262 v.height_ = height;
263 #endif
264 }
265
266 // use as pointer to data
267 //operator void*() { return data; };
268
269 /* If any of the required allocation fails, "width" and "height" are set to -1, and all remaining buffer are freed
270 * Can be safely used to reallocate an existing image */
allocate(int W,int H)271 void allocate (int W, int H) override
272 {
273 if (W == width && H == height) {
274 return;
275 }
276
277 width = W;
278 height = H;
279 #if CHECK_BOUNDS
280 v.width_ = width;
281 v.height_ = height;
282 #endif
283
284 if (sizeof(T) > 1) {
285 // 128 bits memory alignment for >8bits data
286 rowstride = ( width * sizeof(T) + 15 ) / 16 * 16;
287 } else {
288 // No memory alignment for 8bits data
289 rowstride = width * sizeof(T);
290 }
291
292 // find the padding length to ensure a 128 bits alignment for each row
293 size_t size = rowstride * height;
294
295 if (!width) {
296 size = 0;
297 rowstride = 0;
298 }
299
300 if (size && abData.resize(size, 1)
301 && v.resize(height) ) {
302 data = abData.data;
303 } else {
304 // asking for a new size of 0 is safe and will free memory, if any!
305 abData.resize(0);
306 data = nullptr;
307 v.resize(0);
308 width = height = -1;
309 #if CHECK_BOUNDS
310 v.width_ = v.height_ = -1;
311 #endif
312
313 return;
314 }
315
316 char *start = (char*)(data);
317
318 for (int i = 0; i < height; ++i) {
319 int k = i * rowstride;
320 v(i) = (T*)(start + k);
321 }
322 }
323
324 /** Copy the data to another PlanarWhateverData */
copyData(PlanarWhateverData<T> * dest)325 void copyData(PlanarWhateverData<T> *dest) const
326 {
327 assert (dest != NULL);
328 // Make sure that the size is the same, reallocate if necessary
329 dest->allocate(width, height);
330
331 if (dest->width == -1) {
332 return;
333 }
334
335 for (int i = 0; i < height; i++) {
336 memcpy (dest->v(i), v(i), width * sizeof(T));
337 }
338 }
339
rotate(int deg)340 void rotate (int deg) override
341 {
342
343 if (deg == 90) {
344 PlanarWhateverData<T> rotatedImg(height, width); // New image, rotated
345
346 for (int ny = 0; ny < rotatedImg.height; ny++) {
347 int ox = ny;
348 int oy = height - 1;
349
350 for (int nx = 0; nx < rotatedImg.width; nx++) {
351 rotatedImg.v(ny, nx) = v(oy, ox);
352 --oy;
353 }
354 }
355
356 swap(rotatedImg);
357 } else if (deg == 270) {
358 PlanarWhateverData<T> rotatedImg(height, width); // New image, rotated
359
360 for (int nx = 0; nx < rotatedImg.width; nx++) {
361 int oy = nx;
362 int ox = width - 1;
363
364 for (int ny = 0; ny < rotatedImg.height; ny++) {
365 rotatedImg.v(ny, nx) = v(oy, ox);
366 --ox;
367 }
368 }
369
370 swap(rotatedImg);
371 } else if (deg == 180) {
372 int height2 = height / 2 + (height & 1);
373
374 #ifdef _OPENMP
375 // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
376 bool bigImage = width > 32 && height > 50;
377 #pragma omp parallel for schedule(static) if(bigImage)
378 #endif
379
380 for (int i = 0; i < height2; i++) {
381 for (int j = 0; j < width; j++) {
382 T tmp;
383 int x = width - 1 - j;
384 int y = height - 1 - i;
385
386 tmp = v(i, j);
387 v(i, j) = v(y, x);
388 v(y, x) = tmp;
389 }
390 }
391 #ifdef _OPENMP
392 static_cast<void>(bigImage); // to silence cppcheck warning
393 #endif
394 }
395 }
396
397 template <class IC>
resizeImgTo(int nw,int nh,TypeInterpolation interp,PlanarWhateverData<IC> * imgPtr)398 void resizeImgTo (int nw, int nh, TypeInterpolation interp, PlanarWhateverData<IC> *imgPtr) const
399 {
400 //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height);
401 if (width == nw && height == nh) {
402 // special case where no resizing is necessary, just type conversion....
403 for (int i = 0; i < height; i++) {
404 for (int j = 0; j < width; j++) {
405 convertTo(v(i, j), imgPtr->v(i, j));
406 }
407 }
408 } else if (interp == TI_Nearest) {
409 for (int i = 0; i < nh; i++) {
410 int ri = i * height / nh;
411
412 for (int j = 0; j < nw; j++) {
413 int ci = j * width / nw;
414 convertTo(v(ri, ci), imgPtr->v(i, j));
415 }
416 }
417 } else if (interp == TI_Bilinear) {
418 for (int i = 0; i < nh; i++) {
419 int sy = i * height / nh;
420
421 if (sy >= height) {
422 sy = height - 1;
423 }
424
425 float dy = float(i) * float(height) / float(nh) - float(sy);
426 int ny = sy + 1;
427
428 if (ny >= height) {
429 ny = sy;
430 }
431
432 for (int j = 0; j < nw; j++) {
433 int sx = j * width / nw;
434
435 if (sx >= width) {
436 sx = width;
437 }
438
439 float dx = float(j) * float(width) / float(nw) - float(sx);
440 int nx = sx + 1;
441
442 if (nx >= width) {
443 nx = sx;
444 }
445
446 convertTo(v(sy, sx) * (1.f - dx) * (1.f - dy) + v(sy, nx)*dx * (1.f - dy) + v(ny, sx) * (1.f - dx)*dy + v(ny, nx)*dx * dy, imgPtr->v(i, j));
447 }
448 }
449 } else {
450 // This case should never occur!
451 for (int i = 0; i < nh; i++) {
452 for (int j = 0; j < nw; j++) {
453 v(i, j) = 0;
454 }
455 }
456 }
457 }
458
hflip()459 void hflip () override
460 {
461 int width2 = width / 2;
462
463 #ifdef _OPENMP
464 // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
465 bool bigImage = width > 32 && height > 50;
466 #pragma omp parallel for schedule(static) if(bigImage)
467 #endif
468
469 for (int i = 0; i < height; i++)
470 for (int j = 0; j < width2; j++) {
471 float temp;
472 int x = width - 1 - j;
473
474 temp = v(i, j);
475 v(i, j) = v(i, x);
476 v(i, x) = temp;
477 }
478 #ifdef _OPENMP
479 static_cast<void>(bigImage); // to silence cppcheck warning
480 #endif
481 }
482
vflip()483 void vflip () override
484 {
485
486 int height2 = height / 2;
487
488 #ifdef _OPENMP
489 // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
490 bool bigImage = width > 32 && height > 50;
491 #pragma omp parallel for schedule(static) if(bigImage)
492 #endif
493
494 for (int i = 0; i < height2; i++)
495 for (int j = 0; j < width; j++) {
496 T temp;
497 int y = height - 1 - i;
498
499 temp = v(i, j);
500 v(i, j) = v(y, j);
501 v(y, j) = temp;
502 }
503 #ifdef _OPENMP
504 static_cast<void>(bigImage); // to silence cppcheck warning
505 #endif
506 }
507
transformPixel(int x,int y,int tran,int & tx,int & ty)508 void transformPixel (int x, int y, int tran, int& tx, int& ty) const
509 {
510
511 if (!tran) {
512 tx = x;
513 ty = y;
514 return;
515 }
516
517 int W = width;
518 int H = height;
519 int sw = W, sh = H;
520
521 if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) {
522 sw = H;
523 sh = W;
524 }
525
526 int ppx = x, ppy = y;
527
528 if (tran & TR_HFLIP) {
529 ppx = sw - 1 - x;
530 }
531
532 if (tran & TR_VFLIP) {
533 ppy = sh - 1 - y;
534 }
535
536 tx = ppx;
537 ty = ppy;
538
539 if ((tran & TR_ROT) == TR_R180) {
540 tx = W - 1 - ppx;
541 ty = H - 1 - ppy;
542 } else if ((tran & TR_ROT) == TR_R90) {
543 tx = ppy;
544 ty = H - 1 - ppx;
545 } else if ((tran & TR_ROT) == TR_R270) {
546 tx = W - 1 - ppy;
547 ty = ppx;
548 }
549 }
550
getPipetteData(T & value,int posX,int posY,int squareSize,int tran)551 void getPipetteData (T &value, int posX, int posY, int squareSize, int tran) const
552 {
553 int x;
554 int y;
555 float accumulator = 0.f; // using float to avoid range overflow; -> please creates specialization if necessary
556 unsigned long int n = 0;
557 int halfSquare = squareSize / 2;
558 transformPixel (posX, posY, tran, x, y);
559
560 for (int iy = y - halfSquare; iy < y - halfSquare + squareSize; ++iy) {
561 for (int ix = x - halfSquare; ix < x - halfSquare + squareSize; ++ix) {
562 if (ix >= 0 && iy >= 0 && ix < width && iy < height) {
563 accumulator += float(this->v(iy, ix));
564 ++n;
565 }
566 }
567 }
568
569 value = n ? T(accumulator / float(n)) : T(0);
570 }
571
readData(FILE * f)572 void readData (FILE *f)
573 {
574 for (int i = 0; i < height; i++) {
575 fread (v(i), sizeof(T), width, f);
576 }
577 }
578
writeData(FILE * f)579 void writeData (FILE *f) const
580 {
581 for (int i = 0; i < height; i++) {
582 fwrite (v(i), sizeof(T), width, f);
583 }
584 }
585
fill(T value)586 void fill (T value) {
587 for (int i = 0; i < height; i++) {
588 for (int j = 0; j < width; j++) {
589 v(i, j) = value;
590 }
591 }
592 }
593
594 };
595
596
597 template <class T>
598 class PlanarRGBData : virtual public ImageDatas
599 {
600
601 private:
602 AlignedBuffer<T> abData;
603
604 size_t rowstride; // Plan size, in bytes (all padding bytes included)
605 size_t planestride; // Row length, in bytes (padding bytes included)
606 protected:
607 T* data;
608
609 public:
610 PlanarPtr<T> r;
611 PlanarPtr<T> g;
612 PlanarPtr<T> b;
613
PlanarRGBData()614 PlanarRGBData() : rowstride(0), planestride(0), data (nullptr) {}
PlanarRGBData(size_t w,size_t h)615 PlanarRGBData(size_t w, size_t h) : rowstride(0), planestride(0), data (nullptr)
616 {
617 allocate(w, h);
618 }
619
620 // Send back the row stride. WARNING: unit = byte, not element!
getRowStride()621 size_t getRowStride () const
622 {
623 return rowstride;
624 }
625 // Send back the plane stride. WARNING: unit = byte, not element!
getPlaneStride()626 size_t getPlaneStride () const
627 {
628 return planestride;
629 }
630
swap(PlanarRGBData<T> & other)631 void swap(PlanarRGBData<T> &other)
632 {
633 abData.swap(other.abData);
634 r.swap(other.r);
635 g.swap(other.g);
636 b.swap(other.b);
637 T* tmpData = other.data;
638 other.data = data;
639 data = tmpData;
640 int tmpWidth = other.width;
641 other.width = width;
642 width = tmpWidth;
643 int tmpHeight = other.height;
644 other.height = height;
645 height = tmpHeight;
646 #if CHECK_BOUNDS
647 r.width_ = width;
648 r.height_ = height;
649 g.width_ = width;
650 g.height_ = height;
651 b.width_ = width;
652 b.height_ = height;
653 #endif
654 }
655
656 // use as pointer to data
657 //operator void*() { return data; };
658
659 /* If any of the required allocation fails, "width" and "height" are set to -1, and all remaining buffer are freed
660 * Can be safely used to reallocate an existing image */
allocate(int W,int H)661 void allocate (int W, int H) override
662 {
663
664 if (W == width && H == height) {
665 return;
666 }
667
668 width = W;
669 height = H;
670 #if CHECK_BOUNDS
671 r.width_ = width;
672 r.height_ = height;
673 g.width_ = width;
674 g.height_ = height;
675 b.width_ = width;
676 b.height_ = height;
677 #endif
678
679 if (sizeof(T) > 1) {
680 // 128 bits memory alignment for >8bits data
681 rowstride = ( width * sizeof(T) + 15 ) / 16 * 16;
682 planestride = rowstride * height;
683 } else {
684 // No memory alignment for 8bits data
685 rowstride = width * sizeof(T);
686 planestride = rowstride * height;
687 }
688
689 // find the padding length to ensure a 128 bits alignment for each row
690 size_t size = (size_t)rowstride * 3 * (size_t)height;
691
692 if (!width) {
693 size = 0;
694 rowstride = 0;
695 }
696
697 if (size && abData.resize(size, 1)
698 && r.resize(height)
699 && g.resize(height)
700 && b.resize(height) ) {
701 data = abData.data;
702 } else {
703 // asking for a new size of 0 is safe and will free memory, if any!
704 abData.resize(0);
705 data = nullptr;
706 r.resize(0);
707 g.resize(0);
708 b.resize(0);
709 width = height = -1;
710 #if CHECK_BOUNDS
711 r.width_ = r.height_ = -1;
712 g.width_ = g.height_ = -1;
713 b.width_ = b.height_ = -1;
714 #endif
715 return;
716 }
717
718 char *redstart = (char*)(data);
719 char *greenstart = (char*)(data) + planestride;
720 char *bluestart = (char*)(data) + 2 * planestride;
721
722 for (int i = 0; i < height; ++i) {
723 size_t k = i * rowstride;
724 r(i) = (T*)(redstart + k);
725 g(i) = (T*)(greenstart + k);
726 b(i) = (T*)(bluestart + k);
727 }
728 }
729
730 /** Copy the data to another PlanarRGBData */
copyData(PlanarRGBData<T> * dest)731 void copyData(PlanarRGBData<T> *dest) const
732 {
733 assert (dest != nullptr);
734 // Make sure that the size is the same, reallocate if necessary
735 dest->allocate(width, height);
736
737 if (dest->width == -1) {
738 printf("ERROR: PlanarRGBData::copyData >>> allocation failed!\n");
739 return;
740 }
741
742 for (int i = 0; i < height; i++) {
743 memcpy (dest->r(i), r(i), width * sizeof(T));
744 memcpy (dest->g(i), g(i), width * sizeof(T));
745 memcpy (dest->b(i), b(i), width * sizeof(T));
746 }
747 }
748
rotate(int deg)749 void rotate (int deg) override
750 {
751
752 if (deg == 90) {
753 PlanarRGBData<T> rotatedImg(height, width); // New image, rotated
754
755 for (int ny = 0; ny < rotatedImg.height; ny++) {
756 int ox = ny;
757 int oy = height - 1;
758
759 for (int nx = 0; nx < rotatedImg.width; nx++) {
760 rotatedImg.r(ny, nx) = r(oy, ox);
761 rotatedImg.g(ny, nx) = g(oy, ox);
762 rotatedImg.b(ny, nx) = b(oy, ox);
763 --oy;
764 }
765 }
766
767 swap(rotatedImg);
768 } else if (deg == 270) {
769 PlanarRGBData<T> rotatedImg(height, width); // New image, rotated
770
771 for (int nx = 0; nx < rotatedImg.width; nx++) {
772 int oy = nx;
773 int ox = width - 1;
774
775 for (int ny = 0; ny < rotatedImg.height; ny++) {
776 rotatedImg.r(ny, nx) = r(oy, ox);
777 rotatedImg.g(ny, nx) = g(oy, ox);
778 rotatedImg.b(ny, nx) = b(oy, ox);
779 --ox;
780 }
781 }
782
783 swap(rotatedImg);
784 } else if (deg == 180) {
785 int height2 = height / 2 + (height & 1);
786
787 #ifdef _OPENMP
788 // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
789 bool bigImage = width > 32 && height > 50;
790 #pragma omp parallel for schedule(static) if(bigImage)
791 #endif
792
793 for (int i = 0; i < height2; i++) {
794 for (int j = 0; j < width; j++) {
795 T tmp;
796 int x = width - 1 - j;
797 int y = height - 1 - i;
798
799 tmp = r(i, j);
800 r(i, j) = r(y, x);
801 r(y, x) = tmp;
802
803 tmp = g(i, j);
804 g(i, j) = g(y, x);
805 g(y, x) = tmp;
806
807 tmp = b(i, j);
808 b(i, j) = b(y, x);
809 b(y, x) = tmp;
810 }
811 }
812 #ifdef _OPENMP
813 static_cast<void>(bigImage); // to silence cppcheck warning
814 #endif
815 }
816 }
817
818 template <class IC>
resizeImgTo(int nw,int nh,TypeInterpolation interp,IC * imgPtr)819 void resizeImgTo (int nw, int nh, TypeInterpolation interp, IC *imgPtr) const
820 {
821 //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height);
822 if (width == nw && height == nh) {
823 // special case where no resizing is necessary, just type conversion....
824 for (int i = 0; i < height; i++) {
825 for (int j = 0; j < width; j++) {
826 convertTo(r(i, j), imgPtr->r(i, j));
827 convertTo(g(i, j), imgPtr->g(i, j));
828 convertTo(b(i, j), imgPtr->b(i, j));
829 }
830 }
831 } else if (interp == TI_Nearest) {
832 for (int i = 0; i < nh; i++) {
833 int ri = i * height / nh;
834
835 for (int j = 0; j < nw; j++) {
836 int ci = j * width / nw;
837 convertTo(r(ri, ci), imgPtr->r(i, j));
838 convertTo(g(ri, ci), imgPtr->g(i, j));
839 convertTo(b(ri, ci), imgPtr->b(i, j));
840 }
841 }
842 } else if (interp == TI_Bilinear) {
843 float heightByNh = float(height) / float(nh);
844 float widthByNw = float(width) / float(nw);
845 float syf = 0.f;
846
847 for (int i = 0; i < nh; i++, syf += heightByNh) {
848 int sy = syf;
849 float dy = syf - float(sy);
850 int ny = sy < height - 1 ? sy + 1 : sy;
851
852 float sxf = 0.f;
853
854 for (int j = 0; j < nw; j++, sxf += widthByNw) {
855 int sx = sxf;
856 float dx = sxf - float(sx);
857 int nx = sx < width - 1 ? sx + 1 : sx;
858
859 convertTo(r(sy, sx) * (1.f - dx) * (1.f - dy) + r(sy, nx)*dx * (1.f - dy) + r(ny, sx) * (1.f - dx)*dy + r(ny, nx)*dx * dy, imgPtr->r(i, j));
860 convertTo(g(sy, sx) * (1.f - dx) * (1.f - dy) + g(sy, nx)*dx * (1.f - dy) + g(ny, sx) * (1.f - dx)*dy + g(ny, nx)*dx * dy, imgPtr->g(i, j));
861 convertTo(b(sy, sx) * (1.f - dx) * (1.f - dy) + b(sy, nx)*dx * (1.f - dy) + b(ny, sx) * (1.f - dx)*dy + b(ny, nx)*dx * dy, imgPtr->b(i, j));
862 }
863 }
864 } else {
865 // This case should never occur!
866 for (int i = 0; i < nh; i++) {
867 for (int j = 0; j < nw; j++) {
868 imgPtr->r(i, j) = 0;
869 imgPtr->g(i, j) = 0;
870 imgPtr->b(i, j) = 0;
871 }
872 }
873 }
874 }
875
hflip()876 void hflip () override
877 {
878 int width2 = width / 2;
879
880 #ifdef _OPENMP
881 // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
882 bool bigImage = width > 32 && height > 50;
883 #pragma omp parallel for schedule(static) if(bigImage)
884 #endif
885
886 for (int i = 0; i < height; i++)
887 for (int j = 0; j < width2; j++) {
888 float temp;
889 int x = width - 1 - j;
890
891 temp = r(i, j);
892 r(i, j) = r(i, x);
893 r(i, x) = temp;
894
895 temp = g(i, j);
896 g(i, j) = g(i, x);
897 g(i, x) = temp;
898
899 temp = b(i, j);
900 b(i, j) = b(i, x);
901 b(i, x) = temp;
902 }
903 #ifdef _OPENMP
904 static_cast<void>(bigImage); // to silence cppcheck warning
905 #endif
906 }
907
vflip()908 void vflip () override
909 {
910
911 int height2 = height / 2;
912
913 #ifdef _OPENMP
914 // difficult to find a cutoff value where parallelization is counter productive because of processor's data cache collision...
915 bool bigImage = width > 32 && height > 50;
916 #pragma omp parallel for schedule(static) if(bigImage)
917 #endif
918
919 for (int i = 0; i < height2; i++)
920 for (int j = 0; j < width; j++) {
921 T tempR, tempG, tempB;
922 int y = height - 1 - i;
923
924 tempR = r(i, j);
925 r(i, j) = r(y, j);
926 r(y, j) = tempR;
927
928 tempG = g(i, j);
929 g(i, j) = g(y, j);
930 g(y, j) = tempG;
931
932 tempB = b(i, j);
933 b(i, j) = b(y, j);
934 b(y, j) = tempB;
935 }
936 #ifdef _OPENMP
937 static_cast<void>(bigImage); // to silence cppcheck warning
938 #endif
939 }
940
calcGrayscaleHist(unsigned int * hist16)941 void calcGrayscaleHist(unsigned int *hist16) const
942 {
943 for (int row = 0; row < height; row++)
944 for (int col = 0; col < width; col++) {
945 unsigned short rIdx, gIdx, bIdx;
946 convertTo(r(row, col), rIdx);
947 convertTo(g(row, col), gIdx);
948 convertTo(b(row, col), bIdx);
949 hist16[rIdx]++;
950 hist16[gIdx] += 2; // Bayer 2x green correction
951 hist16[bIdx]++;
952 }
953 }
954
computeAutoHistogram(LUTu & histogram,int & histcompr)955 void computeAutoHistogram (LUTu & histogram, int& histcompr) const
956 {
957 histcompr = 3;
958
959 histogram(65536 >> histcompr);
960 histogram.clear();
961 const LUTf& igammatab = getigammatab();
962
963 #ifdef _OPENMP
964 #pragma omp parallel
965 #endif
966 {
967 LUTu histThr(histogram.getSize());
968 histThr.clear();
969 #ifdef _OPENMP
970 #pragma omp for schedule(dynamic,16) nowait
971 #endif
972 for (int i = 0; i < height; i++) {
973 for (int j = 0; j < width; j++) {
974 float r_, g_, b_;
975 convertTo<T, float>(r(i, j), r_);
976 convertTo<T, float>(g(i, j), g_);
977 convertTo<T, float>(b(i, j), b_);
978 histThr[static_cast<int>(igammatab[r_]) >> histcompr]++;
979 histThr[static_cast<int>(igammatab[g_]) >> histcompr]++;
980 histThr[static_cast<int>(igammatab[b_]) >> histcompr]++;
981 }
982 }
983 #ifdef _OPENMP
984 #pragma omp critical
985 #endif
986 {
987 histogram += histThr;
988 }
989 }
990 }
991
computeHistogramAutoWB(double & avg_r,double & avg_g,double & avg_b,int & n,LUTu & histogram,const int compression)992 void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, const int compression) const override
993 {
994 histogram.clear();
995 avg_r = avg_g = avg_b = 0.;
996 n = 0;
997 const LUTf& igammatab = getigammatab();
998 for (unsigned int i = 0; i < (unsigned int)(height); i++)
999 for (unsigned int j = 0; j < (unsigned int)(width); j++) {
1000 float r_, g_, b_;
1001 convertTo<T, float>(r(i, j), r_);
1002 convertTo<T, float>(g(i, j), g_);
1003 convertTo<T, float>(b(i, j), b_);
1004 int rtemp = igammatab[r_];
1005 int gtemp = igammatab[g_];
1006 int btemp = igammatab[b_];
1007
1008 histogram[rtemp >> compression]++;
1009 histogram[gtemp >> compression] += 2;
1010 histogram[btemp >> compression]++;
1011
1012 // autowb computation
1013 if (r_ > 64000.f || g_ > 64000.f || b_ > 64000.f) {
1014 continue;
1015 }
1016
1017 avg_r += double(r_);
1018 avg_g += double(g_);
1019 avg_b += double(b_);
1020 n++;
1021 }
1022 }
1023
getAutoWBMultipliers(double & rm,double & gm,double & bm)1024 void getAutoWBMultipliers (double &rm, double &gm, double &bm) const override
1025 {
1026
1027 double avg_r = 0.;
1028 double avg_g = 0.;
1029 double avg_b = 0.;
1030 int n = 0;
1031 //int p = 6;
1032
1033 #ifdef _OPENMP
1034 #pragma omp parallel for reduction(+:avg_r,avg_g,avg_b,n) schedule(dynamic,16)
1035 #endif
1036 for (unsigned int i = 0; i < (unsigned int)(height); i++)
1037 for (unsigned int j = 0; j < (unsigned int)(width); j++) {
1038 float r_, g_, b_;
1039 convertTo<T, float>(r(i, j), r_);
1040 convertTo<T, float>(g(i, j), g_);
1041 convertTo<T, float>(b(i, j), b_);
1042
1043 if (r_ > 64000.f || g_ > 64000.f || b_ > 64000.f) {
1044 continue;
1045 }
1046
1047 avg_r += double(r_);
1048 avg_g += double(g_);
1049 avg_b += double(b_);
1050 n++;
1051 }
1052
1053 rm = avg_r / double(n);
1054 gm = avg_g / double(n);
1055 bm = avg_b / double(n);
1056 }
1057
transformPixel(int x,int y,int tran,int & tx,int & ty)1058 void transformPixel (int x, int y, int tran, int& tx, int& ty) const
1059 {
1060
1061 if (!tran) {
1062 tx = x;
1063 ty = y;
1064 return;
1065 }
1066
1067 int W = width;
1068 int H = height;
1069 int sw = W, sh = H;
1070
1071 if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) {
1072 sw = H;
1073 sh = W;
1074 }
1075
1076 int ppx = x, ppy = y;
1077
1078 if (tran & TR_HFLIP) {
1079 ppx = sw - 1 - x;
1080 }
1081
1082 if (tran & TR_VFLIP) {
1083 ppy = sh - 1 - y;
1084 }
1085
1086 tx = ppx;
1087 ty = ppy;
1088
1089 if ((tran & TR_ROT) == TR_R180) {
1090 tx = W - 1 - ppx;
1091 ty = H - 1 - ppy;
1092 } else if ((tran & TR_ROT) == TR_R90) {
1093 tx = ppy;
1094 ty = H - 1 - ppx;
1095 } else if ((tran & TR_ROT) == TR_R270) {
1096 tx = W - 1 - ppy;
1097 ty = ppx;
1098 }
1099 }
1100
getSpotWBData(double & reds,double & greens,double & blues,int & rn,int & gn,int & bn,std::vector<Coord2D> & red,std::vector<Coord2D> & green,std::vector<Coord2D> & blue,int tran)1101 void getSpotWBData (double &reds, double &greens, double &blues, int &rn, int &gn, int &bn,
1102 std::vector<Coord2D> &red, std::vector<Coord2D> &green, std::vector<Coord2D> &blue,
1103 int tran) const override
1104 {
1105 int x;
1106 int y;
1107 reds = 0, greens = 0, blues = 0;
1108 rn = 0, gn = 0, bn = 0;
1109
1110 for (size_t i = 0; i < red.size(); i++) {
1111 transformPixel (red[i].x, red[i].y, tran, x, y);
1112
1113 if (x >= 0 && y >= 0 && x < width && y < height) {
1114 float v;
1115 convertTo<T, float>(this->r(y, x), v);
1116 reds += double(v);
1117 rn++;
1118 }
1119
1120 transformPixel (green[i].x, green[i].y, tran, x, y);
1121
1122 if (x >= 0 && y >= 0 && x < width && y < height) {
1123 float v;
1124 convertTo<T, float>(this->g(y, x), v);
1125 greens += double(v);
1126 gn++;
1127 }
1128
1129 transformPixel (blue[i].x, blue[i].y, tran, x, y);
1130
1131 if (x >= 0 && y >= 0 && x < width && y < height) {
1132 float v;
1133 convertTo<T, float>(this->b(y, x), v);
1134 blues += double(v);
1135 bn++;
1136 }
1137 }
1138 }
1139
getPipetteData(T & valueR,T & valueG,T & valueB,int posX,int posY,const int squareSize,int tran)1140 void getPipetteData (T &valueR, T &valueG, T &valueB, int posX, int posY, const int squareSize, int tran) const
1141 {
1142 int x;
1143 int y;
1144 float accumulatorR = 0.f; // using float to avoid range overflow; -> please creates specialization if necessary
1145 float accumulatorG = 0.f; // "
1146 float accumulatorB = 0.f; // "
1147 unsigned long int n = 0;
1148 int halfSquare = squareSize / 2;
1149 transformPixel (posX, posY, tran, x, y);
1150
1151 for (int iy = y - halfSquare; iy < y - halfSquare + squareSize; ++iy) {
1152 for (int ix = x - halfSquare; ix < x - halfSquare + squareSize; ++ix) {
1153 if (ix >= 0 && iy >= 0 && ix < width && iy < height) {
1154 accumulatorR += float(this->r(iy, ix));
1155 accumulatorG += float(this->g(iy, ix));
1156 accumulatorB += float(this->b(iy, ix));
1157 ++n;
1158 }
1159 }
1160 }
1161
1162 valueR = n ? T(accumulatorR / float(n)) : T(0);
1163 valueG = n ? T(accumulatorG / float(n)) : T(0);
1164 valueB = n ? T(accumulatorB / float(n)) : T(0);
1165 }
1166
readData(FILE * f)1167 void readData (FILE *f)
1168 {
1169 for (int i = 0; i < height; i++) {
1170 if (fread(r(i), sizeof(T), width, f) < static_cast<size_t>(width)) {
1171 break;
1172 }
1173 }
1174
1175 for (int i = 0; i < height; i++) {
1176 if (fread(g(i), sizeof(T), width, f) < static_cast<size_t>(width)) {
1177 break;
1178 }
1179 }
1180
1181 for (int i = 0; i < height; i++) {
1182 if (fread(b(i), sizeof(T), width, f) < static_cast<size_t>(width)) {
1183 break;
1184 }
1185 }
1186 }
1187
writeData(FILE * f)1188 void writeData (FILE *f) const
1189 {
1190 for (int i = 0; i < height; i++) {
1191 fwrite (r(i), sizeof(T), width, f);
1192 }
1193
1194 for (int i = 0; i < height; i++) {
1195 fwrite (g(i), sizeof(T), width, f);
1196 }
1197
1198 for (int i = 0; i < height; i++) {
1199 fwrite (b(i), sizeof(T), width, f);
1200 }
1201 }
1202
1203 };
1204
1205 // --------------------------------------------------------------------
1206 // Chunky order classes
1207 // --------------------------------------------------------------------
1208
1209 template <class T>
1210 class ChunkyPtr
1211 {
1212 private:
1213 T* ptr;
1214 ssize_t width;
1215 public:
1216 #if CHECK_BOUNDS
1217 size_t width_, height_;
1218 #endif
1219
1220 #if CHECK_BOUNDS
ChunkyPtr()1221 ChunkyPtr() : ptr (NULL), width(-1), width_(0), height_(0) {}
1222 #else
ChunkyPtr()1223 ChunkyPtr() : ptr (nullptr), width(-1) {}
1224 #endif
1225 void init(T* base, ssize_t w = -1)
1226 {
1227 ptr = base;
1228 width = w;
1229 }
swap(ChunkyPtr<T> & other)1230 void swap (ChunkyPtr<T> &other)
1231 {
1232 T* tmpsPtr = other.ptr;
1233 other.ptr = ptr;
1234 ptr = tmpsPtr;
1235
1236 ssize_t tmpWidth = other.width;
1237 other.width = width;
1238 width = tmpWidth;
1239
1240 #if CHECK_BOUNDS
1241 size_t tmp = other.width_;
1242 other.width_ = width_;
1243 width_ = tmp;
1244 tmp = other.height_;
1245 other.height_ = height_;
1246 height_ = tmp;
1247 #endif
1248
1249 }
1250
1251 // Will send back the start of a row, starting with a red, green or blue value
operator()1252 T* operator() (size_t row) const
1253 {
1254 #if CHECK_BOUNDS
1255 assert (row < height_);
1256 #endif
1257 return &ptr[3 * (row * width)];
1258 }
1259 // Will send back a value at a given row, col position
operator()1260 T& operator() (size_t row, size_t col)
1261 {
1262 #if CHECK_BOUNDS
1263 assert (row < height_ && col < width_);
1264 #endif
1265 return ptr[3 * (row * width + col)];
1266 }
operator()1267 const T operator() (size_t row, size_t col) const
1268 {
1269 #if CHECK_BOUNDS
1270 assert (row < height_ && col < width_);
1271 #endif
1272 return ptr[3 * (row * width + col)];
1273 }
1274 };
1275
1276 template <class T>
1277 class ChunkyRGBData : virtual public ImageDatas
1278 {
1279
1280 private:
1281 AlignedBuffer<T> abData;
1282
1283 public:
1284 T* data;
1285 ChunkyPtr<T> r;
1286 ChunkyPtr<T> g;
1287 ChunkyPtr<T> b;
1288
ChunkyRGBData()1289 ChunkyRGBData() : data (nullptr) {}
ChunkyRGBData(int w,int h)1290 ChunkyRGBData(int w, int h) : data (nullptr)
1291 {
1292 allocate(w, h);
1293 }
1294
1295 /** Returns the pixel data, in r/g/b order from top left to bottom right continuously.
1296 * @return a pointer to the pixel data */
getData()1297 const T* getData ()
1298 {
1299 return data;
1300 }
1301
swap(ChunkyRGBData<T> & other)1302 void swap(ChunkyRGBData<T> &other)
1303 {
1304 abData.swap(other.abData);
1305 r.swap(other.r);
1306 g.swap(other.g);
1307 b.swap(other.b);
1308 T* tmpData = other.data;
1309 other.data = data;
1310 data = tmpData;
1311 int tmpWidth = other.width;
1312 other.width = width;
1313 width = tmpWidth;
1314 int tmpHeight = other.height;
1315 other.height = height;
1316 height = tmpHeight;
1317 #if CHECK_BOUNDS
1318 r.width_ = width;
1319 r.height_ = height;
1320 g.width_ = width;
1321 g.height_ = height;
1322 b.width_ = width;
1323 b.height_ = height;
1324 #endif
1325 }
1326
1327 /*
1328 * If any of the required allocation fails, "width" and "height" are set to -1, and all remaining buffer are freed
1329 * Can be safely used to reallocate an existing image or to free up it's memory with "allocate (0,0);"
1330 */
allocate(int W,int H)1331 void allocate (int W, int H) override
1332 {
1333
1334 if (W == width && H == height) {
1335 return;
1336 }
1337
1338 width = W;
1339 height = H;
1340 #if CHECK_BOUNDS
1341 r.width_ = width;
1342 r.height_ = height;
1343 g.width_ = width;
1344 g.height_ = height;
1345 b.width_ = width;
1346 b.height_ = height;
1347 #endif
1348
1349 abData.resize((size_t)width * (size_t)height * (size_t)3);
1350
1351 if (!abData.isEmpty()) {
1352 data = abData.data;
1353 r.init(data, width);
1354 g.init(data + 1, width);
1355 b.init(data + 2, width);
1356 } else {
1357 data = nullptr;
1358 r.init(nullptr);
1359 g.init(nullptr);
1360 b.init(nullptr);
1361 width = height = -1;
1362 #if CHECK_BOUNDS
1363 r.width_ = r.height_ = -1;
1364 g.width_ = g.height_ = -1;
1365 b.width_ = b.height_ = -1;
1366 #endif
1367 }
1368 }
1369
1370 /** Copy the data to another ChunkyRGBData */
copyData(ChunkyRGBData<T> * dest)1371 void copyData(ChunkyRGBData<T> *dest) const
1372 {
1373 assert (dest != nullptr);
1374 // Make sure that the size is the same, reallocate if necessary
1375 dest->allocate(width, height);
1376
1377 if (dest->width == -1) {
1378 printf("ERROR: ChunkyRGBData::copyData >>> allocation failed!\n");
1379 return;
1380 }
1381
1382 memcpy (dest->data, data, 3 * width * height * sizeof(T));
1383 }
1384
rotate(int deg)1385 void rotate (int deg) override
1386 {
1387
1388 if (deg == 90) {
1389 ChunkyRGBData<T> rotatedImg(height, width); // New image, rotated
1390
1391 for (int ny = 0; ny < rotatedImg.height; ny++) {
1392 int ox = ny;
1393 int oy = height - 1;
1394
1395 for (int nx = 0; nx < rotatedImg.width; nx++) {
1396 rotatedImg.r(ny, nx) = r(oy, ox);
1397 rotatedImg.g(ny, nx) = g(oy, ox);
1398 rotatedImg.b(ny, nx) = b(oy, ox);
1399 --oy;
1400 }
1401 }
1402
1403 swap(rotatedImg);
1404 } else if (deg == 270) {
1405 ChunkyRGBData<T> rotatedImg(height, width); // New image, rotated
1406
1407 for (int nx = 0; nx < rotatedImg.width; nx++) {
1408 int oy = nx;
1409 int ox = width - 1;
1410
1411 for (int ny = 0; ny < rotatedImg.height; ny++) {
1412 rotatedImg.r(ny, nx) = r(oy, ox);
1413 rotatedImg.g(ny, nx) = g(oy, ox);
1414 rotatedImg.b(ny, nx) = b(oy, ox);
1415 --ox;
1416 }
1417 }
1418
1419 swap(rotatedImg);
1420 } else if (deg == 180) {
1421 int height2 = height / 2 + (height & 1);
1422
1423 // Maybe not sufficiently optimized, but will do what it has to do
1424 for (int i = 0; i < height2; i++) {
1425 for (int j = 0; j < width; j++) {
1426 T tmp;
1427 int x = width - 1 - j;
1428 int y = height - 1 - i;
1429
1430 tmp = r(i, j);
1431 r(i, j) = r(y, x);
1432 r(y, x) = tmp;
1433
1434 tmp = g(i, j);
1435 g(i, j) = g(y, x);
1436 g(y, x) = tmp;
1437
1438 tmp = b(i, j);
1439 b(i, j) = b(y, x);
1440 b(y, x) = tmp;
1441 }
1442 }
1443 }
1444 }
1445
1446 template <class IC>
resizeImgTo(int nw,int nh,TypeInterpolation interp,IC * imgPtr)1447 void resizeImgTo (int nw, int nh, TypeInterpolation interp, IC *imgPtr) const
1448 {
1449 //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height);
1450 if (width == nw && height == nh) {
1451 // special case where no resizing is necessary, just type conversion....
1452 for (int i = 0; i < height; i++) {
1453 for (int j = 0; j < width; j++) {
1454 convertTo(r(i, j), imgPtr->r(i, j));
1455 convertTo(g(i, j), imgPtr->g(i, j));
1456 convertTo(b(i, j), imgPtr->b(i, j));
1457 }
1458 }
1459 } else if (interp == TI_Nearest) {
1460 for (int i = 0; i < nh; i++) {
1461 int ri = i * height / nh;
1462
1463 for (int j = 0; j < nw; j++) {
1464 int ci = j * width / nw;
1465 convertTo(r(ri, ci), imgPtr->r(i, j));
1466 convertTo(g(ri, ci), imgPtr->g(i, j));
1467 convertTo(b(ri, ci), imgPtr->b(i, j));
1468 }
1469 }
1470 } else if (interp == TI_Bilinear) {
1471 for (int i = 0; i < nh; i++) {
1472 int sy = i * height / nh;
1473
1474 if (sy >= height) {
1475 sy = height - 1;
1476 }
1477
1478 float dy = float(i) * float(height) / float(nh) - float(sy);
1479 int ny = sy + 1;
1480
1481 if (ny >= height) {
1482 ny = sy;
1483 }
1484
1485 for (int j = 0; j < nw; j++) {
1486 int sx = j * width / nw;
1487
1488 if (sx >= width) {
1489 sx = width;
1490 }
1491
1492 float dx = float(j) * float(width) / float(nw) - float(sx);
1493 int nx = sx + 1;
1494
1495 if (nx >= width) {
1496 nx = sx;
1497 }
1498
1499 T valR = r(sy, sx) * (1.f - dx) * (1.f - dy) + r(sy, nx) * dx * (1.f - dy) + r(ny, sx) * (1.f - dx) * dy + r(ny, nx) * dx * dy;
1500 T valG = g(sy, sx) * (1.f - dx) * (1.f - dy) + g(sy, nx) * dx * (1.f - dy) + g(ny, sx) * (1.f - dx) * dy + g(ny, nx) * dx * dy;
1501 T valB = b(sy, sx) * (1.f - dx) * (1.f - dy) + b(sy, nx) * dx * (1.f - dy) + b(ny, sx) * (1.f - dx) * dy + b(ny, nx) * dx * dy;
1502 convertTo(valR, imgPtr->r(i, j));
1503 convertTo(valG, imgPtr->g(i, j));
1504 convertTo(valB, imgPtr->b(i, j));
1505 }
1506 }
1507 } else {
1508 // This case should never occur!
1509 for (int i = 0; i < nh; i++) {
1510 for (int j = 0; j < nw; j++) {
1511 imgPtr->r(i, j) = 0;
1512 imgPtr->g(i, j) = 0;
1513 imgPtr->b(i, j) = 0;
1514 }
1515 }
1516 }
1517 }
1518
hflip()1519 void hflip () override
1520 {
1521 int width2 = width / 2;
1522
1523 for (int i = 0; i < height; i++) {
1524 int offsetBegin = 0;
1525 int offsetEnd = 3 * (width - 1);
1526
1527 for (int j = 0; j < width2; j++) {
1528 T temp;
1529
1530 temp = data[offsetBegin];
1531 data[offsetBegin] = data[offsetEnd];
1532 data[offsetEnd] = temp;
1533
1534 ++offsetBegin;
1535 ++offsetEnd;
1536
1537 temp = data[offsetBegin];
1538 data[offsetBegin] = data[offsetEnd];
1539 data[offsetEnd] = temp;
1540
1541 ++offsetBegin;
1542 ++offsetEnd;
1543
1544 temp = data[offsetBegin];
1545 data[offsetBegin] = data[offsetEnd];
1546 data[offsetEnd] = temp;
1547
1548 ++offsetBegin;
1549 offsetEnd -= 5;
1550
1551 }
1552 }
1553 }
1554
vflip()1555 void vflip () override
1556 {
1557
1558 AlignedBuffer<T> lBuffer(3 * width);
1559 T* lineBuffer = lBuffer.data;
1560 size_t size = 3 * width * sizeof(T);
1561
1562 for (int i = 0; i < height / 2; i++) {
1563 T *lineBegin1 = r(i);
1564 T *lineBegin2 = r(height - 1 - i);
1565 memcpy (lineBuffer, lineBegin1, size);
1566 memcpy (lineBegin1, lineBegin2, size);
1567 memcpy (lineBegin2, lineBuffer, size);
1568 }
1569 }
1570
calcGrayscaleHist(unsigned int * hist16)1571 void calcGrayscaleHist(unsigned int *hist16) const
1572 {
1573 for (int row = 0; row < height; row++)
1574 for (int col = 0; col < width; col++) {
1575 unsigned short rIdx, gIdx, bIdx;
1576 convertTo(r(row, col), rIdx);
1577 convertTo(g(row, col), gIdx);
1578 convertTo(b(row, col), bIdx);
1579 hist16[rIdx]++;
1580 hist16[gIdx] += 2; // Bayer 2x green correction
1581 hist16[bIdx]++;
1582 }
1583 }
1584
computeAutoHistogram(LUTu & histogram,int & histcompr)1585 void computeAutoHistogram (LUTu & histogram, int& histcompr) const
1586 {
1587 histcompr = 3;
1588
1589 histogram(65536 >> histcompr);
1590 histogram.clear();
1591 const LUTf& igammatab = getigammatab();
1592
1593 #ifdef _OPENMP
1594 #pragma omp parallel
1595 #endif
1596 {
1597 LUTu histThr(histogram.getSize());
1598 histThr.clear();
1599 #ifdef _OPENMP
1600 #pragma omp for schedule(dynamic,16) nowait
1601 #endif
1602 for (int i = 0; i < height; i++) {
1603 for (int j = 0; j < width; j++) {
1604 float r_, g_, b_;
1605 convertTo<T, float>(r(i, j), r_);
1606 convertTo<T, float>(g(i, j), g_);
1607 convertTo<T, float>(b(i, j), b_);
1608 histThr[static_cast<int>(igammatab[r_]) >> histcompr]++;
1609 histThr[static_cast<int>(igammatab[g_]) >> histcompr]++;
1610 histThr[static_cast<int>(igammatab[b_]) >> histcompr]++;
1611 }
1612 }
1613 #ifdef _OPENMP
1614 #pragma omp critical
1615 #endif
1616 {
1617 histogram += histThr;
1618 }
1619 }
1620 }
1621
computeHistogramAutoWB(double & avg_r,double & avg_g,double & avg_b,int & n,LUTu & histogram,const int compression)1622 void computeHistogramAutoWB (double &avg_r, double &avg_g, double &avg_b, int &n, LUTu &histogram, const int compression) const override
1623 {
1624 histogram.clear();
1625 avg_r = avg_g = avg_b = 0.;
1626 n = 0;
1627 const LUTf& igammatab = getigammatab();
1628
1629 for (unsigned int i = 0; i < (unsigned int)(height); i++)
1630 for (unsigned int j = 0; j < (unsigned int)(width); j++) {
1631 float r_, g_, b_;
1632 convertTo<T, float>(r(i, j), r_);
1633 convertTo<T, float>(g(i, j), g_);
1634 convertTo<T, float>(b(i, j), b_);
1635 int rtemp = igammatab[r_];
1636 int gtemp = igammatab[g_];
1637 int btemp = igammatab[b_];
1638
1639 histogram[rtemp >> compression]++;
1640 histogram[gtemp >> compression] += 2;
1641 histogram[btemp >> compression]++;
1642
1643 // autowb computation
1644 if (r_ > 64000.f || g_ > 64000.f || b_ > 64000.f) {
1645 continue;
1646 }
1647
1648 avg_r += double(r_);
1649 avg_g += double(g_);
1650 avg_b += double(b_);
1651 n++;
1652 }
1653 }
1654
getAutoWBMultipliers(double & rm,double & gm,double & bm)1655 void getAutoWBMultipliers (double &rm, double &gm, double &bm) const override
1656 {
1657
1658 double avg_r = 0.;
1659 double avg_g = 0.;
1660 double avg_b = 0.;
1661 int n = 0;
1662 //int p = 6;
1663
1664 #ifdef _OPENMP
1665 #pragma omp parallel for reduction(+:avg_r,avg_g,avg_b,n) schedule(dynamic,16)
1666 #endif
1667 for (unsigned int i = 0; i < (unsigned int)(height); i++)
1668 for (unsigned int j = 0; j < (unsigned int)(width); j++) {
1669 float r_, g_, b_;
1670 convertTo<T, float>(r(i, j), r_);
1671 convertTo<T, float>(g(i, j), g_);
1672 convertTo<T, float>(b(i, j), b_);
1673
1674 if (r_ > 64000.f || g_ > 64000.f || b_ > 64000.f) {
1675 continue;
1676 }
1677
1678 avg_r += double(r_);
1679 avg_g += double(g_);
1680 avg_b += double(b_);
1681 n++;
1682 }
1683
1684 rm = avg_r / double(n);
1685 gm = avg_g / double(n);
1686 bm = avg_b / double(n);
1687 }
1688
transformPixel(int x,int y,int tran,int & tx,int & ty)1689 void transformPixel (int x, int y, int tran, int& tx, int& ty) const
1690 {
1691
1692 if (!tran) {
1693 tx = x;
1694 ty = y;
1695 return;
1696 }
1697
1698 int W = width;
1699 int H = height;
1700 int sw = W, sh = H;
1701
1702 if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) {
1703 sw = H;
1704 sh = W;
1705 }
1706
1707 int ppx = x, ppy = y;
1708
1709 if (tran & TR_HFLIP) {
1710 ppx = sw - 1 - x;
1711 }
1712
1713 if (tran & TR_VFLIP) {
1714 ppy = sh - 1 - y;
1715 }
1716
1717 tx = ppx;
1718 ty = ppy;
1719
1720 if ((tran & TR_ROT) == TR_R180) {
1721 tx = W - 1 - ppx;
1722 ty = H - 1 - ppy;
1723 } else if ((tran & TR_ROT) == TR_R90) {
1724 tx = ppy;
1725 ty = H - 1 - ppx;
1726 } else if ((tran & TR_ROT) == TR_R270) {
1727 tx = W - 1 - ppy;
1728 ty = ppx;
1729 }
1730 }
1731
getSpotWBData(double & reds,double & greens,double & blues,int & rn,int & gn,int & bn,std::vector<Coord2D> & red,std::vector<Coord2D> & green,std::vector<Coord2D> & blue,int tran)1732 void getSpotWBData (double &reds, double &greens, double &blues, int &rn, int &gn, int &bn,
1733 std::vector<Coord2D> &red, std::vector<Coord2D> &green, std::vector<Coord2D> &blue,
1734 int tran) const override
1735 {
1736 int x;
1737 int y;
1738 reds = 0, greens = 0, blues = 0;
1739 rn = 0, gn = 0, bn = 0;
1740
1741 for (size_t i = 0; i < red.size(); i++) {
1742 transformPixel (red[i].x, red[i].y, tran, x, y);
1743
1744 if (x >= 0 && y >= 0 && x < width && y < height) {
1745 float v;
1746 convertTo<T, float>(this->r(y, x), v);
1747 reds += double(v);
1748 rn++;
1749 }
1750
1751 transformPixel (green[i].x, green[i].y, tran, x, y);
1752
1753 if (x >= 0 && y >= 0 && x < width && y < height) {
1754 float v;
1755 convertTo<T, float>(this->g(y, x), v);
1756 greens += double(v);
1757 gn++;
1758 }
1759
1760 transformPixel (blue[i].x, blue[i].y, tran, x, y);
1761
1762 if (x >= 0 && y >= 0 && x < width && y < height) {
1763 float v;
1764 convertTo<T, float>(this->b(y, x), v);
1765 blues += double(v);
1766 bn++;
1767 }
1768 }
1769 }
1770
readData(FILE * f)1771 void readData (FILE *f)
1772 {
1773 for (int i = 0; i < height; i++) {
1774 if (fread(r(i), sizeof(T), 3 * width, f) < 3 * static_cast<size_t>(width)) {
1775 break;
1776 }
1777 }
1778 }
1779
writeData(FILE * f)1780 void writeData (FILE *f) const
1781 {
1782 for (int i = 0; i < height; i++) {
1783 fwrite (r(i), sizeof(T), 3 * width, f);
1784 }
1785 }
1786
1787 };
1788
1789 // --------------------------------------------------------------------
1790
1791
1792 /** @brief This class represents an image (the result of the image processing) */
1793 class IImage : virtual public ImageDimensions
1794 {
1795 public:
1796
~IImage()1797 virtual ~IImage() {}
1798 /** @brief Returns a mutex that can is useful in many situations. No image operations shuold be performed without locking this mutex.
1799 * @return The mutex */
1800 virtual MyMutex& getMutex () = 0;
1801 virtual cmsHPROFILE getProfile () const = 0;
1802 /** @brief Returns the bits per pixel of the image.
1803 * @return The bits per pixel of the image */
1804 virtual int getBitsPerPixel () const = 0;
1805 /** @brief Saves the image to file. It autodetects the format (jpg, tif, png are supported).
1806 * @param fname is the name of the file
1807 @return the error code, 0 if none */
1808 virtual int saveToFile (const Glib::ustring &fname) const = 0;
1809 /** @brief Saves the image to file in a png format.
1810 * @param fname is the name of the file
1811 * @param compression is the amount of compression (0-6), -1 corresponds to the default
1812 * @param bps can be 8 or 16 depending on the bits per pixels the output file will have
1813 @return the error code, 0 if none */
1814 virtual int saveAsPNG (const Glib::ustring &fname, int bps = -1) const = 0;
1815 /** @brief Saves the image to file in a jpg format.
1816 * @param fname is the name of the file
1817 * @param quality is the quality of the jpeg (0...100), set it to -1 to use default
1818 @return the error code, 0 if none */
1819 virtual int saveAsJPEG (const Glib::ustring &fname, int quality = 100, int subSamp = 3 ) const = 0;
1820 /** @brief Saves the image to file in a tif format.
1821 * @param fname is the name of the file
1822 * @param bps can be 8 or 16 depending on the bits per pixels the output file will have
1823 * @param isFloat is true for saving float images. Will be ignored by file format not supporting float data
1824 @return the error code, 0 if none */
1825 virtual int saveAsTIFF (const Glib::ustring &fname, int bps = -1, bool isFloat = false, bool uncompressed = false) const = 0;
1826 /** @brief Sets the progress listener if you want to follow the progress of the image saving operations (optional).
1827 * @param pl is the pointer to the class implementing the ProgressListener interface */
1828 virtual void setSaveProgressListener (ProgressListener* pl) = 0;
1829 /** @brief Free the image */
1830 virtual void free () = 0;
1831 };
1832
1833 /** @brief This class represents an image having a float pixel planar representation.
1834 The planes are stored as two dimensional arrays. All the rows have a 128 bits alignment. */
1835 class IImagefloat : public IImage, public PlanarRGBData<float>
1836 {
1837 public:
~IImagefloat()1838 ~IImagefloat() override {}
1839 };
1840
1841 /** @brief This class represents an image having a classical 8 bits/pixel representation */
1842 class IImage8 : public IImage, public ChunkyRGBData<unsigned char>
1843 {
1844 public:
~IImage8()1845 ~IImage8() override {}
1846 };
1847
1848 /** @brief This class represents an image having a 16 bits/pixel planar representation.
1849 The planes are stored as two dimensional arrays. All the rows have a 128 bits alignment. */
1850 class IImage16 : public IImage, public PlanarRGBData<unsigned short>
1851 {
1852 public:
~IImage16()1853 ~IImage16() override {}
1854 };
1855
1856 }
1857