Filmenegative core cleanup

This commit is contained in:
Flössie 2019-06-17 08:03:46 +02:00
parent 80f2b6a002
commit 54cc02eea9
6 changed files with 422 additions and 344 deletions

View File

@ -1,7 +1,7 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
* Copyright (c) 2019 rom9
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -19,15 +19,17 @@
#include <cmath>
#include <iostream>
#include "rtengine.h"
#include "rawimagesource.h"
#include "mytime.h"
#include "procparams.h"
#ifdef _OPENMP
#include <omp.h>
#endif
#include "rawimagesource.h"
#include "mytime.h"
#include "opthelper.h"
#include "procparams.h"
#include "rt_algo.h"
#include "rtengine.h"
//#define BENCHMARK
#include "StopWatch.h"
@ -37,168 +39,221 @@ namespace rtengine
extern const Settings* settings;
bool RawImageSource::channelsAvg(Coord spotPos, int spotSize, float avgs[3], const FilmNegativeParams &params)
}
namespace
{
avgs[0] = avgs[1] = avgs[2] = 0.f; // Channel averages
if(ri->getSensorType() != ST_BAYER && ri->getSensorType() != ST_FUJI_XTRANS)
bool channelsAvg(
const rtengine::RawImage* ri,
int width,
int height,
array2D<float>& rawData,
rtengine::Coord spotPos,
int spotSize,
const rtengine::procparams::FilmNegativeParams& params,
std::array<float, 3>& avgs
)
{
avgs = {}; // Channel averages
if (ri->getSensorType() != rtengine::ST_BAYER && ri->getSensorType() != rtengine::ST_FUJI_XTRANS) {
return false;
}
if (settings->verbose)
if (rtengine::settings->verbose) {
printf("Spot coord: x=%d y=%d\n", spotPos.x, spotPos.y);
}
int x1 = spotPos.x - spotSize / 2;
int x2 = spotPos.x + spotSize / 2;
int y1 = spotPos.y - spotSize / 2;
int y2 = spotPos.y + spotSize / 2;
const int half_spot_size = spotSize / 2;
if(x1<0 || x2>W || y1<0 || y2>H)
const int& x1 = spotPos.x - half_spot_size;
const int& x2 = spotPos.x + half_spot_size;
const int& y1 = spotPos.y - half_spot_size;
const int& y2 = spotPos.y + half_spot_size;
if (x1 < 0 || x2 > width || y1 < 0 || y2 > height) {
return false; // Spot goes outside bounds, bail out.
}
int pxCount[3] = {0}; // Per-channel sample counts
for(int c=spotPos.x-spotSize; c<spotPos.x+spotSize; c++) {
for(int r=spotPos.y-spotSize; r<spotPos.y+spotSize; r++) {
std::array<int, 3> pxCount = {}; // Per-channel sample counts
for (int c = spotPos.x - spotSize; c < spotPos.x + spotSize; ++c) {
for (int r = spotPos.y - spotSize; r < spotPos.y + spotSize; ++r) {
const int ch = ri->getSensorType() == rtengine::ST_BAYER ? ri->FC(r,c) : ri->XTRANSFC(r,c);
int ch = (ri->getSensorType() == ST_BAYER) ? FC(r,c) : ri->XTRANSFC(r,c);
++pxCount[ch];
pxCount[ch]++;
// If film negative is currently enabled, undo the effect by elevating to 1/exp,
// in order to sample the original, linear value
if(params.enabled)
avgs[ch] += powf(rawData[r][c], -1 / (ch==0 ? params.redExp : ch==1 ? params.greenExp : params.blueExp));
else
if (params.enabled) {
avgs[ch] += powf(rawData[r][c], -1.f / (ch == 0 ? params.redExp : ch == 1 ? params.greenExp : params.blueExp));
} else {
avgs[ch] += rawData[r][c];
}
}
}
for(int ch=0; ch<3; ch++)
avgs[ch] = avgs[ch] / (pxCount[ch]);
for (int ch = 0; ch < 3; ++ch) {
avgs[ch] /= pxCount[ch];
}
return true;
}
// Calculate logarithms in arbitrary base
float logBase(float base, float num) {
return log(num) / log(base);
}
bool RawImageSource::getFilmNegativeExponents(Coord2D spotA, Coord2D spotB, int tran, const FilmNegativeParams &currentParams, std::array<float, 3>& newExps)
bool rtengine::RawImageSource::getFilmNegativeExponents(Coord2D spotA, Coord2D spotB, int tran, const FilmNegativeParams &currentParams, std::array<float, 3>& newExps)
{
float clearVals[3], denseVals[3];
newExps = {
static_cast<float>(currentParams.redExp),
static_cast<float>(currentParams.greenExp),
static_cast<float>(currentParams.blueExp)
};
newExps[0] = currentParams.redExp;
newExps[1] = currentParams.greenExp;
newExps[2] = currentParams.blueExp;
constexpr int spotSize = 32; // TODO: Make this configurable?
int spotSize = 32; // TODO : make this confugurable ?
Coord spot;
std::array<float, 3> clearVals;
std::array<float, 3> denseVals;
// Sample first spot
transformPosition (spotA.x, spotA.y, tran, spot.x, spot.y);
if(!channelsAvg(spot, spotSize, clearVals, currentParams))
return false;
// Sample second spot
transformPosition (spotB.x, spotB.y, tran, spot.x, spot.y);
if(!channelsAvg(spot, spotSize, denseVals, currentParams))
return false;
// Detect which one is the dense spot, based on green channel
if(clearVals[1] < denseVals[1])
std::swap(clearVals, denseVals);
if (settings->verbose) {
printf("Clear film values: R=%g G=%g B=%g\n", clearVals[0], clearVals[1], clearVals[2]);
printf("Dense film values: R=%g G=%g B=%g\n", denseVals[0], denseVals[1], denseVals[2]);
transformPosition(spotA.x, spotA.y, tran, spot.x, spot.y);
if (!channelsAvg(ri, W, H, rawData, spot, spotSize, currentParams, clearVals)) {
return false;
}
float denseGreenRatio = clearVals[1] / denseVals[1];
// Sample second spot
transformPosition(spotB.x, spotB.y, tran, spot.x, spot.y);
if (!channelsAvg(ri, W, H, rawData, spot, spotSize, currentParams, denseVals)) {
return false;
}
// Detect which one is the dense spot, based on green channel
if (clearVals[1] < denseVals[1]) {
std::swap(clearVals, denseVals);
}
if (settings->verbose) {
printf("Clear film values: R=%g G=%g B=%g\n", clearVals[0], clearVals[1], clearVals[2]);
printf("Dense film values: R=%g G=%g B=%g\n", denseVals[0], denseVals[1], denseVals[2]);
}
const float denseGreenRatio = clearVals[1] / denseVals[1];
// Calculate logarithms in arbitrary base
const auto logBase =
[](float base, float num) -> float
{
return std::log(num) / std::log(base);
};
// Calculate exponents for each channel, based on the ratio between the bright and dark values,
// compared to the ratio in the reference channel (green)
for(int ch=0; ch<3; ch++)
if(ch==1)
for (int ch = 0; ch < 3; ++ch) {
if (ch == 1) {
newExps[ch] = 2.f; // Green is the reference channel
else
} else {
newExps[ch] = CLAMP(2.f * logBase(clearVals[ch] / denseVals[ch], denseGreenRatio), 0.3f, 6.f);
}
}
if (settings->verbose)
if (settings->verbose) {
printf("New exponents: R=%g G=%g B=%g\n", newExps[0], newExps[1], newExps[2]);
}
return true;
}
void RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams &params)
void rtengine::RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams &params)
{
// BENCHFUNMICRO
if(!params.enabled)
if (!params.enabled) {
return;
}
float exps[3] = { (float)params.redExp, (float)params.greenExp, (float)params.blueExp };
const std::array<float, 3> exps = {
static_cast<float>(params.redExp),
static_cast<float>(params.greenExp),
static_cast<float>(params.blueExp)
};
MyTime t1, t2, t3,t4, t5, t6;
MyTime t1, t2, t3,t4, t5;
t1.set();
// Channel vectors to calculate medians
std::vector<float> cvs[3] = {
std::vector<float>(),
std::vector<float>(),
std::vector<float>()
};
std::array<std::vector<float>, 3> cvs;
// Sample one every 5 pixels, and push the value in the appropriate channel vector.
// Chose an odd step, not multiple of the CFA size, to get a chance to visit each channel.
if(ri->getSensorType() == ST_BAYER) {
for (int row = 0; row < H; row+=5) {
for (int col = 0; col < W; col+=5) {
int c = FC(row, col); // three colors, 0=R, 1=G, 2=B
// Choose an odd step, not a multiple of the CFA size, to get a chance to visit each channel.
if (ri->getSensorType() == ST_BAYER) {
for (int row = 0; row < H; row += 5) {
for (int col = 0; col < W; col += 5) {
const int c = FC(row, col); // three colors: 0=R, 1=G, 2=B
cvs[c].push_back(rawData[row][col]);
}
}
} else if(ri->getSensorType() == ST_FUJI_XTRANS) {
for (int row = 0; row < H; row+=5) {
for (int col = 0; col < W; col+=5) {
int c = ri->XTRANSFC(row, col); // three colors, 0=R, 1=G, 2=B
}
else if (ri->getSensorType() == ST_FUJI_XTRANS) {
for (int row = 0; row < H; row += 5) {
for (int col = 0; col < W; col += 5) {
const int c = ri->XTRANSFC(row, col); // three colors: 0=R, 1=G, 2=B
cvs[c].push_back(rawData[row][col]);
}
}
}
const float MAX_OUT_VALUE = 65000.f;
constexpr float MAX_OUT_VALUE = 65000.f;
t2.set();
if (settings->verbose)
if (settings->verbose) {
printf("Median vector fill loop time us: %d\n", t2.etime(t1));
}
float medians[3]; // Channel median values
float mults[3] = { 1.f }; // Channel normalization multipliers
t2.set();
for (int c=0; c<3; c++) {
std::array<float, 3> medians; // Channel median values
std::array<float, 3> mults = {
1.f,
1.f,
1.f
}; // Channel normalization multipliers
for (int c = 0; c < 3; ++c) {
// Find median values for each channel
if(cvs[c].size() > 0) {
findMinMaxPercentile(&cvs[c][0], cvs[c].size(), 0.5f, medians[c], 0.5f, medians[c], true);
medians[c] = pow_F(max(medians[c], 1.f), -exps[c]);
if (!cvs[c].empty()) {
findMinMaxPercentile(cvs[c].data(), cvs[c].size(), 0.5f, medians[c], 0.5f, medians[c], true);
medians[c] = pow_F(rtengine::max(medians[c], 1.f), -exps[c]);
// Determine the channel multipler so that N times the median becomes 65k. This clips away
// the values in the dark border surrounding the negative (due to the film holder, for example),
// the reciprocal of which have blown up to stellar values.
mults[c] = MAX_OUT_VALUE / (medians[c] * 24);
mults[c] = MAX_OUT_VALUE / (medians[c] * 24.f);
}
}
t3.set();
if (settings->verbose) {
printf("Sample count : %lu, %lu, %lu\n", cvs[0].size(), cvs[1].size(), cvs[2].size());
printf("Medians : %g %g %g\n", medians[0], medians[1], medians[2] );
printf("Computed multipliers : %g %g %g\n", mults[0], mults[1], mults[2] );
printf("Sample count: %zu, %zu, %zu\n", cvs[0].size(), cvs[1].size(), cvs[2].size());
printf("Medians: %g %g %g\n", medians[0], medians[1], medians[2] );
printf("Computed multipliers: %g %g %g\n", mults[0], mults[1], mults[2] );
printf("Median calc time us: %d\n", t3.etime(t2));
}
t3.set();
if (ri->getSensorType() == ST_BAYER) {
#ifdef __SSE2__
const vfloat onev = F2V(1.f);
const vfloat c65535v = F2V(65535.f);
#endif
if(ri->getSensorType() == ST_BAYER) {
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16)
#endif
for (int row = 0; row < H; row ++) {
for (int row = 0; row < H; ++row) {
int col = 0;
// Exponents are expressed as positive in the parameters, so negate them in order
// to get the reciprocals. Avoid trouble with zeroes, minimum pixel value is 1.
@ -209,21 +264,24 @@ void RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams &p
#ifdef __SSE2__
const vfloat expsv = _mm_setr_ps(exps0, exps1, exps0, exps1);
const vfloat multsv = _mm_setr_ps(mult0, mult1, mult0, mult1);
const vfloat onev = F2V(1.f);
const vfloat c65535v = F2V(65535.f);
for (; col < W - 3; col+=4) {
for (; col < W - 3; col += 4) {
STVFU(rawData[row][col], vminf(multsv * pow_F(vmaxf(LVFU(rawData[row][col]), onev), expsv), c65535v));
}
#endif // __SSE2__
for (; col < W - 1; col+=2) {
rawData[row][col] = rtengine::min(mult0 * pow_F(max(rawData[row][col], 1.f), exps0), 65535.f);
rawData[row][col + 1] = rtengine::min(mult1 * pow_F(max(rawData[row][col + 1], 1.f), exps1), 65535.f);
for (; col < W - 1; col += 2) {
rawData[row][col] = rtengine::min(mult0 * pow_F(rtengine::max(rawData[row][col], 1.f), exps0), 65535.f);
rawData[row][col + 1] = rtengine::min(mult1 * pow_F(rtengine::max(rawData[row][col + 1], 1.f), exps1), 65535.f);
}
if (col < W) {
rawData[row][col] = rtengine::min(mult0 * pow_F(max(rawData[row][col], 1.f), exps0), 65535.f);
rawData[row][col] = rtengine::min(mult0 * pow_F(rtengine::max(rawData[row][col], 1.f), exps0), 65535.f);
}
}
} else if(ri->getSensorType() == ST_FUJI_XTRANS) {
} else if (ri->getSensorType() == ST_FUJI_XTRANS) {
#ifdef __SSE2__
const vfloat onev = F2V(1.f);
const vfloat c65535v = F2V(65535.f);
#endif
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 16)
#endif
@ -231,8 +289,22 @@ void RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams &p
int col = 0;
// Exponents are expressed as positive in the parameters, so negate them in order
// to get the reciprocals. Avoid trouble with zeroes, minimum pixel value is 1.
const float expsc[6] = {-exps[ri->XTRANSFC(row, 0)], -exps[ri->XTRANSFC(row, 1)], -exps[ri->XTRANSFC(row, 2)], -exps[ri->XTRANSFC(row, 3)], -exps[ri->XTRANSFC(row, 4)], -exps[ri->XTRANSFC(row, 5)]};
const float multsc[6] = {mults[ri->XTRANSFC(row, 0)], mults[ri->XTRANSFC(row, 1)], mults[ri->XTRANSFC(row, 2)], mults[ri->XTRANSFC(row, 3)], mults[ri->XTRANSFC(row, 4)], mults[ri->XTRANSFC(row, 5)]};
const std::array<float, 6> expsc = {
-exps[ri->XTRANSFC(row, 0)],
-exps[ri->XTRANSFC(row, 1)],
-exps[ri->XTRANSFC(row, 2)],
-exps[ri->XTRANSFC(row, 3)],
-exps[ri->XTRANSFC(row, 4)],
-exps[ri->XTRANSFC(row, 5)]
};
const std::array<float, 6> multsc = {
mults[ri->XTRANSFC(row, 0)],
mults[ri->XTRANSFC(row, 1)],
mults[ri->XTRANSFC(row, 2)],
mults[ri->XTRANSFC(row, 3)],
mults[ri->XTRANSFC(row, 4)],
mults[ri->XTRANSFC(row, 5)]
};
#ifdef __SSE2__
const vfloat expsv0 = _mm_setr_ps(expsc[0], expsc[1], expsc[2], expsc[3]);
const vfloat expsv1 = _mm_setr_ps(expsc[4], expsc[5], expsc[0], expsc[1]);
@ -240,81 +312,75 @@ void RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams &p
const vfloat multsv0 = _mm_setr_ps(multsc[0], multsc[1], multsc[2], multsc[3]);
const vfloat multsv1 = _mm_setr_ps(multsc[4], multsc[5], multsc[0], multsc[1]);
const vfloat multsv2 = _mm_setr_ps(multsc[2], multsc[3], multsc[4], multsc[5]);
const vfloat onev = F2V(1.f);
const vfloat c65535v = F2V(65535.f);
for (; col < W - 11; col+=12) {
for (; col < W - 11; col += 12) {
STVFU(rawData[row][col], vminf(multsv0 * pow_F(vmaxf(LVFU(rawData[row][col]), onev), expsv0), c65535v));
STVFU(rawData[row][col + 4], vminf(multsv1 * pow_F(vmaxf(LVFU(rawData[row][col + 4]), onev), expsv1), c65535v));
STVFU(rawData[row][col + 8], vminf(multsv2 * pow_F(vmaxf(LVFU(rawData[row][col + 8]), onev), expsv2), c65535v));
}
#endif // __SSE2__
for (; col < W - 5; col+=6) {
for (; col < W - 5; col += 6) {
for (int c = 0; c < 6; ++c) {
rawData[row][col + c] = rtengine::min(multsc[c] * pow_F(max(rawData[row][col + c], 1.f), expsc[c]), 65535.f);
rawData[row][col + c] = rtengine::min(multsc[c] * pow_F(rtengine::max(rawData[row][col + c], 1.f), expsc[c]), 65535.f);
}
}
for (int c = 0; col < W; col++, c++) {
rawData[row][col + c] = rtengine::min(multsc[c] * pow_F(max(rawData[row][col + c], 1.f), expsc[c]), 65535.f);
rawData[row][col + c] = rtengine::min(multsc[c] * pow_F(rtengine::max(rawData[row][col + c], 1.f), expsc[c]), 65535.f);
}
}
}
t4.set();
if (settings->verbose) {
printf("Pow loop time us: %d\n", t4.etime(t3));
}
t4.set();
if (settings->verbose)
printf("Pow loop time us: %d\n", t4.etime(t3));
t5.set();
PixelsMap bitmapBads(W, H);
int totBP = 0; // Hold count of bad pixels to correct
if(ri->getSensorType() == ST_BAYER) {
if (ri->getSensorType() == ST_BAYER) {
#ifdef _OPENMP
#pragma omp parallel for reduction(+:totBP) schedule(dynamic,16)
#endif
for(int i = 0; i < H; i++)
for(int j = 0; j < W; j++) {
for (int i = 0; i < H; ++i) {
for (int j = 0; j < W; ++j) {
if (rawData[i][j] >= MAX_OUT_VALUE) {
bitmapBads.set(j, i);
totBP++;
++totBP;
}
}
if (totBP > 0) {
interpolateBadPixelsBayer( bitmapBads, rawData );
}
} else if(ri->getSensorType() == ST_FUJI_XTRANS) {
#ifdef _OPENMP
#pragma omp parallel for reduction(+:totBP) schedule(dynamic,16)
#endif
for(int i = 0; i < H; i++)
for(int j = 0; j < W; j++) {
if (rawData[i][j] >= MAX_OUT_VALUE) {
bitmapBads.set(j, i);
totBP++;
}
}
if (totBP > 0) {
interpolateBadPixelsXtrans( bitmapBads );
interpolateBadPixelsBayer(bitmapBads, rawData);
}
}
else if (ri->getSensorType() == ST_FUJI_XTRANS) {
#ifdef _OPENMP
#pragma omp parallel for reduction(+:totBP) schedule(dynamic,16)
#endif
for (int i = 0; i < H; ++i) {
for (int j = 0; j < W; ++j) {
if (rawData[i][j] >= MAX_OUT_VALUE) {
bitmapBads.set(j, i);
totBP++;
}
}
}
if (totBP > 0) {
interpolateBadPixelsXtrans(bitmapBads);
}
}
t5.set();
t6.set();
if (settings->verbose) {
printf("Bad pixels count: %d\n", totBP);
printf("Bad pixels interpolation time us: %d\n", t6.etime(t5));
printf("Bad pixels interpolation time us: %d\n", t5.etime(t4));
}
}
}

View File

@ -67,7 +67,7 @@ RawImage::~RawImage()
}
}
eSensorType RawImage::getSensorType()
eSensorType RawImage::getSensorType() const
{
if (isBayer()) {
return ST_BAYER;

View File

@ -185,7 +185,7 @@ public:
return float_raw_image;
}
eSensorType getSensorType();
eSensorType getSensorType() const;
void getRgbCam (float rgbcam[3][4]);
void getXtransMatrix ( int xtransMatrix[6][6]);

View File

@ -43,7 +43,6 @@ private:
static LUTf initInvGrad ();
static void colorSpaceConversion_ (Imagefloat* im, const procparams::ColorManagementParams& cmp, const ColorTemp &wb, double pre_mul[3], cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], const std::string &camName);
int defTransform (int tran);
bool channelsAvg(Coord spotPos, int spotSize, float avgs[3], const FilmNegativeParams &params);
protected:
MyMutex getImageMutex; // locks getImage

View File

@ -1,7 +1,7 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
* Copyright (c) 2019 rom9
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,52 +16,52 @@
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#include "filmnegative.h"
#include <iomanip>
#include "rtimage.h"
#include "options.h"
#include "filmnegative.h"
#include "editwidgets.h"
#include "eventmapper.h"
#include "options.h"
#include "rtimage.h"
#include "../rtengine/procparams.h"
using namespace rtengine;
using namespace rtengine::procparams;
FilmNegative::FilmNegative () : FoldableToolPanel(this, "filmnegative", M("TP_FILMNEGATIVE_LABEL"), false, true), EditSubscriber(ET_OBJECTS)
namespace
{
auto mkExponentAdjuster = [this](Glib::ustring label, double defaultVal) {
Adjuster *adj = Gtk::manage(new Adjuster (label, 0.3, 6, 0.001, defaultVal)); //exponent
adj->setAdjusterListener (this);
if (adj->delay < options.adjusterMaxDelay) {
adj->delay = options.adjusterMaxDelay;
}
adj->show();
return adj;
};
Adjuster* createExponentAdjuster(AdjusterListener* listener, const Glib::ustring& label, double defaultVal)
{
Adjuster* const adj = Gtk::manage(new Adjuster(label, 0.3, 6, 0.001, defaultVal)); // exponent
adj->setAdjusterListener(listener);
redExp = mkExponentAdjuster(M("TP_FILMNEGATIVE_RED"), 2.72);
greenExp = mkExponentAdjuster(M("TP_FILMNEGATIVE_GREEN"), 2.0);
blueExp = mkExponentAdjuster(M("TP_FILMNEGATIVE_BLUE"), 1.72);
if (adj->delay < options.adjusterMaxDelay) {
adj->delay = options.adjusterMaxDelay;
}
redRatio = redExp->getValue() / greenExp->getValue();
blueRatio = blueExp->getValue() / greenExp->getValue();
adj->show();
return adj;
}
auto m = ProcEventMapper::getInstance();
EvFilmNegativeEnabled = m->newEvent(FIRST, "HISTORY_MSG_FILMNEGATIVE_ENABLED");
EvFilmNegativeExponents = m->newEvent(FIRST, "HISTORY_MSG_FILMNEGATIVE_EXPONENTS");
}
spotgrid = Gtk::manage(new Gtk::Grid());
FilmNegative::FilmNegative() :
FoldableToolPanel(this, "filmnegative", M("TP_FILMNEGATIVE_LABEL"), false, true),
EditSubscriber(ET_OBJECTS),
evFilmNegativeExponents(ProcEventMapper::getInstance()->newEvent(FIRST, "HISTORY_MSG_FILMNEGATIVE_EXPONENTS")),
evFilmNegativeEnabled(ProcEventMapper::getInstance()->newEvent(FIRST, "HISTORY_MSG_FILMNEGATIVE_ENABLED")),
fnp(nullptr),
redExp(createExponentAdjuster(this, M("TP_FILMNEGATIVE_RED"), 2.72)),
greenExp(createExponentAdjuster(this, M("TP_FILMNEGATIVE_GREEN"), 2.0)),
blueExp(createExponentAdjuster(this, M("TP_FILMNEGATIVE_BLUE"), 1.72)),
spotgrid(Gtk::manage(new Gtk::Grid())),
spotbutton(Gtk::manage(new Gtk::ToggleButton(M("TP_FILMNEGATIVE_PICK")))),
redRatio(redExp->getValue() / greenExp->getValue()),
blueRatio(blueExp->getValue() / greenExp->getValue())
{
spotgrid->get_style_context()->add_class("grid-spacing");
setExpandAlignProperties(spotgrid, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
spotbutton = Gtk::manage (new Gtk::ToggleButton (M("TP_FILMNEGATIVE_PICK")));
setExpandAlignProperties(spotbutton, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
spotbutton->get_style_context()->add_class("independent");
spotbutton->set_tooltip_text(M("TP_FILMNEGATIVE_GUESS_TOOLTIP"));
@ -83,120 +83,47 @@ FilmNegative::FilmNegative () : FoldableToolPanel(this, "filmnegative", M("TP_FI
// spotsize->append ("4");
spotgrid->attach (*spotbutton, 0, 1, 1, 1);
// spotgrid->attach (*slab, 1, 0, 1, 1);
// spotgrid->attach (*slab, 1, 0, 1, 1);
// spotgrid->attach (*wbsizehelper, 2, 0, 1, 1);
pack_start (*redExp, Gtk::PACK_SHRINK, 0);
pack_start (*greenExp, Gtk::PACK_SHRINK, 0);
pack_start (*blueExp, Gtk::PACK_SHRINK, 0);
pack_start (*spotgrid, Gtk::PACK_SHRINK, 0 );
spotbutton->signal_toggled().connect( sigc::mem_fun(*this, &FilmNegative::editToggled) );
// spotsize->signal_changed().connect( sigc::mem_fun(*this, &WhiteBalance::spotSizeChanged) );
pack_start(*redExp, Gtk::PACK_SHRINK, 0);
pack_start(*greenExp, Gtk::PACK_SHRINK, 0);
pack_start(*blueExp, Gtk::PACK_SHRINK, 0);
pack_start(*spotgrid, Gtk::PACK_SHRINK, 0);
spotbutton->signal_toggled().connect(sigc::mem_fun(*this, &FilmNegative::editToggled));
// spotsize->signal_changed().connect( sigc::mem_fun(*this, &WhiteBalance::spotSizeChanged) );
// Editing geometry; create the spot rectangle
Rectangle *spotRect = new Rectangle();
Rectangle* const spotRect = new Rectangle();
spotRect->filled = false;
EditSubscriber::visibleGeometry.push_back( spotRect );
visibleGeometry.push_back(spotRect);
// Stick a dummy rectangle over the whole image in mouseOverGeometry.
// This is to make sure the getCursor call is fired everywhere.
Rectangle *imgRect = new Rectangle();
// This is to make sure the getCursor() call is fired everywhere.
Rectangle* imgRect = new Rectangle();
imgRect->filled = true;
EditSubscriber::mouseOverGeometry.push_back( imgRect );
mouseOverGeometry.push_back(imgRect);
}
FilmNegative::~FilmNegative()
{
// idle_register.destroy();
for (std::vector<Geometry*>::const_iterator i = visibleGeometry.begin(); i != visibleGeometry.end(); ++i) {
delete *i;
for (auto geometry : visibleGeometry) {
delete geometry;
}
for (std::vector<Geometry*>::const_iterator i = mouseOverGeometry.begin(); i != mouseOverGeometry.end(); ++i) {
delete *i;
}
}
void FilmNegative::enabledChanged()
{
if (listener) {
if (get_inconsistent()) {
listener->panelChanged(EvFilmNegativeEnabled, M("GENERAL_UNCHANGED"));
} else if (getEnabled()) {
listener->panelChanged(EvFilmNegativeEnabled, M("GENERAL_ENABLED"));
} else {
listener->panelChanged(EvFilmNegativeEnabled, M("GENERAL_DISABLED"));
}
for (auto geometry : mouseOverGeometry) {
delete geometry;
}
}
void FilmNegative::adjusterChanged(Adjuster* a, double newval)
void FilmNegative::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited)
{
if (listener) {
if(a == redExp || a == greenExp || a == blueExp) {
disableListener();
if(a == greenExp) {
redExp->setValue(a->getValue() * redRatio);
blueExp->setValue(a->getValue() * blueRatio);
} else if(a == redExp) {
redRatio = newval / greenExp->getValue();
} else if(a == blueExp) {
blueRatio = newval / greenExp->getValue();
}
enableListener();
disableListener();
if(getEnabled()) {
listener->panelChanged (EvFilmNegativeExponents, Glib::ustring::compose (
"R=%1 ; G=%2 ; B=%3", redExp->getTextValue(), greenExp->getTextValue(), blueExp->getTextValue()));
}
}
}
}
void FilmNegative::adjusterAutoToggled(Adjuster* a, bool newval)
{
}
void FilmNegative::setEditProvider (EditDataProvider* provider)
{
EditSubscriber::setEditProvider(provider);
}
void FilmNegative::editToggled ()
{
if (spotbutton->get_active()) {
subscribe();
int w, h;
getEditProvider()->getImageSize(w, h);
// Stick a dummy rectangle over the whole image in mouseOverGeometry.
// This is to make sure the getCursor call is fired everywhere.
const auto imgRect = static_cast<Rectangle*>(mouseOverGeometry.at(0));
imgRect->setXYWH(0, 0, w, h);
} else {
this->refSpotCoords.clear();
unsubscribe();
}
}
void FilmNegative::read (const ProcParams* pp, const ParamsEdited* pedited)
{
disableListener ();
if(pedited) {
if (pedited) {
redExp->setEditedState(pedited->filmNegative.redExp ? Edited : UnEdited);
greenExp->setEditedState(pedited->filmNegative.greenExp ? Edited : UnEdited);
blueExp->setEditedState(pedited->filmNegative.blueExp ? Edited : UnEdited);
@ -208,10 +135,10 @@ void FilmNegative::read (const ProcParams* pp, const ParamsEdited* pedited)
greenExp->setValue(pp->filmNegative.greenExp);
blueExp->setValue(pp->filmNegative.blueExp);
enableListener ();
enableListener();
}
void FilmNegative::write (ProcParams* pp, ParamsEdited* pedited)
void FilmNegative::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited)
{
pp->filmNegative.redExp = redExp->getValue();
pp->filmNegative.greenExp = greenExp->getValue();
@ -226,37 +153,103 @@ void FilmNegative::write (ProcParams* pp, ParamsEdited* pedited)
}
}
void FilmNegative::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited)
void FilmNegative::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited)
{
redExp->setValue(defParams->filmNegative.redExp);
greenExp->setValue(defParams->filmNegative.greenExp);
blueExp->setValue(defParams->filmNegative.blueExp);
if (pedited) {
redExp->setDefaultEditedState (pedited->filmNegative.redExp ? Edited : UnEdited);
greenExp->setDefaultEditedState (pedited->filmNegative.greenExp ? Edited : UnEdited);
blueExp->setDefaultEditedState (pedited->filmNegative.blueExp ? Edited : UnEdited);
redExp->setDefaultEditedState(pedited->filmNegative.redExp ? Edited : UnEdited);
greenExp->setDefaultEditedState(pedited->filmNegative.greenExp ? Edited : UnEdited);
blueExp->setDefaultEditedState(pedited->filmNegative.blueExp ? Edited : UnEdited);
} else {
redExp->setDefaultEditedState (Irrelevant);
greenExp->setDefaultEditedState (Irrelevant);
blueExp->setDefaultEditedState (Irrelevant);
redExp->setDefaultEditedState(Irrelevant);
greenExp->setDefaultEditedState(Irrelevant);
blueExp->setDefaultEditedState(Irrelevant);
}
}
void FilmNegative::setBatchMode (bool batchMode)
void FilmNegative::setBatchMode(bool batchMode)
{
spotConn.disconnect();
removeIfThere(this, spotgrid, false);
ToolPanel::setBatchMode (batchMode);
redExp->showEditedCB ();
greenExp->showEditedCB ();
blueExp->showEditedCB ();
ToolPanel::setBatchMode(batchMode);
redExp->showEditedCB();
greenExp->showEditedCB();
blueExp->showEditedCB();
}
void FilmNegative::adjusterChanged(Adjuster* a, double newval)
{
if (listener) {
if (a == redExp || a == greenExp || a == blueExp) {
disableListener();
if (a == greenExp) {
redExp->setValue(a->getValue() * redRatio);
blueExp->setValue(a->getValue() * blueRatio);
}
else if (a == redExp) {
redRatio = newval / greenExp->getValue();
}
else if (a == blueExp) {
blueRatio = newval / greenExp->getValue();
}
enableListener();
if (getEnabled()) {
listener->panelChanged(
evFilmNegativeExponents,
Glib::ustring::compose(
"R=%1 ; G=%2 ; B=%3",
redExp->getTextValue(),
greenExp->getTextValue(),
blueExp->getTextValue()
)
);
}
}
}
}
void FilmNegative::adjusterAutoToggled(Adjuster* a, bool newval)
{
}
void FilmNegative::enabledChanged()
{
if (listener) {
if (get_inconsistent()) {
listener->panelChanged(evFilmNegativeEnabled, M("GENERAL_UNCHANGED"));
}
else if (getEnabled()) {
listener->panelChanged(evFilmNegativeEnabled, M("GENERAL_ENABLED"));
}
else {
listener->panelChanged(evFilmNegativeEnabled, M("GENERAL_DISABLED"));
}
}
}
void FilmNegative::setFilmNegProvider(FilmNegProvider* p)
{
fnp = p;
}
void FilmNegative::setEditProvider(EditDataProvider* provider)
{
EditSubscriber::setEditProvider(provider);
}
CursorShape FilmNegative::getCursor(int objectID) const
{
return CSSpotWB;
}
bool FilmNegative::mouseOver(int modifierKey)
{
EditDataProvider *provider = getEditProvider();
const auto spotRect = static_cast<Rectangle*>(visibleGeometry.at(0));
EditDataProvider* const provider = getEditProvider();
Rectangle* const spotRect = static_cast<Rectangle*>(visibleGeometry.at(0));
spotRect->setXYWH(provider->posImage.x - 16, provider->posImage.y - 16, 32, 32);
return true;
@ -264,20 +257,18 @@ bool FilmNegative::mouseOver(int modifierKey)
bool FilmNegative::button1Pressed(int modifierKey)
{
EditDataProvider *provider = getEditProvider();
EditDataProvider* const provider = getEditProvider();
if(provider) { // debug. remove me
if (provider) { // TODO: Remove me (rom9)
printf("x=%d y=%d pv1=%f pv2=%f pv3=%f\n", provider->posImage.x, provider->posImage.y, provider->getPipetteVal1(), provider->getPipetteVal2(), provider->getPipetteVal3());
}
EditSubscriber::action = EditSubscriber::Action::NONE;
if (listener) {
refSpotCoords.push_back(provider->posImage);
if(refSpotCoords.size() == 2) {
if (refSpotCoords.size() == 2) {
// User has selected 2 reference gray spots. Calculating new exponents
// from channel values and updating parameters.
@ -292,8 +283,15 @@ bool FilmNegative::button1Pressed(int modifierKey)
enableListener();
if (listener && getEnabled()) {
listener->panelChanged (EvFilmNegativeExponents, Glib::ustring::compose (
"R=%1 ; G=%2 ; B=%3", redExp->getTextValue(), greenExp->getTextValue(), blueExp->getTextValue()));
listener->panelChanged(
evFilmNegativeExponents,
Glib::ustring::compose(
"R=%1 ; G=%2 ; B=%3",
redExp->getTextValue(),
greenExp->getTextValue(),
blueExp->getTextValue()
)
);
}
}
@ -304,35 +302,50 @@ bool FilmNegative::button1Pressed(int modifierKey)
return true;
}
bool FilmNegative::button1Released ()
bool FilmNegative::button1Released()
{
EditDataProvider *provider = getEditProvider();
EditDataProvider* const provider = getEditProvider();
if(provider) { // debug. remove me
if (provider) { // TODO: Remove me (rom9)
printf("x=%d y=%d pv1=%f pv2=%f pv3=%f\n", provider->posImage.x, provider->posImage.y, provider->getPipetteVal1(), provider->getPipetteVal2(), provider->getPipetteVal3());
}
EditSubscriber::action = EditSubscriber::Action::NONE;
return true;
}
// TODO remove me ; couldn't make Action::PICKING work
bool FilmNegative::pick1 (bool picked) {
EditDataProvider *provider = getEditProvider();
if(provider) { // debug. remove me
printf("Picked pick=%d x=%d y=%d pv1=%f pv2=%f pv3=%f\n", picked, provider->posImage.x, provider->posImage.y, provider->getPipetteVal1(), provider->getPipetteVal2(), provider->getPipetteVal3());
}
return true;
}
CursorShape FilmNegative::getCursor(int objectID) const
{
return CSSpotWB;
}
void FilmNegative::switchOffEditMode ()
void FilmNegative::switchOffEditMode()
{
refSpotCoords.clear();
unsubscribe();
spotbutton->set_active(false);
}
// TODO: Remove me ; couldn't make Action::PICKING work (rom9)
bool FilmNegative::pick1(bool picked)
{
EditDataProvider* const provider = getEditProvider();
if (provider) { // TODO: Remove me (rom9)
printf("Picked pick=%d x=%d y=%d pv1=%f pv2=%f pv3=%f\n", picked, provider->posImage.x, provider->posImage.y, provider->getPipetteVal1(), provider->getPipetteVal2(), provider->getPipetteVal3());
}
return true;
}
void FilmNegative::editToggled()
{
if (spotbutton->get_active()) {
subscribe();
int w, h;
getEditProvider()->getImageSize(w, h);
// Stick a dummy rectangle over the whole image in mouseOverGeometry.
// This is to make sure the getCursor() call is fired everywhere.
Rectangle* const imgRect = static_cast<Rectangle*>(mouseOverGeometry.at(0));
imgRect->setXYWH(0, 0, w, h);
} else {
refSpotCoords.clear();
unsubscribe();
}
}

View File

@ -1,7 +1,7 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
* Copyright (c) 2019 rom9
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -28,66 +28,66 @@
#include "toolpanel.h"
#include "wbprovider.h"
#include "../rtengine/procparams.h"
#include "../rtengine/noncopyable.h"
class FilmNegProvider
{
public:
virtual ~FilmNegProvider() = default;
virtual bool getFilmNegativeExponents(rtengine::Coord spotA, rtengine::Coord spotB, std::array<float, 3>& newExps) = 0;
};
class FilmNegative : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public EditSubscriber
class FilmNegative :
public rtengine::NonCopyable,
public ToolParamBlock,
public AdjusterListener,
public FoldableToolPanel,
public EditSubscriber
{
private:
rtengine::ProcEvent EvFilmNegativeExponents;
rtengine::ProcEvent EvFilmNegativeEnabled;
std::vector<rtengine::Coord> refSpotCoords;
FilmNegProvider *fnp;
Adjuster* redExp;
Adjuster* greenExp;
Adjuster* blueExp;
Gtk::Grid* spotgrid;
Gtk::ToggleButton* spotbutton;
sigc::connection spotConn;
double redRatio, blueRatio;
void editToggled ();
public:
FilmNegative();
~FilmNegative() override;
FilmNegative ();
~FilmNegative () override;
void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override;
void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override;
void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override;
void setBatchMode(bool batchMode) override;
void setFilmNegProvider(FilmNegProvider* p)
{
fnp = p;
};
void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override;
void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override;
void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override;
void setBatchMode (bool batchMode) override;
void adjusterChanged (Adjuster* a, double newval) override;
void adjusterChanged(Adjuster* a, double newval) override;
void adjusterAutoToggled(Adjuster* a, bool newval) override;
void spotPressed ();
void enabledChanged() override;
void setEditProvider (EditDataProvider* provider) override;
void setFilmNegProvider(FilmNegProvider* p);
void setEditProvider(EditDataProvider* provider) override;
// EditSubscriber interface
CursorShape getCursor(int objectID) const override;
bool mouseOver(int modifierKey) override;
bool button1Pressed(int modifierKey) override;
bool button1Released() override;
void switchOffEditMode () override;
void switchOffEditMode() override;
bool pick1(bool picked) override;
private:
void editToggled();
const rtengine::ProcEvent evFilmNegativeExponents;
const rtengine::ProcEvent evFilmNegativeEnabled;
std::vector<rtengine::Coord> refSpotCoords;
FilmNegProvider* fnp;
Adjuster* const redExp;
Adjuster* const greenExp;
Adjuster* const blueExp;
Gtk::Grid* const spotgrid;
Gtk::ToggleButton* const spotbutton;
sigc::connection spotConn;
double redRatio;
double blueRatio;
};