diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 992bf4db3..d075594fd 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -13,7 +13,7 @@ on: jobs: build: - runs-on: macos-11 + runs-on: macos-12 steps: - uses: actions/checkout@v3 - name: Install dependencies diff --git a/AUTHORS.txt b/AUTHORS.txt index 374a7935b..3afd09dc2 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -20,6 +20,7 @@ Development contributors, in last name alphabetical order: Rüdiger Franke Jean-Christophe Frisch Ilias Giarimis + Scott Gilbertson Alberto Griggio Steve Herrell Philippe Hupé diff --git a/CMakeLists.txt b/CMakeLists.txt index abb86a108..7b7c797d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -538,6 +538,10 @@ pkg_check_modules(LCMS REQUIRED lcms2>=2.6) pkg_check_modules(EXPAT REQUIRED expat>=2.1) pkg_check_modules(FFTW3F REQUIRED fftw3f) +# By default little-cms2 uses 'register' keyword which is deprecated and removed in c++17 +# This definition forces not to use it +add_definitions("-DCMS_NO_REGISTER_KEYWORD") + #Set the appropriate FFTW flags on macOS if(APPLE AND OPTION_OMP) set(EXTRA_LIB "-L${LOCAL_PREFIX}/lib -lfftw3f_omp -lfftw3f -lm") diff --git a/rtdata/languages/default b/rtdata/languages/default index 5bdcc7b0f..8c4aa3f74 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1450,6 +1450,7 @@ HISTORY_MSG_LOCALCONTRAST_RADIUS;Local Contrast - Radius HISTORY_MSG_LOCALLAB_TE_PIVOT;Local - Equalizer pivot HISTORY_MSG_LOCAL_DEHAZE_BLACK;Local - Dehaze - black HISTORY_MSG_LOCAL_GAMUTMUNSEL;Local - SC - Avoid Color Shift +HISTORY_MSG_LOCAL_TMO_SATUR;Local Exp Fattal Saturation HISTORY_MSG_METADATA_MODE;Metadata copy mode HISTORY_MSG_MICROCONTRAST_CONTRAST;Microcontrast - Contrast threshold HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CS - Auto threshold @@ -2041,7 +2042,7 @@ PREFERENCES_USEBUNDLEDPROFILES;Use bundled profiles PREFERENCES_WBA;White Balance PREFERENCES_WBACORR;White Balance - Automatic temperature correlation PREFERENCES_WBACORR_TOOLTIP;These settings allow, depending on the images (type of raw file, colorimetry, etc.), an adaptation of the " Temperature correlation " algorithm in order to obtain the best overall results. There is no absolute rule, linking these parameters to the results obtained.\n\nThe settings are of 3 types: \n* those accessible to the user from the GUI.\n* those accessible only in reading from each pp3 file : Itcwb_minsize=20, Itcwb_delta=4 Itcwb_rgreen=1 Itcwb_nopurple=false (See Rawpedia)\n* those accessible to the user in 'options' (see Rawpedia)\n You can use "Awb temperature bias" and "Green refinement" to adjust the results. Each movement of these commands brings a new calculation of temperature, tint and correlation.\n\nPlease note that the 3 indicators 'Correlation factor', 'Patch chroma' and ΔE are given for information only. It is not because one of these indicators is better that the result will necessarily be better. -PREFERENCES_WBAENA;Show White Balance auto Temperature correlation Settings +PREFERENCES_WBAENA;Show White Balance Auto temperature correlation settings PREFERENCES_WBAENACUSTOM;Use Custom temperature & tint PREFERENCES_WBAFORC;Forces Extra algoritm PREFERENCES_WBAGREENDELTA;Delta temperature in green iterate loop (if Force Extra enabled) @@ -2102,10 +2103,12 @@ QINFO_PIXELSHIFT;Pixel Shift / %2 frame(s) QUEUE_AUTOSTART;Auto-start QUEUE_AUTOSTART_TOOLTIP;Start processing automatically when a new job arrives. QUEUE_DESTFILENAME;Path and file name +QUEUE_DESTPREVIEW_TITLE;Select a thumbnail to preview its destination path here +QUEUE_DESTPREVIEW_TOOLTIP;Destination path for the first selected image appears here QUEUE_FORMAT_TITLE;File Format QUEUE_LOCATION_FOLDER;Save to folder QUEUE_LOCATION_TEMPLATE;Use template -QUEUE_LOCATION_TEMPLATE_TOOLTIP;Specify the output location based on the source photo's location, rank, trash status or position in the queue.\n\nUsing the following pathname as an example:\n/home/tom/photos/2010-10-31/photo1.raw\nthe meaning of the formatting strings follows:\n%d4 = home\n%d3 = tom\n%d2 = photos\n%d1 = 2010-10-31\n%f = photo1\n%p1 = /home/tom/photos/2010-10-31/\n%p2 = /home/tom/photos/\n%p3 = /home/tom/\n%p4 = /home/\n\n%r will be replaced by the photo's rank. If the photo is unranked, '0' is used. If the photo is in the trash, 'x' is used.\n\n%s1, ..., %s9 will be replaced by the photo's initial position in the queue at the time the queue is started. The number specifies the padding, e.g. %s3 results in '001'.\n\nIf you want to save the output image alongside the source image, write:\n%p1/%f\n\nIf you want to save the output image in a folder named 'converted' located in the source photo's folder, write:\n%p1/converted/%f\n\nIf you want to save the output image in\n'/home/tom/photos/converted/2010-10-31', write:\n%p2/converted/%d1/%f +QUEUE_LOCATION_TEMPLATE_TOOLTIP;Specify the output location based on the source photo's location, rank, trash status or position in the queue.\n\n%dN, %d-N, %pN, %p-N, %PN and %P-N (N = 1..9) will be replaced by elements of the image file's directory path (not including the file name):\n%dN = Nth directory from the end of the path\n%d-N = Nth directory from the start of the path\n%pN = all directories up to the Nth from the end of the path\n%p-N = the first N directories in the path\n%PN = the last N directories in the path\n%P-N = all directories from the Nth to the end of the path\n%f = base filename (no extension)\nFor Windows paths, %d-1 is the drive letter and colon, and %d-2 is the base directory on that drive.\n\nUsing the following pathname as an example:\n/home/tom/photos/2010-10-31/photo1.raw\nthe meaning of the formatting strings follows:\n%d4 = %d-1 = home\n%d3 = %d-2 = tom\n%d2 = %d-3 = photos\n%d1 = %d-4 = 2010-10-31\n%p1 = %p-4 = /home/tom/photos/2010-10-31/\n%p2 = %p-3 = /home/tom/photos/\n%p3 = %p-2 = /home/tom/\n%p4 = %p-1 = /home/\n%P1 = %P-4 = 2010-10-31/\n%P2 = %P-3 = photos/2010-10-31/\n%P3 = %P-2 = tom/photos/2010-10-31/\n%P4 = %P-1 = /home/tom/photos/2010-10-31/\n%f = photo1\n\n%r will be replaced by the photo's rank. If the photo is unranked, '0' is used. If the photo is in the trash, 'x' is used.\n\n%s1, ..., %s9 will be replaced by the photo's initial position in the queue at the time the queue is started. The number specifies the padding, e.g. %s3 results in '001'.\n\nIf you want to save the output image alongside the source image, write:\n%p1/%f\n\nIf you want to save the output image in a folder named 'converted' located in the source photo's folder, write:\n%p1/converted/%f\n\nIf you want to save the output image in\n'/home/tom/photos/converted/2010-10-31', write:\n%p-3/converted/%P-4/%f QUEUE_LOCATION_TITLE;Output Location QUEUE_STARTSTOP_TOOLTIP;Start or stop processing the images in the queue.\n\nShortcut: Ctrl+s SAMPLEFORMAT_0;Unknown data format @@ -2963,6 +2966,7 @@ TP_LOCALLAB_EXP_TOOLNAME;Dynamic Range & Exposure TP_LOCALLAB_FATAMOUNT;Amount TP_LOCALLAB_FATANCHOR;Anchor TP_LOCALLAB_FATDETAIL;Detail +TP_LOCALLAB_FATSAT;Saturation control TP_LOCALLAB_FATFRA;Dynamic Range Compression ƒ TP_LOCALLAB_FATFRAME_TOOLTIP;PDE Fattal – uses the Fattal Tone-mapping algorithm. TP_LOCALLAB_FATLEVEL;Sigma @@ -4173,7 +4177,7 @@ TP_WBALANCE_ITCWB_CUSTOM;Use Custom temperature & tint TP_WBALANCE_ITCWB_DELTA;Delta temperature in green loop TP_WBALANCE_ITCWB_FGREEN;Find green student TP_WBALANCE_ITCWB_FORCED;Close to full CIE diagram -TP_WBALANCE_ITCWB_FRA;Auto Temperature correlation Settings +TP_WBALANCE_ITCWB_FRA;Auto temperature correlation settings TP_WBALANCE_ITCWB_FRA_TOOLTIP;These settings allow, depending on the images (type of raw, colorimetry, etc.), an adaptation of the 'Temperature correlation' algorithm. There is no absolute rule linking these parameters to the results obtained. TP_WBALANCE_ITCWB_MINSIZEPATCH;Patch minimum size TP_WBALANCE_ITCWB_NOPURPLE;Filter on purple color diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 5a9b2d953..6f329f7be 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -120,6 +120,7 @@ set(RTENGINESOURCEFILES imagedimensions.cc imagefloat.cc imageio.cc + imagesource.cc improccoordinator.cc improcfun.cc impulse_denoise.cc diff --git a/rtengine/camconst.json b/rtengine/camconst.json index 998ddff19..59a2110b2 100644 --- a/rtengine/camconst.json +++ b/rtengine/camconst.json @@ -389,13 +389,18 @@ Camera constants: "ranges": { // black levels are read from raw masked pixels // white levels are same for all colors all ISOs, but safety margin vary on ISO + // actually not, 160 & 320 & 640 seem to have different white levels per color "white": [ { "iso": 50, "levels": 16350 }, // typical for all ISOs: 16383, stdev 2.25 { "iso": 100, "levels": 16350 }, // stdev 2.25 - { "iso": [ 125, 160, 200, 250 ], "levels": 16340 }, // stdev 2.5 - { "iso": [ 320, 400, 500 ], "levels": 16330 }, // stdev 2.95 - { "iso": [ 640, 800, 1000 ], "levels": 16320 }, // stdev x, 4.0 , x - { "iso": [ 1250, 1600, 2000 ], "levels": 16300 }, // stdev x, 6.0 , x + { "iso": [ 125, 200, 250 ], "levels": 16340 }, // stdev 2.5 + { "iso": 160, "levels": [ 16340, 14450, 15600 ] }, // based on CarVac's testing + { "iso": [ 400, 500 ], "levels": 16330 }, // stdev 2.95 + { "iso": 320, "levels": [ 16330, 16000, 16330 ] }, // based on CarVac's testing + { "iso": [ 800, 1000 ], "levels": 16320 }, // stdev x, 4.0 , x + { "iso": 640, "levels": [ 16320, 15800, 16320 ] }, // based on CarVac's testing + { "iso": [ 1600, 2000 ], "levels": 16300 }, // stdev x, 6.0 , x + { "iso": 1250, "levels": [ 16300, 16050, 16300 ] }, // based on CarVac's testing { "iso": [ 2500, 3200, 4000 ], "levels": 16250 }, // STDEV x, 9.8 , x { "iso": [ 5000, 6400, 8000 ], "levels": 16150 }, // stdev x, 17, x { "iso": [ 10000, 12800, 16000 ], "levels": 16100 }, // stdev x, 34 , x diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 6abfc7aba..85661edd3 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -781,7 +781,7 @@ void Crop::update(int todo) if (need_fattal) { parent->ipf.dehaze(f, params.dehaze); - parent->ipf.ToneMapFattal02(f, params.fattal, 3, 0, nullptr, 0, 0, 0); + parent->ipf.ToneMapFattal02(f, params.fattal, 3, 0, nullptr, 0, 0, 0, false); } // crop back to the size expected by the rest of the pipeline diff --git a/rtengine/dfmanager.cc b/rtengine/dfmanager.cc index 52de10339..5ad664b52 100644 --- a/rtengine/dfmanager.cc +++ b/rtengine/dfmanager.cc @@ -317,7 +317,6 @@ private: typedef std::map > bpList_t; dfList_t dfList; bpList_t bpList; - bool initialized; Glib::ustring currentPath; dfInfo* addFileInfo(const Glib::ustring &filename, bool pool = true); dfInfo* find(const std::string &mak, const std::string &mod, int isospeed, double shut, time_t t); diff --git a/rtengine/iccstore.cc b/rtengine/iccstore.cc index 8ba5fb4af..af1b94fbe 100644 --- a/rtengine/iccstore.cc +++ b/rtengine/iccstore.cc @@ -18,6 +18,7 @@ */ #include #include +#include #include #include @@ -51,9 +52,9 @@ namespace // Not recursive void loadProfiles( const Glib::ustring& dirName, - std::map* profiles, - std::map* profileContents, - std::map* profileNames, + std::map* profiles, + std::map* profileContents, + std::map* profileNames, bool nameUpper ) { @@ -114,8 +115,8 @@ void loadProfiles( bool loadProfile( const Glib::ustring& profile, const Glib::ustring& dirName, - std::map* profiles, - std::map* profileContents + std::map* profiles, + std::map* profileContents ) { if (dirName.empty() || profiles == nullptr) { @@ -996,10 +997,10 @@ parse_error: return false; } - using ProfileMap = std::map; - using MatrixMap = std::map; - using ContentMap = std::map; - using NameMap = std::map; + using ProfileMap = std::map; + using MatrixMap = std::map; + using ContentMap = std::map; + using NameMap = std::map; ProfileMap wProfiles; // ProfileMap wProfilesGamma; diff --git a/rtengine/imagesource.cc b/rtengine/imagesource.cc new file mode 100644 index 000000000..049c6b562 --- /dev/null +++ b/rtengine/imagesource.cc @@ -0,0 +1,275 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2024 RawTherapee team + * + * 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 + * (at your option) any later version. + * + * RawTherapee 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 RawTherapee. If not, see . + */ + +#include "imagesource.h" +#include "procparams.h" + + +namespace rtengine +{ + +void ImageSource::getAutoWBMultipliersItcGreen( + procparams::ProcParams ¶ms, + bool forcewbgrey, + int kcam, + double greenitc, + bool extra, + float &temp0, + float &delta, + int &bia, + int &dread, + int nocam, + float &studgood, + float &minchrom, + int &kmin, + float &minhist, + float &maxhist, + int fh, + int fw, + ColorTemp &currWB, + int tempnotisraw, + double greennotisraw, + bool skipRecalculate, + ColorTemp &autoWB, + double &rm, + double &gm, + double &bm + ) +{ + float tem = 5000.f; + float gre = 1.f; + double tempref0bias = 5000.; + double tempitc = 5000.f; + bool autowb1 = true; + double green_thres = 0.8; + + if (isRAW()) {// only with Raw files + + auto currWBitc = getWB(); + + double greenref = currWBitc.getGreen(); + double tempref0bias0 = currWBitc.getTemp(); + + if (greenref > green_thres && params.wb.itcwb_prim == "srgb") { + forcewbgrey = true; + } + + if (!forcewbgrey && (tempref0bias0 < 3300.f) && (greenref < 1.13f && greenref > 0.88f)) { //seems good with temp and green...To fixe...limits 1.13 and 0.88 + if (settings->verbose) { + printf("Keep camera settings temp=%f green=%f\n", tempref0bias0, greenref); + } + + autowb1 = true; + kcam = 1; + } + + if (autowb1) { + //alternative to camera if camera settings out, using autowb grey to find new ref, then mixed with camera + // kcam = 0; + params.wb.method = "autold"; + tempitc = 5000.f; + greenitc = 1.; + currWBitc = getWB(); + tempref0bias = currWBitc.getTemp(); + double greenref = currWBitc.getGreen(); + bool pargref = true; + bool pargre = true; + + if ((greenref > 1.5f || tempref0bias < 3300.f || tempref0bias > 7700.f || forcewbgrey) && kcam != 1 && !params.wb.itcwb_sampling) { //probably camera out to adjust... + getAutoWBMultipliersitc(extra, tempref0bias, greenref, tempitc, greenitc, temp0, delta, bia, dread, kcam, nocam, studgood, minchrom, kmin, minhist, maxhist, 0, 0, fh, fw, 0, 0, fh, fw, rm, gm, bm, params.wb, params.icm, params.raw, params.toneCurve); + wbMul2Camera(rm, gm, bm); + wbCamera2Mul(rm, gm, bm); + ColorTemp ct(rm, gm, bm, 1.0, currWB.getObserver()); + tem = ct.getTemp(); + gre = ct.getGreen(); + + if (gre > 1.3f) { + pargre = false; + } + + if (greenref > 1.3f) { + pargref = false; + } + + double deltemp = tem - tempref0bias; + + if (gre > 1.5f && !forcewbgrey) { //probable wrong value + tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value + gre = 0.5f + 0.5f * LIM(gre, 0.9f, 1.1f);//empirical formula in case system out + } else { + if (!forcewbgrey) { + gre = 0.2f + 0.8f * LIM(gre, 0.85f, 1.15f); + tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value + nocam = 0; + } else {//set temp and green to init itcwb algorithm + double grepro = LIM(greenref, green_thres, 1.15); + gre = 0.5f * grepro + 0.5f * LIM(gre, 0.9f, 1.1f);//empirical green between green camera and autowb grey + + if (abs(deltemp) < 400.) { //arbitraries thresholds to refine + tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey + + if (deltemp > 0.) { + nocam = 1; + } else { + nocam = 2; + } + } else if (abs(deltemp) < 900.) { //other arbitrary threshold + tem = 0.4 * tem + 0.6 * tempref0bias;//find a mixed value between camera and auto grey + + if (deltemp > 0.) { + nocam = 3; + } else { + nocam = 4; + } + } else if (abs(deltemp) < 1500. && tempref0bias < 4500.f) { + if ((pargre && pargref) || (!pargre && !pargref)) { + tem = 0.45 * tem + 0.55 * tempref0bias;//find a mixed value between camera and auto grey + } + + if (pargre && !pargref) { + tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey + } + + if (!pargre && pargref) { + tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey + } + + nocam = 5; + } else if (abs(deltemp) < 1500. && tempref0bias >= 4500.f) { + if ((pargre && pargref) || (!pargre && !pargref)) { + tem = 0.45 * tem + 0.55 * tempref0bias;//find a mixed value between camera and auto grey + } + + if (pargre && !pargref) { + tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey + } + + if (!pargre && pargref) { + tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey + } + + nocam = 6; + } else if (abs(deltemp) >= 1500. && tempref0bias < 5500.f) { + if (tem >= 4500.f) { + if ((pargre && pargref) || (!pargre && !pargref)) { + tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey + } + + if (pargre && !pargref) { + tem = 0.8 * tem + 0.2 * tempref0bias;//find a mixed value between camera and auto grey + } + + if (!pargre && pargref) { + tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey + } + + nocam = 7; + } else { + tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey + nocam = 8; + } + } else if (abs(deltemp) >= 1500. && tempref0bias >= 5500.f) { + if (tem >= 10000.f) { + tem = 0.99 * tem + 0.01 * tempref0bias;//find a mixed value between camera and auto grey + nocam = 9; + } else { + if ((pargre && pargref) || (!pargre && !pargref)) { + tem = 0.45 * tem + 0.55 * tempref0bias;//find a mixed value between camera and auto grey + } + + if (pargre && !pargref) { + tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey + } + + if (!pargre && pargref) { + tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey + } + + nocam = 10; + } + } else { + tem = 0.4 * tem + 0.6 * tempref0bias; + nocam = 11; + } + } + } + + tempitc = tem ; + + extra = true; + + if (settings->verbose) { + printf("Using new references AWB grey or mixed Enable Extra - temgrey=%f gregrey=%f tempitc=%f nocam=%i\n", (double) tem, (double) gre, (double) tempitc, nocam); + } + } + } + + params.wb.method = "autitcgreen"; + + } else if (!isRAW()) { // Itcwb and no raw + params.wb.temperature = tempnotisraw; + params.wb.green = greennotisraw; + params.wb.equal = 1.; + } + float greenitc_low = 1.f; + float tempitc_low = 5000.f; + //raw files and autitcgreen + if (isRAW() || !skipRecalculate) { + greenitc = 1.; + auto currWBitc = getWB(); + currWBitc = currWBitc.convertObserver(params.wb.observer);//change the temp/green couple with the same multipliers + + double tempref = currWBitc.getTemp() * (1. + params.wb.tempBias); + double greenref = currWBitc.getGreen(); + greenitc = greenref; + + if ((greenref > 1.5f || tempref0bias < 3300.f || tempref0bias > 7700.f || forcewbgrey) && autowb1 && kcam != 1 && !params.wb.itcwb_sampling) { //probably camera out to adjust = greenref ? tempref0bias ? + tempref = tem * (1. + params.wb.tempBias); + greenref = gre; + } else { + + } + + if(params.wb.itcwb_sampling) { + greenitc_low = greenref; + tempitc_low = tempref; + } + + if (settings->verbose) { + printf("tempref=%f greref=%f tempitc=%f greenitc=%f\n", tempref, greenref, tempitc, greenitc); + } + + getAutoWBMultipliersitc(extra, tempref, greenref, tempitc, greenitc, temp0, delta, bia, dread, kcam, nocam, studgood, minchrom, kmin, minhist, maxhist, 0, 0, fh, fw, 0, 0, fh, fw, rm, gm, bm, params.wb, params.icm, params.raw, params.toneCurve); + + params.wb.temperature = tempitc; + params.wb.green = greenitc; + if(params.wb.itcwb_sampling) { + params.wb.temperature = tempitc_low; + params.wb.green = greenitc_low; + } + + currWB = ColorTemp(params.wb.temperature, params.wb.green, 1., params.wb.method, params.wb.observer); + currWB.getMultipliers(rm, gm, bm); + autoWB.update(rm, gm, bm, params.wb.equal, params.wb.observer, 0.); //params.wb.tempBias already used before + + } +} + +} // namespace rtengine + diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 95fd77d21..50bc38baf 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -118,6 +118,33 @@ public: virtual void convertColorSpace (Imagefloat* image, const procparams::ColorManagementParams &cmp, const ColorTemp &wb) = 0; // DIRTY HACK: this method is derived in rawimagesource and strimagesource, but (...,RAWParams raw) will be used ONLY for raw images virtual void getAutoWBMultipliers (double &rm, double &gm, double &bm) = 0; virtual void getAutoWBMultipliersitc(bool extra, double &tempref, double &greenref, double &tempitc, double & greenitc, float &temp0, float &delta, int &bia, int &dread, int &kcam, int &nocam, float &studgood, float &minchrom, int &kmin, float &minhist, float &maxhist, int begx, int begy, int yEn, int xEn, int cx, int cy, int bf_h, int bf_w, double &rm, double &gm, double &bm, const procparams::WBParams & wbpar, const procparams::ColorManagementParams &cmp, const procparams::RAWParams &raw, const procparams::ToneCurveParams &hrp) = 0; + virtual void getAutoWBMultipliersItcGreen( + procparams::ProcParams ¶ms, + bool forcewbgrey, + int kcam, + double greenitc, + bool extra, + float &temp0, + float &delta, + int &bia, + int &dread, + int nocam, + float &studgood, + float &minchrom, + int &kmin, + float &minhist, + float &maxhist, + int fh, + int fw, + ColorTemp &currWB, + int tempnotisraw, + double greennotisraw, + bool skipRecalculate, + ColorTemp &autoWB, + double &rm, + double &gm, + double &bm + ); virtual ColorTemp getWB () const = 0; virtual ColorTemp getSpotWB (std::vector &red, std::vector &green, std::vector &blue, int tran, double equal, StandardObserver observer) = 0; virtual void WBauto(bool extra, double &tempref, double &greenref, array2D &redloc, array2D &greenloc, array2D &blueloc, int bfw, int bfh, double &avg_rm, double &avg_gm, double &avg_bm, double &tempitc, double &greenitc, float &temp0, float &delta, int &bia, int &dread, int &kcam, int &nocam, float &studgood, float &minchrom, int &kmin, float &minhist, float &maxhist, bool &twotimes, const procparams::WBParams & wbpar, int begx, int begy, int yEn, int xEn, int cx, int cy, const procparams::ColorManagementParams &cmp, const procparams::RAWParams &raw, const procparams::ToneCurveParams &hrp) = 0; diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index ef8dc8dbf..5358f2880 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -518,7 +518,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } } - const bool autowb = (params->wb.method == "autold" || params->wb.method == "autitcgreen"); + // const bool autowb = (params->wb.method == "autold" || params->wb.method == "autitcgreen"); if (settings->verbose) { printf("automethod=%s \n", params->wb.method.c_str()); @@ -534,6 +534,24 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } currWB = ColorTemp(params->wb.temperature, params->wb.green, params->wb.equal, params->wb.method, params->wb.observer); + int tempnotisraw = 6501;//D65 with Observer 2° - 6473 with Observer 10° + double greennotisraw = 1.;//D65 with Observer 2° - 0.967 with Observer 10° + + if (!imgsrc->isRAW() && params->wb.method == "autitcgreen") { + if (params->wb.compat_version == 1) { + // ITCWB compatibility version 1 used 5000 K and observer 10 + // degrees for non-raw files. + auto currWBitc = ColorTemp(5000., 1., 1., params->wb.method, StandardObserver::TEN_DEGREES); + currWBitc = currWBitc.convertObserver(params->wb.observer); + tempnotisraw = currWBitc.getTemp(); + greennotisraw = currWBitc.getGreen(); + } else { + auto currWBitc = imgsrc->getWB();//if jpg TIF with another illuminant + currWBitc = currWBitc.convertObserver(params->wb.observer); + tempnotisraw = currWBitc.getTemp(); + greennotisraw = currWBitc.getGreen(); + } + } int dread = 0; int bia = 1; @@ -545,7 +563,6 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) int kmin = 20; float minhist = 1000000000.f; float maxhist = -1000.f; - double tempitc = 5000.f; double greenitc = 1.; float temp0 = 5000.f; bool extra = false; @@ -556,230 +573,21 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } else if (params->wb.method == "Camera") { currWB = imgsrc->getWB(); lastAwbauto = ""; //reinitialize auto - } else if (autowb) { - float tem = 5000.f; - float gre = 1.f; - double tempref0bias = 5000.; - tempitc = 5000.f; - bool autowb1 = true; - double green_thres = 0.8; - - if (params->wb.method == "autitcgreen") { - - currWBitc = imgsrc->getWB(); - - double greenref = currWBitc.getGreen(); - double tempref0bias0 = currWBitc.getTemp(); - - if (greenref > green_thres && params->wb.itcwb_prim == "srgb") { - forcewbgrey = true; - } - - if (!forcewbgrey && (tempref0bias0 < 3300.f) && (greenref < 1.13f && greenref > 0.88f)) { //seems good with temp and green...To fixe...limits 1.13 and 0.88 - if (settings->verbose) { - printf("Keep camera settings temp=%f green=%f\n", tempref0bias0, greenref); - } - - autowb1 = true; - kcam = 1; - } - - if (autowb1) { - //alternative to camera if camera settings out, using autowb grey to find new ref, then mixed with camera - // kcam = 0; - params->wb.method = "autold"; - double rm, gm, bm; - tempitc = 5000.f; - greenitc = 1.; - currWBitc = imgsrc->getWB(); - tempref0bias = currWBitc.getTemp(); - double greenref = currWBitc.getGreen(); - bool pargref = true; - bool pargre = true; - - if ((greenref > 1.5f || tempref0bias < 3300.f || tempref0bias > 7700.f || forcewbgrey) && kcam != 1 && !params->wb.itcwb_sampling) { //probably camera out to adjust... - imgsrc->getAutoWBMultipliersitc(extra, tempref0bias, greenref, tempitc, greenitc, temp0, delta, bia, dread, kcam, nocam, studgood, minchrom, kmin, minhist, maxhist, 0, 0, fh, fw, 0, 0, fh, fw, rm, gm, bm, params->wb, params->icm, params->raw, params->toneCurve); - imgsrc->wbMul2Camera(rm, gm, bm); - imgsrc->wbCamera2Mul(rm, gm, bm); - ColorTemp ct(rm, gm, bm, 1.0, currWB.getObserver()); - tem = ct.getTemp(); - gre = ct.getGreen(); - - if (gre > 1.3f) { - pargre = false; - } - - if (greenref > 1.3f) { - pargref = false; - } - - double deltemp = tem - tempref0bias; - - if (gre > 1.5f && !forcewbgrey) { //probable wrong value - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value - gre = 0.5f + 0.5f * LIM(gre, 0.9f, 1.1f);//empirical formula in case system out - } else { - if (!forcewbgrey) { - gre = 0.2f + 0.8f * LIM(gre, 0.85f, 1.15f); - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value - nocam = 0; - } else {//set temp and green to init itcwb algorithm - double grepro = LIM(greenref, green_thres, 1.15); - gre = 0.5f * grepro + 0.5f * LIM(gre, 0.9f, 1.1f);//empirical green between green camera and autowb grey - - if (abs(deltemp) < 400.) { //arbitraries thresholds to refine - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - - if (deltemp > 0.) { - nocam = 1; - } else { - nocam = 2; - } - } else if (abs(deltemp) < 900.) { //other arbitrary threshold - tem = 0.4 * tem + 0.6 * tempref0bias;//find a mixed value between camera and auto grey - - if (deltemp > 0.) { - nocam = 3; - } else { - nocam = 4; - } - } else if (abs(deltemp) < 1500. && tempref0bias < 4500.f) { - if ((pargre && pargref) || (!pargre && !pargref)) { - tem = 0.45 * tem + 0.55 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (pargre && !pargref) { - tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (!pargre && pargref) { - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - } - - nocam = 5; - } else if (abs(deltemp) < 1500. && tempref0bias >= 4500.f) { - if ((pargre && pargref) || (!pargre && !pargref)) { - tem = 0.45 * tem + 0.55 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (pargre && !pargref) { - tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (!pargre && pargref) { - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - } - - nocam = 6; - } else if (abs(deltemp) >= 1500. && tempref0bias < 5500.f) { - if (tem >= 4500.f) { - if ((pargre && pargref) || (!pargre && !pargref)) { - tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (pargre && !pargref) { - tem = 0.8 * tem + 0.2 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (!pargre && pargref) { - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - } - - nocam = 7; - } else { - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - nocam = 8; - } - } else if (abs(deltemp) >= 1500. && tempref0bias >= 5500.f) { - if (tem >= 10000.f) { - tem = 0.99 * tem + 0.01 * tempref0bias;//find a mixed value between camera and auto grey - nocam = 9; - } else { - if ((pargre && pargref) || (!pargre && !pargref)) { - tem = 0.45 * tem + 0.55 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (pargre && !pargref) { - tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (!pargre && pargref) { - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - } - - nocam = 10; - } - } else { - tem = 0.4 * tem + 0.6 * tempref0bias; - nocam = 11; - } - } - } - - tempitc = tem ; - - extra = true; - - if (settings->verbose) { - printf("Using new references AWB grey or mixed Enable Extra - temgrey=%f gregrey=%f tempitc=%f nocam=%i\n", (double) tem, (double) gre, (double) tempitc, nocam); - } - } - } - - params->wb.method = "autitcgreen"; - - } - float greenitc_low = 1.f; - float tempitc_low = 5000.f; - if (params->wb.method == "autitcgreen" || lastAwbEqual != params->wb.equal || lastAwbObserver != params->wb.observer || lastAwbTempBias != params->wb.tempBias || lastAwbauto != params->wb.method) { + + } else if (params->wb.method == "autold") { + if (lastAwbEqual != params->wb.equal || lastAwbTempBias != params->wb.tempBias || lastAwbauto != params->wb.method) { double rm, gm, bm; - greenitc = 1.; - currWBitc = imgsrc->getWB(); - currWBitc = currWBitc.convertObserver(params->wb.observer);//change the temp/green couple with the same multipliers - - double tempref = currWBitc.getTemp() * (1. + params->wb.tempBias); - double greenref = currWBitc.getGreen(); - greenitc = greenref; - - if ((greenref > 1.5f || tempref0bias < 3300.f || tempref0bias > 7700.f || forcewbgrey) && autowb1 && kcam != 1 && !params->wb.itcwb_sampling) { //probably camera out to adjust = greenref ? tempref0bias ? - tempref = tem * (1. + params->wb.tempBias); - greenref = gre; + if (params->wb.compat_version == 1 && !imgsrc->isRAW()) { + // RGB grey compatibility version 1 used the identity + // multipliers plus temperature bias for non-raw files. + rm = gm = bm = 1.; } else { - - } - - if(params->wb.itcwb_sampling) { - greenitc_low = greenref; - tempitc_low = tempref; - } - - if (settings->verbose && params->wb.method == "autitcgreen") { - printf("tempref=%f greref=%f tempitc=%f greenitc=%f\n", tempref, greenref, tempitc, greenitc); - } - - imgsrc->getAutoWBMultipliersitc(extra, tempref, greenref, tempitc, greenitc, temp0, delta, bia, dread, kcam, nocam, studgood, minchrom, kmin, minhist, maxhist, 0, 0, fh, fw, 0, 0, fh, fw, rm, gm, bm, params->wb, params->icm, params->raw, params->toneCurve); - - if (params->wb.method == "autitcgreen") { - params->wb.temperature = tempitc; - params->wb.green = greenitc; - if(params->wb.itcwb_sampling) { - params->wb.temperature = tempitc_low; - params->wb.green = greenitc_low; - } - - currWB = ColorTemp(params->wb.temperature, params->wb.green, 1., params->wb.method, params->wb.observer); - currWB.getMultipliers(rm, gm, bm); - autoWB.update(rm, gm, bm, params->wb.equal, params->wb.observer, params->wb.tempBias); + imgsrc->getAutoWBMultipliers(rm, gm, bm); } if (rm != -1.) { - double bias = params->wb.tempBias; - if (params->wb.method == "autitcgreen") { - bias = 0.; - } - autoWB.update(rm, gm, bm, params->wb.equal, params->wb.observer, bias); lastAwbEqual = params->wb.equal; lastAwbObserver = params->wb.observer; @@ -791,6 +599,58 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) lastAwbTempBias = 0.0; lastAwbauto = ""; autoWB.useDefaults(params->wb.equal, params->wb.observer); + + } + + //double rr,gg,bb; + //autoWB.getMultipliers(rr,gg,bb); + } + currWB = autoWB; + // lastAwbauto = ""; //reinitialize auto + } else if (params->wb.method == "autitcgreen") { //(// autowb) { + double rm; + double gm; + double bm; + imgsrc->getAutoWBMultipliersItcGreen( + *params, + forcewbgrey, + kcam, + greenitc, + extra, + temp0, + delta, + bia, + dread, + nocam, + studgood, + minchrom, + kmin, + minhist, + maxhist, + fh, + fw, + currWB, + tempnotisraw, + greennotisraw, + lastAwbEqual == params->wb.equal && lastAwbObserver == params->wb.observer && lastAwbTempBias == params->wb.tempBias && lastAwbauto == params->wb.method, + autoWB, + rm, + gm, + bm); + + if (imgsrc->isRAW() || lastAwbEqual != params->wb.equal || lastAwbObserver != params->wb.observer || lastAwbTempBias != params->wb.tempBias || lastAwbauto != params->wb.method) { + if (rm != -1.) { + autoWB.update(rm, gm, bm, params->wb.equal, params->wb.observer); + lastAwbEqual = params->wb.equal; + lastAwbObserver = params->wb.observer; + lastAwbTempBias = params->wb.tempBias; + lastAwbauto = params->wb.method; + } else { + lastAwbEqual = -1.; + lastAwbObserver = ColorTemp::DEFAULT_OBSERVER; + lastAwbTempBias = 0.0; + lastAwbauto = ""; + autoWB.useDefaults(params->wb.equal, params->wb.observer); } } @@ -822,12 +682,12 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) int met = 0; - if (awbListener) { - if (params->wb.method == "autitcgreen") { + if (awbListener && params->wb.enabled) { + if (params->wb.method == "autitcgreen" && imgsrc->isRAW()) {//Raw files if (params->wb.itcwb_sampling) { dread = 1; studgood = 1.f; - awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, 0, 1, 0, dread, studgood, 0, 0, 0, 0); + awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, 0, 1, 0, dread, studgood, 0, 0, 0, 0, AutoWBListener::AWBMode::TEMP_CORRELATION_RAW); } else { minchrom = LIM(minchrom, 0.f, 0.9f); @@ -836,10 +696,19 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) maxhist = std::max(maxhist, 1000.f); kmin = std::max(kmin, 18); dread = LIM(dread, 10, 239); - awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, temp0, delta, bia, dread, studgood, minchrom, kmin, minhist, maxhist); + awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, temp0, delta, bia, dread, studgood, minchrom, kmin, minhist, maxhist, AutoWBListener::AWBMode::TEMP_CORRELATION_RAW); } + } else if (params->wb.method == "autitcgreen" && !imgsrc->isRAW()) {//non raw files + params->wb.temperature = tempnotisraw; + params->wb.green = greennotisraw; + currWB = ColorTemp(params->wb.temperature, params->wb.green, params->wb.equal, params->wb.method, params->wb.observer); + + awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, -1.f, -1.f, 1, 1, -1.f, -1.f, 1, -1.f, -1.f, AutoWBListener::AWBMode::TEMP_CORRELATION_NON_RAW);//false => hide settings + + } else if (params->wb.method == "autold"){ + awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, -1.f, -1.f, 1, 1, -1.f, -1.f, 1, -1.f, -1.f, AutoWBListener::AWBMode::RGB_GREY); } else { - awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, -1.f, -1.f, 1, 1, -1.f, -1.f, 1, -1.f, -1.f); + awbListener->WBChanged(met, params->wb.temperature, params->wb.green, rw, gw, bw, -1.f, -1.f, 1, 1, -1.f, -1.f, 1, -1.f, -1.f, AutoWBListener::AWBMode::NONE); } } @@ -980,7 +849,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } ipf.dehaze(orig_prev, params->dehaze); - ipf.ToneMapFattal02(orig_prev, params->fattal, 3, 0, nullptr, 0, 0, 0); + ipf.ToneMapFattal02(orig_prev, params->fattal, 3, 0, nullptr, 0, 0, 0, false); if (oprevi != orig_prev) { delete oprevi; diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index 7e4e3bb06..e236aed17 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -78,7 +78,6 @@ protected: ColorTemp autoWB; ColorTemp currWBloc; ColorTemp autoWBloc; - ColorTemp currWBitc; double lastAwbEqual; StandardObserver lastAwbObserver{ColorTemp::DEFAULT_OBSERVER}; diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 62dfba0a0..b956e6c4b 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -492,7 +492,7 @@ enum class BlurType { void dehaze(Imagefloat *rgb, const procparams::DehazeParams &dehazeParams); void dehazeloc(Imagefloat *rgb, const procparams::DehazeParams &dehazeParams, int sk, int sp); - void ToneMapFattal02(Imagefloat *rgb, const procparams::FattalToneMappingParams &fatParams, int detail_level, int Lalone, float **Lum, int WW, int HH, int algo); + void ToneMapFattal02(Imagefloat *rgb, const procparams::FattalToneMappingParams &fatParams, int detail_level, int Lalone, float **Lum, int WW, int HH, int algo, bool sat); void localContrast(LabImage *lab, float **destination, const procparams::LocalContrastParams &localContrastParams, bool fftwlc, double scale); void colorToningLabGrid(LabImage *lab, int xstart, int xend, int ystart, int yend, bool MultiThread); //void shadowsHighlights(LabImage *lab); diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc index bf7d1a391..3d02c9df6 100644 --- a/rtengine/iplocallab.cc +++ b/rtengine/iplocallab.cc @@ -6590,7 +6590,7 @@ void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int Imagefloat *tmpImagefat = nullptr; tmpImagefat = new Imagefloat(bfw, bfh); lab2rgb(*bufmaskblurcol, *tmpImagefat, params->icm.workingProfile); - ToneMapFattal02(tmpImagefat, fatParams, nlev, 0, nullptr, 0, 0, 0); + ToneMapFattal02(tmpImagefat, fatParams, nlev, 0, nullptr, 0, 0, 0, false); rgb2lab(*tmpImagefat, *bufmaskblurcol, params->icm.workingProfile); delete tmpImagefat; } @@ -8699,8 +8699,10 @@ void ImProcFunctions::transit_shapedetect2(int sp, float meantm, float stdtm, in const float dE = rsob + std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); //reduction action with deltaE - const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); - + float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + if(varsens == 100.f) { + reducdE = 1.f; + } float cli = (bufexpfin->L[y][x] - bufexporig->L[y][x]); float cla = (bufexpfin->a[y][x] - bufexporig->a[y][x]); float clb = (bufexpfin->b[y][x] - bufexporig->b[y][x]); @@ -17054,7 +17056,11 @@ void ImProcFunctions::Lab_Local( if(fatParams.anchor == 50.f) { alg = 1; } - ToneMapFattal02(tmpImagefat.get(), fatParams, 3, 0, nullptr, 0, 0, alg);//last parameter = 1 ==>ART algorithm + bool satu = false; + if(params->locallab.spots.at(sp).fatsatur) { + satu = true; + } + ToneMapFattal02(tmpImagefat.get(), fatParams, 3, 0, nullptr, 0, 0, alg, satu);//last parameter alg = 1 ==>ART algorithm rgb2lab(*tmpImagefat, *bufexpfin, params->icm.workingProfile); if (params->locallab.spots.at(sp).expcie && params->locallab.spots.at(sp).modecie == "dr") { bool HHcurvejz = false, CHcurvejz = false, LHcurvejz = false; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index beaf66ffd..2af043cd9 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1409,7 +1409,8 @@ WBParams::WBParams() : itcwb_nopurple(false),//keep for settings itcwb_alg(false),//checkbox itcwb_prim("beta"),//combobox - itcwb_sampling(false)//keep for 5.9 and for settings + itcwb_sampling(false),//keep for 5.9 and for settings + compat_version(WBParams::CURRENT_COMPAT_VERSION) { } @@ -1434,6 +1435,7 @@ bool WBParams::isPanningRelatedChange(const WBParams& other) const && itcwb_green == other.itcwb_green && itcwb_prim == other.itcwb_prim && itcwb_alg == other.itcwb_alg + && compat_version == other.compat_version ) ) @@ -1455,7 +1457,8 @@ bool WBParams::operator ==(const WBParams& other) const && itcwb_nopurple == other.itcwb_nopurple && itcwb_alg == other.itcwb_alg && itcwb_prim == other.itcwb_prim - && itcwb_sampling == other.itcwb_sampling; + && itcwb_sampling == other.itcwb_sampling + && compat_version == other.compat_version; } @@ -3344,6 +3347,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : gamm(0.4), fatamount(1.0), fatdetail(40.0), + fatsatur(false), fatanchor(50.0), fatlevel(1.), recothrese(1.), @@ -4824,6 +4828,7 @@ bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const && gamm == other.gamm && fatamount == other.fatamount && fatdetail == other.fatdetail + && fatsatur == other.fatsatur && fatanchor == other.fatanchor && fatlevel == other.fatlevel && recothrese == other.recothrese @@ -6315,6 +6320,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->wb.itcwb_alg, "White Balance", "Itcwb_alg", wb.itcwb_alg, keyFile); saveToKeyfile(!pedited || pedited->wb.itcwb_prim, "White Balance", "Itcwb_prim", wb.itcwb_prim, keyFile); saveToKeyfile(!pedited || pedited->wb.itcwb_sampling, "White Balance", "Itcwb_sampling", wb.itcwb_sampling, keyFile); + saveToKeyfile(!pedited || pedited->wb.compat_version, "White Balance", "CompatibilityVersion", wb.compat_version, keyFile); // Colorappearance saveToKeyfile(!pedited || pedited->colorappearance.enabled, "Color appearance", "Enabled", colorappearance.enabled, keyFile); @@ -6712,6 +6718,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || spot_edited->gamm, "Locallab", "Gamm_" + index_str, spot.gamm, keyFile); saveToKeyfile(!pedited || spot_edited->fatamount, "Locallab", "Fatamount_" + index_str, spot.fatamount, keyFile); saveToKeyfile(!pedited || spot_edited->fatdetail, "Locallab", "Fatdetail_" + index_str, spot.fatdetail, keyFile); + saveToKeyfile(!pedited || spot_edited->fatsatur, "Locallab", "Fatsatur_" + index_str, spot.fatsatur, keyFile); saveToKeyfile(!pedited || spot_edited->fatanchor, "Locallab", "Fatanchor_" + index_str, spot.fatanchor, keyFile); saveToKeyfile(!pedited || spot_edited->fatlevel, "Locallab", "Fatlevel_" + index_str, spot.fatlevel, keyFile); saveToKeyfile(!pedited || spot_edited->recothrese, "Locallab", "Recothrese_" + index_str, spot.recothrese, keyFile); @@ -8295,7 +8302,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "White Balance", "Enabled", wb.enabled, pedited->wb.enabled); assignFromKeyfile(keyFile, "White Balance", "Setting", wb.method, pedited->wb.method); if (wb.method == "Auto") { - wb.method = "autold"; + wb.method = "autitcgreen"; //"autold"; } assignFromKeyfile(keyFile, "White Balance", "Temperature", wb.temperature, pedited->wb.temperature); assignFromKeyfile(keyFile, "White Balance", "Green", wb.green, pedited->wb.green); @@ -8320,6 +8327,19 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) } } assignFromKeyfile(keyFile, "White Balance", "Itcwb_sampling", wb.itcwb_sampling, pedited->wb.itcwb_sampling); + if (!assignFromKeyfile(keyFile, "White Balance", "CompatibilityVersion", wb.compat_version, pedited->wb.compat_version)) { + bool compat_version_edited = true; + if (ppVersion <= 346) { // 5.8 and earlier. + wb.compat_version = 0; + } else if (ppVersion <= 349) { // 5.9. + wb.compat_version = 1; + } else { + compat_version_edited = false; + } + if (pedited) { + pedited->wb.compat_version = pedited->wb.compat_version || compat_version_edited; + } + } } if (keyFile.has_group("Defringing")) { @@ -8895,6 +8915,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Locallab", "Gamm_" + index_str, spot.gamm, spotEdited.gamm); assignFromKeyfile(keyFile, "Locallab", "Fatamount_" + index_str, spot.fatamount, spotEdited.fatamount); assignFromKeyfile(keyFile, "Locallab", "Fatdetail_" + index_str, spot.fatdetail, spotEdited.fatdetail); + assignFromKeyfile(keyFile, "Locallab", "Fatsatur_" + index_str, spot.fatsatur, spotEdited.fatsatur); assignFromKeyfile(keyFile, "Locallab", "Fatanchor_" + index_str, spot.fatanchor, spotEdited.fatanchor); assignFromKeyfile(keyFile, "Locallab", "Fatlevel_" + index_str, spot.fatlevel, spotEdited.fatlevel); assignFromKeyfile(keyFile, "Locallab", "Recothrese_" + index_str, spot.recothrese, spotEdited.recothrese); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index de0fbb598..ba3e2d5e9 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -636,6 +636,8 @@ struct WBEntry { }; struct WBParams { + static constexpr int CURRENT_COMPAT_VERSION = 2; + bool enabled; Glib::ustring method; int temperature; @@ -649,6 +651,24 @@ struct WBParams { bool itcwb_alg; Glib::ustring itcwb_prim; bool itcwb_sampling; + /** + * Used to maintain edits from previous versions of RawTherapee where + * compatibility cannot be maintained simply by converting the parameters, + * for example, when the output depends on the file type. + * + * Version 0: + * - Base version. + * Version 1 (5.9): + * - RGB Gray fixed to (1, 1, 1) RGB multipliers before temperature bias + * for non-raw files. + * - Temperature correlation fixed to temperature 5000, green 1, and equal + * 1 or equivalent for non-raw files. + * Version 2 (5.10): + * - RGB grey restored to version 0. + * - Temperature correlation equivalent to method "Camera" for non-raw + * files. + */ + int compat_version; WBParams(); @@ -1195,6 +1215,7 @@ struct LocallabParams { double gamm; double fatamount; double fatdetail; + bool fatsatur; double fatanchor; double fatlevel; double recothrese; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 738ba4e71..8149c0464 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ +#include #include #include #include @@ -743,6 +744,8 @@ void RawImageSource::getWBMults(const ColorTemp &ctemp, const RAWParams &raw, st void RawImageSource::getImage(const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp, const ToneCurveParams &hrp, const RAWParams &raw) { + assert(rawData.getHeight() == H && rawData.getWidth() == W); + MyMutex::MyLock lock(getImageMutex); tran = defTransform(ri, tran); @@ -1744,6 +1747,8 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &contrastThreshold, bool cache) { + assert(rawData.getHeight() == H && rawData.getWidth() == W); + MyTime t1, t2; t1.set(); @@ -3837,6 +3842,8 @@ void RawImageSource::hlRecovery(const std::string &method, float* red, float* gr void RawImageSource::getAutoExpHistogram(LUTu & histogram, int& histcompr) { + assert(rawData.getHeight() == H && rawData.getWidth() == W); + // BENCHFUN histcompr = 3; @@ -5307,8 +5314,8 @@ void RawImageSource::ItcWB(bool extra, double &tempref, double &greenref, double Glib::ustring profuse; profuse = "JDCmax"; - int limx = 0.05f; - int limy = 0.04f; + float limx = 0.05f; + float limy = 0.04f; if (wbpar.itcwb_prim == "srgb") { profuse = "sRGB"; @@ -7407,7 +7414,6 @@ void RawImageSource::getrgbloc(int begx, int begy, int yEn, int xEn, int cx, int int precision = 3;//must be 3 5 or 9 bool oldsampling = wbpar.itcwb_sampling; - if (oldsampling == true) { precision = 5; } @@ -7481,6 +7487,8 @@ void RawImageSource::getrgbloc(int begx, int begy, int yEn, int xEn, int cx, int void RawImageSource::getAutoWBMultipliersitc(bool extra, double & tempref, double & greenref, double & tempitc, double & greenitc, float &temp0, float &delta, int &bia, int &dread, int &kcam, int &nocam, float &studgood, float &minchrom, int &kmin, float &minhist, float &maxhist, int begx, int begy, int yEn, int xEn, int cx, int cy, int bf_h, int bf_w, double & rm, double & gm, double & bm, const WBParams & wbpar, const ColorManagementParams & cmp, const RAWParams & raw, const ToneCurveParams &hrp) { + assert(rawData.getHeight() == H && rawData.getWidth() == W); + // BENCHFUN constexpr double clipHigh = 64000.0; @@ -7692,12 +7700,7 @@ void RawImageSource::getAutoWBMultipliersitc(bool extra, double & tempref, doubl printf("RGB grey AVG: %g %g %g\n", avg_r / std::max(1, rn), avg_g / std::max(1, gn), avg_b / std::max(1, bn)); } - if (wbpar.method == "autitcgreen") { - //not used - redAWBMul = rm = avg_rm * refwb_red; - greenAWBMul = gm = avg_gm * refwb_green; - blueAWBMul = bm = avg_bm * refwb_blue; - } else { + if (wbpar.method != "autitcgreen") { const double reds = avg_r / std::max(1, rn) * refwb_red; const double greens = avg_g / std::max(1, gn) * refwb_green; const double blues = avg_b / std::max(1, bn) * refwb_blue; @@ -7710,6 +7713,8 @@ void RawImageSource::getAutoWBMultipliersitc(bool extra, double & tempref, doubl void RawImageSource::getAutoWBMultipliers(double &rm, double &gm, double &bm) { + assert(rawData.getHeight() == H && rawData.getWidth() == W); + // BENCHFUN constexpr double clipHigh = 64000.0; @@ -7926,6 +7931,7 @@ void RawImageSource::getAutoWBMultipliers(double &rm, double &gm, double &bm) ColorTemp RawImageSource::getSpotWB(std::vector &red, std::vector &green, std::vector &blue, int tran, double equal, StandardObserver observer) { + assert(rawData.getHeight() == H && rawData.getWidth() == W); int x; int y; @@ -8265,7 +8271,7 @@ void RawImageSource::init() void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int &B) { - if (d1x) { // Nikon D1x has special sensor. We just skip it + if (rawData.getWidth() != W || rawData.getHeight() != H || d1x) { // Nikon D1x has special sensor. We just skip it R = G = B = 0; return; } @@ -8313,6 +8319,8 @@ bool RawImageSource::isGainMapSupported() const void RawImageSource::applyDngGainMap(const float black[4], const std::vector &gainMaps) { + assert(rawData.getHeight() == H && rawData.getWidth() == W); + // now we can apply each gain map to raw_data array2D mvals[2][2]; diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index ba4d2ed60..fb669e1c8 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -458,8 +458,15 @@ public: class AutoWBListener { public: + enum class AWBMode { + NONE, + RGB_GREY, + TEMP_CORRELATION_NON_RAW, + TEMP_CORRELATION_RAW, + }; + virtual ~AutoWBListener() = default; - virtual void WBChanged(int met, double temp, double green, double rw, double gw, double bw, float temp0, float delta, int bia, int dread, float studgood, float minchrom, int kmin, float histmin, float histmax) = 0; + virtual void WBChanged(int met, double temp, double green, double rw, double gw, double bw, float temp0, float delta, int bia, int dread, float studgood, float minchrom, int kmin, float histmin, float histmax, AWBMode aWBMode) = 0; }; class FrameCountListener diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index bc832661a..95b1a24c9 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -1202,7 +1202,19 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT double cam_b = colorMatrix[2][0] * camwbRed + colorMatrix[2][1] * camwbGreen + colorMatrix[2][2] * camwbBlue; currWB = ColorTemp (cam_r, cam_g, cam_b, params.wb.equal, params.wb.observer); } else if (params.wb.method == "autold") { - currWB = ColorTemp (autoWBTemp, autoWBGreen, wbEqual, "Custom", wbObserver); + if (params.wb.compat_version == 1 && !isRaw) { + // RGB grey compatibility version 1 used the identity multipliers + // plus temperature bias for non-raw files. + currWB.update(1., 1., 1., params.wb.equal, params.wb.observer, params.wb.tempBias); + } else { + currWB = ColorTemp(autoWBTemp, autoWBGreen, wbEqual, "Custom", wbObserver); + } + } else if (params.wb.method == "autitcgreen") { + if (params.wb.compat_version == 1 && !isRaw) { + currWB = ColorTemp(5000., 1., 1., params.wb.method, StandardObserver::TEN_DEGREES); + } else { + // TODO: Temperature correlation AWB. + } } double rm, gm, bm; @@ -1331,7 +1343,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT ipf.firstAnalysis (baseImg, params, hist16); ipf.dehaze(baseImg, params.dehaze); - ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0, 0); + ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0, 0, false); // perform transform int origFW; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 319ba7f3b..ad0ce9cc0 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -275,7 +275,7 @@ private: if (params.wb.method == "autitcgreen" && flush) { imgsrc->getrgbloc(0, 0, fh, fw, 0, 0, fh, fw, params.wb); } - const bool autowb = params.wb.method == "autitcgreen"; + const bool autowb = (params.wb.method == "autitcgreen" && imgsrc->isRAW() && flush); ColorTemp autoWB; int dread = 0; int bia = 1; @@ -295,233 +295,60 @@ private: if (!params.wb.enabled) { currWB = ColorTemp(); - } else if (params.wb.method == "Camera") { + } else if (params.wb.method == "Camera" || (params.wb.method == "autitcgreen" && params.wb.compat_version >= 2 && !imgsrc->isRAW() && flush)) {//Use also Camera settings for Temperature correlation and TIF/Jpg currWB = imgsrc->getWB(); } else if (params.wb.method == "autold") {//for Auto RGB double rm, gm, bm; - imgsrc->getAutoWBMultipliers(rm, gm, bm); + if (params.wb.compat_version == 1 && !imgsrc->isRAW()) { + // RGB grey compatibility version 1 used the identity + // multipliers plus temperature bias for non-raw files. + rm = gm = bm = 1.; + } else { + imgsrc->getAutoWBMultipliers(rm, gm, bm); + } currWB.update(rm, gm, bm, params.wb.equal, params.wb.observer, params.wb.tempBias); - } else if (autowb && flush) {//for auto Itcwb - flush to enable only when batch + } else if (autowb) {//for auto Itcwb - flush to enable only when batch only with Raw files //code similar to that present in improccoordinator.cc - float tem = 5000.f; - float gre = 1.f; - double tempref0bias = 5000.; - tempitc = 5000.f; - bool autowb1 = true; - double green_thres = 0.8; - - { - currWBitc = imgsrc->getWB(); - - double greenref = currWBitc.getGreen(); - double tempref0bias0 = currWBitc.getTemp(); - - if (greenref > green_thres && params.wb.itcwb_prim == "srgb") { - forcewbgrey = true; - } - - if (!forcewbgrey && (tempref0bias0 < 3300.f) && (greenref < 1.13f && greenref > 0.88f)) { //seems good with temp and green...To fixe...limits 1.13 and 0.88 - if (settings->verbose) { - printf("Keep camera settings temp=%f green=%f\n", tempref0bias0, greenref); - } - - autowb1 = true; - kcam = 1; - } - - if (autowb1) { - //alternative to camera if camera settings out, using autowb grey to find new ref, then mixed with camera - // kcam = 0; - params.wb.method = "autold"; - double rm, gm, bm; - tempitc = 5000.f; - greenitc = 1.; - currWBitc = imgsrc->getWB(); - tempref0bias = currWBitc.getTemp(); - double greenref = currWBitc.getGreen(); - bool pargref = true; - bool pargre = true; - - if ((greenref > 1.5f || tempref0bias < 3300.f || tempref0bias > 7700.f || forcewbgrey) && kcam != 1 && !params.wb.itcwb_sampling) { //probably camera out to adjust... - imgsrc->getAutoWBMultipliersitc(extra, tempref0bias, greenref, tempitc, greenitc, temp0, delta, bia, dread, kcam, nocam, studgood, minchrom, kmin, minhist, maxhist, 0, 0, fh, fw, 0, 0, fh, fw, rm, gm, bm, params.wb, params.icm, params.raw, params.toneCurve); - imgsrc->wbMul2Camera(rm, gm, bm); - imgsrc->wbCamera2Mul(rm, gm, bm); - ColorTemp ct(rm, gm, bm, 1.0, currWB.getObserver()); - tem = ct.getTemp(); - gre = ct.getGreen(); - - if (gre > 1.3f) { - pargre = false; - } - - if (greenref > 1.3f) { - pargref = false; - } - - double deltemp = tem - tempref0bias; - - if (gre > 1.5f && !forcewbgrey) { //probable wrong value - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value - gre = 0.5f + 0.5f * LIM(gre, 0.9f, 1.1f);//empirical formula in case system out - } else { - if (!forcewbgrey) { - gre = 0.2f + 0.8f * LIM(gre, 0.85f, 1.15f); - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value - nocam = 0; - } else {//set temp and green to init itcwb algorithm - double grepro = LIM(greenref, green_thres, 1.15); - gre = 0.5f * grepro + 0.5f * LIM(gre, 0.9f, 1.1f);//empirical green between green camera and autowb grey - - if (abs(deltemp) < 400.) { //arbitraries thresholds to refine - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - - if (deltemp > 0.) { - nocam = 1; - } else { - nocam = 2; - } - } else if (abs(deltemp) < 900.) { //other arbitrary threshold - tem = 0.4 * tem + 0.6 * tempref0bias;//find a mixed value between camera and auto grey - - if (deltemp > 0.) { - nocam = 3; - } else { - nocam = 4; - } - } else if (abs(deltemp) < 1500. && tempref0bias < 4500.f) { - if ((pargre && pargref) || (!pargre && !pargref)) { - tem = 0.45 * tem + 0.55 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (pargre && !pargref) { - tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (!pargre && pargref) { - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - } - - nocam = 5; - } else if (abs(deltemp) < 1500. && tempref0bias >= 4500.f) { - if ((pargre && pargref) || (!pargre && !pargref)) { - tem = 0.45 * tem + 0.55 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (pargre && !pargref) { - tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (!pargre && pargref) { - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - } - - nocam = 6; - } else if (abs(deltemp) >= 1500. && tempref0bias < 5500.f) { - if (tem >= 4500.f) { - if ((pargre && pargref) || (!pargre && !pargref)) { - tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (pargre && !pargref) { - tem = 0.8 * tem + 0.2 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (!pargre && pargref) { - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - } - - nocam = 7; - } else { - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - nocam = 8; - } - } else if (abs(deltemp) >= 1500. && tempref0bias >= 5500.f) { - if (tem >= 10000.f) { - tem = 0.99 * tem + 0.01 * tempref0bias;//find a mixed value between camera and auto grey - nocam = 9; - } else { - if ((pargre && pargref) || (!pargre && !pargref)) { - tem = 0.45 * tem + 0.55 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (pargre && !pargref) { - tem = 0.7 * tem + 0.3 * tempref0bias;//find a mixed value between camera and auto grey - } - - if (!pargre && pargref) { - tem = 0.3 * tem + 0.7 * tempref0bias;//find a mixed value between camera and auto grey - } - - nocam = 10; - } - } else { - tem = 0.4 * tem + 0.6 * tempref0bias; - nocam = 11; - } - } - } - - tempitc = tem ; - - extra = true; - - if (settings->verbose) { - printf("Using new references AWB grey or mixed Enable Extra - temgrey=%f gregrey=%f tempitc=%f nocam=%i\n", (double) tem, (double) gre, (double) tempitc, nocam); - } - } - } - - params.wb.method = "autitcgreen"; - - } - - float greenitc_low = 1.f; - float tempitc_low = 5000.f; - { - double rm, gm, bm; - greenitc = 1.; - currWBitc = imgsrc->getWB(); - currWBitc = currWBitc.convertObserver(params.wb.observer);//change the temp/green couple with the same multipliers - - double tempref = currWBitc.getTemp() * (1. + params.wb.tempBias); - double greenref = currWBitc.getGreen(); - greenitc = greenref; - - if ((greenref > 1.5f || tempref0bias < 3300.f || tempref0bias > 7700.f || forcewbgrey) && autowb1 && kcam != 1 && !params.wb.itcwb_sampling) { //probably camera out to adjust = greenref ? tempref0bias ? - tempref = tem * (1. + params.wb.tempBias); - greenref = gre; - } else { - - } - - if(params.wb.itcwb_sampling) { - greenitc_low = greenref; - tempitc_low = tempref; - } - - if (settings->verbose && params.wb.method == "autitcgreen") { - printf("tempref=%f greref=%f tempitc=%f greenitc=%f\n", tempref, greenref, tempitc, greenitc); - } - - imgsrc->getAutoWBMultipliersitc(extra, tempref, greenref, tempitc, greenitc, temp0, delta, bia, dread, kcam, nocam, studgood, minchrom, kmin, minhist, maxhist, 0, 0, fh, fw, 0, 0, fh, fw, rm, gm, bm, params.wb, params.icm, params.raw, params.toneCurve); - - - params.wb.temperature = tempitc; - params.wb.green = greenitc; - - if(params.wb.itcwb_sampling) { - params.wb.temperature = tempitc_low; - params.wb.green = greenitc_low; - } - - currWB = ColorTemp(params.wb.temperature, params.wb.green, 1., params.wb.method, params.wb.observer); - currWB.getMultipliers(rm, gm, bm); - autoWB.update(rm, gm, bm, params.wb.equal, params.wb.observer, params.wb.tempBias); - - } + double rm; + double gm; + double bm; + imgsrc->getAutoWBMultipliersItcGreen( + params, + forcewbgrey, + kcam, + greenitc, + extra, + temp0, + delta, + bia, + dread, + nocam, + studgood, + minchrom, + kmin, + minhist, + maxhist, + fh, + fw, + currWB, + 0, + 0., + false, + autoWB, + rm, + gm, + bm); currWB = autoWB; + } else if (params.wb.method == "autitcgreen" && params.wb.compat_version == 1 && !imgsrc->isRAW() && flush) { + // ITCWB compatibility version 1 used 5000 K and observer 10 degrees + // for non-raw files. + currWB = ColorTemp(5000., 1., 1., params.wb.method, StandardObserver::TEN_DEGREES); + currWB.convertObserver(params.wb.observer); + params.wb.temperature = currWB.getTemp(); + params.wb.green = currWB.getGreen(); + params.wb.equal = currWB.getEqual(); } //end WB auto @@ -1140,7 +967,7 @@ private: ipf.firstAnalysis(baseImg, params, hist16); ipf.dehaze(baseImg, params.dehaze); - ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0, 0); + ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0, 0, false); // perform transform (excepted resizing) if (ipf.needsTransform(fw, fh, imgsrc->getRotateDegree(), imgsrc->getMetaData())) { diff --git a/rtengine/stdimagesource.cc b/rtengine/stdimagesource.cc index 9363c84cb..435f2f9a0 100644 --- a/rtengine/stdimagesource.cc +++ b/rtengine/stdimagesource.cc @@ -315,6 +315,10 @@ void StdImageSource::WBauto(bool extra, double &tempref, double &greenref, array { } +void StdImageSource::getrgbloc(int begx, int begy, int yEn, int xEn, int cx, int cy, int bf_h, int bf_w, const procparams::WBParams & wbpar) +{ +} + void StdImageSource::getAutoWBMultipliersitc(bool extra, double &tempref, double &greenref, double &tempitc, double &greenitc, float &temp0, float &delta, int &bia, int &dread, int &kcam, int &nocam, float &studgood, float &minchrom, int &kmin, float &minhist, float &maxhist, int begx, int begy, int yEn, int xEn, int cx, int cy, int bf_h, int bf_w, double &rm, double &gm, double &bm, const WBParams & wbpar, const ColorManagementParams &cmp, const RAWParams &raw, const ToneCurveParams &hrp) { if (redAWBMul != -1.) { diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 8c2a54a3b..1ffb0158d 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -59,7 +59,7 @@ public: int load (const Glib::ustring &fname) override; void getWBMults (const ColorTemp &ctemp, const procparams::RAWParams &raw, std::array& scale_mul, float &autoGainComp, float &rm, float &gm, float &bm) const override {}; void getImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp, const procparams::ToneCurveParams &hrp, const procparams::RAWParams &raw) override; - void getrgbloc (int begx, int begy, int yEn, int xEn, int cx, int cy, int bf_h, int bf_w, const procparams::WBParams & wbpar) override {}; + void getrgbloc (int begx, int begy, int yEn, int xEn, int cx, int cy, int bf_h, int bf_w, const procparams::WBParams & wbpar) override; ColorTemp getWB () const override { return wb; diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index f49fe9c53..50b795f61 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -84,7 +84,6 @@ namespace rtengine /****************************************************************************** * RT code ******************************************************************************/ - extern MyMutex *fftwMutex; using namespace std; @@ -310,7 +309,7 @@ float calculateGradients(Array2Df* H, Array2Df* G, int k, bool multithread) // however, the impact is not visible so we ignore this here (*G)(x, y) = sqrt(gx * gx + gy * gy) / divider; - avgGrad += static_cast((*G) (x, y)); + avgGrad += (*G) (x, y); } } @@ -378,6 +377,7 @@ void calculateFiMatrix(Array2Df* FI, Array2Df* gradients[], // only apply gradients to levels>=detail_level but at least to the coarsest if ((k >= detail_level || k == nlevels - 1) && beta != 1.f) { + const float a = alfa * avgGrad[k]; //DEBUG_STR << "calculateFiMatrix: apply gradient to level " << k << endl; #ifdef _OPENMP #pragma omp parallel for shared(fi,avgGrad) if(multithread) @@ -385,8 +385,7 @@ void calculateFiMatrix(Array2Df* FI, Array2Df* gradients[], for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - float grad = ((*gradients[k]) (x, y) < 1e-4f) ? 1e-4f : (*gradients[k]) (x, y); - float a = alfa * avgGrad[k]; + float grad = ((*gradients[k]) (x, y) < 1e-4f) ? 1e-4 : (*gradients[k]) (x, y); float value = pow((grad + noise) / a, beta - 1.0f); (*fi[k])(x, y) *= value; @@ -470,7 +469,7 @@ void tmo_fattal02(size_t width, //paramet // find max value, normalize to range 0..100 and take logarithm // float minLum = Y (0, 0); - float maxLum = Y(0, 0); + float maxLum = Y(0, 0); #ifdef _OPENMP #pragma omp parallel for reduction(max:maxLum) if(multithread) @@ -482,7 +481,7 @@ void tmo_fattal02(size_t width, Array2Df* H = new Array2Df(width, height); float temp = 100.f / maxLum; - + float eps = 1e-4f; if (algo == 1) { temp = 1.f; } @@ -491,7 +490,6 @@ void tmo_fattal02(size_t width, #pragma omp parallel if(multithread) #endif { - const float eps = 1e-4f; #ifdef __SSE2__ const vfloat epsv = F2V(eps); const vfloat tempv = F2V(temp); @@ -567,9 +565,9 @@ void tmo_fattal02(size_t width, gradients[k] = new Array2Df(pyramids[k]->getCols(), pyramids[k]->getRows()); avgGrad[k] = calculateGradients(pyramids[k], gradients[k], k, multithread); - if (k != 0) { // pyramids[0] is H. Will be deleted later + if (k != 0) // pyramids[0] is H. Will be deleted later delete pyramids[k]; - } + } @@ -615,8 +613,8 @@ void tmo_fattal02(size_t width, // sets index+1 based on the boundary assumption H(N+1)=H(N-1) unsigned int xp1 = (x + 1 >= width ? width - 2 : x + 1); // forward differences in H, so need to use between-points approx of FI - (*Gx) (x, y) = ((*H) (xp1, y) - (*H) (x, y)) * 0.5f * ((*FI) (xp1, y) + (*FI) (x, y)); - (*Gy) (x, y) = ((*H) (x, yp1) - (*H) (x, y)) * 0.5f * ((*FI) (x, yp1) + (*FI) (x, y)); + (*Gx) (x, y) = ((*H) (xp1, y) - (*H) (x, y)) * 0.5 * ((*FI) (xp1, y) + (*FI) (x, y)); + (*Gy) (x, y) = ((*H) (x, yp1) - (*H) (x, y)) * 0.5 * ((*FI) (x, yp1) + (*FI) (x, y)); } } @@ -759,7 +757,7 @@ void transform_ev2normal(Array2Df *A, Array2Df *T, bool multithread) } for (int y = 1 ; y < height - 1 ; y++) { - (*A) (0, y) *= 0.5f; + (*A) (0, y) *= 0.5; (*A)(width - 1, y) *= 0.5f; } @@ -889,7 +887,7 @@ void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread, in if (multithread) { fftwf_init_threads(); - fftwf_plan_with_nthreads(omp_get_max_threads()); + fftwf_plan_with_nthreads(omp_get_num_procs()); } // #else @@ -924,7 +922,7 @@ void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread, in for (int y = 0 ; y < height ; y++) { for (int x = 0 ; x < width ; x++) { - (*F_tr) (x, y) = static_cast((*F_tr) (x, y)) / (l1[y] + l2[x]); + (*F_tr) (x, y) = (*F_tr) (x, y) / (l1[y] + l2[x]); } } @@ -932,7 +930,7 @@ void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread, in // transforms F_tr back to the normal space transform_ev2normal(F_tr, U, multithread); - +/* // the solution U as calculated will satisfy something like int U = 0 // since for any constant c, U-c is also a solution and we are mainly // working in the logspace of (0,1) data we prefer to have @@ -957,6 +955,8 @@ void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread, in (*U)(i) -= maxVal; } } + + */ } @@ -1064,7 +1064,7 @@ inline int find_fast_dim(int dim) } // namespace -void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingParams &fatParams, int detail_level, int Lalone, float **Lum, int WW, int HH, int algo) +void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingParams &fatParams, int detail_level, int Lalone, float **Lum, int WW, int HH, int algo, bool sat) //algo allows to use ART algorithme algo = 0 RT, algo = 1 ART //Lalone allows to use L without RGB values in RT mode { @@ -1073,7 +1073,7 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingPa } BENCHFUN -// const int detail_level = 3; + // const int detail_level = 3; float alpha = 1.f; @@ -1137,7 +1137,7 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingPa Array2Df L(w2, h2); { #ifdef _OPENMP - int num_threads = multiThread ? omp_get_max_threads() : 1; + int num_threads = multiThread ? omp_get_num_procs() : 1; #else int num_threads = 1; #endif @@ -1224,16 +1224,18 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingPa } + const bool satcontrol = sat; + #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if(multiThread) #endif for (int y = 0; y < h; y++) { - int yy = y * hr + 1; + int yy = std::min(int(y * hr + 1), h2-1); for (int x = 0; x < w; x++) { - int xx = x * wr + 1; + int xx = std::min(int(x * wr + 1), w2-1); float Y = std::max(Yr(x, y), epsilon); float l = std::max(L(xx, yy), epsilon) * (scale / Y); @@ -1242,15 +1244,33 @@ void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingPa float &r = rgb->r(y, x); float &g = rgb->g(y, x); float &b = rgb->b(y, x); + float s = 1.f; if(l > 1.f) { r = max(r * l - offset, r); g = max(g * l - offset, g); b = max(b * l - offset, b); + if (satcontrol) { + s = pow_F(1.f / l, 0.3f); + } } else { r *= l; g *= l; b *= l; + if (satcontrol) { + s = pow_F(l, 0.3f); + } } + + if (satcontrol && s != 1.f) { + float ll = luminance(r, g, b, ws); + float rl = r - ll; + float gl = g - ll; + float bl = b - ll; + r = ll + s * rl; + g = ll + s * gl; + b = ll + s * bl; + } + assert(std::isfinite(rgb->r(y, x))); assert(std::isfinite(rgb->g(y, x))); assert(std::isfinite(rgb->b(y, x))); diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 6ab75bd47..2811e3445 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -43,6 +43,59 @@ using namespace std; using namespace rtengine; +#ifdef _WIN32 +#define PATH_SEPARATOR '\\'; +#else +#define PATH_SEPARATOR '/'; +#endif + +namespace // local helper functions +{ + // Look for N or -N in templateText at position ix, meaning "index from end" and "index from start". + // For N, return Nth index from the end, and for -N return the Nth index from the start. + // N is a digit 1 through 9. The returned value is not range-checked, so it may be >=numPathElements. + // or negative. The caller performs any required range-checking. + int decodePathIndex(unsigned int& ix, Glib::ustring& templateText, size_t numPathElements) + { + int pathIndex = static_cast(numPathElements); // a value that means input was invalid + bool fromStart = false; + if (ix < templateText.size()) { + if (templateText[ix] == '-') { + fromStart = true; // minus sign means N is from the start rather than the end of the path + ix++; + } + } + if (ix < templateText.size()) { + pathIndex = templateText[ix] - '1'; + if (!fromStart) { + pathIndex = numPathElements - pathIndex - 1; + } + } + return pathIndex; + } + + // Extract the initial characters from a canonical absolute path, and append + // those to a path string. Initial characters are '/' for Unix/Linux paths and + // '\\' or '//' for UNC paths. A single backslash is also accepted, for driveless + // Windows paths. + void appendAbsolutePathPrefix(Glib::ustring& path, const Glib::ustring& absolutePath) + { + if (absolutePath[0] == '/') { + if (absolutePath.size() > 1 && absolutePath[1] == '/') { + path += "//"; // Start of a Samba UNC path + } else { + path += '/'; // Start of a Unix/Linux path + } + } else if (absolutePath[0] == '\\') { + if (absolutePath.size() > 1 && absolutePath[1] == '\\') { + path += "\\\\"; // Start of a UNC path + } else { + path += '\\'; // Start of a Windows path that does not include a drive letter + } + } + } +} + BatchQueue::BatchQueue (FileCatalog* aFileCatalog) : processing(nullptr), fileCatalog(aFileCatalog), sequence(0), listener(nullptr) { @@ -394,7 +447,7 @@ Glib::ustring BatchQueue::getTempFilenameForParams( const Glib::ustring &filenam timeval tv; gettimeofday(&tv, nullptr); char mseconds[11]; - snprintf(mseconds, sizeof(mseconds), "%d", (int)(tv.tv_usec / 1000)); + snprintf(mseconds, sizeof(mseconds), "%d", static_cast((tv.tv_usec / 1000))); time_t rawtime; struct tm *timeinfo; char stringTimestamp [80]; @@ -561,12 +614,28 @@ void BatchQueue::openLastSelectedItemInEditor() { MYREADERLOCK(l, entryRW); - if (selected.size() > 0) { + if (!selected.empty()) { openItemInEditor(selected.back()); } } } +void BatchQueue::updateDestinationPathPreview() +{ + MYWRITERLOCK(l, entryRW); + + if (!selected.empty()) { + auto& entry = *selected.at(0); + int sequence = 0; // Sequence during subsequent queue processing can't be determined here + Glib::ustring baseDestination = calcAutoFileNameBase(entry.filename, sequence); + Glib::ustring destination = Glib::ustring::compose ("%1.%2", baseDestination, options.saveFormatBatch.format); + + if (listener) { + listener->setDestinationPreviewText(destination); + } + } +} + void BatchQueue::openItemInEditor(ThumbBrowserEntryBase* item) { if (item) { @@ -811,7 +880,6 @@ rtengine::ProcessingJob* BatchQueue::imageReady(rtengine::IImagefloat* img) Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileName, int sequence) { - std::vector pa; std::vector da; for (size_t i = 0; i < origFileName.size(); i++) { @@ -829,29 +897,13 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam tok = tok + origFileName[i++]; } - da.push_back (tok); - } - - if (origFileName[0] == '/') { - pa.push_back ("/" + da[0]); - } else if (origFileName[0] == '\\') { - if (origFileName.size() > 1 && origFileName[1] == '\\') { - pa.push_back ("\\\\" + da[0]); - } else { - pa.push_back ("/" + da[0]); + if (i < origFileName.size()) { // omit the last token, which is the file name + da.push_back (tok); } - } else { - pa.push_back (da[0]); } - for (size_t i = 1; i < da.size(); i++) { - pa.push_back (pa[i - 1] + "/" + da[i]); - } - -// for (int i=0; i(da.size())) { + if (n == 0) { + appendAbsolutePathPrefix(path, origFileName); + } + for (unsigned int i = static_cast(n); i < da.size(); i++) { + path += da[i] + PATH_SEPARATOR; + } + } + // If the next template character is a separator, skip it, because path already has one ix++; + if (ix < options.savePathTemplate.size() && options.savePathTemplate[ix] != '/' && options.savePathTemplate[ix] != '\\') { + ix--; + } + } else if (options.savePathTemplate[ix] == 'p') { + // insert path elements from the start of the path up to the given index + ix++; + int n = decodePathIndex(ix, options.savePathTemplate, da.size()); + if (n >= 0) { + appendAbsolutePathPrefix(path, origFileName); + } + for (unsigned int i=0; static_cast(i) <= n && i < da.size(); i++) { + path += da[i] + PATH_SEPARATOR; + } + // If the next template character is a separator, skip it, because path already has one + ix++; + if (ix < options.savePathTemplate.size() && options.savePathTemplate[ix] != '/' && options.savePathTemplate[ix] != '\\') { + ix--; + } } else if (options.savePathTemplate[ix] == 'd') { + // insert a single directory name from the file's path ix++; - unsigned i = options.savePathTemplate[ix] - '0'; - - if (i < da.size()) { - path = path + da[da.size() - i - 1]; + int n = decodePathIndex(ix, options.savePathTemplate, da.size()); + if (n >= 0 && n < static_cast(da.size())) { + path += da[n]; } } else if (options.savePathTemplate[ix] == 'f') { - path = path + filename; + path += filename; } else if (options.savePathTemplate[ix] == 'r') { // rank from pparams char rank; rtengine::procparams::ProcParams pparams; @@ -927,7 +1005,7 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam } else { - path = path + options.savePathTemplate[ix]; + path += options.savePathTemplate[ix]; } ix++; @@ -1021,3 +1099,8 @@ void BatchQueue::redrawNeeded (LWButton* button) GThreadLock lock; queue_draw (); } + +void BatchQueue::selectionChanged() +{ + updateDestinationPathPreview(); +} diff --git a/rtgui/batchqueue.h b/rtgui/batchqueue.h index 5cde37748..f8f1cf255 100644 --- a/rtgui/batchqueue.h +++ b/rtgui/batchqueue.h @@ -38,6 +38,7 @@ public: virtual ~BatchQueueListener() = default; virtual void queueSizeChanged(int qsize, bool queueRunning, bool queueError, const Glib::ustring& queueErrorMessage) = 0; virtual bool canStartNext() = 0; + virtual void setDestinationPreviewText(const Glib::ustring& destinationPath) = 0; }; class FileCatalog; @@ -59,6 +60,7 @@ public: void selectAll (); void openItemInEditor(ThumbBrowserEntryBase* item); void openLastSelectedItemInEditor(); + void updateDestinationPathPreview(); void startProcessing (); @@ -79,6 +81,7 @@ public: bool keyPressed (GdkEventKey* event) override; void buttonPressed (LWButton* button, int actionCode, void* actionData) override; void redrawNeeded (LWButton* button) override; + void selectionChanged () override; void setBatchQueueListener (BatchQueueListener* l) { diff --git a/rtgui/batchqueuepanel.cc b/rtgui/batchqueuepanel.cc index 8a6dd25b4..e35149326 100644 --- a/rtgui/batchqueuepanel.cc +++ b/rtgui/batchqueuepanel.cc @@ -108,6 +108,14 @@ BatchQueuePanel::BatchQueuePanel (FileCatalog* aFileCatalog) : parent(nullptr) #endif odvb->pack_start (*hb3, Gtk::PACK_SHRINK, 4); + destinationPreviewLabel = Gtk::manage (new Gtk::Label ()); + destinationPreviewLabel->set_tooltip_markup (M("QUEUE_DESTPREVIEW_TOOLTIP")); + destinationPreviewLabel->set_selectable (true); // so users can copy the path to the clipboard + destinationPreviewLabel->set_halign (Gtk::ALIGN_START); + auto destinationPreviewScrolledWindow = Gtk::manage(new Gtk::ScrolledWindow ()); + destinationPreviewScrolledWindow->set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + destinationPreviewScrolledWindow->add (*destinationPreviewLabel); + odvb->pack_start (*destinationPreviewScrolledWindow, Gtk::PACK_SHRINK); Gtk::RadioButton::Group g = useTemplate->get_group(); useFolder->set_group (g); fdir->add (*odvb); @@ -122,6 +130,7 @@ BatchQueuePanel::BatchQueuePanel (FileCatalog* aFileCatalog) : parent(nullptr) outdirTemplate->set_text (options.savePathTemplate); useTemplate->set_active (options.saveUsePathTemplate); useFolder->set_active (!options.saveUsePathTemplate); + destinationPreviewLabel->set_text (M("QUEUE_DESTPREVIEW_TITLE")); // setup signal handlers outdirTemplate->signal_changed().connect (sigc::mem_fun(*this, &BatchQueuePanel::saveOptions)); @@ -329,6 +338,7 @@ void BatchQueuePanel::saveOptions () options.savePathTemplate = outdirTemplate->get_text(); options.saveUsePathTemplate = useTemplate->get_active(); options.procQueueEnabled = qAutoStart->get_active(); + batchQueue->updateDestinationPathPreview(); } bool BatchQueuePanel::handleShortcutKey (GdkEventKey* event) @@ -358,6 +368,11 @@ bool BatchQueuePanel::canStartNext () return queueShouldRun; } +void BatchQueuePanel::setDestinationPreviewText(const Glib::ustring &destinationPath) +{ + destinationPreviewLabel->set_text(destinationPath); +} + void BatchQueuePanel::pathFolderButtonPressed () { @@ -381,9 +396,11 @@ void BatchQueuePanel::pathFolderButtonPressed () void BatchQueuePanel::pathFolderChanged () { options.savePathFolder = outdirFolder->get_filename(); + batchQueue->updateDestinationPathPreview(); } void BatchQueuePanel::formatChanged(const Glib::ustring& format) { options.saveFormatBatch = saveFormatPanel->getFormat(); + batchQueue->updateDestinationPathPreview(); } diff --git a/rtgui/batchqueuepanel.h b/rtgui/batchqueuepanel.h index db4e243e9..d73f8b893 100644 --- a/rtgui/batchqueuepanel.h +++ b/rtgui/batchqueuepanel.h @@ -42,6 +42,7 @@ class BatchQueuePanel : public Gtk::Box, Gtk::CheckButton* qAutoStart; Gtk::Entry* outdirTemplate; + Gtk::Label* destinationPreviewLabel; MyFileChooserButton* outdirFolder; Gtk::Button* outdirFolderButton; Gtk::RadioButton* useTemplate; @@ -72,6 +73,7 @@ public: // batchqueuelistener interface void queueSizeChanged(int qsize, bool queueRunning, bool queueError, const Glib::ustring& queueErrorMessage) override; bool canStartNext() override; + void setDestinationPreviewText(const Glib::ustring& destinationPath) override; private: void startBatchProc (); diff --git a/rtgui/dirbrowser.cc b/rtgui/dirbrowser.cc index 7f5a4c3e3..925fdc606 100644 --- a/rtgui/dirbrowser.cc +++ b/rtgui/dirbrowser.cc @@ -393,7 +393,9 @@ Gtk::TreePath DirBrowser::expandToDir (const Glib::ustring& absDirPath) char* dcpy = strdup (absDirPath.c_str()); char* dir = strtok (dcpy, "/\\"); +#ifdef _WIN32 int count = 0; +#endif expandSuccess = true; #ifndef _WIN32 @@ -436,8 +438,9 @@ Gtk::TreePath DirBrowser::expandToDir (const Glib::ustring& absDirPath) ++ix; ++i; } - +#ifdef _WIN32 count++; +#endif dir = strtok(nullptr, "/\\"); } diff --git a/rtgui/editwindow.h b/rtgui/editwindow.h index a5932c081..02b7cd23b 100644 --- a/rtgui/editwindow.h +++ b/rtgui/editwindow.h @@ -24,7 +24,7 @@ #include "guiutils.h" class EditorPanel; -class ExternalEditor; +struct ExternalEditor; class RTWindow; class EditWindow : diff --git a/rtgui/flatfield.cc b/rtgui/flatfield.cc index 21cdb4315..5246381c3 100644 --- a/rtgui/flatfield.cc +++ b/rtgui/flatfield.cc @@ -445,7 +445,7 @@ void FlatField::setGainMap(bool enabled) { flatFieldFromMetaData->set_sensitive(enabled); if (!enabled) { idle_register.add( - [this, enabled]() -> bool + [this]() -> bool { disableListener(); flatFieldFromMetaData->setValue(false); diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc index 562cbf674..f659d3dfd 100644 --- a/rtgui/locallabtools.cc +++ b/rtgui/locallabtools.cc @@ -2568,6 +2568,7 @@ LocallabExposure::LocallabExposure(): expfat(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_FATFRA")))), fatamount(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATAMOUNT"), 1., 100., 1., 1.))), fatdetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATDETAIL"), -100., 300., 1., 0.))), + fatsatur(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_FATSAT")))), norm(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_EQUIL")))), fatlevel(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATLEVEL"), 0.5, 2.0, 0.01, 1.))), fatanchor(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATANCHOR"), 0.1, 100.0, 0.01, 50., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))), @@ -2617,7 +2618,9 @@ LocallabExposure::LocallabExposure(): strmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -2., 2., 0.05, 0.))), angmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180., 180., 0.1, 0.))), mask2expCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), - Lmaskexpshape(static_cast(mask2expCurveEditorG->addCurve(CT_Diagonal, "L(L)"))) + Lmaskexpshape(static_cast(mask2expCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + Evlocallabtmosatur(ProcEventMapper::getInstance()->newEvent(AUTOEXP, "HISTORY_MSG_LOCAL_TMO_SATUR")) + { set_orientation(Gtk::ORIENTATION_VERTICAL); @@ -2705,6 +2708,7 @@ LocallabExposure::LocallabExposure(): decaye->setAdjusterListener(this); setExpandAlignProperties(exprecove, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); normConn = norm->signal_toggled().connect(sigc::mem_fun(*this, &LocallabExposure::normChanged)); + fatsaturConn = fatsatur->signal_toggled().connect(sigc::mem_fun(*this, &LocallabExposure::fatsaturChanged)); inversexConn = inversex->signal_toggled().connect(sigc::mem_fun(*this, &LocallabExposure::inversexChanged)); inversex->set_tooltip_text(M("TP_LOCALLAB_INVERS_TOOLTIP")); @@ -2799,6 +2803,7 @@ LocallabExposure::LocallabExposure(): // fatBox->pack_start(*norm); // fatBox->pack_start(*fatlevel); fatBox->pack_start(*fatanchor); + fatBox->pack_start(*fatsatur); // fatFrame->add(*fatBox); expfat->add(*fatBox, false); // pack_start(*fatFrame); @@ -2985,6 +2990,7 @@ void LocallabExposure::disableListener() exnoiseMethodConn.block(true); inversexConn.block(true); normConn.block(true); + fatsaturConn.block(true); showmaskexpMethodConn.block(true); showmaskexpMethodConninv.block(true); enaExpMaskConn.block(true); @@ -2999,6 +3005,7 @@ void LocallabExposure::enableListener() exnoiseMethodConn.block(false); inversexConn.block(false); normConn.block(false); + fatsaturConn.block(false); showmaskexpMethodConn.block(false); showmaskexpMethodConninv.block(false); enaExpMaskConn.block(false); @@ -3084,6 +3091,7 @@ void LocallabExposure::read(const rtengine::procparams::ProcParams* pp, const Pa angexp->setValue(spot.angexp); softradiusexp->setValue(spot.softradiusexp); norm->set_active(spot.norm); + fatsatur->set_active(spot.fatsatur); inversex->set_active(spot.inversex); enaExpMask->set_active(spot.enaExpMask); enaExpMaskaft->set_active(spot.enaExpMaskaft); @@ -3175,6 +3183,7 @@ void LocallabExposure::write(rtengine::procparams::ProcParams* pp, ParamsEdited* spot.softradiusexp = softradiusexp->getValue(); spot.inversex = inversex->get_active(); spot.norm = norm->get_active(); + spot.fatsatur = fatsatur->get_active(); spot.enaExpMask = enaExpMask->get_active(); spot.enaExpMaskaft = enaExpMaskaft->get_active(); spot.CCmaskexpcurve = CCmaskexpshape->getCurve(); @@ -3780,6 +3789,21 @@ void LocallabExposure::normChanged() } } +void LocallabExposure::fatsaturChanged() +{ + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (fatsatur->get_active()) { + listener->panelChanged(Evlocallabtmosatur, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(getSpotName()) + ")"); + } else { + listener->panelChanged(Evlocallabtmosatur, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(getSpotName()) + ")"); + } + } + } +} void LocallabExposure::inversexChanged() { diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h index 627be34bd..f99b3bc41 100644 --- a/rtgui/locallabtools.h +++ b/rtgui/locallabtools.h @@ -345,6 +345,7 @@ private: MyExpander* const expfat; Adjuster* const fatamount; Adjuster* const fatdetail; + Gtk::CheckButton* const fatsatur; Gtk::CheckButton* const norm; Adjuster* const fatlevel; Adjuster* const fatanchor; @@ -395,8 +396,9 @@ private: Adjuster* const angmaskexp; CurveEditorGroup* const mask2expCurveEditorG; DiagonalCurveEditor* const Lmaskexpshape; + rtengine::ProcEvent Evlocallabtmosatur; - sigc::connection expMethodConn, exnoiseMethodConn, inversexConn, normConn, showmaskexpMethodConn, showmaskexpMethodConninv, enaExpMaskConn, enaExpMaskaftConn; + sigc::connection expMethodConn, exnoiseMethodConn, inversexConn, normConn, fatsaturConn, showmaskexpMethodConn, showmaskexpMethodConninv, enaExpMaskConn, enaExpMaskaftConn; public: LocallabExposure(); @@ -429,6 +431,7 @@ private: void exnoiseMethodChanged(); void inversexChanged(); void normChanged(); + void fatsaturChanged(); void showmaskexpMethodChanged(); void showmaskexpMethodChangedinv(); void enaExpMaskChanged(); diff --git a/rtgui/lockablecolorpicker.h b/rtgui/lockablecolorpicker.h index b18a56028..3de6256e9 100644 --- a/rtgui/lockablecolorpicker.h +++ b/rtgui/lockablecolorpicker.h @@ -30,7 +30,7 @@ namespace rtengine namespace procparams { -class ColorManagementParams; +struct ColorManagementParams; } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 0792d33c1..759f128b7 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -273,6 +273,7 @@ void ParamsEdited::set(bool v) wb.itcwb_alg = v; wb.itcwb_prim = v; wb.itcwb_sampling = v; + wb.compat_version = v; //colorShift.a = v; //colorShift.b = v; //lumaDenoise.enabled = v; @@ -991,6 +992,7 @@ void ParamsEdited::initFrom(const std::vector& wb.itcwb_alg = wb.itcwb_alg && p.wb.itcwb_alg == other.wb.itcwb_alg; wb.itcwb_prim = wb.itcwb_prim && p.wb.itcwb_prim == other.wb.itcwb_prim; wb.itcwb_sampling = wb.itcwb_sampling && p.wb.itcwb_sampling == other.wb.itcwb_sampling; + wb.compat_version = wb.compat_version && p.wb.compat_version == other.wb.compat_version; //colorShift.a = colorShift.a && p.colorShift.a == other.colorShift.a; //colorShift.b = colorShift.b && p.colorShift.b == other.colorShift.b; //lumaDenoise.enabled = lumaDenoise.enabled && p.lumaDenoise.enabled == other.lumaDenoise.enabled; @@ -1269,6 +1271,7 @@ void ParamsEdited::initFrom(const std::vector& locallab.spots.at(j).gamm = locallab.spots.at(j).gamm && pSpot.gamm == otherSpot.gamm; locallab.spots.at(j).fatamount = locallab.spots.at(j).fatamount && pSpot.fatamount == otherSpot.fatamount; locallab.spots.at(j).fatdetail = locallab.spots.at(j).fatdetail && pSpot.fatdetail == otherSpot.fatdetail; + locallab.spots.at(j).fatsatur = locallab.spots.at(j).fatsatur && pSpot.fatsatur == otherSpot.fatsatur; locallab.spots.at(j).fatanchor = locallab.spots.at(j).fatanchor && pSpot.fatanchor == otherSpot.fatanchor; locallab.spots.at(j).fatlevel = locallab.spots.at(j).fatlevel && pSpot.fatlevel == otherSpot.fatlevel; locallab.spots.at(j).recothrese = locallab.spots.at(j).recothrese && pSpot.recothrese == otherSpot.recothrese; @@ -2903,6 +2906,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.wb.temperature = dontforceSet && options.baBehav[ADDSET_WB_TEMPERATURE] ? toEdit.wb.temperature + mods.wb.temperature : mods.wb.temperature; } + if (wb.compat_version) { + toEdit.wb.compat_version = mods.wb.compat_version; + } + //if (colorShift.a) toEdit.colorShift.a = dontforceSet && options.baBehav[ADDSET_CS_BLUEYELLOW] ? toEdit.colorShift.a + mods.colorShift.a : mods.colorShift.a; //if (colorShift.b) toEdit.colorShift.b = dontforceSet && options.baBehav[ADDSET_CS_GREENMAGENTA] ? toEdit.colorShift.b + mods.colorShift.b : mods.colorShift.b; //if (lumaDenoise.enabled) toEdit.lumaDenoise.enabled = mods.lumaDenoise.enabled; @@ -4124,6 +4131,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.locallab.spots.at(i).fatdetail = mods.locallab.spots.at(i).fatdetail; } + if (locallab.spots.at(i).fatsatur) { + toEdit.locallab.spots.at(i).fatsatur = mods.locallab.spots.at(i).fatsatur; + } + if (locallab.spots.at(i).fatanchor) { toEdit.locallab.spots.at(i).fatanchor = mods.locallab.spots.at(i).fatanchor; } @@ -7685,6 +7696,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : fatdetail(v), fatanchor(v), fatlevel(v), + fatsatur(v), recothrese(v), lowthrese(v), higthrese(v), @@ -8376,6 +8388,7 @@ void LocallabParamsEdited::LocallabSpotEdited::set(bool v) gamm = v; fatamount = v; fatdetail = v; + fatsatur = v; fatanchor = v; fatlevel = v; recothrese = v; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 7a3e8bbc5..a7d460f30 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -255,6 +255,7 @@ struct WBParamsEdited { bool itcwb_prim; bool itcwb_sampling; bool itcwb_green; + bool compat_version; }; @@ -568,6 +569,7 @@ public: bool fatdetail; bool fatanchor; bool fatlevel; + bool fatsatur; bool recothrese; bool lowthrese; bool higthrese; diff --git a/rtgui/pointermotionlistener.h b/rtgui/pointermotionlistener.h index f9fda419c..2f19ae0b9 100644 --- a/rtgui/pointermotionlistener.h +++ b/rtgui/pointermotionlistener.h @@ -28,7 +28,7 @@ namespace rtengine namespace procparams { -class ColorManagementParams; +struct ColorManagementParams; } diff --git a/rtgui/whitebalance.cc b/rtgui/whitebalance.cc index 2f3686242..f245e42f2 100644 --- a/rtgui/whitebalance.cc +++ b/rtgui/whitebalance.cc @@ -396,8 +396,9 @@ WhiteBalance::WhiteBalance () : FoldableToolPanel(this, TOOL_NAME, M("TP_WBALANC itcwb_prim->set_active(1); itcwb_primconn = itcwb_prim->signal_changed().connect(sigc::mem_fun(*this, &WhiteBalance::itcwb_prim_changed)); itcwb_prim ->set_tooltip_markup (M("TP_WBALANCE_ITCWPRIM_TOOLTIP")); - - + + compatVersionAdjuster.reset(new Adjuster("", 0., procparams::WBParams::CURRENT_COMPAT_VERSION, 1., procparams::WBParams::CURRENT_COMPAT_VERSION)); + /* Gtk::Box* boxgreen = Gtk::manage (new Gtk::Box ()); boxgreen->show (); @@ -624,6 +625,7 @@ void WhiteBalance::optChanged () equal->setEditedState (UnEdited); tempBias->setEditedState (UnEdited); observer10->setEdited(false); + compatVersionAdjuster->setEditedState(UnEdited); } else { unsigned int methodId = findWBEntryId (row[methodColumns.colLabel], WBLT_GUI); const WBEntry& currMethod = WBParams::getWbEntries()[methodId]; @@ -723,6 +725,17 @@ void WhiteBalance::optChanged () break; } + + if (compatVersionAdjuster->getIntValue() == 1 && + (!batchMode || currMethod.type != WBEntry::Type::AUTO)) { + // Safe to upgrade version because method changed. In batch + // mode, this method may be called even if there is no change, + // so it's only safe to upgrade if the new method is not auto. + compatVersionAdjuster->setValue(procparams::WBParams::CURRENT_COMPAT_VERSION); + if (batchMode) { + compatVersionAdjuster->setEditedState(Edited); + } + } } if (listener && getEnabled()) { @@ -768,6 +781,7 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) lastitcwb_alg = pp->wb.itcwb_alg; itcwb_green->setValue (pp->wb.itcwb_green); + compatVersionAdjuster->setValue(pp->wb.compat_version); itcwb_primconn.block (true); @@ -806,6 +820,7 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) observer10->setEdited(pedited->wb.observer); itcwb_alg->set_inconsistent (!pedited->wb.itcwb_alg); itcwb_green->setEditedState (pedited->wb.itcwb_green ? Edited : UnEdited); + compatVersionAdjuster->setEditedState(pedited->wb.compat_version ? Edited : UnEdited); } if (pedited && !pedited->wb.method) { @@ -961,6 +976,7 @@ void WhiteBalance::write (ProcParams* pp, ParamsEdited* pedited) pedited->wb.enabled = !get_inconsistent(); pedited->wb.itcwb_prim = itcwb_prim->get_active_text() != M("GENERAL_UNCHANGED"); pedited->wb.itcwb_green = itcwb_green->getEditedState (); + pedited->wb.compat_version = compatVersionAdjuster->getEditedState(); } pp->wb.enabled = getEnabled(); @@ -996,6 +1012,7 @@ void WhiteBalance::write (ProcParams* pp, ParamsEdited* pedited) pp->wb.itcwb_alg = itcwb_alg->get_active (); pp->wb.tempBias = tempBias->getValue (); pp->wb.itcwb_green = itcwb_green->getValue (); + pp->wb.compat_version = compatVersionAdjuster->getIntValue(); } void WhiteBalance::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) @@ -1044,6 +1061,7 @@ void WhiteBalance::setBatchMode (bool batchMode) green->showEditedCB (); equal->showEditedCB (); tempBias->showEditedCB (); + compatVersionAdjuster->showEditedCB(); Gtk::TreeModel::Row row = *(refTreeModel->append()); row[methodColumns.colId] = WBParams::getWbEntries().size(); row[methodColumns.colLabel] = M("GENERAL_UNCHANGED"); @@ -1184,10 +1202,10 @@ inline Gtk::TreeRow WhiteBalance::getActiveMethod () return *(method->get_active()); } -void WhiteBalance::WBChanged(int met, double temperature, double greenVal, double rw, double gw, double bw, float temp0, float delta, int bia, int dread, float studgood, float minchrom, int kmin, float histmin, float histmax) +void WhiteBalance::WBChanged(int met, double temperature, double greenVal, double rw, double gw, double bw, float temp0, float delta, int bia, int dread, float studgood, float minchrom, int kmin, float histmin, float histmax, AWBMode aWBMode) { idle_register.add( - [this, met, temperature, greenVal, rw, gw, bw, temp0, delta, bia, dread, studgood, minchrom, kmin, histmin, histmax]() -> bool + [this, met, temperature, greenVal, rw, gw, bw, temp0, delta, bia, dread, studgood, minchrom, kmin, histmin, histmax, aWBMode]() -> bool { disableListener(); temp->setValue(temperature); @@ -1239,7 +1257,22 @@ void WhiteBalance::WBChanged(int met, double temperature, double greenVal, doubl Glib::ustring::format(std::fixed, std::setprecision(0), histmin), Glib::ustring::format(std::fixed, std::setprecision(0), histmax)) ); - + if (aWBMode == AWBMode::TEMP_CORRELATION_RAW) { + itcwb_green->set_sensitive(true); + tempBias->set_sensitive(true); + itcwb_alg->set_sensitive(true); + itcwb_prim->set_sensitive(true); + } else if (aWBMode == AWBMode::RGB_GREY) { + itcwb_green->set_sensitive(false); + tempBias->set_sensitive(true); + itcwb_alg->set_sensitive(false); + itcwb_prim->set_sensitive(false); + } else { + itcwb_green->set_sensitive(false); + tempBias->set_sensitive(false); + itcwb_alg->set_sensitive(false); + itcwb_prim->set_sensitive(false); + } temp->setDefault(temperature); green->setDefault(greenVal); enableListener(); diff --git a/rtgui/whitebalance.h b/rtgui/whitebalance.h index 060026a48..4667a2fdf 100644 --- a/rtgui/whitebalance.h +++ b/rtgui/whitebalance.h @@ -85,6 +85,7 @@ protected: Gtk::CheckButton* itcwb_alg; MyComboBoxText* itcwb_prim; Adjuster* itcwb_green; + std::unique_ptr compatVersionAdjuster; bool lastitcwb_alg; @@ -142,7 +143,7 @@ public: } void setWB (int temp, double green); void resetWB (); - void WBChanged (int met, double temp, double green, double rw, double gw, double bw, float temp0, float delta, int bia, int dread, float studgood, float minchrom, int kmin, float histmin, float histmax) override; + void WBChanged (int met, double temp, double green, double rw, double gw, double bw, float temp0, float delta, int bia, int dread, float studgood, float minchrom, int kmin, float histmin, float histmax, AWBMode aWBMode) override; void itcwb_alg_toggled (); void itcwb_prim_changed (); void setAdjusterBehavior (bool tempadd, bool greenadd, bool equaladd, bool tempbiasadd);