diff --git a/rtdata/languages/default b/rtdata/languages/default index 963659884..cc978ce92 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -728,7 +728,8 @@ HISTORY_MSG_492;RGB Curves HISTORY_MSG_493;L*a*b* Adjustments HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction -HISTORY_MSG_DUALDEMOSAIC_CONTRAST;AMaZE+VNG4 - Contrast threshold +HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dual demosaic - Contrast threshold +HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dual demosaic - Auto threshold HISTORY_MSG_HISTMATCHING;Auto-matched tone curve HISTORY_MSG_ICM_OUTPUT_PRIMARIES;Output - Primaries HISTORY_MSG_ICM_OUTPUT_TEMP;Output - ICC-v4 illuminant D @@ -1816,6 +1817,8 @@ TP_RAW_DMETHOD;Method TP_RAW_DMETHOD_PROGRESSBAR;%1 demosaicing... TP_RAW_DMETHOD_PROGRESSBAR_REFINE;Demosaicing refinement... TP_RAW_DMETHOD_TOOLTIP;Note: IGV and LMMSE are dedicated to high ISO images to aid in noise reduction without leading to maze patterns, posterization or a washed-out look.\nPixel Shift is for Pentax/Sony Pixel Shift files. It falls back to AMaZE for non-Pixel Shift files. +TP_RAW_DUALDEMOSAICAUTOCONTRAST;Auto threshold +TP_RAW_DUALDEMOSAICAUTOCONTRAST_TOOLTIP;If the check-box is checked (recommended), RawTherapee calculates an optimum value based on flat regions in the image.\nIf there is no flat region in the image or the image is too noisy, the value will be set to 0.\nTo set the value manually, uncheck the check-box first (reasonable values depend on the image). TP_RAW_DUALDEMOSAICCONTRAST;Contrast threshold TP_RAW_EAHD;EAHD TP_RAW_FALSECOLOR;False color suppression steps diff --git a/rtengine/demosaic_algos.cc b/rtengine/demosaic_algos.cc index c0d0c2a2b..10ef0dd0e 100644 --- a/rtengine/demosaic_algos.cc +++ b/rtengine/demosaic_algos.cc @@ -37,7 +37,7 @@ #include "sleef.c" #include "opthelper.h" #include "median.h" -#define BENCHMARK +//#define BENCHMARK #include "StopWatch.h" #ifdef _OPENMP #include diff --git a/rtengine/dual_demosaic_RT.cc b/rtengine/dual_demosaic_RT.cc index 6e92fd1e2..4ffac9296 100644 --- a/rtengine/dual_demosaic_RT.cc +++ b/rtengine/dual_demosaic_RT.cc @@ -36,11 +36,11 @@ using namespace std; namespace rtengine { -void RawImageSource::dual_demosaic_RT(bool isBayer, const RAWParams &raw, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, double &contrast, bool autoContrast, int autoX, int autoY) +void RawImageSource::dual_demosaic_RT(bool isBayer, const RAWParams &raw, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, double &contrast, bool autoContrast) { BENCHFUN - if (contrast == 0.0 && !autoContrast) { + if (contrast == 0.f && !autoContrast) { // contrast == 0.0 means only first demosaicer will be used if(isBayer) { if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZEVNG4) ) { @@ -91,24 +91,6 @@ void RawImageSource::dual_demosaic_RT(bool isBayer, const RAWParams &raw, int wi { 0.019334, 0.119193, 0.950227 } }; - if (autoContrast && autoX >= 0 && autoY >= 0) { - constexpr int rectSize = 40; - const int autoWidth = min(rectSize, winw - autoX); - const int autoHeight = min(rectSize, winh - autoY); - if (std::min(autoWidth, autoHeight) > 20) { - array2D autoL(autoWidth, autoHeight); - for(int i = 0; i < autoHeight; ++i) { - Color::RGB2L(red[i + autoY] + autoX, green[i + autoY] + autoX, blue[i + autoY] + autoX, autoL[i], xyz_rgb, autoWidth); - } - // calculate contrast based blend factors to use vng4 in regions with low contrast - JaggedArray blend(autoWidth - 2, autoHeight - 2); - int c = calcContrastThreshold(autoL, blend, autoWidth, autoHeight); - if(c < 100) { - contrast = c; // alternative : contrast = c - 1 - } - } - } - #pragma omp parallel { #pragma omp for @@ -118,7 +100,10 @@ void RawImageSource::dual_demosaic_RT(bool isBayer, const RAWParams &raw, int wi } // calculate contrast based blend factors to use vng4 in regions with low contrast JaggedArray blend(winw, winh); - buildBlendMask(L, blend, winw, winh, contrast / 100.f); + float contrastf = contrast / 100.f; + + buildBlendMask(L, blend, winw, winh, contrastf, 1.f, autoContrast); + contrast = contrastf * 100.f; // the following is split into 3 loops intentionally to avoid cache conflicts on CPUs with only 4-way cache #pragma omp parallel for diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 8532a2382..78c1a6a09 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -93,7 +93,7 @@ ImProcCoordinator::ImProcCoordinator() fw(0), fh(0), tr(0), fullw(1), fullh(1), pW(-1), pH(-1), - plistener(nullptr), imageListener(nullptr), aeListener(nullptr), acListener(nullptr), abwListener(nullptr), awbListener(nullptr), frameCountListener(nullptr), imageTypeListener(nullptr), actListener(nullptr), adnListener(nullptr), awavListener(nullptr), dehaListener(nullptr), hListener(nullptr), + plistener(nullptr), imageListener(nullptr), aeListener(nullptr), acListener(nullptr), abwListener(nullptr), awbListener(nullptr), bayerAutoContrastListener(nullptr), xtransAutoContrastListener(nullptr), frameCountListener(nullptr), imageTypeListener(nullptr), actListener(nullptr), adnListener(nullptr), awavListener(nullptr), dehaListener(nullptr), hListener(nullptr), resultValid(false), lastOutputProfile("BADFOOD"), lastOutputIntent(RI__COUNT), lastOutputBPC(false), thread(nullptr), changeSinceLast(0), updaterRunning(false), destroying(false), utili(false), autili(false), butili(false), ccutili(false), cclutili(false), clcutili(false), opautili(false), wavcontlutili(false), colourToningSatLimit(0.f), colourToningSatLimitOpacity(0.f), highQualityComputed(false) {} @@ -239,10 +239,17 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) imgsrc->setBorder(std::max(params.raw.bayersensor.border, 2)); } } - bool autoContrast = false; - double contrastThreshold = 0.f; + bool autoContrast = imgsrc->getSensorType() == ST_BAYER ? params.raw.bayersensor.dualDemosaicAutoContrast : params.raw.xtranssensor.dualDemosaicAutoContrast; + double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params.raw.bayersensor.dualDemosaicContrast : params.raw.xtranssensor.dualDemosaicContrast; imgsrc->demosaic(rp, autoContrast, contrastThreshold); //enabled demosaic + if (imgsrc->getSensorType() == ST_BAYER && bayerAutoContrastListener && autoContrast) { + bayerAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0); + } + if (imgsrc->getSensorType() == ST_FUJI_XTRANS && xtransAutoContrastListener && autoContrast) { + xtransAutoContrastListener->autoContrastChanged(autoContrast ? contrastThreshold : -1.0); + } + // if a demosaic happened we should also call getimage later, so we need to set the M_INIT flag todo |= M_INIT; diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index dba12cdd7..1c87ff4cd 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -158,6 +158,8 @@ protected: AutoCamListener* acListener; AutoBWListener* abwListener; AutoWBListener* awbListener; + AutoContrastListener *bayerAutoContrastListener; + AutoContrastListener *xtransAutoContrastListener; FrameCountListener *frameCountListener; ImageTypeListener *imageTypeListener; @@ -344,6 +346,16 @@ public: frameCountListener = fcl; } + void setBayerAutoContrastListener (AutoContrastListener* acl) + { + bayerAutoContrastListener = acl; + } + + void setXtransAutoContrastListener (AutoContrastListener* acl) + { + xtransAutoContrastListener = acl; + } + void setImageTypeListener (ImageTypeListener* itl) { imageTypeListener = itl; diff --git a/rtengine/ipsharpen.cc b/rtengine/ipsharpen.cc index eaf0db732..321dd635f 100644 --- a/rtengine/ipsharpen.cc +++ b/rtengine/ipsharpen.cc @@ -176,7 +176,8 @@ BENCHFUN // calculate contrast based blend factors to reduce sharpening in regions with low contrast JaggedArray blend(W, H); - buildBlendMask(luminance, blend, W, H, sharpenParam.contrast / 100.f, sharpenParam.deconvamount / 100.0); + float contrast = sharpenParam.contrast / 100.f; + buildBlendMask(luminance, blend, W, H, contrast, sharpenParam.deconvamount / 100.f); const float damping = sharpenParam.deconvdamping / 5.0; const bool needdamp = sharpenParam.deconvdamping > 0; @@ -222,7 +223,8 @@ void ImProcFunctions::sharpening (LabImage* lab, const SharpeningParams &sharpen if(showMask) { // calculate contrast based blend factors to reduce sharpening in regions with low contrast JaggedArray blend(W, H); - buildBlendMask(lab->L, blend, W, H, sharpenParam.contrast / 100.f, sharpenParam.method == "rld" ? sharpenParam.deconvamount / 100.0 : 1.f); + float contrast = sharpenParam.contrast / 100.f; + buildBlendMask(lab->L, blend, W, H, contrast, sharpenParam.method == "rld" ? sharpenParam.deconvamount / 100.f : 1.f); #ifdef _OPENMP #pragma omp parallel for #endif @@ -256,7 +258,8 @@ BENCHFUN // calculate contrast based blend factors to reduce sharpening in regions with low contrast JaggedArray blend(W, H); - buildBlendMask(lab->L, blend, W, H, sharpenParam.contrast / 100.f); + float contrast = sharpenParam.contrast / 100.f; + buildBlendMask(lab->L, blend, W, H, contrast); #ifdef _OPENMP #pragma omp parallel @@ -610,7 +613,8 @@ BENCHFUN // calculate contrast based blend factors to reduce sharpening in regions with low contrast JaggedArray blend(W, H); - buildBlendMask(luminance, blend, W, H, params->sharpenMicro.contrast / 100.f); + float contrast = params->sharpenMicro.contrast / 100.f; + buildBlendMask(luminance, blend, W, H, contrast); #ifdef _OPENMP #pragma omp parallel @@ -819,7 +823,8 @@ void ImProcFunctions::sharpeningcam (CieImage* ncie, float** b2, bool showMask) if(showMask) { // calculate contrast based blend factors to reduce sharpening in regions with low contrast JaggedArray blend(W, H); - buildBlendMask(ncie->sh_p, blend, W, H, params->sharpening.contrast / 100.f); + float contrast = params->sharpening.contrast / 100.f; + buildBlendMask(ncie->sh_p, blend, W, H, contrast); #ifdef _OPENMP #pragma omp parallel for #endif @@ -852,7 +857,8 @@ void ImProcFunctions::sharpeningcam (CieImage* ncie, float** b2, bool showMask) // calculate contrast based blend factors to reduce sharpening in regions with low contrast JaggedArray blend(W, H); - buildBlendMask(ncie->sh_p, blend, W, H, params->sharpening.contrast / 100.f); + float contrast = params->sharpening.contrast / 100.f; + buildBlendMask(ncie->sh_p, blend, W, H, contrast); #ifdef _OPENMP #pragma omp parallel diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 66293abbb..526a36765 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2390,6 +2390,7 @@ RAWParams::BayerSensor::BayerSensor() : greenthresh(0), dcb_iterations(2), lmmse_iterations(2), + dualDemosaicAutoContrast(true), dualDemosaicContrast(20), pixelShiftMotionCorrectionMethod(PSMotionCorrectionMethod::AUTO), pixelShiftEperIso(0.0), @@ -2427,6 +2428,7 @@ bool RAWParams::BayerSensor::operator ==(const BayerSensor& other) const && greenthresh == other.greenthresh && dcb_iterations == other.dcb_iterations && lmmse_iterations == other.lmmse_iterations + && dualDemosaicAutoContrast == other.dualDemosaicAutoContrast && dualDemosaicContrast == other.dualDemosaicContrast && pixelShiftMotionCorrectionMethod == other.pixelShiftMotionCorrectionMethod && pixelShiftEperIso == other.pixelShiftEperIso @@ -2514,6 +2516,7 @@ Glib::ustring RAWParams::BayerSensor::getPSDemosaicMethodString(PSDemosaicMethod RAWParams::XTransSensor::XTransSensor() : method(getMethodString(Method::THREE_PASS)), + dualDemosaicAutoContrast(true), dualDemosaicContrast(20), ccSteps(0), blackred(0.0), @@ -2526,6 +2529,7 @@ bool RAWParams::XTransSensor::operator ==(const XTransSensor& other) const { return method == other.method + && dualDemosaicAutoContrast == other.dualDemosaicAutoContrast && dualDemosaicContrast == other.dualDemosaicContrast && ccSteps == other.ccSteps && blackred == other.blackred @@ -3412,6 +3416,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->raw.bayersensor.dcbIterations, "RAW Bayer", "DCBIterations", raw.bayersensor.dcb_iterations, keyFile); saveToKeyfile(!pedited || pedited->raw.bayersensor.dcbEnhance, "RAW Bayer", "DCBEnhance", raw.bayersensor.dcb_enhance, keyFile); saveToKeyfile(!pedited || pedited->raw.bayersensor.lmmseIterations, "RAW Bayer", "LMMSEIterations", raw.bayersensor.lmmse_iterations, keyFile); + saveToKeyfile(!pedited || pedited->raw.bayersensor.dualDemosaicAutoContrast, "RAW Bayer", "DualDemosaicAutoContrast", raw.bayersensor.dualDemosaicAutoContrast, keyFile); saveToKeyfile(!pedited || pedited->raw.bayersensor.dualDemosaicContrast, "RAW Bayer", "DualDemosaicContrast", raw.bayersensor.dualDemosaicContrast, keyFile); saveToKeyfile(!pedited || pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod, "RAW Bayer", "PixelShiftMotionCorrectionMethod", toUnderlying(raw.bayersensor.pixelShiftMotionCorrectionMethod), keyFile); saveToKeyfile(!pedited || pedited->raw.bayersensor.pixelShiftEperIso, "RAW Bayer", "PixelShiftEperIso", raw.bayersensor.pixelShiftEperIso, keyFile); @@ -3429,6 +3434,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->raw.bayersensor.pixelShiftDemosaicMethod, "RAW Bayer", "pixelShiftDemosaicMethod", raw.bayersensor.pixelShiftDemosaicMethod, keyFile); saveToKeyfile(!pedited || pedited->raw.bayersensor.pdafLinesFilter, "RAW Bayer", "PDAFLinesFilter", raw.bayersensor.pdafLinesFilter, keyFile); saveToKeyfile(!pedited || pedited->raw.xtranssensor.method, "RAW X-Trans", "Method", raw.xtranssensor.method, keyFile); + saveToKeyfile(!pedited || pedited->raw.xtranssensor.dualDemosaicAutoContrast, "RAW X-Trans", "DualDemosaicAutoContrast", raw.xtranssensor.dualDemosaicAutoContrast, keyFile); saveToKeyfile(!pedited || pedited->raw.xtranssensor.dualDemosaicContrast, "RAW X-Trans", "DualDemosaicContrast", raw.xtranssensor.dualDemosaicContrast, keyFile); saveToKeyfile(!pedited || pedited->raw.xtranssensor.ccSteps, "RAW X-Trans", "CcSteps", raw.xtranssensor.ccSteps, keyFile); saveToKeyfile(!pedited || pedited->raw.xtranssensor.exBlackRed, "RAW X-Trans", "PreBlackRed", raw.xtranssensor.blackred, keyFile); @@ -4844,6 +4850,13 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "RAW Bayer", "DCBIterations", pedited, raw.bayersensor.dcb_iterations, pedited->raw.bayersensor.dcbIterations); assignFromKeyfile(keyFile, "RAW Bayer", "DCBEnhance", pedited, raw.bayersensor.dcb_enhance, pedited->raw.bayersensor.dcbEnhance); assignFromKeyfile(keyFile, "RAW Bayer", "LMMSEIterations", pedited, raw.bayersensor.lmmse_iterations, pedited->raw.bayersensor.lmmseIterations); + assignFromKeyfile(keyFile, "RAW Bayer", "DualDemosaicAutoContrast", pedited, raw.bayersensor.dualDemosaicAutoContrast, pedited->raw.bayersensor.dualDemosaicAutoContrast); + if (ppVersion < 345) { + raw.bayersensor.dualDemosaicAutoContrast = false; + if (pedited) { + pedited->raw.bayersensor.dualDemosaicAutoContrast = true; + } + } assignFromKeyfile(keyFile, "RAW Bayer", "DualDemosaicContrast", pedited, raw.bayersensor.dualDemosaicContrast, pedited->raw.bayersensor.dualDemosaicContrast); if (keyFile.has_key("RAW Bayer", "PixelShiftMotionCorrectionMethod")) { @@ -4891,6 +4904,13 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) if (keyFile.has_group("RAW X-Trans")) { assignFromKeyfile(keyFile, "RAW X-Trans", "Method", pedited, raw.xtranssensor.method, pedited->raw.xtranssensor.method); + assignFromKeyfile(keyFile, "RAW X-Trans", "DualDemosaicAutoContrast", pedited, raw.xtranssensor.dualDemosaicAutoContrast, pedited->raw.xtranssensor.dualDemosaicAutoContrast); + if (ppVersion < 345) { + raw.xtranssensor.dualDemosaicAutoContrast = false; + if (pedited) { + pedited->raw.xtranssensor.dualDemosaicAutoContrast = true; + } + } assignFromKeyfile(keyFile, "RAW X-Trans", "DualDemosaicContrast", pedited, raw.xtranssensor.dualDemosaicContrast, pedited->raw.xtranssensor.dualDemosaicContrast); assignFromKeyfile(keyFile, "RAW X-Trans", "CcSteps", pedited, raw.xtranssensor.ccSteps, pedited->raw.xtranssensor.ccSteps); assignFromKeyfile(keyFile, "RAW X-Trans", "PreBlackRed", pedited, raw.xtranssensor.blackred, pedited->raw.xtranssensor.exBlackRed); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 700c6271c..22868a844 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1286,6 +1286,7 @@ struct RAWParams { int greenthresh; int dcb_iterations; int lmmse_iterations; + bool dualDemosaicAutoContrast; double dualDemosaicContrast; PSMotionCorrectionMethod pixelShiftMotionCorrectionMethod; double pixelShiftEperIso; @@ -1333,6 +1334,7 @@ struct RAWParams { }; Glib::ustring method; + bool dualDemosaicAutoContrast; double dualDemosaicContrast; int ccSteps; double blackred; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 1f84cad54..3de802232 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2074,7 +2074,7 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c double threshold = raw.bayersensor.dualDemosaicContrast; dual_demosaic_RT (true, raw, W, H, rawData, red, green, blue, threshold, false); } else { - dual_demosaic_RT (true, raw, W, H, rawData, red, green, blue, contrastThreshold, true, 0, 0); + dual_demosaic_RT (true, raw, W, H, rawData, red, green, blue, contrastThreshold, true); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::PIXELSHIFT) ) { pixelshift(0, 0, W, H, raw, currFrame, ri->get_maker(), ri->get_model(), raw.expos); @@ -2107,7 +2107,7 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c double threshold = raw.xtranssensor.dualDemosaicContrast; dual_demosaic_RT (false, raw, W, H, rawData, red, green, blue, threshold, false); } else { - dual_demosaic_RT (false, raw, W, H, rawData, red, green, blue, contrastThreshold, true, 0, 0); + dual_demosaic_RT (false, raw, W, H, rawData, red, green, blue, contrastThreshold, true); } } else if(raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::MONO) ) { nodemosaic(true); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index ad7807a44..1e2a1984d 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -274,7 +274,7 @@ protected: void igv_interpolate(int winw, int winh); void lmmse_interpolate_omp(int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue, int iterations); void amaze_demosaic_RT(int winx, int winy, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue);//Emil's code for AMaZE - void dual_demosaic_RT(bool isBayer, const RAWParams &raw, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, double &contrast, bool autoContrast = false, int autoX = -1, int autoY = -1); + void dual_demosaic_RT(bool isBayer, const RAWParams &raw, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, double &contrast, bool autoContrast = false); void fast_demosaic();//Emil's code for fast demosaicing void dcb_demosaic(int iterations, bool dcb_enhance); void ahd_demosaic(); diff --git a/rtengine/rt_algo.cc b/rtengine/rt_algo.cc index c56a72a2c..ae385508a 100644 --- a/rtengine/rt_algo.cc +++ b/rtengine/rt_algo.cc @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef _OPENMP #include #endif @@ -32,6 +33,7 @@ #include "rt_algo.h" #include "rt_math.h" #include "sleef.c" +#include "jaggedarray.h" namespace { float calcBlendFactor(float val, float threshold) { @@ -190,75 +192,166 @@ void findMinMaxPercentile(const float* data, size_t size, float minPrct, float& maxOut += minVal; } -void buildBlendMask(float** luminance, float **blend, int W, int H, float contrastThreshold, float amount) { +void buildBlendMask(float** luminance, float **blend, int W, int H, float &contrastThreshold, float amount, bool autoContrast) { - constexpr float scale = 0.0625f / 327.68f; - - if(contrastThreshold == 0.f) { + if(contrastThreshold == 0.f && !autoContrast) { for(int j = 0; j < H; ++j) { for(int i = 0; i < W; ++i) { blend[j][i] = amount; } } } else { -#ifdef _OPENMP - #pragma omp parallel -#endif - { -#ifdef __SSE2__ - const vfloat contrastThresholdv = F2V(contrastThreshold); - const vfloat scalev = F2V(scale); - const vfloat amountv = F2V(amount); -#endif -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) -#endif + constexpr float scale = 0.0625f / 327.68f; + if (autoContrast) { + for (int pass = 0; pass < 2; ++pass) { + const int tilesize = 80 / (pass + 1); + const int numTilesW = W / tilesize; + const int numTilesH = H / tilesize; + std::vector>> variances(numTilesH, std::vector>(numTilesW)); - for(int j = 2; j < H - 2; ++j) { - int i = 2; + #pragma omp parallel for + for (int i = 0; i < numTilesH; ++i) { + int tileY = i * tilesize; + for (int j = 0; j < numTilesW; ++j) { + int tileX = j * tilesize; #ifdef __SSE2__ - for(; i < W - 5; i += 4) { - vfloat contrastv = vsqrtf(SQRV(LVFU(luminance[j][i+1]) - LVFU(luminance[j][i-1])) + SQRV(LVFU(luminance[j+1][i]) - LVFU(luminance[j-1][i])) + - SQRV(LVFU(luminance[j][i+2]) - LVFU(luminance[j][i-2])) + SQRV(LVFU(luminance[j+2][i]) - LVFU(luminance[j-2][i]))) * scalev; - - STVFU(blend[j][i], amountv * calcBlendFactor(contrastv, contrastThresholdv)); + vfloat avgv = ZEROV; + for (int y = tileY; y < tileY + tilesize; ++y) { + for (int x = tileX; x < tileX + tilesize; x += 4) { + avgv += LVFU(luminance[y][x]); + } + } + float avg = vhadd(avgv); +#else + float avg = 0.; + for (int y = tileY; y < tileY + tilesize; ++y) { + for (int x = tileX; x < tileX + tilesize; ++x) { + avg += luminance[y][x]; + } + } +#endif + avg /= SQR(tilesize); +#ifdef __SSE2__ + vfloat varv = ZEROV; + avgv = F2V(avg); + for (int y = tileY; y < tileY + tilesize; ++y) { + for (int x = tileX; x < tileX + tilesize; x +=4) { + varv += SQRV(LVFU(luminance[y][x]) - avgv); + } + } + float var = vhadd(varv); +#else + float var = 0.0; + for (int y = tileY; y < tileY + tilesize; ++y) { + for (int x = tileX; x < tileX + tilesize; ++x) { + var += SQR(luminance[y][x] - avg); + } + } + #endif + var /= (SQR(tilesize) * avg); + variances[i][j].first = var; + variances[i][j].second = avg; + } } -#endif - for(; i < W - 2; ++i) { - float contrast = sqrtf(rtengine::SQR(luminance[j][i+1] - luminance[j][i-1]) + rtengine::SQR(luminance[j+1][i] - luminance[j-1][i]) + - rtengine::SQR(luminance[j][i+2] - luminance[j][i-2]) + rtengine::SQR(luminance[j+2][i] - luminance[j-2][i])) * scale; + float minvar = RT_INFINITY_F; + int minI = 0, minJ = 0; + for (int i = 0; i < numTilesH; ++i) { + for (int j = 0; j < numTilesW; ++j) { + if (variances[i][j].first < minvar && variances[i][j].second > 2000.f && variances[i][j].second < 20000.f) { + minvar = variances[i][j].first; + minI = i; + minJ = j; + } + } + } - blend[j][i] = amount * calcBlendFactor(contrast, contrastThreshold); + const int minY = tilesize * minI; + const int minX = tilesize * minJ; + +// std::cout << pass << ": minvar : " << minvar << std::endl; + if (minvar <= 1.f || pass == 1) { + // a variance <= 1 means we already found a flat region and can skip second pass + // in second pass we allow a variance of 2 + JaggedArray Lum(tilesize, tilesize); + JaggedArray Blend(tilesize, tilesize); + for (int i = 0; i < tilesize; ++i) { + for (int j = 0; j < tilesize; ++j) { + Lum[i][j] = luminance[i + minY][j + minX]; + } + } + contrastThreshold = (pass == 0 || minvar <= 2.f) ? calcContrastThreshold(Lum, Blend, tilesize, tilesize) / 100.f : 0.f; + break; } } + } + if(contrastThreshold == 0.f) { + for(int j = 0; j < H; ++j) { + for(int i = 0; i < W; ++i) { + blend[j][i] = amount; + } + } + } else { #ifdef _OPENMP - #pragma omp single + #pragma omp parallel #endif { - // upper border - for(int j = 0; j < 2; ++j) { - for(int i = 2; i < W - 2; ++i) { - blend[j][i] = blend[2][i]; - } - } - // lower border - for(int j = H - 2; j < H; ++j) { - for(int i = 2; i < W - 2; ++i) { - blend[j][i] = blend[H-3][i]; - } - } - for(int j = 0; j < H; ++j) { - // left border - blend[j][0] = blend[j][1] = blend[j][2]; - // right border - blend[j][W - 2] = blend[j][W - 1] = blend[j][W - 3]; - } - } +#ifdef __SSE2__ + const vfloat contrastThresholdv = F2V(contrastThreshold); + const vfloat scalev = F2V(scale); + const vfloat amountv = F2V(amount); +#endif +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif - // blur blend mask to smooth transitions - gaussianBlur(blend, blend, W, H, 2.0); + for(int j = 2; j < H - 2; ++j) { + int i = 2; +#ifdef __SSE2__ + for(; i < W - 5; i += 4) { + vfloat contrastv = vsqrtf(SQRV(LVFU(luminance[j][i+1]) - LVFU(luminance[j][i-1])) + SQRV(LVFU(luminance[j+1][i]) - LVFU(luminance[j-1][i])) + + SQRV(LVFU(luminance[j][i+2]) - LVFU(luminance[j][i-2])) + SQRV(LVFU(luminance[j+2][i]) - LVFU(luminance[j-2][i]))) * scalev; + + STVFU(blend[j][i], amountv * calcBlendFactor(contrastv, contrastThresholdv)); + } +#endif + for(; i < W - 2; ++i) { + + float contrast = sqrtf(rtengine::SQR(luminance[j][i+1] - luminance[j][i-1]) + rtengine::SQR(luminance[j+1][i] - luminance[j-1][i]) + + rtengine::SQR(luminance[j][i+2] - luminance[j][i-2]) + rtengine::SQR(luminance[j+2][i] - luminance[j-2][i])) * scale; + + blend[j][i] = amount * calcBlendFactor(contrast, contrastThreshold); + } + } + +#ifdef _OPENMP + #pragma omp single +#endif + { + // upper border + for(int j = 0; j < 2; ++j) { + for(int i = 2; i < W - 2; ++i) { + blend[j][i] = blend[2][i]; + } + } + // lower border + for(int j = H - 2; j < H; ++j) { + for(int i = 2; i < W - 2; ++i) { + blend[j][i] = blend[H-3][i]; + } + } + for(int j = 0; j < H; ++j) { + // left border + blend[j][0] = blend[j][1] = blend[j][2]; + // right border + blend[j][W - 2] = blend[j][W - 1] = blend[j][W - 3]; + } + } + + // blur blend mask to smooth transitions + gaussianBlur(blend, blend, W, H, 2.0); + } } } } @@ -289,7 +382,7 @@ int calcContrastThreshold(float** luminance, float **blend, int W, int H) { } } - const float limit = (W - 2) * (H - 2) / 100.f; + const float limit = (W - 4) * (H - 4) / 100.f; int c; for (c = 1; c < 100; ++c) { @@ -318,6 +411,7 @@ int calcContrastThreshold(float** luminance, float **blend, int W, int H) { break; } } + return c; } } diff --git a/rtengine/rt_algo.h b/rtengine/rt_algo.h index e20713b8f..0207e6f57 100644 --- a/rtengine/rt_algo.h +++ b/rtengine/rt_algo.h @@ -24,6 +24,6 @@ namespace rtengine { void findMinMaxPercentile(const float* data, size_t size, float minPrct, float& minOut, float maxPrct, float& maxOut, bool multiThread = true); -void buildBlendMask(float** luminance, float **blend, int W, int H, float contrastThreshold, float amount = 1.f); +void buildBlendMask(float** luminance, float **blend, int W, int H, float &contrastThreshold, float amount = 1.f, bool autoContrast = false); int calcContrastThreshold(float** luminance, float **blend, int W, int H); } diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index 7fc71b5b5..ecf273337 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -378,6 +378,13 @@ public: virtual void imageTypeChanged(bool isRaw, bool isBayer, bool isXtrans, bool is_Mono = false) = 0; }; +class AutoContrastListener +{ +public : + virtual ~AutoContrastListener() = default; + virtual void autoContrastChanged (double autoContrast) = 0; +}; + class WaveletListener { public: @@ -482,6 +489,8 @@ public: virtual void setPreviewImageListener (PreviewImageListener* l) = 0; virtual void setAutoCamListener (AutoCamListener* l) = 0; virtual void setFrameCountListener (FrameCountListener* l) = 0; + virtual void setBayerAutoContrastListener (AutoContrastListener* l) = 0; + virtual void setXtransAutoContrastListener (AutoContrastListener* l) = 0; virtual void setAutoBWListener (AutoBWListener* l) = 0; virtual void setAutoWBListener (AutoWBListener* l) = 0; virtual void setAutoColorTonListener (AutoColorTonListener* l) = 0; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index fbee20f44..06e2b13bf 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -209,8 +209,10 @@ private: if (pl) { pl->setProgress (0.20); } - double contrastThresholdDummy; - imgsrc->demosaic (params.raw, false, contrastThresholdDummy); + bool autoContrast = imgsrc->getSensorType() == ST_BAYER ? params.raw.bayersensor.dualDemosaicAutoContrast : params.raw.xtranssensor.dualDemosaicAutoContrast; + double contrastThreshold = imgsrc->getSensorType() == ST_BAYER ? params.raw.bayersensor.dualDemosaicContrast : params.raw.xtranssensor.dualDemosaicContrast; + + imgsrc->demosaic (params.raw, autoContrast, contrastThreshold); if (pl) { diff --git a/rtgui/adjuster.cc b/rtgui/adjuster.cc index 3f12b0cfb..46aa3952b 100644 --- a/rtgui/adjuster.cc +++ b/rtgui/adjuster.cc @@ -239,10 +239,12 @@ void Adjuster::autoToggled () // Disable the slider and spin button spin->set_sensitive(false); slider->set_sensitive(false); + reset->set_sensitive(false); } else { // Enable the slider and spin button spin->set_sensitive(true); slider->set_sensitive(true); + reset->set_sensitive(true); } } @@ -471,10 +473,12 @@ void Adjuster::setAutoValue (bool a) // Disable the slider and spin button spin->set_sensitive(false); slider->set_sensitive(false); + reset->set_sensitive(false); } else { // Enable the slider and spin button spin->set_sensitive(true); slider->set_sensitive(true); + reset->set_sensitive(true); } } } diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 758b1b795..2a65a158e 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -30,6 +30,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA auto m = ProcEventMapper::getInstance(); EvDemosaicBorder = m->newEvent(DEMOSAIC, "HISTORY_MSG_RAW_BORDER"); EvDemosaicContrast = m->newEvent(DEMOSAIC, "HISTORY_MSG_DUALDEMOSAIC_CONTRAST"); + EvDemosaicAutoContrast = m->newEvent(DEMOSAIC, "HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST"); EvDemosaicPixelshiftDemosaicMethod = m->newEvent(DEMOSAIC, "HISTORY_MSG_PIXELSHIFT_DEMOSAIC"); Gtk::HBox* hb1 = Gtk::manage (new Gtk::HBox ()); @@ -46,12 +47,12 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA hb1->pack_end (*method, Gtk::PACK_EXPAND_WIDGET, 4); pack_start( *hb1, Gtk::PACK_SHRINK, 4); + dualDemosaicOptions = Gtk::manage(new Gtk::VBox()); - dualDemosaicOptions = Gtk::manage (new Gtk::VBox ()); - - dualDemosaicContrast = Gtk::manage(new Adjuster (M("TP_RAW_DUALDEMOSAICCONTRAST"), 0, 100, 1, 20)); - dualDemosaicContrast->setAdjusterListener (this); - + dualDemosaicContrast = Gtk::manage(new Adjuster(M("TP_RAW_DUALDEMOSAICCONTRAST"), 0, 100, 1, 20)); + dualDemosaicContrast->setAdjusterListener(this); + dualDemosaicContrast->addAutoButton(M("TP_RAW_DUALDEMOSAICAUTOCONTRAST_TOOLTIP")); + dualDemosaicContrast->setAutoValue(true); if (dualDemosaicContrast->delay < options.adjusterMaxDelay) { dualDemosaicContrast->delay = options.adjusterMaxDelay; } @@ -250,6 +251,10 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA } +BayerProcess::~BayerProcess () +{ + idle_register.destroy(); +} void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) { @@ -300,7 +305,9 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftNonGreenCross->setValue (pp->raw.bayersensor.pixelShiftNonGreenCross); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); + dualDemosaicContrast->setAutoValue(pp->raw.bayersensor.dualDemosaicAutoContrast); dualDemosaicContrast->setValue (pp->raw.bayersensor.dualDemosaicContrast); + pixelShiftMotionMethod->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrectionMethod); pixelShiftEperIso->setValue (pp->raw.bayersensor.pixelShiftEperIso); pixelShiftSigma->setValue (pp->raw.bayersensor.pixelShiftSigma); @@ -325,6 +332,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftNonGreenCross->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenCross); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); dualDemosaicContrast->setEditedState ( pedited->raw.bayersensor.dualDemosaicContrast ? Edited : UnEdited); + dualDemosaicContrast->setAutoInconsistent (multiImage && !pedited->raw.bayersensor.dualDemosaicAutoContrast); pixelShiftEperIso->setEditedState ( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); pixelShiftSigma->setEditedState ( pedited->raw.bayersensor.pixelShiftSigma ? Edited : UnEdited); @@ -345,6 +353,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params } } + lastAutoContrast = pp->raw.bayersensor.dualDemosaicAutoContrast; if (!batchMode) { dcbOptions->set_visible(pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::DCB) || pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::DCBVNG4)); @@ -385,6 +394,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.dcb_enhance = dcbEnhance->getLastActive (); pp->raw.bayersensor.border = border->getIntValue(); pp->raw.bayersensor.lmmse_iterations = lmmseIterations->getIntValue(); + pp->raw.bayersensor.dualDemosaicAutoContrast = dualDemosaicContrast->getAutoValue(); pp->raw.bayersensor.dualDemosaicContrast = dualDemosaicContrast->getValue(); pp->raw.bayersensor.pixelShiftMotionCorrectionMethod = (RAWParams::BayerSensor::PSMotionCorrectionMethod)pixelShiftMotionMethod->get_active_row_number(); pp->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getValue(); @@ -425,6 +435,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.dcbEnhance = !dcbEnhance->get_inconsistent(); //pedited->raw.bayersensor.allEnhance = !allEnhance->get_inconsistent(); pedited->raw.bayersensor.lmmseIterations = lmmseIterations->getEditedState (); + pedited->raw.bayersensor.dualDemosaicAutoContrast = !dualDemosaicContrast->getAutoInconsistent (); pedited->raw.bayersensor.dualDemosaicContrast = dualDemosaicContrast->getEditedState (); pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod = pixelShiftMotionMethod->get_active_text() != M("GENERAL_UNCHANGED"); pedited->raw.bayersensor.pixelShiftDemosaicMethod = pixelShiftDemosaicMethod->get_active_text() != M("GENERAL_UNCHANGED"); @@ -539,10 +550,6 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) } } -void BayerProcess::adjusterAutoToggled(Adjuster* a, bool newval) -{ -} - void BayerProcess::methodChanged () { const int currentSelection = method->get_active_row_number(); @@ -660,6 +667,33 @@ void BayerProcess::checkBoxToggled (CheckBox* c, CheckValue newval) } } +void BayerProcess::adjusterAutoToggled(Adjuster* a, bool newval) +{ + if (multiImage) { + if (dualDemosaicContrast->getAutoInconsistent()) { + dualDemosaicContrast->setAutoInconsistent (false); + dualDemosaicContrast->setAutoValue (false); + } else if (lastAutoContrast) { + dualDemosaicContrast->setAutoInconsistent (true); + } + + lastAutoContrast = dualDemosaicContrast->getAutoValue(); + } + + if (listener) { + + if (a == dualDemosaicContrast) { + if (dualDemosaicContrast->getAutoInconsistent()) { + listener->panelChanged (EvDemosaicAutoContrast, M ("GENERAL_UNCHANGED")); + } else if (dualDemosaicContrast->getAutoValue()) { + listener->panelChanged (EvDemosaicAutoContrast, M ("GENERAL_ENABLED")); + } else { + listener->panelChanged (EvDemosaicAutoContrast, M ("GENERAL_DISABLED")); + } + } + } +} + void BayerProcess::pixelShiftMotionMethodChanged () { if (!batchMode) { @@ -715,23 +749,23 @@ void BayerProcess::FrameCountChanged(int n, int frameNum) }; idle_register.add(func, new Data { this, n, frameNum }); - - // GThreadLock lock; - // imageNumber->block (true); - - // imageNumber->remove_all(); - // imageNumber->append("1"); - // for(int i = 2; i <= std::min(n, 4); ++i) { - // std::ostringstream entry; - // entry << i; - // imageNumber->append(entry.str()); - // } - // imageNumber->set_active(std::min(frameNum, n - 1)); - // if(n == 1) { - // imageNumberBox->hide(); - // } else { - // imageNumberBox->show(); - // } - // imageNumber->block (false); } +void BayerProcess::autoContrastChanged (double autoContrast) +{ + struct Data { + BayerProcess *me; + double autoContrast; + }; + const auto func = [](gpointer data) -> gboolean { + Data *d = static_cast(data); + BayerProcess *me = d->me; + me->disableListener(); + me->dualDemosaicContrast->setValue(d->autoContrast); + me->enableListener(); + delete d; + return FALSE; + }; + + idle_register.add(func, new Data { this, autoContrast }); +} diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index 88b38cc16..b0974e7a4 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -25,7 +25,7 @@ #include "guiutils.h" #include "toolpanel.h" -class BayerProcess : public ToolParamBlock, public AdjusterListener, public CheckBoxListener, public FoldableToolPanel, public rtengine::FrameCountListener +class BayerProcess : public ToolParamBlock, public AdjusterListener, public CheckBoxListener, public FoldableToolPanel, public rtengine::FrameCountListener, public rtengine::AutoContrastListener { protected: @@ -60,15 +60,17 @@ protected: Gtk::VBox *dualDemosaicOptions; Adjuster* dualDemosaicContrast; int oldMethod; - + bool lastAutoContrast; IdleRegister idle_register; rtengine::ProcEvent EvDemosaicBorder; + rtengine::ProcEvent EvDemosaicAutoContrast; rtengine::ProcEvent EvDemosaicContrast; rtengine::ProcEvent EvDemosaicPixelshiftDemosaicMethod; public: BayerProcess (); + ~BayerProcess (); void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr); void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr); @@ -84,6 +86,7 @@ public: void checkBoxToggled(CheckBox* c, CheckValue newval); void pixelShiftMotionMethodChanged(); void pixelShiftDemosaicMethodChanged(); + void autoContrastChanged (double autoContrast); void FrameCountChanged(int n, int frameNum); }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 342d9adf9..570d6ab2b 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -406,6 +406,7 @@ void ParamsEdited::set(bool v) raw.bayersensor.dcbEnhance = v; //raw.bayersensor.allEnhance = v; raw.bayersensor.lmmseIterations = v; + raw.bayersensor.dualDemosaicAutoContrast = v; raw.bayersensor.dualDemosaicContrast = v; raw.bayersensor.pixelShiftMotionCorrectionMethod = v; raw.bayersensor.pixelShiftEperIso = v; @@ -426,6 +427,7 @@ void ParamsEdited::set(bool v) raw.bayersensor.linenoiseDirection = v; raw.bayersensor.pdafLinesFilter = v; raw.xtranssensor.method = v; + raw.xtranssensor.dualDemosaicAutoContrast = v; raw.xtranssensor.dualDemosaicContrast = v; raw.xtranssensor.ccSteps = v; raw.xtranssensor.exBlackRed = v; @@ -964,6 +966,7 @@ void ParamsEdited::initFrom(const std::vector& raw.bayersensor.dcbEnhance = raw.bayersensor.dcbEnhance && p.raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance; //raw.bayersensor.allEnhance = raw.bayersensor.allEnhance && p.raw.bayersensor.all_enhance == other.raw.bayersensor.all_enhance; raw.bayersensor.lmmseIterations = raw.bayersensor.lmmseIterations && p.raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations; + raw.bayersensor.dualDemosaicAutoContrast = raw.bayersensor.dualDemosaicAutoContrast && p.raw.bayersensor.dualDemosaicAutoContrast == other.raw.bayersensor.dualDemosaicAutoContrast; raw.bayersensor.dualDemosaicContrast = raw.bayersensor.dualDemosaicContrast && p.raw.bayersensor.dualDemosaicContrast == other.raw.bayersensor.dualDemosaicContrast; raw.bayersensor.pixelShiftMotionCorrectionMethod = raw.bayersensor.pixelShiftMotionCorrectionMethod && p.raw.bayersensor.pixelShiftMotionCorrectionMethod == other.raw.bayersensor.pixelShiftMotionCorrectionMethod; raw.bayersensor.pixelShiftEperIso = raw.bayersensor.pixelShiftEperIso && p.raw.bayersensor.pixelShiftEperIso == other.raw.bayersensor.pixelShiftEperIso; @@ -984,6 +987,7 @@ void ParamsEdited::initFrom(const std::vector& raw.bayersensor.linenoiseDirection = raw.bayersensor.linenoiseDirection && p.raw.bayersensor.linenoiseDirection == other.raw.bayersensor.linenoiseDirection; raw.bayersensor.pdafLinesFilter = raw.bayersensor.pdafLinesFilter && p.raw.bayersensor.pdafLinesFilter == other.raw.bayersensor.pdafLinesFilter; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; + raw.xtranssensor.dualDemosaicAutoContrast = raw.xtranssensor.dualDemosaicAutoContrast && p.raw.xtranssensor.dualDemosaicAutoContrast == other.raw.xtranssensor.dualDemosaicAutoContrast; raw.xtranssensor.dualDemosaicContrast = raw.xtranssensor.dualDemosaicContrast && p.raw.xtranssensor.dualDemosaicContrast == other.raw.xtranssensor.dualDemosaicContrast; raw.xtranssensor.ccSteps = raw.xtranssensor.ccSteps && p.raw.xtranssensor.ccSteps == other.raw.xtranssensor.ccSteps; raw.xtranssensor.exBlackRed = raw.xtranssensor.exBlackRed && p.raw.xtranssensor.blackred == other.raw.xtranssensor.blackred; @@ -2532,6 +2536,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.raw.bayersensor.lmmse_iterations = mods.raw.bayersensor.lmmse_iterations; } + if (raw.bayersensor.dualDemosaicAutoContrast) { + toEdit.raw.bayersensor.dualDemosaicAutoContrast = mods.raw.bayersensor.dualDemosaicAutoContrast; + } + if (raw.bayersensor.dualDemosaicContrast) { toEdit.raw.bayersensor.dualDemosaicContrast = mods.raw.bayersensor.dualDemosaicContrast; } @@ -2612,6 +2620,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.raw.xtranssensor.method = mods.raw.xtranssensor.method; } + if (raw.xtranssensor.dualDemosaicAutoContrast) { + toEdit.raw.xtranssensor.dualDemosaicAutoContrast = mods.raw.xtranssensor.dualDemosaicAutoContrast; + } + if (raw.xtranssensor.dualDemosaicContrast) { toEdit.raw.xtranssensor.dualDemosaicContrast = mods.raw.xtranssensor.dualDemosaicContrast; } @@ -3132,7 +3144,7 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng bool RAWParamsEdited::BayerSensor::isUnchanged() const { - return method && border && imageNum && dcbIterations && dcbEnhance && lmmseIterations && dualDemosaicContrast /*&& allEnhance*/ && greenEq + return method && border && imageNum && dcbIterations && dcbEnhance && lmmseIterations && dualDemosaicAutoContrast && dualDemosaicContrast /*&& allEnhance*/ && greenEq && pixelShiftMotionCorrectionMethod && pixelShiftEperIso && pixelShiftSigma && pixelShiftShowMotion && pixelShiftShowMotionMaskOnly && pixelShiftHoleFill && pixelShiftMedian && pixelShiftNonGreenCross && pixelShiftDemosaicMethod && pixelShiftGreen && pixelShiftBlur && pixelShiftSmooth && pixelShiftEqualBright && pixelShiftEqualBrightChannel && linenoise && linenoiseDirection && pdafLinesFilter && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; @@ -3140,7 +3152,7 @@ bool RAWParamsEdited::BayerSensor::isUnchanged() const bool RAWParamsEdited::XTransSensor::isUnchanged() const { - return method && exBlackRed && exBlackGreen && exBlackBlue; + return method && exBlackRed && exBlackGreen && exBlackBlue && dualDemosaicAutoContrast && dualDemosaicContrast; } bool RAWParamsEdited::isUnchanged() const diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 177b05d85..aa9c01bd9 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -744,6 +744,7 @@ public: bool dcbIterations; bool dcbEnhance; bool lmmseIterations; + bool dualDemosaicAutoContrast; bool dualDemosaicContrast; bool pixelShiftMotionCorrectionMethod; bool pixelShiftEperIso; @@ -774,6 +775,7 @@ public: public: bool method; + bool dualDemosaicAutoContrast; bool dualDemosaicContrast; bool ccSteps; bool exBlackRed; diff --git a/rtgui/partialpastedlg.cc b/rtgui/partialpastedlg.cc index 6b192dba9..91e542e85 100644 --- a/rtgui/partialpastedlg.cc +++ b/rtgui/partialpastedlg.cc @@ -828,8 +828,10 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param if (!raw_method->get_active ()) { filterPE.raw.bayersensor.method = falsePE.raw.bayersensor.method; + filterPE.raw.bayersensor.dualDemosaicAutoContrast = falsePE.raw.bayersensor.dualDemosaicAutoContrast; filterPE.raw.bayersensor.dualDemosaicContrast = falsePE.raw.bayersensor.dualDemosaicContrast; filterPE.raw.xtranssensor.method = falsePE.raw.xtranssensor.method; + filterPE.raw.xtranssensor.dualDemosaicAutoContrast = falsePE.raw.xtranssensor.dualDemosaicAutoContrast; filterPE.raw.xtranssensor.dualDemosaicContrast = falsePE.raw.xtranssensor.dualDemosaicContrast; } diff --git a/rtgui/ppversion.h b/rtgui/ppversion.h index 19e8c995d..91a9633c0 100644 --- a/rtgui/ppversion.h +++ b/rtgui/ppversion.h @@ -1,11 +1,13 @@ #pragma once // This number has to be incremented whenever the PP3 file format is modified or the behaviour of a tool changes -#define PPVERSION 344 +#define PPVERSION 345 #define PPVERSION_AEXP 301 //value of PPVERSION when auto exposure algorithm was modified /* Log of version changes + 345 2018-10-21 + dual demosaic auto contrast threshold 344 2018-10-04 added Lab/RGB color space selection for shadows/highlights 343 2018-09-06 diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 18f81c155..6cc32a477 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -521,6 +521,8 @@ void ToolPanelCoordinator::initImage (rtengine::StagedImageProcessor* ipc_, bool ipc->setAutoCamListener (colorappearance); ipc->setAutoBWListener (blackwhite); ipc->setFrameCountListener (bayerprocess); + ipc->setBayerAutoContrastListener (bayerprocess); + ipc->setXtransAutoContrastListener (xtransprocess); ipc->setAutoWBListener (whitebalance); ipc->setAutoColorTonListener (colortoning); ipc->setAutoChromaListener (dirpyrdenoise); diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 90d68c7a3..b5a23f690 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -229,6 +229,7 @@ public: void imageTypeChanged (bool isRaw, bool isBayer, bool isXtrans, bool isMono = false); +// void autoContrastChanged (double autoContrast); // profilechangelistener interface void profileChange( const rtengine::procparams::PartialProfile* nparams, diff --git a/rtgui/xtransprocess.cc b/rtgui/xtransprocess.cc index 744c6f148..47556e54f 100644 --- a/rtgui/xtransprocess.cc +++ b/rtgui/xtransprocess.cc @@ -28,6 +28,7 @@ XTransProcess::XTransProcess () : FoldableToolPanel(this, "xtransprocess", M("TP { auto m = ProcEventMapper::getInstance(); EvDemosaicContrast = m->newEvent(DEMOSAIC, "HISTORY_MSG_DUALDEMOSAIC_CONTRAST"); + EvDemosaicAutoContrast = m->newEvent(DEMOSAIC, "HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST"); Gtk::HBox* hb1 = Gtk::manage (new Gtk::HBox ()); hb1->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_DMETHOD") + ": ")), Gtk::PACK_SHRINK, 4); @@ -71,6 +72,8 @@ XTransProcess::XTransProcess () : FoldableToolPanel(this, "xtransprocess", M("TP dualDemosaicContrast = Gtk::manage(new Adjuster (M("TP_RAW_DUALDEMOSAICCONTRAST"), 0, 100, 1, 20)); dualDemosaicContrast->setAdjusterListener (this); + dualDemosaicContrast->addAutoButton(M("TP_RAW_DUALDEMOSAICAUTOCONTRAST_TOOLTIP")); + dualDemosaicContrast->setAutoValue(true); if (dualDemosaicContrast->delay < options.adjusterMaxDelay) { dualDemosaicContrast->delay = options.adjusterMaxDelay; @@ -94,6 +97,9 @@ XTransProcess::XTransProcess () : FoldableToolPanel(this, "xtransprocess", M("TP methodconn = method->signal_changed().connect( sigc::mem_fun(*this, &XTransProcess::methodChanged) ); } +XTransProcess::~XTransProcess () { + idle_register.destroy(); +} void XTransProcess::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) { @@ -108,6 +114,7 @@ void XTransProcess::read(const rtengine::procparams::ProcParams* pp, const Param } if(pedited ) { + dualDemosaicContrast->setAutoInconsistent (multiImage && !pedited->raw.xtranssensor.dualDemosaicAutoContrast); dualDemosaicContrast->setEditedState ( pedited->raw.xtranssensor.dualDemosaicContrast ? Edited : UnEdited); ccSteps->setEditedState (pedited->raw.xtranssensor.ccSteps ? Edited : UnEdited); @@ -115,8 +122,12 @@ void XTransProcess::read(const rtengine::procparams::ProcParams* pp, const Param method->set_active_text(M("GENERAL_UNCHANGED")); } } + dualDemosaicContrast->setAutoValue(pp->raw.xtranssensor.dualDemosaicAutoContrast); dualDemosaicContrast->setValue (pp->raw.xtranssensor.dualDemosaicContrast); ccSteps->setValue (pp->raw.xtranssensor.ccSteps); + + lastAutoContrast = pp->raw.bayersensor.dualDemosaicAutoContrast; + if (!batchMode) { dualDemosaicOptions->set_visible(pp->raw.xtranssensor.method == procparams::RAWParams::XTransSensor::getMethodString(procparams::RAWParams::XTransSensor::Method::FOUR_PASS) || pp->raw.xtranssensor.method == procparams::RAWParams::XTransSensor::getMethodString(procparams::RAWParams::XTransSensor::Method::TWO_PASS)); @@ -129,6 +140,7 @@ void XTransProcess::read(const rtengine::procparams::ProcParams* pp, const Param void XTransProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) { + pp->raw.xtranssensor.dualDemosaicAutoContrast = dualDemosaicContrast->getAutoValue(); pp->raw.xtranssensor.dualDemosaicContrast = dualDemosaicContrast->getValue(); pp->raw.xtranssensor.ccSteps = ccSteps->getIntValue(); @@ -140,6 +152,7 @@ void XTransProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* p if (pedited) { pedited->raw.xtranssensor.method = method->get_active_text() != M("GENERAL_UNCHANGED"); + pedited->raw.xtranssensor.dualDemosaicAutoContrast = !dualDemosaicContrast->getAutoInconsistent (); pedited->raw.xtranssensor.dualDemosaicContrast = dualDemosaicContrast->getEditedState (); pedited->raw.xtranssensor.ccSteps = ccSteps->getEditedState (); } @@ -187,6 +200,29 @@ void XTransProcess::adjusterChanged(Adjuster* a, double newval) void XTransProcess::adjusterAutoToggled(Adjuster* a, bool newval) { + if (multiImage) { + if (dualDemosaicContrast->getAutoInconsistent()) { + dualDemosaicContrast->setAutoInconsistent (false); + dualDemosaicContrast->setAutoValue (false); + } else if (lastAutoContrast) { + dualDemosaicContrast->setAutoInconsistent (true); + } + + lastAutoContrast = dualDemosaicContrast->getAutoValue(); + } + + if (listener) { + + if (a == dualDemosaicContrast) { + if (dualDemosaicContrast->getAutoInconsistent()) { + listener->panelChanged (EvDemosaicAutoContrast, M ("GENERAL_UNCHANGED")); + } else if (dualDemosaicContrast->getAutoValue()) { + listener->panelChanged (EvDemosaicAutoContrast, M ("GENERAL_ENABLED")); + } else { + listener->panelChanged (EvDemosaicAutoContrast, M ("GENERAL_DISABLED")); + } + } + } } void XTransProcess::methodChanged () @@ -211,3 +247,26 @@ void XTransProcess::methodChanged () : EvDemosaicMethod, method->get_active_text()); } } + +void XTransProcess::checkBoxToggled (CheckBox* c, CheckValue newval) +{ +} + +void XTransProcess::autoContrastChanged (double autoContrast) +{ + struct Data { + XTransProcess *me; + double autoContrast; + }; + const auto func = [](gpointer data) -> gboolean { + Data *d = static_cast(data); + XTransProcess *me = d->me; + me->disableListener(); + me->dualDemosaicContrast->setValue(d->autoContrast); + me->enableListener(); + delete d; + return FALSE; + }; + + idle_register.add(func, new Data { this, autoContrast }); +} diff --git a/rtgui/xtransprocess.h b/rtgui/xtransprocess.h index bd2086f77..7a706b1ac 100644 --- a/rtgui/xtransprocess.h +++ b/rtgui/xtransprocess.h @@ -21,11 +21,12 @@ #include #include "adjuster.h" +#include "checkbox.h" #include "guiutils.h" #include "toolpanel.h" -class XTransProcess : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel +class XTransProcess : public ToolParamBlock, public AdjusterListener, public CheckBoxListener, public FoldableToolPanel, public rtengine::AutoContrastListener { protected: @@ -34,14 +35,18 @@ protected: Adjuster* ccSteps; Gtk::VBox *dualDemosaicOptions; Adjuster* dualDemosaicContrast; + bool lastAutoContrast; int oldSelection; sigc::connection methodconn; + IdleRegister idle_register; + rtengine::ProcEvent EvDemosaicAutoContrast; rtengine::ProcEvent EvDemosaicContrast; public: XTransProcess (); + ~XTransProcess (); void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr); void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr); @@ -50,7 +55,9 @@ public: void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr); void methodChanged(); + void autoContrastChanged (double autoContrast); void adjusterChanged(Adjuster* a, double newval); + void checkBoxToggled(CheckBox* c, CheckValue newval); void adjusterAutoToggled(Adjuster* a, bool newval); };