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 ¤tParams, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¤tParams, 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 ¶ms, 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