1 /*
2  *  This file is part of RawTherapee.
3  *
4  *  Copyright (c) 2019 Alberto Romei <aldrop8@gmail.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 #include <cmath>
20 #include <iostream>
21 
22 #include "rawimage.h"
23 #include "rawimagesource.h"
24 #include "stdimagesource.h"
25 #include "imagefloat.h"
26 
27 #include "coord.h"
28 #include "mytime.h"
29 #include "opthelper.h"
30 #include "pixelsmap.h"
31 #include "procparams.h"
32 #include "rt_algo.h"
33 #include "rtengine.h"
34 #include "sleef.h"
35 #include "settings.h"
36 //#define BENCHMARK
37 #include "StopWatch.h"
38 
39 namespace rtengine {
40 
41 extern const Settings *settings;
42 
43 namespace {
44 
channelsAvg(const rtengine::RawImage * ri,int width,int height,const float * cblacksom,rtengine::Coord spotPos,int spotSize,std::array<float,3> & avgs)45 bool channelsAvg(const rtengine::RawImage* ri, int width, int height, const float* cblacksom, rtengine::Coord spotPos, int spotSize, std::array<float, 3> &avgs)
46 {
47     avgs = {}; // Channel averages
48 
49     if (settings->verbose) {
50         printf("Spot coord:  x=%d y=%d\n", spotPos.x, spotPos.y);
51     }
52 
53     const int half_spot_size = spotSize / 2;
54 
55     const int& x1 = spotPos.x - half_spot_size;
56     const int& x2 = spotPos.x + half_spot_size;
57     const int& y1 = spotPos.y - half_spot_size;
58     const int& y2 = spotPos.y + half_spot_size;
59 
60     if (x1 < 0 || x2 > width || y1 < 0 || y2 > height) {
61         return false; // Spot goes outside bounds, bail out.
62     }
63 
64     std::array<int, 3> pxCount = {}; // Per-channel sample counts
65 
66     if (ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS) {
67         for (int c = x1; c < x2; ++c) {
68             for (int r = y1; r < y2; ++r) {
69                 const int ch = ri->getSensorType() == ST_BAYER ? ri->FC(r, c) : ri->XTRANSFC(r, c);
70 
71                 ++pxCount[ch];
72 
73                 // Sample the original unprocessed values from RawImage, subtracting black levels.
74                 // Scaling is irrelevant, as we are only interested in the ratio between two spots.
75                 avgs[ch] += ri->data[r][c] - cblacksom[ch];
76             }
77         }
78     } else if (ri->get_colors() == 3) {
79         for (int c = x1; c < x2; ++c) {
80             for (int r = y1; r < y2; ++r) {
81                 for (int ch = 0; ch < 3; ++ch) {
82                     ++pxCount[ch];
83                     avgs[ch] += ri->data[r][3*c + ch] - cblacksom[c];
84                 }
85             }
86         }
87     } else if (ri->get_colors() == 1) {
88         for (int c = x1; c < x2; ++c) {
89             for (int r = y1; r < y2; ++r) {
90                 ++pxCount[0];
91                 avgs[0] += ri->data[r][c] - cblacksom[0];
92             }
93             pxCount[1] = pxCount[2] = pxCount[0];
94             avgs[1] = avgs[2] = avgs[0];
95         }
96     } else {
97         assert(false);
98         return false;
99     }
100 
101     for (int ch = 0; ch < 3; ++ch) {
102         avgs[ch] /= pxCount[ch];
103     }
104 
105     return true;
106 }
107 
108 
channelsAvg(const rtengine::Imagefloat * img,int width,int height,rtengine::Coord spotPos,int spotSize,std::array<float,3> & avgs)109 bool channelsAvg(const rtengine::Imagefloat* img, int width, int height, rtengine::Coord spotPos, int spotSize, std::array<float, 3>& avgs)
110 {
111     avgs = {}; // Channel averages
112 
113     if (settings->verbose) {
114         printf("Spot coord:  x=%d y=%d\n", spotPos.x, spotPos.y);
115     }
116 
117     const int half_spot_size = spotSize / 2;
118 
119     const int& x1 = spotPos.x - half_spot_size;
120     const int& x2 = spotPos.x + half_spot_size;
121     const int& y1 = spotPos.y - half_spot_size;
122     const int& y2 = spotPos.y + half_spot_size;
123 
124     if (x1 < 0 || x2 > width || y1 < 0 || y2 > height) {
125         return false; // Spot goes outside bounds, bail out.
126     }
127 
128     for (int c = x1; c < x2; ++c) {
129         for (int r = y1; r < y2; ++r) {
130             avgs[0] += img->r(r,c);
131             avgs[1] += img->g(r,c);
132             avgs[2] += img->b(r,c);
133         }
134     }
135 
136     for (int ch = 0; ch < 3; ++ch) {
137         avgs[ch] /= (spotSize*spotSize);
138     }
139 
140     return true;
141 }
142 
143 
calcMedians(const rtengine::RawImage * ri,float ** data,int x1,int y1,int x2,int y2,std::array<float,3> & meds)144 void calcMedians(const rtengine::RawImage *ri, float **data, int x1, int y1, int x2, int y2, std::array<float, 3> &meds)
145 {
146     MyTime t1, t2, t3;
147     t1.set();
148 
149     // Channel vectors to calculate medians
150     std::array<std::vector<float>, 3> cvs;
151 
152     // Sample one every 5 pixels, and push the value in the appropriate channel vector.
153     // Choose an odd step, not a multiple of the CFA size, to get a chance to visit each channel.
154     if (ri->getSensorType() == ST_BAYER) {
155         for (int row = y1; row < y2; row += 5) {
156             const int c0 = ri->FC(row, x1 + 0);
157             const int c1 = ri->FC(row, x1 + 5);
158             int col = x1;
159 
160             for (; col < x2 - 5; col += 10) {
161                 cvs[c0].push_back(data[row][col]);
162                 cvs[c1].push_back(data[row][col + 5]);
163             }
164 
165             if (col < x2) {
166                 cvs[c0].push_back(data[row][col]);
167             }
168         }
169     } else if (ri->getSensorType() == ST_FUJI_XTRANS) {
170         for (int row = y1; row < y2; row += 5) {
171             const std::array<unsigned int, 6> cs = {
172                 ri->XTRANSFC(row, x1 + 0),
173                 ri->XTRANSFC(row, x1 + 5),
174                 ri->XTRANSFC(row, x1 + 10),
175                 ri->XTRANSFC(row, x1 + 15),
176                 ri->XTRANSFC(row, x1 + 20),
177                 ri->XTRANSFC(row, x1 + 25)
178             };
179             int col = x1;
180 
181             for (; col < x2 - 25; col += 30) {
182                 for (int c = 0; c < 6; ++c) {
183                     cvs[cs[c]].push_back(data[row][col + c * 5]);
184                 }
185             }
186 
187             for (int c = 0; col < x2; col += 5, ++c) {
188                 cvs[cs[c]].push_back(data[row][col]);
189             }
190         }
191     } else if (ri->get_colors() == 3) {
192         for (int row = y1; row < y2; row += 5) {
193             for (int col = x1; col < x2; col += 5) {
194                 for (int c = 0; c < 3; ++c) {
195                     cvs[c].push_back(data[row][3*col+c]);
196                 }
197             }
198         }
199     } else if (ri->get_colors() == 1) {
200         for (int row = y1; row < y2; row += 5) {
201             for (int col = x1; col < x2; col += 5) {
202                 for (int c = 0; c < 3; ++c) {
203                     cvs[c].push_back(data[row][col]);
204                 }
205             }
206         }
207     }
208 
209     t2.set();
210 
211     if (settings->verbose) {
212         printf("Median vector fill loop time us: %d\n", t2.etime(t1));
213     }
214 
215     t2.set();
216 
217     for (int c = 0; c < 3; ++c) {
218         // Find median values for each channel
219         if (!cvs[c].empty()) {
220             rtengine::findMinMaxPercentile(cvs[c].data(), cvs[c].size(), 0.5f, meds[c], 0.5f, meds[c], true);
221         }
222     }
223 
224     t3.set();
225 
226     if (settings->verbose) {
227         printf("Sample count: R=%zu, G=%zu, B=%zu\n", cvs[0].size(), cvs[1].size(), cvs[2].size());
228         printf("Median calc time us: %d\n", t3.etime(t2));
229     }
230 
231 }
232 
calcWBMults(const rtengine::ColorTemp & wb,const rtengine::ImageMatrices & imatrices,const rtengine::RawImage * ri,const float ref_pre_mul[4])233 std::array<double, 3> calcWBMults(const rtengine::ColorTemp& wb, const rtengine::ImageMatrices& imatrices, const rtengine::RawImage *ri, const float ref_pre_mul[4])
234 {
235     std::array<double, 3> wb_mul;
236     double r, g, b;
237     wb.getMultipliers(r, g, b);
238     wb_mul[0] = imatrices.cam_rgb[0][0] * r + imatrices.cam_rgb[0][1] * g + imatrices.cam_rgb[0][2] * b;
239     wb_mul[1] = imatrices.cam_rgb[1][0] * r + imatrices.cam_rgb[1][1] * g + imatrices.cam_rgb[1][2] * b;
240     wb_mul[2] = imatrices.cam_rgb[2][0] * r + imatrices.cam_rgb[2][1] * g + imatrices.cam_rgb[2][2] * b;
241 
242     for (int c = 0; c < 3; ++c) {
243         wb_mul[c] = ri->get_pre_mul(c) / wb_mul[c] / ref_pre_mul[c];
244     }
245 
246     // Normalize max channel gain to 1.0
247     float mg = rtengine::max(wb_mul[0], wb_mul[1], wb_mul[2]);
248 
249     for (int c = 0; c < 3; ++c) {
250         wb_mul[c] /= mg;
251     }
252 
253     return wb_mul;
254 }
255 
256 
calcMedians(const Imagefloat * baseImg,const int x1,const int y1,const int x2,const int y2,int skip,std::array<float,3> & meds)257 void calcMedians(const Imagefloat* baseImg, const int x1, const int y1, const int x2, const int y2, int skip, std::array<float, 3> &meds)
258 {
259     // Channel vectors to calculate medians
260     std::vector<float> rv, gv, bv;
261 
262     const int sz = std::max(0, (y2 - y1) * (x2 - x1) / SQR(skip));
263     rv.reserve(sz);
264     gv.reserve(sz);
265     bv.reserve(sz);
266 
267 #ifdef _OPENMP
268 #   pragma omp parallel for
269 #endif
270     for (int i = y1; i < y2; i += skip) {
271         for (int j = x1; j < x2; j += skip) {
272             rv.push_back(baseImg->r(i, j));
273             gv.push_back(baseImg->g(i, j));
274             bv.push_back(baseImg->b(i, j));
275         }
276     }
277 
278     // Calculate channel medians from whole image
279     findMinMaxPercentile(rv.data(), rv.size(), 0.5f, meds[0], 0.5f, meds[0], true);
280     findMinMaxPercentile(gv.data(), gv.size(), 0.5f, meds[1], 0.5f, meds[1], true);
281     findMinMaxPercentile(bv.data(), bv.size(), 0.5f, meds[2], 0.5f, meds[2], true);
282 }
283 
284 
285 } // namespace
286 
getFilmNegativeExponents(Coord2D spotA,Coord2D spotB,int tran,const procparams::FilmNegativeParams & currentParams,std::array<float,3> & newExps)287 bool RawImageSource::getFilmNegativeExponents(Coord2D spotA, Coord2D spotB, int tran, const procparams::FilmNegativeParams &currentParams, std::array<float, 3>& newExps)
288 {
289     newExps = {
290         static_cast<float>(currentParams.redRatio * currentParams.greenExp),
291         static_cast<float>(currentParams.greenExp),
292         static_cast<float>(currentParams.blueRatio * currentParams.greenExp)
293     };
294 
295     constexpr int spotSize = 32; // TODO: Make this configurable?
296 
297     Coord spot;
298     std::array<float, 3> clearVals;
299     std::array<float, 3> denseVals;
300 
301     // Get channel averages in the two spots, sampling from the original ri->data buffer.
302     // NOTE: rawData values might be affected by CA corection, FlatField, etc, so:
303     //   rawData[y][x] == (ri->data[y][x] - cblacksom[c]) * scale_mul[c]
304     // is not always true. To calculate exponents on the exact values, we should keep
305     // a copy of the rawData buffer after preprocessing. Worth the memory waste?
306 
307     // Sample first spot
308     transformPosition(spotA.x, spotA.y, tran, spot.x, spot.y);
309 
310     if (!channelsAvg(ri, W, H, cblacksom, spot, spotSize, clearVals)) {
311         return false;
312     }
313 
314     // Sample second spot
315     transformPosition(spotB.x, spotB.y, tran, spot.x, spot.y);
316 
317     if (!channelsAvg(ri, W, H, cblacksom, spot, spotSize, denseVals)) {
318         return false;
319     }
320 
321     // Detect which one is the dense spot, based on green channel
322     if (clearVals[1] < denseVals[1]) {
323         std::swap(clearVals, denseVals);
324     }
325 
326     if (settings->verbose) {
327         printf("Clear film values: R=%g G=%g B=%g\n", static_cast<double>(clearVals[0]), static_cast<double>(clearVals[1]), static_cast<double>(clearVals[2]));
328         printf("Dense film values: R=%g G=%g B=%g\n", static_cast<double>(denseVals[0]), static_cast<double>(denseVals[1]), static_cast<double>(denseVals[2]));
329     }
330 
331     const float denseGreenRatio = clearVals[1] / denseVals[1];
332 
333     // Calculate logarithms in arbitrary base
334     const auto logBase =
335         [](float base, float num) -> float
336         {
337             return std::log(num) / std::log(base);
338         };
339 
340     // Calculate exponents for each channel, based on the ratio between the bright and dark values,
341     // compared to the ratio in the reference channel (green)
342     for (int ch = 0; ch < 3; ++ch) {
343         if (ch == 1) {
344             newExps[ch] = 1.f;  // Green is the reference channel
345         } else {
346             newExps[ch] = LIM(logBase(clearVals[ch] / denseVals[ch], denseGreenRatio), 0.3f, 4.f);
347         }
348     }
349 
350     if (settings->verbose) {
351         printf("New exponents:  R=%g G=%g B=%g\n", static_cast<double>(newExps[0]), static_cast<double>(newExps[1]), static_cast<double>(newExps[2]));
352     }
353 
354     return true;
355 }
356 
getImageSpotValues(Coord2D spotCoord,int spotSize,int tran,const procparams::FilmNegativeParams & params,std::array<float,3> & rawValues)357 bool RawImageSource::getImageSpotValues(Coord2D spotCoord, int spotSize, int tran, const procparams::FilmNegativeParams &params, std::array<float, 3>& rawValues)
358 {
359     Coord spot;
360     transformPosition(spotCoord.x, spotCoord.y, tran, spot.x, spot.y);
361 
362     if (settings->verbose) {
363         printf("Transformed coords: %d,%d\n", spot.x, spot.y);
364     }
365 
366     if (spotSize < 4) {
367         return false;
368     }
369 
370     // Calculate averages of raw unscaled channels
371     if (!channelsAvg(ri, W, H, cblacksom, spot, spotSize, rawValues)) {
372         return false;
373     }
374 
375     if (settings->verbose) {
376         printf("Raw spot values: R=%g, G=%g, B=%g\n", rawValues[0], rawValues[1], rawValues[2]);
377     }
378 
379     return true;
380 }
381 
filmNegativeProcess(const procparams::FilmNegativeParams & params,std::array<float,3> & filmBaseValues)382 void RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams &params, std::array<float, 3>& filmBaseValues)
383 {
384 //    BENCHFUNMICRO
385 
386     if (!params.enabled) {
387         return;
388     }
389 
390     // Exponents are expressed as positive in the parameters, so negate them in order
391     // to get the reciprocals.
392     const std::array<float, 3> exps = {
393         static_cast<float>(-params.redRatio * params.greenExp),
394         static_cast<float>(-params.greenExp),
395         static_cast<float>(-params.blueRatio * params.greenExp)
396     };
397 
398     constexpr float MAX_OUT_VALUE = 65000.f;
399 
400     // Get multipliers for a known, fixed WB setting, that will be the starting point
401     // for balancing the converted image.
402     const std::array<double, 3> wb_mul = calcWBMults(
403             ColorTemp(3500., 1., 1., "Custom"), imatrices, ri, ref_pre_mul);
404 
405 
406     if (settings->verbose) {
407         printf("Fixed WB mults: %g %g %g\n", wb_mul[0], wb_mul[1], wb_mul[2]);
408     }
409 
410     // Compensation factor to restore the non-autoWB initialGain (see RawImageSource::load)
411     const float autoGainComp = camInitialGain / initialGain;
412 
413     std::array<float, 3> mults;  // Channel normalization multipliers
414 
415     // If film base values are set in params, use those
416     if (filmBaseValues[0] <= 0.f) {
417         // ...otherwise, the film negative tool might have just been enabled on this image,
418         // whithout any previous setting. So, estimate film base values from channel medians
419 
420         std::array<float, 3> medians;
421 
422         // Special value for backwards compatibility with profiles saved by RT 5.7
423         const bool oldChannelScaling = filmBaseValues[0] == -1.f;
424 
425         // If using the old channel scaling method, get medians from the whole current image,
426         // reading values from the already-scaled rawData buffer.
427         if (oldChannelScaling) {
428             calcMedians(ri, rawData, 0, 0, W, H, medians);
429         } else {
430             // Cut 20% border from medians calculation. It will probably contain outlier values
431             // from the film holder, which will bias the median result.
432             const int bW = W * 20 / 100;
433             const int bH = H * 20 / 100;
434             calcMedians(ri, rawData, bW, bH, W - bW, H - bH, medians);
435         }
436 
437         // Un-scale rawData medians
438         for (int c = 0; c < 3; ++c) {
439             medians[c] /= scale_mul[c];
440         }
441 
442         if (settings->verbose) {
443             printf("Channel medians: R=%g, G=%g, B=%g\n", medians[0], medians[1], medians[2]);
444         }
445 
446         for (int c = 0; c < 3; ++c) {
447 
448             // Estimate film base values, so that in the output data, each channel
449             // median will correspond to 1/24th of MAX.
450             filmBaseValues[c] = pow_F(24.f / 512.f, 1.f / exps[c]) * medians[c];
451 
452             if (oldChannelScaling) {
453                 // If using the old channel scaling method, apply WB multipliers here to undo their
454                 // effect later, since fixed wb compensation was not used in previous version.
455                 // Also, undo the effect of the autoGainComp factor (see below).
456                 filmBaseValues[c] /= pow_F((wb_mul[c] / autoGainComp), 1.f / exps[c]);// / autoGainComp;
457             }
458 
459             // normalize to 0-1
460             //filmBaseValues[c] /= 65535.f;
461         }
462     }
463 
464 
465     // Calculate multipliers based on previously obtained film base input values.
466 
467     // Apply current scaling coefficients to raw, unscaled base values.
468     std::array<float, 3> fb = {
469         filmBaseValues[0] * scale_mul[0],
470         filmBaseValues[1] * scale_mul[1],
471         filmBaseValues[2] * scale_mul[2]
472     };
473 
474     if (settings->verbose) {
475         printf("Input film base values: %g %g %g\n", fb[0], fb[1], fb[2]);
476     }
477 
478     for (int c = 0; c < 3; ++c) {
479         // Apply channel exponents, to obtain the corresponding base values in the output data
480         fb[c] = pow_F(max(fb[c], 1.f), exps[c]);
481 
482         // Determine the channel multiplier so that the film base value is 1/512th of max.
483         mults[c] = (MAX_OUT_VALUE / 512.f) / fb[c];
484 
485         // Un-apply the fixed WB multipliers, to reverse their effect later in the WB tool.
486         // This way, the output image will be adapted to this fixed WB setting
487         mults[c] /= wb_mul[c];
488 
489         // Also compensate for the initialGain difference between the default scaling (forceAutoWB=true)
490         // and the non-autoWB scaling (see camInitialGain).
491         // This effectively restores camera scaling multipliers, and gives us stable multipliers
492         // (not depending on image content).
493         mults[c] *= autoGainComp;
494 
495     }
496 
497     if (settings->verbose) {
498         printf("Output film base values: %g %g %g\n", static_cast<double>(fb[0]), static_cast<double>(fb[1]), static_cast<double>(fb[2]));
499         printf("Computed multipliers: %g %g %g\n", static_cast<double>(mults[0]), static_cast<double>(mults[1]), static_cast<double>(mults[2]));
500     }
501 
502 
503     constexpr float CLIP_VAL = 65535.f;
504 
505     MyTime t1, t2, t3;
506 
507     t1.set();
508 
509     if (ri->getSensorType() == ST_BAYER) {
510 #ifdef __SSE2__
511         const vfloat onev = F2V(1.f);
512         const vfloat clipv = F2V(CLIP_VAL);
513 #endif
514 
515 #ifdef _OPENMP
516         #pragma omp parallel for schedule(dynamic, 16)
517 #endif
518 
519         for (int row = 0; row < H; ++row) {
520             int col = 0;
521             // Avoid trouble with zeroes, minimum pixel value is 1.
522             const float exps0 = exps[FC(row, col)];
523             const float exps1 = exps[FC(row, col + 1)];
524             const float mult0 = mults[FC(row, col)];
525             const float mult1 = mults[FC(row, col + 1)];
526 #ifdef __SSE2__
527             const vfloat expsv = _mm_setr_ps(exps0, exps1, exps0, exps1);
528             const vfloat multsv = _mm_setr_ps(mult0, mult1, mult0, mult1);
529 
530             for (; col < W - 3; col += 4) {
531                 STVFU(rawData[row][col], vminf(multsv * pow_F(vmaxf(LVFU(rawData[row][col]), onev), expsv), clipv));
532             }
533 
534 #endif // __SSE2__
535 
536             for (; col < W - 1; col += 2) {
537                 rawData[row][col] = min(mult0 * pow_F(max(rawData[row][col], 1.f), exps0), CLIP_VAL);
538                 rawData[row][col + 1] = min(mult1 * pow_F(max(rawData[row][col + 1], 1.f), exps1), CLIP_VAL);
539             }
540 
541             if (col < W) {
542                 rawData[row][col] = min(mult0 * pow_F(max(rawData[row][col], 1.f), exps0), CLIP_VAL);
543             }
544         }
545     } else if (ri->getSensorType() == ST_FUJI_XTRANS) {
546 #ifdef __SSE2__
547         const vfloat onev = F2V(1.f);
548         const vfloat clipv = F2V(CLIP_VAL);
549 #endif
550 
551 #ifdef _OPENMP
552         #pragma omp parallel for schedule(dynamic, 16)
553 #endif
554 
555         for (int row = 0; row < H; row ++) {
556             int col = 0;
557             // Avoid trouble with zeroes, minimum pixel value is 1.
558             const std::array<float, 6> expsc = {
559                 exps[ri->XTRANSFC(row, 0)],
560                 exps[ri->XTRANSFC(row, 1)],
561                 exps[ri->XTRANSFC(row, 2)],
562                 exps[ri->XTRANSFC(row, 3)],
563                 exps[ri->XTRANSFC(row, 4)],
564                 exps[ri->XTRANSFC(row, 5)]
565             };
566             const std::array<float, 6> multsc = {
567                 mults[ri->XTRANSFC(row, 0)],
568                 mults[ri->XTRANSFC(row, 1)],
569                 mults[ri->XTRANSFC(row, 2)],
570                 mults[ri->XTRANSFC(row, 3)],
571                 mults[ri->XTRANSFC(row, 4)],
572                 mults[ri->XTRANSFC(row, 5)]
573             };
574 #ifdef __SSE2__
575             const vfloat expsv0 = _mm_setr_ps(expsc[0], expsc[1], expsc[2], expsc[3]);
576             const vfloat expsv1 = _mm_setr_ps(expsc[4], expsc[5], expsc[0], expsc[1]);
577             const vfloat expsv2 = _mm_setr_ps(expsc[2], expsc[3], expsc[4], expsc[5]);
578             const vfloat multsv0 = _mm_setr_ps(multsc[0], multsc[1], multsc[2], multsc[3]);
579             const vfloat multsv1 = _mm_setr_ps(multsc[4], multsc[5], multsc[0], multsc[1]);
580             const vfloat multsv2 = _mm_setr_ps(multsc[2], multsc[3], multsc[4], multsc[5]);
581 
582             for (; col < W - 11; col += 12) {
583                 STVFU(rawData[row][col], vminf(multsv0 * pow_F(vmaxf(LVFU(rawData[row][col]), onev), expsv0), clipv));
584                 STVFU(rawData[row][col + 4], vminf(multsv1 * pow_F(vmaxf(LVFU(rawData[row][col + 4]), onev), expsv1), clipv));
585                 STVFU(rawData[row][col + 8], vminf(multsv2 * pow_F(vmaxf(LVFU(rawData[row][col + 8]), onev), expsv2), clipv));
586             }
587 
588 #endif // __SSE2__
589 
590             for (; col < W - 5; col += 6) {
591                 for (int c = 0; c < 6; ++c) {
592                     rawData[row][col + c] = min(multsc[c] * pow_F(max(rawData[row][col + c], 1.f), expsc[c]), CLIP_VAL);
593                 }
594             }
595 
596             for (int c = 0; col < W; col++, c++) {
597                 rawData[row][col + c] = min(multsc[c] * pow_F(max(rawData[row][col + c], 1.f), expsc[c]), CLIP_VAL);
598             }
599         }
600     } else if (ri->get_colors() == 3) {
601 #ifdef _OPENMP
602         #pragma omp parallel for schedule(dynamic, 16)
603 #endif
604         for (int row = 0; row < H; ++row) {
605             for (int col = 0; col < W; ++col) {
606                 for (int c = 0; c < 3; ++c) {
607                     rawData[row][3*col+c] = min(mults[c] * pow_F(max(rawData[row][3*col+c], 1.f), exps[c]), CLIP_VAL);
608                 }
609             }
610         }
611     } else if (ri->get_colors() == 1) {
612 #ifdef _OPENMP
613         #pragma omp parallel for schedule(dynamic, 16)
614 #endif
615         for (int row = 0; row < H; ++row) {
616             for (int col = 0; col < W; ++col) {
617                 rawData[row][col] = min(mults[1] * pow_F(max(rawData[row][col], 1.f), exps[1]), CLIP_VAL);
618             }
619         }
620     }
621 
622     t2.set();
623 
624     if (settings->verbose) {
625         printf("Pow loop time us: %d\n", t2.etime(t1));
626     }
627 
628     t2.set();
629 
630     PixelsMap bitmapBads(W, H);
631 
632     int totBP = 0; // Hold count of bad pixels to correct
633 
634     if (ri->getSensorType() == ST_BAYER) {
635 #ifdef _OPENMP
636         #pragma omp parallel for reduction(+:totBP) schedule(dynamic,16)
637 #endif
638 
639         for (int i = 0; i < H; ++i) {
640             for (int j = 0; j < W; ++j) {
641                 if (rawData[i][j] >= MAX_OUT_VALUE) {
642                     bitmapBads.set(j, i);
643                     ++totBP;
644                 }
645             }
646         }
647 
648         if (totBP > 0) {
649             interpolateBadPixelsBayer(bitmapBads, rawData);
650         }
651 
652     } else if (ri->getSensorType() == ST_FUJI_XTRANS) {
653 #ifdef _OPENMP
654         #pragma omp parallel for reduction(+:totBP) schedule(dynamic,16)
655 #endif
656 
657         for (int i = 0; i < H; ++i) {
658             for (int j = 0; j < W; ++j) {
659                 if (rawData[i][j] >= MAX_OUT_VALUE) {
660                     bitmapBads.set(j, i);
661                     totBP++;
662                 }
663             }
664         }
665 
666         if (totBP > 0) {
667             interpolateBadPixelsXtrans(bitmapBads);
668         }
669     }
670 
671     t3.set();
672 
673     if (settings->verbose) {
674         printf("Bad pixels count: %d\n", totBP);
675         printf("Bad pixels interpolation time us: %d\n", t3.etime(t2));
676     }
677 }
678 
679 
680 //-----------------------------------------------------------------------------
681 
682 // *** Non-raw processing ***
683 
684 
getImageSpotValues(Coord2D spotCoord,int spotSize,int tran,const procparams::FilmNegativeParams & params,std::array<float,3> & out)685 bool StdImageSource::getImageSpotValues(Coord2D spotCoord, int spotSize, int tran, const procparams::FilmNegativeParams &params, std::array<float, 3>& out)
686 {
687     if (!imgCopy) {
688         imgCopy = new Imagefloat(img->getWidth(), img->getHeight());
689         img->getStdImage(wb, 0, imgCopy, PreviewProps(0,0,img->getWidth(), img->getHeight(), 1));
690     }
691 
692     Coord spot;
693     imgCopy->transformPixel(spotCoord.x, spotCoord.y, tran, spot.x, spot.y);
694 
695     if (settings->verbose) {
696         printf("Transformed coords: %d,%d\n", spot.x, spot.y);
697     }
698 
699     if (spotSize < 4) {
700         return false;
701     }
702 
703     // Calculate averages of raw unscaled channels
704     if (!channelsAvg(imgCopy, imgCopy->getWidth(), imgCopy->getHeight(), spot, spotSize, out)) {
705         return false;
706     }
707 
708     // normalize to 0-1
709     // for (auto &v : out) {
710     //     v /= 65535.f;
711     // }
712 
713     if (settings->verbose) {
714         printf("Image spot values: R=%g, G=%g, B=%g\n", out[0], out[1], out[2]);
715     }
716 
717     return true;
718 }
719 
720 
getFilmNegativeExponents(Coord2D spotA,Coord2D spotB,int tran,const procparams::FilmNegativeParams & currentParams,std::array<float,3> & newExps)721 bool StdImageSource::getFilmNegativeExponents(Coord2D spotA, Coord2D spotB, int tran, const procparams::FilmNegativeParams &currentParams, std::array<float, 3>& newExps)
722 {
723 //    using rtengine::Imagefloat;
724 
725     if (!imgCopy) {
726         imgCopy = new Imagefloat(img->getWidth(), img->getHeight());
727         img->getStdImage(wb, 0, imgCopy, PreviewProps(0,0,img->getWidth(), img->getHeight(), 1));
728     }
729 
730 
731     newExps = {
732         static_cast<float>(currentParams.redRatio * currentParams.greenExp),
733         static_cast<float>(currentParams.greenExp),
734         static_cast<float>(currentParams.blueRatio * currentParams.greenExp)
735     };
736 
737     constexpr int spotSize = 32; // TODO: Make this configurable?
738 
739     Coord spot;
740     std::array<float, 3> clearVals;
741     std::array<float, 3> denseVals;
742 
743     // Sample first spot
744     imgCopy->transformPixel(spotA.x, spotA.y, tran, spot.x, spot.y);
745 
746     if (!channelsAvg(imgCopy, imgCopy->getWidth(), imgCopy->getHeight(), spot, spotSize, clearVals)) {
747         return false;
748     }
749 
750     // Sample second spot
751     imgCopy->transformPixel(spotB.x, spotB.y, tran, spot.x, spot.y);
752 
753     if (!channelsAvg(imgCopy, imgCopy->getWidth(), imgCopy->getHeight(), spot, spotSize, denseVals)) {
754         return false;
755     }
756 
757     // Detect which one is the dense spot, based on green channel
758     if (clearVals[1] < denseVals[1]) {
759         std::swap(clearVals, denseVals);
760     }
761 
762     if (settings->verbose) {
763         printf("Clear film values: R=%g G=%g B=%g\n", clearVals[0], clearVals[1], clearVals[2]);
764         printf("Dense film values: R=%g G=%g B=%g\n", denseVals[0], denseVals[1], denseVals[2]);
765     }
766 
767     const float denseGreenRatio = clearVals[1] / denseVals[1];
768 
769     // Calculate logarithms in arbitrary base
770     const auto logBase =
771         [](float base, float num) -> float
772         {
773             return std::log(num) / std::log(base);
774         };
775 
776     // Calculate exponents for each channel, based on the ratio between the bright and dark values,
777     // compared to the ratio in the reference channel (green)
778     for (int ch = 0; ch < 3; ++ch) {
779         if (ch == 1) {
780             newExps[ch] = 1.f;  // Green is the reference channel
781         } else {
782             newExps[ch] = LIM(logBase(clearVals[ch] / denseVals[ch], denseGreenRatio), 0.3f, 4.f);
783         }
784     }
785 
786     if (settings->verbose) {
787         printf("New exponents:  R=%g G=%g B=%g\n", newExps[0], newExps[1], newExps[2]);
788     }
789 
790     return true;
791 }
792 
filmNegativeProcess(const procparams::FilmNegativeParams & params,std::array<float,3> & filmBaseValues)793 void rtengine::StdImageSource::filmNegativeProcess(const procparams::FilmNegativeParams &params, std::array<float, 3>& filmBaseValues)
794 {
795     if (!params.enabled) {
796         // If filmneg is not enabled, restore the copy as main image.
797         if (imgCopy) {
798             if(img) {
799                 img->allocate(0,0);
800                 delete img;
801             }
802             img = imgCopy;
803             imgCopy = nullptr;
804         }
805 
806         return;
807     }
808 
809     if (params.enabled && !imgCopy) {
810         //printf("ONCE!!!!!!\n");
811         imgCopy = new Imagefloat(img->getWidth(), img->getHeight());
812         img->getStdImage(wb, 0, imgCopy, PreviewProps(0,0,img->getWidth(), img->getHeight(), 1));
813     }
814 
815     // Destroy old buffer
816     img->allocate(0,0);
817     delete img;
818 
819     // Overwrite working buffer with a copy of the float version
820     Imagefloat* posImg = imgCopy->copy();
821     img = posImg;
822 
823 
824     std::array<float, 3> exps = {
825         float(-(params.greenExp * params.redRatio)), // 2.2f;
826         float(-params.greenExp),  // 2.2f;
827         float(-(params.greenExp * params.blueRatio)) // 2.2f;
828     };
829 
830     std::array<float, 3> mults;  // Channel normalization multipliers
831     constexpr float MAX_OUT_VALUE = 65000.f;
832 
833     const int W = imgCopy->getWidth();
834     const int H = imgCopy->getHeight();
835 
836     // If film base values are set in params, use those
837     if (filmBaseValues[0] <= 0.f) {
838         // ...otherwise, the film negative tool might have just been enabled on this image,
839         // whithout any previous setting. So, estimate film base values from channel medians
840 
841         std::array<float, 3> medians;
842 
843         // Cut 20% border from medians calculation. It will probably contain outlier values
844         // from the film holder, which will bias the median result.
845         const int bW = W * 20 / 100;
846         const int bH = H * 20 / 100;
847         calcMedians(imgCopy, bW, bH, W - bW, H - bH, 4, medians);
848 
849         for (int c = 0; c < 3; ++c) {
850 
851             // Estimate film base values, so that in the output data, each channel
852             // median will correspond to 1/24th of MAX.
853             filmBaseValues[c] = pow_F(24.f / 512.f, 1.f / exps[c]) * medians[c];
854         }
855 
856         if (settings->verbose) {
857             printf("Channel medians: R=%g, G=%g, B=%g\n", medians[0], medians[1], medians[2]);
858             printf("Estimated base values: R=%g, G=%g, B=%g\n", filmBaseValues[0], filmBaseValues[1], filmBaseValues[2]);
859         }
860     }
861 
862     std::array<float, 3> fb;
863 
864     for (int c = 0; c < 3; ++c) {
865         // Apply channel exponents, to obtain the corresponding base values in the output data
866         fb[c] = pow_F(std::max(filmBaseValues[c], 1.f), exps[c]);
867 
868         // Determine the channel multiplier so that the film base value is 1/512th of max.
869         mults[c] = (MAX_OUT_VALUE / 512.f) / fb[c];
870     }
871 
872     if (settings->verbose) {
873         printf("Output film base values: %g %g %g\n", static_cast<double>(fb[0]), static_cast<double>(fb[1]), static_cast<double>(fb[2]));
874         printf("Computed multipliers: %g %g %g\n", static_cast<double>(mults[0]), static_cast<double>(mults[1]), static_cast<double>(mults[2]));
875     }
876     const float rmult = mults[0];
877     const float gmult = mults[1];
878     const float bmult = mults[2];
879 
880     const float rexp = exps[0];
881     const float gexp = exps[1];
882     const float bexp = exps[2];
883 
884 
885 #ifdef __SSE2__
886     const vfloat clipv = F2V(MAXVALF);
887     const vfloat rexpv = F2V(rexp);
888     const vfloat gexpv = F2V(gexp);
889     const vfloat bexpv = F2V(bexp);
890     const vfloat rmultv = F2V(rmult);
891     const vfloat gmultv = F2V(gmult);
892     const vfloat bmultv = F2V(bmult);
893 #endif
894 
895     const int rheight = imgCopy->getHeight();
896     const int rwidth = imgCopy->getWidth();
897 
898     for (int i = 0; i < rheight; i++) {
899         float *rlinein = imgCopy->r(i);
900         float *glinein = imgCopy->g(i);
901         float *blinein = imgCopy->b(i);
902         float *rlineout = posImg->r(i);
903         float *glineout = posImg->g(i);
904         float *blineout = posImg->b(i);
905         int j = 0;
906 #ifdef __SSE2__
907 
908         for (; j < rwidth - 3; j += 4) {
909             STVFU(rlineout[j], vminf(rmultv * pow_F(LVFU(rlinein[j]), rexpv), clipv));
910             STVFU(glineout[j], vminf(gmultv * pow_F(LVFU(glinein[j]), gexpv), clipv));
911             STVFU(blineout[j], vminf(bmultv * pow_F(LVFU(blinein[j]), bexpv), clipv));
912         }
913 
914 #endif
915 
916         for (; j < rwidth; ++j) {
917             rlineout[j] = CLIP(rmult * pow_F(rlinein[j], rexp));
918             glineout[j] = CLIP(gmult * pow_F(glinein[j], gexp));
919             blineout[j] = CLIP(bmult * pow_F(blinein[j], bexp));
920         }
921     }
922 }
923 
924 } // namespace rtengine
925