From 29748d057a6d3be0013824e10de8c15eb78ef508 Mon Sep 17 00:00:00 2001 From: CarVac Date: Sat, 14 Oct 2023 15:22:08 -0400 Subject: [PATCH 01/27] Update camconst.json white levels for 1DxII --- rtengine/camconst.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) 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 From de82b9fc7a7db13c06d03dcca6530567e6893e1d Mon Sep 17 00:00:00 2001 From: Alexander Gruzintsev <0v3rt1r3d@gmail.com> Date: Mon, 30 Oct 2023 08:18:33 +0100 Subject: [PATCH 02/27] Fix warnings: conversion to double/float, unused variables, register keyword --- CMakeLists.txt | 4 ++++ rtengine/dfmanager.cc | 1 - rtengine/rawimagesource.cc | 4 ++-- rtgui/dirbrowser.cc | 5 ++++- rtgui/editwindow.h | 2 +- rtgui/flatfield.cc | 2 +- rtgui/lockablecolorpicker.h | 2 +- rtgui/pointermotionlistener.h | 2 +- 8 files changed, 14 insertions(+), 8 deletions(-) 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/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/rawimagesource.cc b/rtengine/rawimagesource.cc index eabeb2fc9..38d22a5b9 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -5307,8 +5307,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"; 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/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/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; } From 831a9bbd55ff3cbdd6c4712131339fa0dbf44ff5 Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Fri, 3 Nov 2023 21:48:21 -0700 Subject: [PATCH 03/27] Fix crash when opening image in editor Do not access uninitialized raw image data. The raw data is requested when the demosaic mode is set to None and the cursor is moved over the image in the editor. It can occur before the raw data is loaded. --- rtengine/rawimagesource.cc | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index eabeb2fc9..9b987e9f5 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; @@ -7481,6 +7488,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; @@ -7710,6 +7719,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 +7937,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 +8277,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 +8325,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]; From a95a58a8a399173e6947ff12a4b82b5d83ae80d9 Mon Sep 17 00:00:00 2001 From: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> Date: Sun, 19 Nov 2023 17:22:43 -0800 Subject: [PATCH 04/27] Fix sRGB working profile crash The sRGB working profile cannot be found under some conditions because the profile name is stored as a Glib::ustring and the same strings may not be equal when using different locales. Use std::string whenever comparing profile names. --- rtengine/iccstore.cc | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) 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; From 380d96407751668e80d20976843b1d5bfc9cf1b5 Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Sat, 6 Jan 2024 16:00:19 -0500 Subject: [PATCH 05/27] Add destination path preview label to batch queue panel. Label initially says "Destination path for the first selected image appears here" so the feature is discoverable. Select a file and it shows the destination path that would be used if it is the first file to be processed and the file does not already exist. Label is updated as the template is edited, so you can see the effect of the edits. If "Save to folder" is selected, the previewed path uses that folder. --- rtdata/languages/default | 2 ++ rtgui/batchqueue.cc | 40 ++++++++++++++++++++++++++++++++++++++++ rtgui/batchqueue.h | 3 +++ rtgui/batchqueuepanel.cc | 11 +++++++++++ rtgui/batchqueuepanel.h | 2 ++ 5 files changed, 58 insertions(+) diff --git a/rtdata/languages/default b/rtdata/languages/default index 403fccdf0..2f22b4642 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2101,6 +2101,8 @@ 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 diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 6ab75bd47..8d2ad176e 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -567,6 +567,41 @@ void BatchQueue::openLastSelectedItemInEditor() } } +void BatchQueue::updateDestinationPathPreview() +{ + MYWRITERLOCK(l, entryRW); + + if (selected.size()) + { + auto &entry = *selected.at(0); + int sequence = 0; // Sequence during subsequent queue processing can't be determined here + Glib::ustring baseDestination; + if (options.saveUsePathTemplate) + { + baseDestination = calcAutoFileNameBase(entry.filename, sequence); + } + else + { + Glib::ustring baseFilename; + int extpos = entry.filename.size() - 1; + for (; extpos >= 0 && entry.filename[extpos] != '.'; extpos--) + { + } + for (int k = extpos - 1; k >= 0 && entry.filename[k] != '/' && entry.filename[k] != '\\'; k--) + { + baseFilename = entry.filename[k] + baseFilename; + } + baseDestination = options.savePathFolder + '/' + baseFilename; + } + Glib::ustring destination = Glib::ustring::compose ("%1.%2", baseDestination, options.saveFormatBatch.format); + + if (listener) + { + listener->setDestinationPreviewText(destination); + } + } +} + void BatchQueue::openItemInEditor(ThumbBrowserEntryBase* item) { if (item) { @@ -1021,3 +1056,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..59201d9f3 100644 --- a/rtgui/batchqueuepanel.cc +++ b/rtgui/batchqueuepanel.cc @@ -108,6 +108,9 @@ 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")); + odvb->pack_start (*destinationPreviewLabel); Gtk::RadioButton::Group g = useTemplate->get_group(); useFolder->set_group (g); fdir->add (*odvb); @@ -122,6 +125,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 +333,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 +363,11 @@ bool BatchQueuePanel::canStartNext () return queueShouldRun; } +void BatchQueuePanel::setDestinationPreviewText(const Glib::ustring &destinationPath) +{ + destinationPreviewLabel->set_text(destinationPath); +} + void BatchQueuePanel::pathFolderButtonPressed () { @@ -381,6 +391,7 @@ void BatchQueuePanel::pathFolderButtonPressed () void BatchQueuePanel::pathFolderChanged () { options.savePathFolder = outdirFolder->get_filename(); + batchQueue->updateDestinationPathPreview(); } void BatchQueuePanel::formatChanged(const Glib::ustring& format) 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 (); From 587e0c67829259c68fac020ffc3b43fba965a836 Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Sat, 6 Jan 2024 16:32:14 -0500 Subject: [PATCH 06/27] Simplified BatchQueue::updateDestinationPathPreview after noticing that calcAutoFileNameBase handles all cases --- rtgui/batchqueue.cc | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 8d2ad176e..27b4400e1 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -575,24 +575,7 @@ void BatchQueue::updateDestinationPathPreview() { auto &entry = *selected.at(0); int sequence = 0; // Sequence during subsequent queue processing can't be determined here - Glib::ustring baseDestination; - if (options.saveUsePathTemplate) - { - baseDestination = calcAutoFileNameBase(entry.filename, sequence); - } - else - { - Glib::ustring baseFilename; - int extpos = entry.filename.size() - 1; - for (; extpos >= 0 && entry.filename[extpos] != '.'; extpos--) - { - } - for (int k = extpos - 1; k >= 0 && entry.filename[k] != '/' && entry.filename[k] != '\\'; k--) - { - baseFilename = entry.filename[k] + baseFilename; - } - baseDestination = options.savePathFolder + '/' + baseFilename; - } + Glib::ustring baseDestination = calcAutoFileNameBase(entry.filename, sequence); Glib::ustring destination = Glib::ustring::compose ("%1.%2", baseDestination, options.saveFormatBatch.format); if (listener) From b8d3f90ca041779cbd8052ba25ba926ab32114a6 Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Sat, 6 Jan 2024 18:01:56 -0500 Subject: [PATCH 07/27] Reimplement template %dN specifier, making %d-N count from start of path, not end Also made %pN selectively skip the next character only if it's a slash or backslash, so for example %p2/a, %p2\a and %p2a produce the same result. --- rtgui/batchqueue.cc | 62 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 27b4400e1..732580599 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -824,6 +824,47 @@ rtengine::ProcessingJob* BatchQueue::imageReady(rtengine::IImagefloat* img) return processing ? processing->job : nullptr; } +// Combine a range of elements from "names" into a slash-delimited path +static inline Glib::ustring combineDirectoryNames(unsigned startIndex, unsigned endIndex, const std::vector & names) +{ + Glib::ustring resultPath; + for (unsigned i = startIndex; i <= endIndex && i < names.size(); ++i) + { + resultPath = resultPath + names[i] + '/'; + } + return resultPath; +} + +// 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 +static inline unsigned decodePathIndex(unsigned & ix, Glib::ustring & templateText, size_t numPathElements) +{ + unsigned pathIndex = numPathElements; // 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()) + { + unsigned n = templateText[ix] - '1'; + if (!fromStart) + { + // n=1 is the last element, n=2 is the one before that + n = numPathElements - n - 1; + } + if (n < numPathElements) + { + pathIndex = n; + } + } + return pathIndex; +} + // Calculates automatic filename of processed batch entry, but just the base name // example output: "c:\out\converted\dsc0121" Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileName, int sequence) @@ -847,7 +888,9 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam tok = tok + origFileName[i++]; } - da.push_back (tok); + if (i < origFileName.size()) { // omit the last token, which is the file name + da.push_back (tok); + } } if (origFileName[0] == '/') { @@ -898,19 +941,24 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam if (options.savePathTemplate[ix] == 'p') { ix++; - unsigned int i = options.savePathTemplate[ix] - '0'; + unsigned int i = options.savePathTemplate[ix] - '1'; if (i < pa.size()) { path = path + pa[pa.size() - i - 1] + '/'; } - + // If the next template character is a slash or backslash, skip it, because path already has a trailing slash 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 + // da.size()-1 omits the last element, which is the filename ix++; - unsigned i = options.savePathTemplate[ix] - '0'; - - if (i < da.size()) { - path = path + da[da.size() - i - 1]; + unsigned n = decodePathIndex(ix, options.savePathTemplate, da.size()); + if (n < da.size()) + { + path += da[n]; } } else if (options.savePathTemplate[ix] == 'f') { path = path + filename; From 21c53823ee766c90d57a0e7d1b3b8882d0ffb4db Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Sat, 6 Jan 2024 18:21:59 -0500 Subject: [PATCH 08/27] Reimplemented %p template specifier to use new method, added %P specifier The %P specifier includes directories from the start of the file path, whereas the original %p specifier includes them from the end. In both cases N is from the last directory in the path, and -N is from the first directory in the path (N=1..9). --- rtgui/batchqueue.cc | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 732580599..cb9127333 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -837,6 +837,7 @@ static inline Glib::ustring combineDirectoryNames(unsigned startIndex, unsigned // 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 static inline unsigned decodePathIndex(unsigned & ix, Glib::ustring & templateText, size_t numPathElements) { unsigned pathIndex = numPathElements; // means input was invalid @@ -854,7 +855,6 @@ static inline unsigned decodePathIndex(unsigned & ix, Glib::ustring & templateTe unsigned n = templateText[ix] - '1'; if (!fromStart) { - // n=1 is the last element, n=2 is the one before that n = numPathElements - n - 1; } if (n < numPathElements) @@ -870,7 +870,6 @@ static inline unsigned decodePathIndex(unsigned & ix, Glib::ustring & templateTe 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++) { @@ -893,26 +892,8 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam } } - 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]); - } - } 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 Date: Sun, 7 Jan 2024 10:09:45 -0500 Subject: [PATCH 09/27] Update destination preview whenever the save format changes. --- rtgui/batchqueuepanel.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/rtgui/batchqueuepanel.cc b/rtgui/batchqueuepanel.cc index 59201d9f3..fb6e37c46 100644 --- a/rtgui/batchqueuepanel.cc +++ b/rtgui/batchqueuepanel.cc @@ -397,4 +397,5 @@ void BatchQueuePanel::pathFolderChanged () void BatchQueuePanel::formatChanged(const Glib::ustring& format) { options.saveFormatBatch = saveFormatPanel->getFormat(); + batchQueue->updateDestinationPathPreview(); } From 5accf1e9dbe381de17942e3b7c1fc006ab560e58 Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Sun, 7 Jan 2024 10:31:05 -0500 Subject: [PATCH 10/27] Add myself to the list of authors --- AUTHORS.txt | 1 + 1 file changed, 1 insertion(+) 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é From d9511d9af8b55e6771bc2665132a3b196013e4e4 Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Sun, 7 Jan 2024 10:42:44 -0500 Subject: [PATCH 11/27] Update QUEUE_LOCATION_TEMPLATE_TOOLTIP text to describe new specifiers Describe %dN, %d-N, %pN, %p-N, %PN, %P-N and %f specifiers Add examples using the new P specifier and -N terms --- rtdata/languages/default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 2f22b4642..3af6b108c 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2106,7 +2106,7 @@ QUEUE_DESTPREVIEW_TOOLTIP;Destination path for the first selected image appears 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 will be replaced with the base name of the file, without the extension removed.\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 From f92e07e5cf904757e19f9da61f44aa19b0c4e5eb Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Sun, 7 Jan 2024 10:52:01 -0500 Subject: [PATCH 12/27] Make destination path preview label selectable, so its contents can be copied to the clipboard --- rtgui/batchqueuepanel.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/rtgui/batchqueuepanel.cc b/rtgui/batchqueuepanel.cc index fb6e37c46..3fd52df93 100644 --- a/rtgui/batchqueuepanel.cc +++ b/rtgui/batchqueuepanel.cc @@ -110,6 +110,7 @@ BatchQueuePanel::BatchQueuePanel (FileCatalog* aFileCatalog) : parent(nullptr) 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 odvb->pack_start (*destinationPreviewLabel); Gtk::RadioButton::Group g = useTemplate->get_group(); useFolder->set_group (g); From 6df930b639a833354772747f43ac3e46d14e8b30 Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Sun, 7 Jan 2024 11:09:24 -0500 Subject: [PATCH 13/27] Minor tooltip change: shorter explanation of %f location template specifier --- rtdata/languages/default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 3af6b108c..bf43596dc 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2106,7 +2106,7 @@ QUEUE_DESTPREVIEW_TOOLTIP;Destination path for the first selected image appears 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\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 will be replaced with the base name of the file, without the extension removed.\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_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 From df4102846831f67aa960d7ff74346014f92be85b Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Sun, 7 Jan 2024 11:10:05 -0500 Subject: [PATCH 14/27] On Windows, use backslashes in template-generated paths (but continue to accept either / or \ in input strings, on any platform) Also deleted an unused helper function. --- rtgui/batchqueue.cc | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index cb9127333..61f1c082d 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -43,6 +43,12 @@ using namespace std; using namespace rtengine; +#ifdef _WIN32 +#define PATH_SEPARATOR '\\'; +#else +#define PATH_SEPARATOR '/'; +#endif + BatchQueue::BatchQueue (FileCatalog* aFileCatalog) : processing(nullptr), fileCatalog(aFileCatalog), sequence(0), listener(nullptr) { @@ -824,17 +830,6 @@ rtengine::ProcessingJob* BatchQueue::imageReady(rtengine::IImagefloat* img) return processing ? processing->job : nullptr; } -// Combine a range of elements from "names" into a slash-delimited path -static inline Glib::ustring combineDirectoryNames(unsigned startIndex, unsigned endIndex, const std::vector & names) -{ - Glib::ustring resultPath; - for (unsigned i = startIndex; i <= endIndex && i < names.size(); ++i) - { - resultPath = resultPath + names[i] + '/'; - } - return resultPath; -} - // 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 @@ -926,7 +921,7 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam unsigned n = decodePathIndex(ix, options.savePathTemplate, da.size()); if (n < da.size()) { for (unsigned i=n; i Date: Sun, 7 Jan 2024 11:14:52 -0500 Subject: [PATCH 15/27] Minor comment clarification --- rtgui/batchqueue.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 61f1c082d..bdf6c6e5e 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -924,7 +924,7 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam path = path + da[i] + PATH_SEPARATOR; } } - // If the next template character is a slash or backslash, skip it, because path already has a trailing slash + // 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--; @@ -936,7 +936,7 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam for (unsigned i=0; i<=n && i Date: Sun, 7 Jan 2024 11:49:19 -0500 Subject: [PATCH 16/27] Reversed %p and %P processing cases for location templates, which were opposite to what the help says. --- rtgui/batchqueue.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index bdf6c6e5e..c3723515f 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -915,7 +915,7 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam if (options.savePathTemplate[ix] == '%') { ix++; - if (options.savePathTemplate[ix] == 'p') { + if (options.savePathTemplate[ix] == 'P') { // insert path elements from given index to the end ix++; unsigned n = decodePathIndex(ix, options.savePathTemplate, da.size()); @@ -929,7 +929,7 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam if (ix < options.savePathTemplate.size() && options.savePathTemplate[ix] != '/' && options.savePathTemplate[ix] != '\\') { ix--; } - } else if (options.savePathTemplate[ix] == 'P') { + } else if (options.savePathTemplate[ix] == 'p') { // insert path elements from the start of the path up to the given index ix++; unsigned n = decodePathIndex(ix, options.savePathTemplate, da.size()); From 9e310b7454a604ae298ef11d05806809129a9b87 Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Wed, 10 Jan 2024 18:47:14 -0500 Subject: [PATCH 17/27] Minor changes per feedback in issue #6915 (mostly code formatting) --- rtgui/batchqueue.cc | 87 +++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index c3723515f..349cefddf 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -49,6 +49,34 @@ using namespace rtengine; #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 + unsigned int decodePathIndex(unsigned int & ix, Glib::ustring & templateText, size_t numPathElements) + { + unsigned int pathIndex = numPathElements; // 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()) { + unsigned int n = templateText[ix] - '1'; + if (!fromStart) { + n = numPathElements - n - 1; + } + if (n < numPathElements) { + pathIndex = n; + } + } + return pathIndex; + } +} + BatchQueue::BatchQueue (FileCatalog* aFileCatalog) : processing(nullptr), fileCatalog(aFileCatalog), sequence(0), listener(nullptr) { @@ -567,7 +595,7 @@ void BatchQueue::openLastSelectedItemInEditor() { MYREADERLOCK(l, entryRW); - if (selected.size() > 0) { + if (!selected.empty()) { openItemInEditor(selected.back()); } } @@ -577,15 +605,13 @@ void BatchQueue::updateDestinationPathPreview() { MYWRITERLOCK(l, entryRW); - if (selected.size()) - { + 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) - { + if (listener) { listener->setDestinationPreviewText(destination); } } @@ -830,36 +856,6 @@ rtengine::ProcessingJob* BatchQueue::imageReady(rtengine::IImagefloat* img) return processing ? processing->job : nullptr; } -// 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 -static inline unsigned decodePathIndex(unsigned & ix, Glib::ustring & templateText, size_t numPathElements) -{ - unsigned pathIndex = numPathElements; // 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()) - { - unsigned n = templateText[ix] - '1'; - if (!fromStart) - { - n = numPathElements - n - 1; - } - if (n < numPathElements) - { - pathIndex = n; - } - } - return pathIndex; -} - // Calculates automatic filename of processed batch entry, but just the base name // example output: "c:\out\converted\dsc0121" Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileName, int sequence) @@ -918,10 +914,10 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam if (options.savePathTemplate[ix] == 'P') { // insert path elements from given index to the end ix++; - unsigned n = decodePathIndex(ix, options.savePathTemplate, da.size()); + unsigned int n = decodePathIndex(ix, options.savePathTemplate, da.size()); if (n < da.size()) { - for (unsigned i=n; i Date: Wed, 10 Jan 2024 18:55:52 -0500 Subject: [PATCH 18/27] Code formatting: move ampersands next to types --- rtgui/batchqueue.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 349cefddf..df653976c 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -54,7 +54,7 @@ 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 - unsigned int decodePathIndex(unsigned int & ix, Glib::ustring & templateText, size_t numPathElements) + unsigned int decodePathIndex(unsigned int& ix, Glib::ustring& templateText, size_t numPathElements) { unsigned int pathIndex = numPathElements; // means input was invalid bool fromStart = false; @@ -606,7 +606,7 @@ void BatchQueue::updateDestinationPathPreview() MYWRITERLOCK(l, entryRW); if (!selected.empty()) { - auto &entry = *selected.at(0); + 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); From 343b5ecc683d4a5f7ed71bf99e6800b8d1d470d8 Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Mon, 15 Jan 2024 14:40:56 -0500 Subject: [PATCH 19/27] Left-justify the queue destination preview label and allow it to scroll horizontally --- rtgui/batchqueuepanel.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/rtgui/batchqueuepanel.cc b/rtgui/batchqueuepanel.cc index 3fd52df93..e35149326 100644 --- a/rtgui/batchqueuepanel.cc +++ b/rtgui/batchqueuepanel.cc @@ -109,9 +109,13 @@ BatchQueuePanel::BatchQueuePanel (FileCatalog* aFileCatalog) : parent(nullptr) 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 - odvb->pack_start (*destinationPreviewLabel); + 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); From a39e6779e4cee8b92ff035a9cd21fbb3d2843657 Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Mon, 15 Jan 2024 15:27:09 -0500 Subject: [PATCH 20/27] Fix: If %p or %P queue template term includes start of file path, prepend the leading /, \ or \\ from the file path --- rtgui/batchqueue.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index df653976c..bb2d7526f 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -75,6 +75,23 @@ namespace // local helper functions } 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 + // '\\' 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] == '/') { + 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) @@ -916,6 +933,9 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam ix++; unsigned int n = decodePathIndex(ix, options.savePathTemplate, da.size()); if (n < da.size()) { + if (n == 0) { + appendAbsolutePathPrefix(path, origFileName); + } for (unsigned int i=n; i < da.size(); i++) { path += da[i] + PATH_SEPARATOR; } @@ -930,6 +950,9 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam ix++; unsigned int n = decodePathIndex(ix, options.savePathTemplate, da.size()); for (unsigned int i=0; i <= n && i < da.size(); i++) { + if (i == 0) { + appendAbsolutePathPrefix(path, origFileName); + } path += da[i] + PATH_SEPARATOR; } // If the next template character is a separator, skip it, because path already has one From 1df9b4869e379f8f937831c6487b88c7208f015e Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Mon, 15 Jan 2024 16:39:38 -0500 Subject: [PATCH 21/27] Make %p and %P queue output template specifiers limit invalid range rather than treating that as an error. For example in a 4-element path "first 5 elements" and "last 5 elements" now return all four elements. --- rtgui/batchqueue.cc | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index bb2d7526f..53d31d76b 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -51,12 +51,13 @@ using namespace rtengine; 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 - unsigned int decodePathIndex(unsigned int& ix, Glib::ustring& templateText, size_t numPathElements) + // 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) { - unsigned int pathIndex = numPathElements; // means input was invalid + int pathIndex = (int)numPathElements; // a value that means input was invalid bool fromStart = false; if (ix < templateText.size()) { if (templateText[ix] == '-') { @@ -65,12 +66,9 @@ namespace // local helper functions } } if (ix < templateText.size()) { - unsigned int n = templateText[ix] - '1'; + pathIndex = templateText[ix] - '1'; if (!fromStart) { - n = numPathElements - n - 1; - } - if (n < numPathElements) { - pathIndex = n; + pathIndex = numPathElements - pathIndex - 1; } } return pathIndex; @@ -900,8 +898,8 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam } } -// for (int i=0; i 0) { + appendAbsolutePathPrefix(path, origFileName); + } + for (unsigned int i=0; (int)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 @@ -962,10 +963,9 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam } } else if (options.savePathTemplate[ix] == 'd') { // insert a single directory name from the file's path - // da.size()-1 omits the last element, which is the filename ix++; unsigned int n = decodePathIndex(ix, options.savePathTemplate, da.size()); - if (n < da.size()) { + if (n >= 0 && n < da.size()) { path += da[n]; } } else if (options.savePathTemplate[ix] == 'f') { From 6cbc448f63f16bf660d590306b666ecac0d3d85c Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Mon, 15 Jan 2024 17:13:40 -0500 Subject: [PATCH 22/27] Handle Samba-style UNC prefix '//' in %p and %P queue output template specifiers --- rtgui/batchqueue.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 53d31d76b..e7de30d30 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -76,12 +76,16 @@ namespace // local helper functions // Extract the initial characters from a canonical absolute path, and append // those to a path string. Initial characters are '/' for Unix/Linux paths and - // '\\' for UNC paths. A single backslash is also accepted, for driveless + // '\\' 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] == '/') { - path += '/'; // Start of a Unix/Linux path + 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 From 11a0a0b6975a62aa28d6623770b60f9dbcbd7063 Mon Sep 17 00:00:00 2001 From: Scott Gilbertson Date: Tue, 16 Jan 2024 07:54:12 -0500 Subject: [PATCH 23/27] Fixes per pull request comments: fix %p-1 missing slash, use static_cast, fix int/unsigned mismatches --- rtgui/batchqueue.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index e7de30d30..2811e3445 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -57,7 +57,7 @@ namespace // local helper functions // or negative. The caller performs any required range-checking. int decodePathIndex(unsigned int& ix, Glib::ustring& templateText, size_t numPathElements) { - int pathIndex = (int)numPathElements; // a value that means input was invalid + int pathIndex = static_cast(numPathElements); // a value that means input was invalid bool fromStart = false; if (ix < templateText.size()) { if (templateText[ix] == '-') { @@ -447,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]; @@ -937,11 +937,11 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam if (n < 0) { n = 0; // if too many elements specified, return all available elements } - if (n < (int)da.size()) { + if (n < static_cast(da.size())) { if (n == 0) { appendAbsolutePathPrefix(path, origFileName); } - for (unsigned int i = (unsigned int)n; i < da.size(); i++) { + for (unsigned int i = static_cast(n); i < da.size(); i++) { path += da[i] + PATH_SEPARATOR; } } @@ -954,10 +954,10 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam // 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) { + if (n >= 0) { appendAbsolutePathPrefix(path, origFileName); } - for (unsigned int i=0; (int)i <= n && i < da.size(); i++) { + 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 @@ -968,8 +968,8 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam } else if (options.savePathTemplate[ix] == 'd') { // insert a single directory name from the file's path ix++; - unsigned int n = decodePathIndex(ix, options.savePathTemplate, da.size()); - if (n >= 0 && n < da.size()) { + 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') { From 7c781aa7b1884c51dec6fab15fae19f70d695ad9 Mon Sep 17 00:00:00 2001 From: Richard E Barber Date: Sat, 27 Jan 2024 22:21:28 -0800 Subject: [PATCH 24/27] bump macos CI workflow to macos-12 macos-11 is obsolete to homebrew --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From e4087e2fa2f4b0bcee08e1aa903df1a3201c86d2 Mon Sep 17 00:00:00 2001 From: Desmis Date: Thu, 1 Feb 2024 07:48:07 +0100 Subject: [PATCH 25/27] White balance auto temperature correlation - improve behavior with non raw files (#6903) * Improve Itcwb with non-raw files * Change pre-dev builds wbrefinement * Change template in pre-dev * Improvment improccordinator.cc * Forgotten observer convert * Reenable wbauto autogrey as 5.8 * Remove wrong code * Missing getrgbloc references * Fixed bug due to bias in queu with temperaure correlation issue 6911 * Simpleprocess queue compatibility tif-jpg * Preserve AWB edits from 5.9 In 5.9 for non-raw files, 1. RGB grey uses the unit multipliers with temperature bias applied. 2. Temperature correlation uses the equivalent of temperature 5000, green 1, and red/blue equalizer 1. * Refactor temperature correlation AWB code * Fix inaccurate RGB grey WB preview after using ITC The RGB grey automatic white balance algorithm caches the multipliers. Temperature correlation automatic white balance also caches results to the same location, but never uses it. This causes the RGB grey method to produce incorrect results in the editor. Removing the temperature correlation cache fixes the issue and does not have side-effects. --------- Co-authored-by: Lawrence Lee <45837045+Lawrence37@users.noreply.github.com> --- .github/workflows/appimage.yml | 2 +- .github/workflows/windows.yml | 2 +- rtdata/languages/default | 4 +- rtengine/CMakeLists.txt | 1 + rtengine/imagesource.cc | 275 ++++++++++++++++++++++++++++ rtengine/imagesource.h | 27 +++ rtengine/improccoordinator.cc | 317 ++++++++++----------------------- rtengine/improccoordinator.h | 1 - rtengine/procparams.cc | 23 ++- rtengine/procparams.h | 20 +++ rtengine/rawimagesource.cc | 8 +- rtengine/rtengine.h | 9 +- rtengine/rtthumbnail.cc | 14 +- rtengine/simpleprocess.cc | 267 +++++---------------------- rtengine/stdimagesource.cc | 4 + rtengine/stdimagesource.h | 2 +- rtgui/paramsedited.cc | 6 + rtgui/paramsedited.h | 1 + rtgui/whitebalance.cc | 43 ++++- rtgui/whitebalance.h | 3 +- 20 files changed, 561 insertions(+), 468 deletions(-) create mode 100644 rtengine/imagesource.cc diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 0d3c76399..94bee4ebf 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -12,7 +12,7 @@ on: workflow_dispatch: env: - publish_pre_dev_labels: '[]' + publish_pre_dev_labels: '["Beep6581:wbrefinement"]' jobs: build: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9b49edd91..b466ec265 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -12,7 +12,7 @@ on: workflow_dispatch: env: - publish_pre_dev_labels: '[]' + publish_pre_dev_labels: '["Beep6581:wbrefinement"]' jobs: build: diff --git a/rtdata/languages/default b/rtdata/languages/default index bf43596dc..dfa149857 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2040,7 +2040,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) @@ -4170,7 +4170,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/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..5a5dd12af 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); } } 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/procparams.cc b/rtengine/procparams.cc index afbce8849..4f6285ac9 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; } @@ -6316,6 +6319,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); @@ -8293,7 +8297,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); @@ -8318,6 +8322,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")) { diff --git a/rtengine/procparams.h b/rtengine/procparams.h index b748a7027..9b0f6463a 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(); diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index fdecf638a..8149c0464 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -7414,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; } @@ -7701,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; 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..4337477da 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; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 319ba7f3b..b82c7fe79 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 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/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index ea7c4adc2..ed7ad2754 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; @@ -988,6 +989,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; @@ -2897,6 +2899,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; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 9f9e4f620..565eca36a 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; }; 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); From 542946ef43b5e0282b067697256e2c9856c5d93c Mon Sep 17 00:00:00 2001 From: "U-PCSPECIALIST01\\jdesm" Date: Thu, 1 Feb 2024 08:20:12 +0100 Subject: [PATCH 26/27] Restore windows.yml and appimage.yml --- .github/workflows/appimage.yml | 2 +- .github/workflows/windows.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 94bee4ebf..0d3c76399 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -12,7 +12,7 @@ on: workflow_dispatch: env: - publish_pre_dev_labels: '["Beep6581:wbrefinement"]' + publish_pre_dev_labels: '[]' jobs: build: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b466ec265..9b49edd91 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -12,7 +12,7 @@ on: workflow_dispatch: env: - publish_pre_dev_labels: '["Beep6581:wbrefinement"]' + publish_pre_dev_labels: '[]' jobs: build: From 5349f7a860af322b24b08a12a80dbe1d96c0e5e7 Mon Sep 17 00:00:00 2001 From: Desmis Date: Fri, 2 Feb 2024 07:42:34 +0100 Subject: [PATCH 27/27] Improve Dynamic Range Compression - for some images with very high DR - Main and LA (#6931) * Improve Dynamic Range Compression - for some images with very high DR * Clean code * Improve TM fattal with saturation control in LA * Saturation control fattal in LA * Re-order paramsedit * Change history_msg_tmo_satur with saturation --- rtdata/languages/default | 2 ++ rtengine/dcrop.cc | 2 +- rtengine/improccoordinator.cc | 2 +- rtengine/improcfun.h | 2 +- rtengine/iplocallab.cc | 14 +++++--- rtengine/procparams.cc | 4 +++ rtengine/procparams.h | 1 + rtengine/rtthumbnail.cc | 2 +- rtengine/simpleprocess.cc | 2 +- rtengine/tmo_fattal02.cc | 60 +++++++++++++++++++++++------------ rtgui/locallabtools.cc | 26 ++++++++++++++- rtgui/locallabtools.h | 5 ++- rtgui/paramsedited.cc | 7 ++++ rtgui/paramsedited.h | 1 + 14 files changed, 99 insertions(+), 31 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index dfa149857..38f3201c1 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 @@ -2962,6 +2963,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 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/improccoordinator.cc b/rtengine/improccoordinator.cc index 5a5dd12af..5358f2880 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -849,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/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 4f6285ac9..99e7d8b7a 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3348,6 +3348,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : gamm(0.4), fatamount(1.0), fatdetail(40.0), + fatsatur(false), fatanchor(50.0), fatlevel(1.), recothrese(1.), @@ -4828,6 +4829,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 @@ -6714,6 +6716,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); @@ -8907,6 +8910,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 9b0f6463a..5ffcf9c6f 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1211,6 +1211,7 @@ struct LocallabParams { double gamm; double fatamount; double fatdetail; + bool fatsatur; double fatanchor; double fatlevel; double recothrese; diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 4337477da..95b1a24c9 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -1343,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 b82c7fe79..ad0ce9cc0 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -967,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/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/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/paramsedited.cc b/rtgui/paramsedited.cc index ed7ad2754..ca705682b 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -1265,6 +1265,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; @@ -4109,6 +4110,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; } @@ -7670,6 +7675,7 @@ LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : fatdetail(v), fatanchor(v), fatlevel(v), + fatsatur(v), recothrese(v), lowthrese(v), higthrese(v), @@ -8361,6 +8367,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 565eca36a..260779a7f 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -567,6 +567,7 @@ public: bool fatdetail; bool fatanchor; bool fatlevel; + bool fatsatur; bool recothrese; bool lowthrese; bool higthrese;