diff --git a/rtdata/languages/default b/rtdata/languages/default index e2b817056..0bd3e1538 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1769,6 +1769,7 @@ TP_RAW_AHD;AHD TP_RAW_AMAZE;AMaZE TP_RAW_AMAZEVNG4;AMaZE+VNG4 TP_RAW_DCB;DCB +TP_RAW_DCBVNG4;DCB+VNG4 TP_RAW_DCBENHANCE;DCB enhancement TP_RAW_DCBITERATIONS;Number of DCB iterations TP_RAW_DMETHOD;Method @@ -1822,6 +1823,7 @@ TP_RAW_PIXELSHIFTSIGMA_TOOLTIP;The default radius of 1.0 usually fits well for b TP_RAW_PIXELSHIFTSMOOTH;Smooth transitions TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP;Smooth transitions between areas with motion and areas without.\nSet to 0 to disable transition smoothing.\nSet to 1 to either get the AMaZE/LMMSE result of the selected frame (depending on whether "Use LMMSE" is selected), or the median of all four frames if "Use median" is selected. TP_RAW_RCD;RCD +TP_RAW_RCDVNG4;RCD+VNG4 TP_RAW_SENSOR_BAYER_LABEL;Sensor with Bayer Matrix TP_RAW_SENSOR_XTRANS_DMETHOD_TOOLTIP;3-pass gives best results (recommended for low ISO images).\n1-pass is almost undistinguishable from 3-pass for high ISO images and is faster. TP_RAW_SENSOR_XTRANS_LABEL;Sensor with X-Trans Matrix diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 67b07c48c..e48fe4cbb 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -33,7 +33,6 @@ set(RTENGINESOURCEFILES FTblockDN.cc PF_correct_RT.cc amaze_demosaic_RT.cc - amaze_vng4_demosaic_RT.cc cJSON.c calc_distort.cc camconst.cc @@ -53,6 +52,7 @@ set(RTENGINESOURCEFILES dfmanager.cc diagonalcurves.cc dirpyr_equalizer.cc + dual_demosaic_RT.cc dynamicprofile.cc expo_before_b.cc fast_demo.cc diff --git a/rtengine/amaze_vng4_demosaic_RT.cc b/rtengine/amaze_vng4_demosaic_RT.cc deleted file mode 100644 index 08e142a4e..000000000 --- a/rtengine/amaze_vng4_demosaic_RT.cc +++ /dev/null @@ -1,93 +0,0 @@ -//////////////////////////////////////////////////////////////// -// -// combined AMaZE + VNG4 demosaic algorithm -// -// -// copyright (c) 2018 Ingo Weyrich -// -// blends AMaZE and VNG4 output based on contrast -// -// -// amaze_vng4_demosaic_RT.cc is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -//////////////////////////////////////////////////////////////// - -#include "rtengine.h" -#include "rawimagesource.h" -#include "rt_math.h" -//#define BENCHMARK -#include "StopWatch.h" -#include "rt_algo.h" - -using namespace std; - -namespace rtengine -{ - -void RawImageSource::amaze_vng4_demosaic_RT(int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, double contrast) -{ - BENCHFUN - - if (contrast == 0.0) { - // contrast == 0.0 means only AMaZE will be used - amaze_demosaic_RT(0, 0, winw, winh, rawData, red, green, blue); - return; - } - - vng4_demosaic(rawData, red, green, blue); - - array2D redTmp(winw, winh); - array2D greenTmp(winw, winh); - array2D blueTmp(winw, winh); - array2D L(winw, winh); - - amaze_demosaic_RT(0, 0, winw, winh, rawData, redTmp, greenTmp, blueTmp); - const float xyz_rgb[3][3] = { // XYZ from RGB - { 0.412453, 0.357580, 0.180423 }, - { 0.212671, 0.715160, 0.072169 }, - { 0.019334, 0.119193, 0.950227 } - }; - #pragma omp parallel - { - #pragma omp for - for(int i = 0; i < winh; ++i) { - Color::RGB2L(redTmp[i], greenTmp[i], blueTmp[i], L[i], xyz_rgb, winw); - } - } - // 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); - - // the following is split into 3 loops intentionally to avoid cache conflicts on CPUs with only 4-way cache - #pragma omp parallel for - for(int i = 0; i < winh; ++i) { - for(int j = 0; j < winw; ++j) { - red[i][j] = intp(blend[i][j], redTmp[i][j], red[i][j]); - } - } - #pragma omp parallel for - for(int i = 0; i < winh; ++i) { - for(int j = 0; j < winw; ++j) { - green[i][j] = intp(blend[i][j], greenTmp[i][j], green[i][j]); - } - } - #pragma omp parallel for - for(int i = 0; i < winh; ++i) { - for(int j = 0; j < winw; ++j) { - blue[i][j] = intp(blend[i][j], blueTmp[i][j], blue[i][j]); - } - } - -} -} diff --git a/rtengine/dual_demosaic_RT.cc b/rtengine/dual_demosaic_RT.cc new file mode 100644 index 000000000..291cef94a --- /dev/null +++ b/rtengine/dual_demosaic_RT.cc @@ -0,0 +1,136 @@ +//////////////////////////////////////////////////////////////// +// +// combine demosaic algorithms +// +// +// copyright (c) 2018 Ingo Weyrich +// +// blends output of two demosaicers based on contrast +// +// +// dual_demosaic_RT.cc is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//////////////////////////////////////////////////////////////// + +#include "jaggedarray.h" +#include "rtengine.h" +#include "rawimagesource.h" +#include "rt_math.h" +//#define BENCHMARK +#include "StopWatch.h" +#include "rt_algo.h" + +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) +{ + BENCHFUN + + if (contrast == 0.0 && !autoContrast) { + // contrast == 0.0 means only first demosaicer will be used + if(isBayer) { + if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZEVNG4) ) { + amaze_demosaic_RT(0, 0, winw, winh, rawData, red, green, blue); + } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCBVNG4) ) { + dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); + } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::RCDVNG4) ) { + rcd_demosaic(); + } + } else { + xtrans_interpolate (3, true); + } + + return; + } + + array2D redTmp(winw, winh); + array2D greenTmp(winw, winh); + array2D blueTmp(winw, winh); + array2D L(winw, winh); + + if (isBayer) { + vng4_demosaic(rawData, redTmp, greenTmp, blueTmp); + + if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZEVNG4) ) { + amaze_demosaic_RT(0, 0, winw, winh, rawData, red, green, blue); + } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCBVNG4) ) { + dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); + } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::RCDVNG4) ) { + rcd_demosaic(); + } + } else { + xtrans_interpolate (3, true); + fast_xtrans_interpolate(rawData, redTmp, greenTmp, blueTmp); + } + + const float xyz_rgb[3][3] = { // XYZ from RGB + { 0.412453, 0.357580, 0.180423 }, + { 0.212671, 0.715160, 0.072169 }, + { 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 + for(int i = 0; i < winh; ++i) { + Color::RGB2L(red[i], green[i], blue[i], L[i], xyz_rgb, winw); + } + } + // 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); + + // the following is split into 3 loops intentionally to avoid cache conflicts on CPUs with only 4-way cache + #pragma omp parallel for + for(int i = 0; i < winh; ++i) { + for(int j = 0; j < winw; ++j) { + red[i][j] = intp(blend[i][j], red[i][j], redTmp[i][j]); + } + } + #pragma omp parallel for + for(int i = 0; i < winh; ++i) { + for(int j = 0; j < winw; ++j) { + green[i][j] = intp(blend[i][j], green[i][j], greenTmp[i][j]); + } + } + #pragma omp parallel for + for(int i = 0; i < winh; ++i) { + for(int j = 0; j < winw; ++j) { + blue[i][j] = intp(blend[i][j], blue[i][j], blueTmp[i][j]); + } + } + +} +} diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index c0565a4a3..c62a0ac96 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -68,7 +68,7 @@ public: virtual ~ImageSource () {} virtual int load (const Glib::ustring &fname) = 0; virtual void preprocess (const RAWParams &raw, const LensProfParams &lensProf, const CoarseTransformParams& coarse, bool prepareDenoise = true) {}; - virtual void demosaic (const RAWParams &raw) {}; + virtual void demosaic (const RAWParams &raw, bool autoContrast, double &contrastThreshold) {}; virtual void retinex (const ColorManagementParams& cmp, const RetinexParams &deh, const ToneCurveParams& Tc, LUTf & cdcurve, LUTf & mapcurve, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, multi_array2D &conversionBuffer, bool dehacontlutili, bool mapcontlutili, bool useHsl, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, LUTu &histLRETI) {}; virtual void retinexPrepareCurves (const RetinexParams &retinexParams, LUTf &cdcurve, LUTf &mapcurve, RetinextransmissionCurve &retinextransmissionCurve, RetinexgaintransmissionCurve &retinexgaintransmissionCurve, bool &retinexcontlutili, bool &mapcontlutili, bool &useHsl, LUTu & lhist16RETI, LUTu & histLRETI) {}; virtual void retinexPrepareBuffers (const ColorManagementParams& cmp, const RetinexParams &retinexParams, multi_array2D &conversionBuffer, LUTu &lhist16RETI) {}; diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 714ad2b41..927394d4f 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -230,8 +230,10 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) printf ("Demosaic X-Trans image with using method: %s\n", rp.xtranssensor.method.c_str()); } } + bool autoContrast = false; + double contrastThreshold = 0.f; + imgsrc->demosaic (rp, autoContrast, contrastThreshold); //enabled demosaic - imgsrc->demosaic ( rp); //enabled demosaic // if a demosaic happened we should also call getimage later, so we need to set the M_INIT flag todo |= M_INIT; @@ -1181,7 +1183,8 @@ void ImProcCoordinator::saveInputICCReference (const Glib::ustring& fname, bool ppar.icm.input = "(none)"; Imagefloat* im = new Imagefloat (fW, fH); imgsrc->preprocess ( ppar.raw, ppar.lensProf, ppar.coarse ); - imgsrc->demosaic (ppar.raw ); + double dummy = 0.0; + imgsrc->demosaic (ppar.raw, false, dummy); ColorTemp currWB = ColorTemp (params.wb.temperature, params.wb.green, params.wb.equal, params.wb.method); if (params.wb.method == "Camera") { diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 174c02065..cdf4f990a 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -295,7 +295,7 @@ void calcFrameBrightnessFactor(unsigned int frame, uint32_t datalen, LUTu *histo using namespace std; using namespace rtengine; -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParamsIn, unsigned int frame, const std::string &make, const std::string &model, float rawWpCorrection) +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RAWParams &rawParamsIn, unsigned int frame, const std::string &make, const std::string &model, float rawWpCorrection) { BENCHFUN if(numFrames != 4) { // fallback for non pixelshift files @@ -303,7 +303,7 @@ BENCHFUN return; } - RAWParams::BayerSensor bayerParams = bayerParamsIn; + RAWParams::BayerSensor bayerParams = rawParamsIn.bayersensor; bool motionDetection = true; @@ -326,7 +326,7 @@ BENCHFUN if (bayerParams.pixelShiftDemosaicMethod == bayerParams.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::LMMSE)) { lmmse_interpolate_omp(winw, winh, *(rawDataFrames[0]), red, green, blue, bayerParams.lmmse_iterations); } else if (bayerParams.pixelShiftDemosaicMethod == bayerParams.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::AMAZEVNG4)) { - amaze_vng4_demosaic_RT (winw, winh, *(rawDataFrames[0]), red, green, blue, bayerParams.dualDemosaicContrast); + dual_demosaic_RT (true, rawParamsIn, winw, winh, *(rawDataFrames[0]), red, green, blue, bayerParams.dualDemosaicContrast); } else { amaze_demosaic_RT(winx, winy, winw, winh, *(rawDataFrames[0]), red, green, blue); } @@ -338,7 +338,7 @@ BENCHFUN if (bayerParams.pixelShiftDemosaicMethod == bayerParams.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::LMMSE)) { lmmse_interpolate_omp(winw, winh, *(rawDataFrames[i + 1]), redTmp[i], greenTmp[i], blueTmp[i], bayerParams.lmmse_iterations); } else if (bayerParams.pixelShiftDemosaicMethod == bayerParams.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::AMAZEVNG4)) { - amaze_vng4_demosaic_RT (winw, winh, *(rawDataFrames[i + 1]), redTmp[i], greenTmp[i], blueTmp[i], bayerParams.dualDemosaicContrast); + dual_demosaic_RT (true, rawParamsIn, winw, winh, *(rawDataFrames[i + 1]), redTmp[i], greenTmp[i], blueTmp[i], bayerParams.dualDemosaicContrast); } else { amaze_demosaic_RT(winx, winy, winw, winh, *(rawDataFrames[i + 1]), redTmp[i], greenTmp[i], blueTmp[i]); } @@ -365,7 +365,9 @@ BENCHFUN if (bayerParams.pixelShiftDemosaicMethod == bayerParams.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::LMMSE)) { lmmse_interpolate_omp(winw, winh, rawData, red, green, blue, bayerParams.lmmse_iterations); } else if (bayerParams.pixelShiftDemosaicMethod == bayerParams.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::AMAZEVNG4)) { - amaze_vng4_demosaic_RT (winw, winh, rawData, red, green, blue, bayerParams.dualDemosaicContrast); + RAWParams rawParamsTmp = rawParamsIn; + rawParamsTmp.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZEVNG4); + dual_demosaic_RT (true, rawParamsTmp, winw, winh, rawData, red, green, blue, bayerParams.dualDemosaicContrast); } else { amaze_demosaic_RT(winx, winy, winw, winh, rawData, red, green, blue); } diff --git a/rtengine/previewimage.cc b/rtengine/previewimage.cc index 0b3610de2..6c548f92a 100644 --- a/rtengine/previewimage.cc +++ b/rtengine/previewimage.cc @@ -113,7 +113,8 @@ PreviewImage::PreviewImage (const Glib::ustring &fname, const Glib::ustring &ext params.raw.ca_autocorrect = false; params.raw.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST); rawImage.preprocess(params.raw, params.lensProf, params.coarse); - rawImage.demosaic(params.raw); + double contrastThresholdDummy = 0.0; + rawImage.demosaic(params.raw, false, contrastThresholdDummy); Imagefloat image(fw, fh); rawImage.getImage (wb, TR_NONE, &image, pp, params.toneCurve, params.raw); rtengine::Image8 output(fw, fh); diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index a704a8a9c..5e609c0bb 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2439,7 +2439,9 @@ const std::vector& RAWParams::BayerSensor::getMethodStrings() "amaze", "amazevng4", "rcd", + "rcdvng4", "dcb", + "dcbvng4", "lmmse", "igv", "ahd", diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 54e8b2d56..9ed6d9c5c 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1227,7 +1227,9 @@ struct RAWParams { AMAZE, AMAZEVNG4, RCD, + RCDVNG4, DCB, + DCBVNG4, LMMSE, IGV, AHD, diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 73a63a1a1..2d8db2309 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2071,7 +2071,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void RawImageSource::demosaic(const RAWParams &raw) +void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &contrastThreshold) { MyTime t1, t2; t1.set(); @@ -2085,10 +2085,17 @@ void RawImageSource::demosaic(const RAWParams &raw) ahd_demosaic (); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZE) ) { amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); - } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZEVNG4) ) { - amaze_vng4_demosaic_RT (W, H, rawData, red, green, blue, raw.bayersensor.dualDemosaicContrast); + } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZEVNG4) + || raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCBVNG4) + || raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::RCDVNG4)) { + if (!autoContrast) { + 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); + } } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::PIXELSHIFT) ) { - pixelshift(0, 0, W, H, raw.bayersensor, currFrame, ri->get_maker(), ri->get_model(), raw.expos); + pixelshift(0, 0, W, H, raw, currFrame, ri->get_maker(), ri->get_model(), raw.expos); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCB) ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::EAHD)) { @@ -2106,9 +2113,6 @@ void RawImageSource::demosaic(const RAWParams &raw) } else { nodemosaic(false); } - - //if (raw.all_enhance) refinement_lassus(); - } else if (ri->getSensorType() == ST_FUJI_XTRANS) { if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST) ) { fast_xtrans_interpolate(rawData, red, green, blue); @@ -2117,7 +2121,12 @@ void RawImageSource::demosaic(const RAWParams &raw) } else if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::THREE_PASS) ) { xtrans_interpolate(3, true); } else if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FOUR_PASS)) { - xtrans_4pass_demosaic_RT(3, true, raw.xtranssensor.dualDemosaicContrast); + if (!autoContrast) { + 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); + } } else if(raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::MONO) ) { nodemosaic(true); } else { diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index b5418d0ec..51add0e97 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -121,7 +121,7 @@ public: int load(const Glib::ustring &fname) { return load(fname, false); } int load(const Glib::ustring &fname, bool firstFrameOnly); void preprocess (const RAWParams &raw, const LensProfParams &lensProf, const CoarseTransformParams& coarse, bool prepareDenoise = true); - void demosaic (const RAWParams &raw); + void demosaic (const RAWParams &raw, bool autoContrast, double &contrastThreshold); void retinex (const ColorManagementParams& cmp, const RetinexParams &deh, const ToneCurveParams& Tc, LUTf & cdcurve, LUTf & mapcurve, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, multi_array2D &conversionBuffer, bool dehacontlutili, bool mapcontlutili, bool useHsl, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, LUTu &histLRETI); void retinexPrepareCurves (const RetinexParams &retinexParams, LUTf &cdcurve, LUTf &mapcurve, RetinextransmissionCurve &retinextransmissionCurve, RetinexgaintransmissionCurve &retinexgaintransmissionCurve, bool &retinexcontlutili, bool &mapcontlutili, bool &useHsl, LUTu & lhist16RETI, LUTu & histLRETI); void retinexPrepareBuffers (const ColorManagementParams& cmp, const RetinexParams &retinexParams, multi_array2D &conversionBuffer, LUTu &lhist16RETI); @@ -268,7 +268,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 amaze_vng4_demosaic_RT(int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, double contrast = 0.0); + 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 fast_demosaic();//Emil's code for fast demosaicing void dcb_demosaic(int iterations, bool dcb_enhance); void ahd_demosaic(); @@ -291,10 +291,9 @@ protected: void dcb_color_full(float (*image)[3], int x0, int y0, float (*chroma)[2]); void cielab (const float (*rgb)[3], float* l, float* a, float *b, const int width, const int height, const int labWidth, const float xyz_cam[3][3]); void xtransborder_interpolate (int border); - void xtrans_4pass_demosaic_RT (const int passes, const bool useCieLab, double contrast); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (const array2D &rawData, array2D &red, array2D &green, array2D &blue); - void pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParams, unsigned int frame, const std::string &make, const std::string &model, float rawWpCorrection); + void pixelshift(int winx, int winy, int winw, int winh, const RAWParams &rawParams, unsigned int frame, const std::string &make, const std::string &model, float rawWpCorrection); void hflip (Imagefloat* im); void vflip (Imagefloat* im); void getRawValues(int x, int y, int rotate, int &R, int &G, int &B); diff --git a/rtengine/rt_algo.cc b/rtengine/rt_algo.cc index cff145de8..c56a72a2c 100644 --- a/rtengine/rt_algo.cc +++ b/rtengine/rt_algo.cc @@ -1,6 +1,8 @@ /* * This file is part of RawTherapee. * + * Copyright (c) 2017-2018 Ingo Weyrich + * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -20,7 +22,7 @@ #include #include #include - +#include #ifdef _OPENMP #include #endif @@ -188,7 +190,9 @@ void findMinMaxPercentile(const float* data, size_t size, float minPrct, float& maxOut += minVal; } -void buildBlendMask(float** luminance, rtengine::JaggedArray &blend, int W, int H, float contrastThreshold, float amount) { +void buildBlendMask(float** luminance, float **blend, int W, int H, float contrastThreshold, float amount) { + + constexpr float scale = 0.0625f / 327.68f; if(contrastThreshold == 0.f) { for(int j = 0; j < H; ++j) { @@ -197,64 +201,123 @@ void buildBlendMask(float** luminance, rtengine::JaggedArray &blend, int } } } else { - constexpr float scale = 0.0625f / 327.68f; #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 + + 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); + } + } +} + +int calcContrastThreshold(float** luminance, float **blend, int W, int H) { + + constexpr float scale = 0.0625f / 327.68f; + +#ifdef __SSE2__ + const vfloat scalev = F2V(scale); +#endif + + 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 -2 ][i - 2], contrastv); + } +#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 -2][i- 2] = contrast; + } + } + + const float limit = (W - 2) * (H - 2) / 100.f; + + int c; + for (c = 1; c < 100; ++c) { + const float contrastThreshold = c / 100.f; + float sum = 0.f; #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) + vfloat sumv = ZEROV; #endif - for(int j = 2; j < H - 2; ++j) { - int i = 2; + for(int j = 0; j < H - 4; ++j) { + int i = 0; #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)); + for(; i < W - 7; i += 4) { + sumv += calcBlendFactor(LVFU(blend[j][i]), 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); + for(; i < W - 4; ++i) { + sum += calcBlendFactor(blend[j][i], contrastThreshold); } } -#ifdef _OPENMP - #pragma omp single +#ifdef __SSE2__ + sum += vhadd(sumv); #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]; - } + if (sum <= limit) { + break; } - // blur blend mask to smooth transitions - gaussianBlur(blend, blend, W, H, 2.0); - } } + return c; } } diff --git a/rtengine/rt_algo.h b/rtengine/rt_algo.h index 1aac26c3e..e20713b8f 100644 --- a/rtengine/rt_algo.h +++ b/rtengine/rt_algo.h @@ -1,7 +1,7 @@ /* * This file is part of RawTherapee. * - * Copyright (c) 2017 Ingo Weyrich + * Copyright (c) 2017-2018 Ingo Weyrich * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,10 +20,10 @@ #pragma once #include -#include "jaggedarray.h" 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, rtengine::JaggedArray &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); +int calcContrastThreshold(float** luminance, float **blend, int W, int H); } diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 3c2d3e6f5..47a6d7349 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -289,7 +289,8 @@ Image8 *load_inspector_mode(const Glib::ustring &fname, RawMetaDataLocation &rml neutral.icm.working = "RT_sRGB"; src.preprocess(neutral.raw, neutral.lensProf, neutral.coarse, false); - src.demosaic(neutral.raw); + double thresholdDummy = 0.f; + src.demosaic(neutral.raw, false, thresholdDummy); PreviewProps pp(0, 0, w, h, 1); diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index ad622c6bd..9fb7e42a8 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -203,8 +203,8 @@ private: if (pl) { pl->setProgress (0.20); } - - imgsrc->demosaic ( params.raw); + double contrastThresholdDummy; + imgsrc->demosaic (params.raw, false, contrastThresholdDummy); if (pl) { pl->setProgress (0.30); diff --git a/rtengine/xtrans_demosaic.cc b/rtengine/xtrans_demosaic.cc index 411388ee5..edf5e250b 100644 --- a/rtengine/xtrans_demosaic.cc +++ b/rtengine/xtrans_demosaic.cc @@ -1019,60 +1019,5 @@ void RawImageSource::fast_xtrans_interpolate (const array2D &rawData, arr #undef fcol #undef isgreen -void RawImageSource::xtrans_4pass_demosaic_RT(int passes, bool useCieLab, double contrast) -{ - BENCHFUN - - if (contrast == 0.0) { - // contrast == 0.0 means only AMaZE will be used - xtrans_interpolate (passes, useCieLab); - return; - } - - xtrans_interpolate (passes, useCieLab); - - array2D redTmp(W, H); - array2D greenTmp(W, H); - array2D blueTmp(W, H); - array2D L(W, H); - - fast_xtrans_interpolate(rawData, redTmp, greenTmp, blueTmp); - const float xyz_rgb[3][3] = { // XYZ from RGB - { 0.412453, 0.357580, 0.180423 }, - { 0.212671, 0.715160, 0.072169 }, - { 0.019334, 0.119193, 0.950227 } - }; - #pragma omp parallel - { - #pragma omp for - for(int i = 0; i < H; ++i) { - Color::RGB2L(red[i], green[i], blue[i], L[i], xyz_rgb, W); - } - } - // calculate contrast based blend factors to use vng4 in regions with low contrast - JaggedArray blend(W, H); - buildBlendMask(L, blend, W, H, contrast / 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 - for(int i = 0; i < H; ++i) { - for(int j = 0; j < W; ++j) { - red[i][j] = intp(blend[i][j], red[i][j], redTmp[i][j]); - } - } - #pragma omp parallel for - for(int i = 0; i < H; ++i) { - for(int j = 0; j < W; ++j) { - green[i][j] = intp(blend[i][j], green[i][j], greenTmp[i][j]); - } - } - #pragma omp parallel for - for(int i = 0; i < H; ++i) { - for(int j = 0; j < W; ++j) { - blue[i][j] = intp(blend[i][j], blue[i][j], blueTmp[i][j]); - } - } - } -} \ No newline at end of file diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index c6f04c053..4c5661773 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -333,7 +333,9 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params if (!batchMode) { dcbOptions->set_visible(pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::DCB)); lmmseOptions->set_visible(pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::LMMSE)); - dualDemosaicOptions->set_visible(pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::AMAZEVNG4)); + dualDemosaicOptions->set_visible(pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::AMAZEVNG4) + || pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::DCBVNG4) + || pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::RCDVNG4)); if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::getMethodString(procparams::RAWParams::BayerSensor::Method::PIXELSHIFT)) { pixelShiftOptions->set_visible(pp->raw.bayersensor.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::PSMotionCorrectionMethod::CUSTOM); pixelShiftFrame->show(); @@ -530,7 +532,7 @@ void BayerProcess::methodChanged () lmmseOptions->hide(); } - if (currentMethod == procparams::RAWParams::BayerSensor::Method::AMAZEVNG4) { + if (currentMethod == procparams::RAWParams::BayerSensor::Method::AMAZEVNG4 || currentMethod == procparams::RAWParams::BayerSensor::Method::DCBVNG4 || currentMethod == procparams::RAWParams::BayerSensor::Method::RCDVNG4) { dualDemosaicOptions->show(); } else { dualDemosaicOptions->hide();