diff --git a/AUTHORS.txt b/AUTHORS.txt
index 227390faa..aae24ff63 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -7,6 +7,7 @@ Development contributors, in last name alphabetical order:
Roel Baars
Martin Burri
+ Pierre Cabrera
Javier Celaya
Jacques Desmis
Pavlov Dmitry
@@ -20,6 +21,7 @@ Development contributors, in last name alphabetical order:
Steve Herrell
Philippe Hupé
Wolfgang Kuehnel
+ Lawrence Lee
Guokai Ma
Emil Martinec
Wyatt Olson
@@ -36,8 +38,9 @@ Development contributors, in last name alphabetical order:
Ingo Weyrich
Makoto Yoshida
-Other contributors (profiles, ideas, mockups, testing, forum activity, translations, etc.), in last name alphabetical order:
+Other contributors (profiles, ideas, mockups, testing, forum activity, translations, tutorials etc.), in last name alphabetical order:
+ Andy Astbury
Marcin Bajor
Javier Bartol
Thorsten Bartolomäus
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7d66109e0..b7cf35099 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -44,6 +44,13 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION
"Building RawTherapee requires using GCC version 4.9 or higher!")
endif()
+# Warning for GCC 10, which causes problems #5749:
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "10.0")
+ message(STATUS "WARNING: gcc ${CMAKE_CXX_COMPILER_VERSION} is known to miscompile RawTherapee when using -ftree-loop-vectorize, forcing the option to be off")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-tree-loop-vectorize")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-tree-loop-vectorize")
+endif()
+
# We might want to build using the old C++ ABI, even when using a new GCC
# version:
if(USE_OLD_CXX_ABI)
@@ -68,6 +75,11 @@ set(CACHE_NAME_SUFFIX
""
CACHE STRING "RawTherapee's cache folder suffix")
+# For macOS only, OSX_DEV_BUILD option allows using relative paths instead of absolute
+# paths. Consequently, for development builds, application can be launching without
+# being bundled. However, file access can be restricted for some folder.
+option(OSX_DEV_BUILD "Generate macOS development builds" OFF)
+
# By default we don't use a specific processor target, so PROC_TARGET_NUMBER is
# set to 0. Specify other values to optimize for specific processor architecture
# as listed in ProcessorTargets.cmake:
diff --git a/rtdata/images/svg/perspective-horizontal-vertical.svg b/rtdata/images/svg/perspective-horizontal-vertical.svg
new file mode 100644
index 000000000..0c5046879
--- /dev/null
+++ b/rtdata/images/svg/perspective-horizontal-vertical.svg
@@ -0,0 +1,116 @@
+
+
+
+
diff --git a/rtdata/languages/Czech b/rtdata/languages/Czech
index 19182a1e5..b6006cd36 100644
--- a/rtdata/languages/Czech
+++ b/rtdata/languages/Czech
@@ -40,10 +40,12 @@
#39 2017-07-21 updated by mkyral
#40 2017-12-13 updated by mkyral
#41 2018-03-03 updated by mkyral
-#42 2018-04-28 updated by mkyral
-#43 2018-12-13 updated by mkyral
-#44 2019-04-17 updated by mkyral
-
+#42 2018-10-24 updated by mkyral
+#43 2018-12-04 updated by mkyral
+#44 2018-12-13 updated by mkyral
+#45 2020-04-20 updated by mkyral
+#46 2020-04-21 updated by mkyral
+#47 2020-06-02 updated by mkyral
ABOUT_TAB_BUILD;Verze
ABOUT_TAB_CREDITS;Zásluhy
ABOUT_TAB_LICENSE;Licence
@@ -153,7 +155,7 @@ FILEBROWSER_APPLYPROFILE;Použít
FILEBROWSER_APPLYPROFILE_PARTIAL;Aplikovat - částečně
FILEBROWSER_AUTODARKFRAME;Automatický tmavý snímek
FILEBROWSER_AUTOFLATFIELD;Auto Flat Field
-FILEBROWSER_BROWSEPATHBUTTONHINT;Klikněte pro otevření zadané cesty, obnovte složku a aplikujte klíčové slovo "find".
+FILEBROWSER_BROWSEPATHBUTTONHINT;Klikněte pro otevření zadané cesty, obnovte složku a aplikujte klíčová slova z pole "Najít:".
FILEBROWSER_BROWSEPATHHINT;Vložte cestu pro procházení.\n\nKlávesové zkratky:\nCtrl-o pro přepnutí do adresního řádku.\nEnter/ Ctrl-Enter pro procházení ;\nEsc pro zrušení změn.\nShift-Esc pro zrušení přepnutí.\n\nZkratky pro cesty:\n~\t- domácí složka uživatele.\n!\t- složka s obrázky uživatele.
FILEBROWSER_CACHE;Mezipaměť
FILEBROWSER_CACHECLEARFROMFULL;Smazat vše včetně profilů zpracování v mezipaměti
@@ -163,8 +165,12 @@ FILEBROWSER_COLORLABEL_TOOLTIP;Barevný štítek.\n\nPoužijte výběr ze seznam
FILEBROWSER_COPYPROFILE;Kopírovat
FILEBROWSER_CURRENT_NAME;Současné jméno:
FILEBROWSER_DARKFRAME;Tmavý snímek
-FILEBROWSER_DELETEDIALOG_HEADER;Potvrzení smazání souboru
+FILEBROWSER_DELETEDIALOG_ALL;Jste si jisti, že chcete trvale vymazat %1 vybraných souborů?
+FILEBROWSER_DELETEDIALOG_HEADER;Potvrzení smazání souboru:
+FILEBROWSER_DELETEDIALOG_SELECTED;Jste si jisti, že chcete trvale vymazat %1 vybraných souborů?
+FILEBROWSER_DELETEDIALOG_SELECTEDINCLPROC;Jste si jisti, že chcete trvale vymazat %1 vybraných souborů včetně výstupů dávkového zpracování?
FILEBROWSER_EMPTYTRASH;Vysypat koš
+FILEBROWSER_EMPTYTRASHHINT;Trvale smaže všechny soubory z koše.
FILEBROWSER_EXTPROGMENU;Otevřít pomocí
FILEBROWSER_FLATFIELD;Flat Field
FILEBROWSER_MOVETODARKFDIR;Přesunout do složky tmavých snímků
@@ -198,6 +204,8 @@ FILEBROWSER_POPUPRANK2;Hodnocení 2 **
FILEBROWSER_POPUPRANK3;Hodnocení 3 ***
FILEBROWSER_POPUPRANK4;Hodnocení 4 ****
FILEBROWSER_POPUPRANK5;Hodnocení 5 *****
+FILEBROWSER_POPUPREMOVE;Trvale smazat
+FILEBROWSER_POPUPREMOVEINCLPROC;Trvale smazat, včetně dávkově zpracovaných verzí
FILEBROWSER_POPUPRENAME;Přejmenovat
FILEBROWSER_POPUPSELECTALL;Vybrat vše
FILEBROWSER_POPUPTRASH;Přesunout do koše
@@ -224,6 +232,7 @@ FILEBROWSER_SHOWDIRHINT;Smazat všechny filtry.\nZkratka: d
FILEBROWSER_SHOWEDITEDHINT;Ukázat upravené obrázky.\nZkratka: 7
FILEBROWSER_SHOWEDITEDNOTHINT;Ukázat neupravené obrázky.\nZkratka: 6
FILEBROWSER_SHOWEXIFINFO;Zobrazit Exif informace.\n\nZkratky:\ni - režim více karet editoru,\nAlt-i - režim jedné karty editoru.
+FILEBROWSER_SHOWNOTTRASHHINT;Zobrazit pouze snímky které nejsou v koši.
FILEBROWSER_SHOWORIGINALHINT;Zobrazí pouze originální obrázky.\n\nPokud existuje několik obrázků se stejným názvem, ale rozdílnými příponami, bude jako originál vybrán ten, jehož přípona je nejvýše v seznamu přípon veVolby > Prohlížeč souborů > Analyzované přípony.
FILEBROWSER_SHOWRANK1HINT;Ukázat obrázky hodnocené jednou hvězdičkou.\nZkratka: 1
FILEBROWSER_SHOWRANK2HINT;Ukázat obrázky hodnocené dvěma hvězdičkami.\nZkratka: 2
@@ -260,6 +269,7 @@ GENERAL_DISABLED;Vypnuto
GENERAL_ENABLE;Zapnout
GENERAL_ENABLED;Zapnuto
GENERAL_FILE;Soubor
+GENERAL_HELP;Nápověda
GENERAL_LANDSCAPE;Na šířku
GENERAL_NA;n/a
GENERAL_NO;Ne
@@ -402,11 +412,11 @@ HISTORY_MSG_113;L*a*b* - Ochrana červ. a pleť. tónů
HISTORY_MSG_114;Průchody DCB
HISTORY_MSG_115;Potlačení chybných barev
HISTORY_MSG_116;Vylepšení DCB
-HISTORY_MSG_117;Raw korekce CA - červená
-HISTORY_MSG_118;Raw korekce CA - modrá
+HISTORY_MSG_117;Raw korekce ChA - červená
+HISTORY_MSG_118;Raw korekce ChA - modrá
HISTORY_MSG_119;Filtrovat linkové rušení
HISTORY_MSG_120;Vyrovnání zelené
-HISTORY_MSG_121;Raw korekce CA - automatická
+HISTORY_MSG_121;Raw korekce ChA - automatická
HISTORY_MSG_122;Tmavé snímky - Automatický výběr
HISTORY_MSG_123;Tmavé snímky - Soubor
HISTORY_MSG_124;Korekce bílého bodu
@@ -601,10 +611,10 @@ HISTORY_MSG_314;Vlnka - Gamut - Omezení artefaktů
HISTORY_MSG_315;Vlnka - Zůstatek - Kontrast
HISTORY_MSG_316;Vlnka - Gamut - Ochrana a zaměření pleťových tónů
HISTORY_MSG_317;Vlnka - Gamut - Odstín pleti
-HISTORY_MSG_318;Vlnka - Kontrast - Úrovně světel
-HISTORY_MSG_319;Vlnka - Kontrast - - rozsah světel
-HISTORY_MSG_320;Vlnka - Kontrast - Rozsah stínů
-HISTORY_MSG_321;Vlnka - Kontrast - Úrovně stínů
+HISTORY_MSG_318;Vlnka - Kontrast - Jemnější úrovně
+HISTORY_MSG_319;Vlnka - Kontrast - Jemnější rozsah
+HISTORY_MSG_320;Vlnka - Kontrast - Hrubší rozsah
+HISTORY_MSG_321;Vlnka - Kontrast - Hrubší úrovně
HISTORY_MSG_322;Vlnka - Gamut - Zabránit posunu barev
HISTORY_MSG_323;Vlnka - DH - Místní kontrast
HISTORY_MSG_324;Vlnka - Barevnost - Pastelové
@@ -761,6 +771,12 @@ HISTORY_MSG_490;DRC - Míra
HISTORY_MSG_491;Vyvážení bílé
HISTORY_MSG_492;RGB křivky
HISTORY_MSG_493;L*a*b* úpravy
+HISTORY_MSG_494;Doostření vstupu
+HISTORY_MSG_BLSHAPE;Rozmazat dle úrovně
+HISTORY_MSG_BLURCWAV;Rozmazat barevnost
+HISTORY_MSG_BLURWAV;Rozmazat jas
+HISTORY_MSG_BLUWAV;Útlum
+HISTORY_MSG_CAT02PRESET;Automatické přednastavení Cat02
HISTORY_MSG_CLAMPOOG;Oříznout barvy mimo gamut
HISTORY_MSG_COLORTONING_LABGRID_VALUE;Barevné tónování - Korekce barev
HISTORY_MSG_COLORTONING_LABREGION_AB;Barevné tónování - Korekce barev
@@ -769,7 +785,7 @@ HISTORY_MSG_COLORTONING_LABREGION_CHROMATICITYMASK;BT -oblast C masky
HISTORY_MSG_COLORTONING_LABREGION_HUEMASK;Barevné tónování - H maska
HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESS;BT - Světlost
HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESSMASK;BT - L maska
-HISTORY_MSG_COLORTONING_LABREGION_LIST;BT -
+HISTORY_MSG_COLORTONING_LABREGION_LIST;BT -
HISTORY_MSG_COLORTONING_LABREGION_MASKBLUR;BT - oblast masky rozostření
HISTORY_MSG_COLORTONING_LABREGION_OFFSET;BT - oblast posunu
HISTORY_MSG_COLORTONING_LABREGION_POWER;BT - oblast síly
@@ -778,10 +794,15 @@ HISTORY_MSG_COLORTONING_LABREGION_SHOWMASK;BT - oblast zobrazené masky
HISTORY_MSG_COLORTONING_LABREGION_SLOPE;BT - oblast sklonu
HISTORY_MSG_DEHAZE_DEPTH;Závoj - Hloubka
HISTORY_MSG_DEHAZE_ENABLED;Odstranění závoje
+HISTORY_MSG_DEHAZE_LUMINANCE;Závoj - Pouze jas
HISTORY_MSG_DEHAZE_SHOW_DEPTH_MAP;Závoj - Ukázat hloubkovou mapu
HISTORY_MSG_DEHAZE_STRENGTH;Závoj - Síla
HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dvojité demozajkování - automatický práh
HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dvojité demozajkování - Práh kontrastu
+HISTORY_MSG_EDGEFFECT;Útlum hrany
+HISTORY_MSG_FILMNEGATIVE_ENABLED;Negativní film
+HISTORY_MSG_FILMNEGATIVE_FILMBASE;Barva podkladu filmu
+HISTORY_MSG_FILMNEGATIVE_VALUES;Film negativní hodnoty
HISTORY_MSG_HISTMATCHING;Automaticky nalezená tónová křivka
HISTORY_MSG_ICM_OUTPUT_PRIMARIES;Výstup - Základní barvy
HISTORY_MSG_ICM_OUTPUT_TEMP;Výstup - ICC-v4 světelný zdroj D
@@ -789,6 +810,7 @@ HISTORY_MSG_ICM_OUTPUT_TYPE;Výstup - Typ
HISTORY_MSG_ICM_WORKING_GAMMA;Pracovní - Gama
HISTORY_MSG_ICM_WORKING_SLOPE;Pracovní - sklon
HISTORY_MSG_ICM_WORKING_TRC_METHOD;Pracovní - Metoda TRC
+HISTORY_MSG_ILLUM;Osvětlení
HISTORY_MSG_LOCALCONTRAST_AMOUNT;Místní kontrast - Míra
HISTORY_MSG_LOCALCONTRAST_DARKNESS;Místní kontrast - Tmavé
HISTORY_MSG_LOCALCONTRAST_ENABLED;Místní kontrast
@@ -796,20 +818,56 @@ HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Místní kontrast - Světlé
HISTORY_MSG_LOCALCONTRAST_RADIUS;Místní kontrast - Poloměr
HISTORY_MSG_METADATA_MODE;Režim kopírování metadat
HISTORY_MSG_MICROCONTRAST_CONTRAST;Mikrokontrast - Práh kontrastu
+HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;DV - Automatický práh
+HISTORY_MSG_PDSHARPEN_AUTO_RADIUS;DV - Automatický poloměr
+HISTORY_MSG_PDSHARPEN_CHECKITER;DV- Automatický počet průchodů
+HISTORY_MSG_PDSHARPEN_CONTRAST;DV - Práh kontrastu
+HISTORY_MSG_PDSHARPEN_ITERATIONS;DV - Průchody
+HISTORY_MSG_PDSHARPEN_RADIUS;DV - Poloměr
+HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;DV - Zvýšení poloměru rohu
HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Metoda demozajkování pohybu
HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;Směr filtru linkového rušení
HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;Filtr PDAF linek
+HISTORY_MSG_PREPROCWB_MODE;Režim předzpracování VB
+HISTORY_MSG_PROTAB;Ochrana
HISTORY_MSG_PRSHARPEN_CONTRAST;Doostření - Práh kontrastu
-HISTORY_MSG_RAWCACORR_AUTOIT;Raw korekce CA - Iterace
-HISTORY_MSG_RAWCACORR_COLORSHIFT;Raw korekce CA - Zabránit posunu barev
+HISTORY_MSG_RANGEAB;Rozsah ab
+HISTORY_MSG_RAWCACORR_AUTOIT;Raw korekce ChA - Iterace
+HISTORY_MSG_RAWCACORR_COLORSHIFT;Raw korekce ChA - Zabránit posunu barev
HISTORY_MSG_RAW_BORDER;Okraj Raw
HISTORY_MSG_RESIZE_ALLOWUPSCALING;Změna rozměrů - Povolit zvětšení
HISTORY_MSG_SHARPENING_BLUR;Doostření - Poloměr rozmazání
HISTORY_MSG_SHARPENING_CONTRAST;Doostření - Práh kontrastu
HISTORY_MSG_SH_COLORSPACE;S/S - Barevný prostor
+HISTORY_MSG_SIGMACOL;Útlum barevnosti
+HISTORY_MSG_SIGMADIR;Útlum směru
+HISTORY_MSG_SIGMAFIN;Finální útlum kontrastu
+HISTORY_MSG_SIGMATON;Útlum tónování
HISTORY_MSG_SOFTLIGHT_ENABLED;Měkké světlo
HISTORY_MSG_SOFTLIGHT_STRENGTH;Měkká světla - Síla
+HISTORY_MSG_TEMPOUT;CAM02 - Automatická teplota
+HISTORY_MSG_THRESWAV;Práh vyvážení
HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Kotva
+HISTORY_MSG_TRANS_Method;Geometrie - Metoda
+HISTORY_MSG_WAVBALCHROM;Vyvážení barevnosti
+HISTORY_MSG_WAVBALLUM;Vyvážení jasu
+HISTORY_MSG_WAVBL;Úrovně rozmazání
+HISTORY_MSG_WAVCHROMCO;Hrubá barevnost
+HISTORY_MSG_WAVCHROMFI;Jemné barevnost
+HISTORY_MSG_WAVCLARI;Čirost
+HISTORY_MSG_WAVEDGS;Zachování hran
+HISTORY_MSG_WAVLOWTHR;Práh nízkého kontrastu
+HISTORY_MSG_WAVMERGEC;Sloučení barevnosti
+HISTORY_MSG_WAVMERGEL;Sloučení jasu
+HISTORY_MSG_WAVOFFSET;Posun
+HISTORY_MSG_WAVOLDSH;Starý algoritmus
+HISTORY_MSG_WAVRADIUS;Poloměr Stíny-Světla
+HISTORY_MSG_WAVSCALE;Měřítko
+HISTORY_MSG_WAVSHOWMASK;Ukázat masku vlnky
+HISTORY_MSG_WAVSIGMA;Útlum
+HISTORY_MSG_WAVSOFTRAD;Čirost jemný poloměr
+HISTORY_MSG_WAVSOFTRADEND;Konečný jemný poloměr
+HISTORY_MSG_WAVUSHAMET;Metoda čirosti
HISTORY_NEWSNAPSHOT;Přidat
HISTORY_NEWSNAPSHOT_TOOLTIP;Zkratka: Alt-s
HISTORY_SNAPSHOT;Snímek
@@ -939,15 +997,15 @@ MAIN_TAB_COLOR;Barvy
MAIN_TAB_COLOR_TOOLTIP;Zkratka: Alt-c
MAIN_TAB_DETAIL;Detaily
MAIN_TAB_DETAIL_TOOLTIP;Zkratka: Alt-d
-MAIN_TAB_DEVELOP; Dávková editace
+MAIN_TAB_DEVELOP; Dávková editace
MAIN_TAB_EXIF;Exif
-MAIN_TAB_EXPORT; Rychlý export
+MAIN_TAB_EXPORT; Rychlý export
MAIN_TAB_EXPOSURE;Expozice
MAIN_TAB_EXPOSURE_TOOLTIP;Zkratka: Alt-e
MAIN_TAB_FAVORITES;Oblíbené
MAIN_TAB_FAVORITES_TOOLTIP;Zkratka: Alt-u
-MAIN_TAB_FILTER; Filtr
-MAIN_TAB_INSPECT; Prohlížení
+MAIN_TAB_FILTER; Filtr
+MAIN_TAB_INSPECT; Prohlížení
MAIN_TAB_IPTC;IPTC
MAIN_TAB_METADATA;Metadata
MAIN_TAB_METADATA_TOOLTIP;Zkratka: Alt-m
@@ -961,8 +1019,8 @@ MAIN_TOOLTIP_BACKCOLOR2;Barva pozadí náhledu: bílá\nZkratka: 9
MAIN_TOOLTIP_BACKCOLOR3;Barva pozadí náhledu: středně šedá\nZkratka: 9
MAIN_TOOLTIP_BEFOREAFTERLOCK;Zamknout / Odemknout pohled Před\n\nZamknout: ponechá pohled Před nezměněn.\nUžitečné pro posouzení výsledného efektu po použití více nástrojů.\nNavíc může být porovnání provedeno proti kterémukoli stavu v historii.\n\nOdemknout: pohled Před bude následovat pohled Poté, vždy jen o jeden krok zpět, představí vliv právě použitého nástroje.
MAIN_TOOLTIP_HIDEHP;Zobrazit či schovat levý panel (obsahující historii).\nZkratka: l
-MAIN_TOOLTIP_INDCLIPPEDH;Zvýraznit oříznutá světla.\nZkratka: >
-MAIN_TOOLTIP_INDCLIPPEDS;Zvýraznit oříznuté stíny.\nZkratka: <
+MAIN_TOOLTIP_INDCLIPPEDH;Zvýraznění oříznutých světel.\nZkratka: >
+MAIN_TOOLTIP_INDCLIPPEDS;Zvýraznění oříznutých stínů.\nZkratka: <
MAIN_TOOLTIP_PREVIEWB;Náhled modrého kanálu.\nZkratka: b
MAIN_TOOLTIP_PREVIEWFOCUSMASK;Náhled masky zaostření.\nZkratka: Shift-f\n\nVíce přesné u snímků s nízkou hloubkou ostrosti, nízkým šumem a na vyšších úrovních zvětšení.\n\nPoužijte přiblížení v rozsahu 10 až 30% pro zlepšení přesnosti detekce u zašuměných snímků.
MAIN_TOOLTIP_PREVIEWG;Náhled zeleného kanálu.\nZkratka: g
@@ -1017,6 +1075,7 @@ PARTIALPASTE_EQUALIZER;Úrovně vlnky
PARTIALPASTE_EVERYTHING;Vše
PARTIALPASTE_EXIFCHANGES;Exif
PARTIALPASTE_EXPOSURE;Expozice
+PARTIALPASTE_FILMNEGATIVE;Negativní film
PARTIALPASTE_FILMSIMULATION;Simulace filmu
PARTIALPASTE_FLATFIELDAUTOSELECT;Automatický výběr Flat Field
PARTIALPASTE_FLATFIELDBLURRADIUS;Poloměr rozostření Flat Field
@@ -1041,6 +1100,7 @@ PARTIALPASTE_PREPROCESS_GREENEQUIL;Vyrovnání zelené
PARTIALPASTE_PREPROCESS_HOTPIXFILT;Filtr vypálených pixelů
PARTIALPASTE_PREPROCESS_LINEDENOISE;Filtrovat linkové rušení
PARTIALPASTE_PREPROCESS_PDAFLINESFILTER;Filtr PDAF linek
+PARTIALPASTE_PREPROCWB;Předzpracování Vyvážení bílé
PARTIALPASTE_PRSHARPENING;Doostření po změně velikosti
PARTIALPASTE_RAWCACORR_AUTO;Automatická korekce CA
PARTIALPASTE_RAWCACORR_AVOIDCOLORSHIFT;CA zabránit posunu barev
@@ -1075,6 +1135,7 @@ PREFERENCES_APPEARANCE_COLORPICKERFONT;Písmo Průzkumníka barev
PREFERENCES_APPEARANCE_CROPMASKCOLOR;Barva masky ořezu
PREFERENCES_APPEARANCE_MAINFONT;Hlavní písmo
PREFERENCES_APPEARANCE_NAVGUIDECOLOR;Barva vodítek navigátoru
+PREFERENCES_APPEARANCE_PSEUDOHIDPI;Pseudo-HiDPI režim
PREFERENCES_APPEARANCE_THEME;Motiv
PREFERENCES_APPLNEXTSTARTUP;vyžaduje restart aplikace
PREFERENCES_AUTOMONPROFILE;Použít barevný profil hlavního monitoru z operačního systému
@@ -1094,11 +1155,11 @@ PREFERENCES_CACHEMAXENTRIES;Maximální počet záznamů v mezipaměti
PREFERENCES_CACHEOPTS;Vlastnosti mezipaměti
PREFERENCES_CACHETHUMBHEIGHT;Maximální výška náhledu
PREFERENCES_CHUNKSIZES;Dlaždic na vlákno
-PREFERENCES_CHUNKSIZE_RAW_AMAZE;AMaZE demozajkování
-PREFERENCES_CHUNKSIZE_RAW_CA;Raw korekce CA
+PREFERENCES_CHUNKSIZE_RAW_AMAZE;Demozajkování AMaZE
+PREFERENCES_CHUNKSIZE_RAW_CA;Raw korekce ChA
PREFERENCES_CHUNKSIZE_RAW_RCD;RCD demozajkování
-PREFERENCES_CHUNKSIZE_RAW_XT;Xtrans demozajkování
-PREFERENCES_CHUNKSIZE_RGB;RGB zpracování
+PREFERENCES_CHUNKSIZE_RAW_XT;Demozajkování Xtrans
+PREFERENCES_CHUNKSIZE_RGB;Zpracování barev
PREFERENCES_CLIPPINGIND;Indikace oříznutí
PREFERENCES_CLUTSCACHE;Mezipaměť HaldCLUT
PREFERENCES_CLUTSCACHE_LABEL;Maximální počet přednačtených CLUTů
@@ -1186,8 +1247,8 @@ PREFERENCES_PARSEDEXTADDHINT;Vymazat označenou příponu ze seznamu.
PREFERENCES_PARSEDEXTDELHINT;Vymazat označenou příponu ze seznamu.
PREFERENCES_PARSEDEXTDOWNHINT;Vybranou příponu posunout na seznamu níže.
PREFERENCES_PARSEDEXTUPHINT;Vybranou příponu posunout na seznamu výše.
-PREFERENCES_PERFORMANCE_MEASURE;Měřit
-PREFERENCES_PERFORMANCE_MEASURE_HINT;Vypisovat časy zpracování v konzoli
+PREFERENCES_PERFORMANCE_MEASURE;Měření
+PREFERENCES_PERFORMANCE_MEASURE_HINT;Vypisuje doby zpracování do konzole
PREFERENCES_PERFORMANCE_THREADS;Vlákna
PREFERENCES_PERFORMANCE_THREADS_LABEL;Maximální počet vláken pro Redukci šumu a Úrovně vlnky (0 = Automaticky)
PREFERENCES_PREVDEMO;Metoda demozajkování náhledu
@@ -1262,11 +1323,11 @@ PROFILEPANEL_TOOLTIPCOPY;Kopírovat současný profil do schránky.\nCtrl-kli
PROFILEPANEL_TOOLTIPLOAD;Nahrát profil ze souboru.\nCtrl-klik umožní vybrat parametry pro nahrání.
PROFILEPANEL_TOOLTIPPASTE;Vložit profil ze schránky.\nCtrl-klik umožní vybrat parametry pro vložení.
PROFILEPANEL_TOOLTIPSAVE;Uložit současný profil.\nCtrl-klik umožní vybrat parametry pro uložení.
-PROGRESSBAR_DECODING;Dekodování...
+PROGRESSBAR_DECODING;Dekódování…
PROGRESSBAR_GREENEQUIL;Vyrovnání zelené...
-PROGRESSBAR_HLREC;Rekonstrukce světel...
-PROGRESSBAR_HOTDEADPIXELFILTER;Filtr vypálených/mrtvých pixelů...
-PROGRESSBAR_LINEDENOISE;Filtr linkového rušení...
+PROGRESSBAR_HLREC;Rekonstrukce světel…
+PROGRESSBAR_HOTDEADPIXELFILTER;Filtrování vypálených/mrtvých pixelů…
+PROGRESSBAR_LINEDENOISE;Filtr linkového rušení…
PROGRESSBAR_LOADING;Načítání obrázku...
PROGRESSBAR_LOADINGTHUMBS;Načítání náhledů...
PROGRESSBAR_LOADJPEG;Načítání JPEG...
@@ -1275,7 +1336,7 @@ PROGRESSBAR_LOADTIFF;Načítání TIFF...
PROGRESSBAR_NOIMAGES;Složka neobsahuje obrázky
PROGRESSBAR_PROCESSING;Zpracovávaní obrázku...
PROGRESSBAR_PROCESSING_PROFILESAVED;Profil zpracování uložen
-PROGRESSBAR_RAWCACORR;RAW korekce chromatické aberace...
+PROGRESSBAR_RAWCACORR;Korekce ChA…
PROGRESSBAR_READY;Připraven
PROGRESSBAR_SAVEJPEG;Ukládání JPEG souboru...
PROGRESSBAR_SAVEPNG;Ukládání PNG souboru...
@@ -1293,8 +1354,8 @@ QUEUE_DESTFILENAME;Cesta a název souboru
QUEUE_FORMAT_TITLE;Formát souboru
QUEUE_LOCATION_FOLDER;Ulož do souboru
QUEUE_LOCATION_TEMPLATE;Použít šablonu
-QUEUE_LOCATION_TEMPLATE_TOOLTIP;Specifikujte kam se mají uložit výstupy. Lze použít umístění zdrojových souborů, pořadí, stav koše nebo pozice ve frontě.\n\nNapříklad pokud má zpracovávaná fotografie následující cestu:\n/home/tomas/fotky/2010-10-31/dsc0042.nef,\nmají jednotlivé formátovací řetězce tento význam:\n%d4 = home\n%d3 = tomas\n%d2 = fotky\n%d1 = 2010-10-31\n%f = dsc0042\n%p1 = /home/tomas/fotky/2010-10-31/\n%p2 = /home/tomas/fotky/\n%p3 = /home/tomas/\n%p4 = /home/\n\n%r bude nahrazeno hodnocením fotografie.Pokud není fotografie ohodnocena, bude %r nahrazeno '0'.Pokud je fotografie v koši, bude %r nahrazeno 'x'.\n\nPokud si přejete uložit výstupní obrázek vedle originálu, napište:\n%p1/%f\n\nPokud si jej ale přejete uložit do adresáře "converted" ve stejném adresáři jako otevřený obrázek, napište:\n%p1/converted/%f\n\nPro uložení výstupního obrázku do adresáře"/home/tom/photos/converted/2010-10-31", napište:\n%p2/converted/%d1/%f
-QUEUE_LOCATION_TITLE;Výstupní umístění
+QUEUE_LOCATION_TEMPLATE_TOOLTIP;Lze použít následující formátovací řetězce:\n%f, %d1, %d2, ..., %p1, %p2, ..., %r\n\nTyto formátovací řetězce reprezentují různé části cesty k uložené fotografii.\n\nNapříklad pokud má zpracovávaná fotografie následující cestu:\n/home/tomas/fotky/2010-10-31/fotka1.raw,\nmají jednotlivé formátovací řetězce tento význam:\n%d4 = home\n%d3 = tomas\n%d2 = fotky\n%d1 = 2010-10-31\n%f = fotka1\n%p1 = /home/tomas/fotky/2010-10-31/\n%p2 = /home/tomas/fotky/\n%p3 = /home/tomas/\n%p4 = /home/\n\n%r bude nahrazeno hodnocením fotografie.Pokud není fotografie ohodnocena, bude %r nahrazeno '0'.Pokud je fotografie v koši, bude %r nahrazeno 'x'.\n\nPokud si přejete uložit výstupní obrázek vedle originálu, napište:\n%p1/%f\n\nPokud si jej ale přejete uložit do adresáře "converted" ve stejném adresáři jako otevřený obrázek, napište:\n%p1/converted/%f\n\nPro uložení výstupního obrázku do adresáře"/home/tom/photos/converted/2010-10-31", napište:\n%p2/converted/%d1/%f
+QUEUE_LOCATION_TITLE;Umístění výstupu
QUEUE_STARTSTOP_TOOLTIP;Spustit nebo zastavit zpracování obrázků ve frontě.\n\nZkratka: Ctrl+s
SAMPLEFORMAT_0;Neznámý datový formát
SAMPLEFORMAT_1;8-bitový neznaménkový
@@ -1331,7 +1392,7 @@ THRESHOLDSELECTOR_HINT;Držte klávesu Shift pro přesun individuálních
THRESHOLDSELECTOR_T;Nahoře
THRESHOLDSELECTOR_TL;Nahoře vlevo
THRESHOLDSELECTOR_TR;Nahoře vpravo
-TOOLBAR_TOOLTIP_COLORPICKER;Uzamykatelný Průzkumník barev\n\nPokud je nástroj aktivní:\n- Přidání sondy: levý-klik.\n- Posunutí sondy: levý-klik a posunutí.\n- Smazání sondy: pravý-klik.\n- Smazání všech sond: Ctrl+Shift+pravý-klik.\n- Návrat k nástroji posunu: pravý-klik mimo průzkumníky.
+TOOLBAR_TOOLTIP_COLORPICKER;Uzamykatelný Průzkumník barev\n\nPokud je nástroj aktivní:\n- Přidání sondy: levý-klik.\n- Posunutí sondy: levý-klik a posunutí.\n- Smazání sondy: pravý-klik.\n- Smazání všech sond: Ctrl+Shift+pravý-klik.\n- Návrat k nástroji posunu: pravý-klik mimo jakoukoli sondu.
TOOLBAR_TOOLTIP_CROP;Oříznutí výběru.\nZkratka: c\nVýřez posunete pomocí Shift + tažení myši
TOOLBAR_TOOLTIP_HAND;Posun.\nZkratka: h
TOOLBAR_TOOLTIP_STRAIGHTEN;Vyznačení roviny / rotace.\nZkratka: s\n\nZobrazení míry rotace pomocí vodící linky na náhledu snímky. Úhel rotace je zobrazen vedle vodící linky. Střed rotace je geometrický střed snímku.
@@ -1417,7 +1478,7 @@ TP_COLORAPP_ALGO_JS;Světlost + Nasycení (JS)
TP_COLORAPP_ALGO_QM;Jas a pestrobarevnost (QM)
TP_COLORAPP_ALGO_TOOLTIP;Umožňuje vybrat mezi podmnožinou nebo všemi parametry.
TP_COLORAPP_BADPIXSL;Filtr vypálených/mrtvých pixelů
-TP_COLORAPP_BADPIXSL_TOOLTIP;Potlačení vypálených/mrtvých (jasně zabarvených) pixelů.\n0 = Bez efektu\n1 = Medián\n2 = Gaussův.\nPopřípadě obrázek upravte tak, aby jste se vyhnuli velmi tmavým stínům.\n\nTyto artefakty vznikají díky omezením CIECAM02.
+TP_COLORAPP_BADPIXSL_TOOLTIP;Potlačení vypálených/mrtvých (jasně zabarvených) pixelů.\n0 = Bez efektu\n1 = Medián\n2 = Gaussův.\nPopřípadě obrázek upravte tak, aby jste se vyhnuli velmi tmavým stínům.\n\nTyto artefakty vznikají díky omezením CIECAM02.
TP_COLORAPP_BRIGHT;Jas (O)
TP_COLORAPP_BRIGHT_TOOLTIP;Jas v CIECAM02 bere v potaz svítivost bílé a rozdíly jasů mezi L*a*b* a RGB.
TP_COLORAPP_CAT02ADAPTATION_TOOLTIP;U ručního nastavení jsou doporučeny hodnoty nad 65.
@@ -1428,7 +1489,7 @@ TP_COLORAPP_CHROMA_S;Nasycení (S)
TP_COLORAPP_CHROMA_S_TOOLTIP;Nasycení se v CIECAM02 liší od nasycení L*a*b* a RGB.
TP_COLORAPP_CHROMA_TOOLTIP;Barevnost se v CIECAM02 liší od barevnosti L*a*b* a RGB.
TP_COLORAPP_CIECAT_DEGREE;CAT02 přizpůsobení
-TP_COLORAPP_CONTRAST;Kontrast (I)
+TP_COLORAPP_CONTRAST;Kontrast (J)
TP_COLORAPP_CONTRAST_Q;Kontrast (O)
TP_COLORAPP_CONTRAST_Q_TOOLTIP;Liší se od kontrastu L*a*b* a RGB.
TP_COLORAPP_CONTRAST_TOOLTIP;Liší se od kontrastu L*a*b* a RGB.
@@ -1445,17 +1506,29 @@ TP_COLORAPP_GAMUT;Kontrola gamutu (L*a*b*)
TP_COLORAPP_GAMUT_TOOLTIP;Povolí kontrolu gamutu v L*a*b* režimu.
TP_COLORAPP_HUE;Odstín (h)
TP_COLORAPP_HUE_TOOLTIP;Odstín (h) - úhel mezi 0° a 360°.
+TP_COLORAPP_IL41;D41
+TP_COLORAPP_IL50;D50
+TP_COLORAPP_IL55;D55
+TP_COLORAPP_IL60;D60
+TP_COLORAPP_IL65;D65
+TP_COLORAPP_IL75;D75
+TP_COLORAPP_ILA;Žárovka StdA 2856K
+TP_COLORAPP_ILFREE;Volná
+TP_COLORAPP_ILLUM;Osvětlení
+TP_COLORAPP_ILLUM_TOOLTIP;Vyberte osvětlení nejvíce se blížící podmínkám v době pořízení snímku.\nObecně D50, to se ale může lišit v závislosti na denní době a zeměpisné šířce.
TP_COLORAPP_LABEL;CIE model přizpůsobení barev 2002
TP_COLORAPP_LABEL_CAM02;Úpravy obrázku
TP_COLORAPP_LABEL_SCENE;Podmínky scény
TP_COLORAPP_LABEL_VIEWING;Podmínky zobrazení
-TP_COLORAPP_LIGHT;Světlost (I)
+TP_COLORAPP_LIGHT;Světlost (J)
TP_COLORAPP_LIGHT_TOOLTIP;Světlost v CIECAM02 se liší od světlosti v L*a*b* a RGB.
TP_COLORAPP_MEANLUMINANCE;Střední jas (Yb%)
TP_COLORAPP_MODEL;VB - Model
TP_COLORAPP_MODEL_TOOLTIP;Model bílého bodu.\n\nWB [RT] + [výstup]: Pro scénu je použito vyvážení bílé RawTherapee , CIECAM02 je nastaven na D50 a vyvážení bílé výstupního zařízení je nastaveno v Podmínkách prohlížení.\n\nWB [RT+CAT02] + [výstup]: CAT02 používá RawTherapee nastavení vyvážení bílé a vyvážení bílé výstupního zařízení je nastaveno v Podmínkách prohlížení.\n\nVolná teplota+zelená + CAT02 + [výstup]: teplota a zelená je vybrána uživatelem, vyvážení bílé výstupního zařízení je nastaveno v Podmínkách prohlížení.
TP_COLORAPP_NEUTRAL;Obnovit
TP_COLORAPP_NEUTRAL_TIP;Obnoví původní hodnoty u všech posuvníků a křivek.
+TP_COLORAPP_PRESETCAT02;Automatické přednastavení Cat02
+TP_COLORAPP_PRESETCAT02_TIP;Nastaví volby, posuvníky, teplotu a zelenou podle Cat02 automatického přednastavení.\nMusíte nastavit světelné podmínky při fotografování.\nPokud je potřeba, musíte změnit Cat02 podmínky přizpůsobení pro prohlížení.\nPokud je potřeba, můžete změnit teplotu a odstín podmínek při prohlížení a také další nastavení.
TP_COLORAPP_RSTPRO;Ochrana červených a pleťových tónů
TP_COLORAPP_RSTPRO_TOOLTIP;Ochrana červených a pleťových tónů ovlivňuje posuvníky i křivky.
TP_COLORAPP_SURROUND;Okolí
@@ -1472,7 +1545,9 @@ TP_COLORAPP_TCMODE_LABEL2;Mód křivky 2
TP_COLORAPP_TCMODE_LABEL3;Mód barevné křivky
TP_COLORAPP_TCMODE_LIGHTNESS;Světlost
TP_COLORAPP_TCMODE_SATUR;Nasycení
-TP_COLORAPP_TEMP_TOOLTIP;Pro výběr osvětlení vždy nastavte Tint=1.\n\nA barva=2856\nD50 barva=5003\nD55 barva=5503\nD65 barva=6504\nD75 barva=7504
+TP_COLORAPP_TEMP2_TOOLTIP;Buď symetrický režim teploty = Nastavení bílé,\nNebo vyberte osvětlení, vždy nastavte Odstín=1.\n\nA barva=2856\nD50 barva=5003\nD55 barva=5503\nD65 barva=6504\nD75 barva=7504
+TP_COLORAPP_TEMPOUT_TOOLTIP;Zakažte pro změnu teploty a nádechu
+TP_COLORAPP_TEMP_TOOLTIP;Pro výběr osvětlení vždy nastavte Odstín=1.\n\nA barva=2856\nD41 temp=4100\nD50 barva=5003\nD55 barva=5503\nD60 temp=6000\nD65 barva=6504\nD75 barva=7504
TP_COLORAPP_TONECIE;Mapování tónů pomocí CIECAM02
TP_COLORAPP_TONECIE_TOOLTIP;Pokud je volba zakázána, probíhá mapování tónů v prostoru L*a*b*.\nPokud je volba povolena. probíhá mapování tónů pomocí CIECAM02.\nAby měla tato volba efekt, musí být povolen nástroj Mapování tónů.
TP_COLORAPP_VIEWING_ABSOLUTELUMINANCE_TOOLTIP;Absolutní jas prostředí prohlížení\n(obvykle 16 cd/m²).
@@ -1499,7 +1574,7 @@ TP_COLORTONING_LABREGION_CHANNEL_B;Modrá
TP_COLORTONING_LABREGION_CHANNEL_G;Zelená
TP_COLORTONING_LABREGION_CHANNEL_R;Červená
TP_COLORTONING_LABREGION_CHROMATICITYMASK;C
-TP_COLORTONING_LABREGION_HUEMASK;H
+TP_COLORTONING_LABREGION_HUEMASK;H
TP_COLORTONING_LABREGION_LIGHTNESS;Světlost
TP_COLORTONING_LABREGION_LIGHTNESSMASK;L
TP_COLORTONING_LABREGION_LIST_TITLE;Oprava
@@ -1561,6 +1636,7 @@ TP_DEFRINGE_RADIUS;Poloměr
TP_DEFRINGE_THRESHOLD;Práh
TP_DEHAZE_DEPTH;Hloubka
TP_DEHAZE_LABEL;Odstranění závoje
+TP_DEHAZE_LUMINANCE;Pouze jas
TP_DEHAZE_SHOW_DEPTH_MAP;Ukázat hloubkovou mapu
TP_DEHAZE_STRENGTH;Síla
TP_DIRPYRDENOISE_CHROMINANCE_AMZ;Více zónová automatika
@@ -1669,6 +1745,15 @@ TP_EXPOSURE_TCMODE_STANDARD;Běžný
TP_EXPOSURE_TCMODE_WEIGHTEDSTD;Běžný vážený
TP_EXPOS_BLACKPOINT_LABEL;Raw černé body
TP_EXPOS_WHITEPOINT_LABEL;Raw bílé body
+TP_FILMNEGATIVE_BLUE;Poměr modré
+TP_FILMNEGATIVE_FILMBASE_PICK;Výběr barvy podkladu filmu
+TP_FILMNEGATIVE_FILMBASE_TOOLTIP;Vyberte místo neexponovaného filmu (například okraj mezi snímky) pro získání aktuální barvy podkladu a uložte jej do profilu zpracování.\nTo umožňuje jednoduchou kontrolu konzistence vyvážení barev během dávkového zpracování více obrázků ze stejného filmu.\nTaké použijte pokud jsou převáděné snímky moc tmavé, přesvícené nebo barevně nevyvážené.
+TP_FILMNEGATIVE_FILMBASE_VALUES;Barva podkladu filmu:
+TP_FILMNEGATIVE_GREEN;Referenční exponent (kontrast)
+TP_FILMNEGATIVE_GUESS_TOOLTIP;Automaticky nastaví poměr červené a modré výběrem dvou vzorků s neutrálním odstínem (bez barvy) v původní scéně. Vzorky by se měly lišit jasem. Následně je nastaveno vyvážení bílé.
+TP_FILMNEGATIVE_LABEL;Negativní film
+TP_FILMNEGATIVE_PICK;Výběr neutrálních míst
+TP_FILMNEGATIVE_RED;Poměr červené
TP_FILMSIMULATION_LABEL;Simulace filmu
TP_FILMSIMULATION_SLOWPARSEDIR;RawTherapee je nakonfigurován aby hledal Hald CLUT obrázky pro nástroj Simulace filmu ve složce, jejíž načítání trvá velmi dlouho.\nZkontrolujte prosím nastavení v menu Volby > Zpracování obrázku > Simulace filmu.\nNastavená složka by měla buď obsahovat jen a pouze Hald CLUT obrázky nebo být prázdná, pokud nechcete nástroj Simulace filmu používat.\n\nVíce informací získáte v článku o nástroji Simulace filmu na RawPedii.\n\nChcete zrušit právě probíhající prohledávání složky?
TP_FILMSIMULATION_STRENGTH;Síla
@@ -1681,7 +1766,7 @@ TP_FLATFIELD_BT_HORIZONTAL;Vodorovně
TP_FLATFIELD_BT_VERTHORIZ;Vodorovně a svisle
TP_FLATFIELD_BT_VERTICAL;Svisle
TP_FLATFIELD_CLIPCONTROL;Kontrola oříznutí
-TP_FLATFIELD_CLIPCONTROL_TOOLTIP;Kontrola oříznutí zabrání oříznutí světel po aplikaci Flat Field. Pokud byly světla oříznuta ještě před aplikací Flat field, může se objevit barevný nádech.
+TP_FLATFIELD_CLIPCONTROL_TOOLTIP;Kontrola oříznutí zabrání oříznutí světel po aplikaci Flat Field. Pokud byly světla oříznuta ještě před aplikací Flat field, použije se hodnota 0.
TP_FLATFIELD_LABEL;Flat Field
TP_GENERAL_11SCALE_TOOLTIP;Efekt tohoto nástroje je viditelný pouze při přiblížení 1:1.
TP_GRADIENT_CENTER;Střed
@@ -1789,6 +1874,8 @@ TP_LABCURVE_RSTPRO_TOOLTIP;Pracuje s posuvníkem barevnosti a CC křivkou.
TP_LENSGEOM_AUTOCROP;Automatický ořez
TP_LENSGEOM_FILL;Automatické vyplnění
TP_LENSGEOM_LABEL;Objektiv / Geometrie
+TP_LENSGEOM_LIN;Lineární
+TP_LENSGEOM_LOG;Logaritmická
TP_LENSPROFILE_CORRECTION_AUTOMATCH;Automaticky vybráno
TP_LENSPROFILE_CORRECTION_LCPFILE;LCP soubor
TP_LENSPROFILE_CORRECTION_MANUAL;Ručně vybráno
@@ -1817,6 +1904,7 @@ TP_PCVIGNETTE_ROUNDNESS;Zaoblení
TP_PCVIGNETTE_ROUNDNESS_TOOLTIP;Zaoblení:\n0 = čtverec,\n50 = elipsa,\n100 = kruh.
TP_PCVIGNETTE_STRENGTH;Síla
TP_PCVIGNETTE_STRENGTH_TOOLTIP;Síla filtru v expozičních stupních (v rozích).
+TP_PDSHARPENING_LABEL;Doostření vstupu
TP_PERSPECTIVE_HORIZONTAL;Vodorovně
TP_PERSPECTIVE_LABEL;Perspektiva
TP_PERSPECTIVE_VERTICAL;Svisle
@@ -1836,6 +1924,10 @@ TP_PREPROCESS_LINEDENOISE_DIRECTION_PDAF_LINES;Vodorovně pouze u PDAF řádků
TP_PREPROCESS_LINEDENOISE_DIRECTION_VERTICAL;Svisle
TP_PREPROCESS_NO_FOUND;Nic nenalezeno
TP_PREPROCESS_PDAFLINESFILTER;Filtr PDAF linek
+TP_PREPROCWB_LABEL;Předzpracování Vyvážení bílé
+TP_PREPROCWB_MODE;Mód
+TP_PREPROCWB_MODE_AUTO;Automaticky
+TP_PREPROCWB_MODE_CAMERA;Fotoaparát
TP_PRSHARPENING_LABEL;Doostření po změně velikosti
TP_PRSHARPENING_TOOLTIP;Obrázek po zmenšení doostří. Funguje pouze pokud je použita "Lanczos" metoda zmenšení. Náhled výsledku není v tomto nástroji možný. Podívejte se do RawPedie pro návod k použití.
TP_RAWCACORR_AUTO;Automatická korekce
@@ -1868,7 +1960,7 @@ TP_RAW_DCBENHANCE;Vylepšení DCB
TP_RAW_DCBITERATIONS;Počet průchodů DCB
TP_RAW_DCBVNG4;DCB+VNG4
TP_RAW_DMETHOD;Metoda
-TP_RAW_DMETHOD_PROGRESSBAR;%1 demozajkování...
+TP_RAW_DMETHOD_PROGRESSBAR;%1 demozajkování…
TP_RAW_DMETHOD_PROGRESSBAR_REFINE;Vylepšení demozajkování...
TP_RAW_DMETHOD_TOOLTIP;Poznámka: IGV a LMMSE jsou určeny pro obrázky s vysokým ISO, kterým pomáhají vyhnout se u redukce šumu vzniku vzorů, posterizaci a vyžehlenému vzhledu.\n\nPixel Shift je určen pro soubory Pentax/Sony Pixel Shift.\nPro soubory neobsahující Pixel Shift data je použita metoda AMaZE.
TP_RAW_DUALDEMOSAICAUTOCONTRAST;Automatický práh
@@ -1882,7 +1974,7 @@ TP_RAW_HD_TOOLTIP;Nižší hodnoty učiní detekci vypálených/mrtvých bodů a
TP_RAW_HPHD;HPHD
TP_RAW_IGV;IGV
TP_RAW_IMAGENUM;Dílčí snímek
-TP_RAW_IMAGENUM_SN;Režim SN
+TP_RAW_IMAGENUM_SN;Režim Signál/Šum
TP_RAW_IMAGENUM_TOOLTIP;Některé raw snímky obsahují několik podsnímků (Pentax/Sony Pixel Shift, Pentax 3-in-1 HDR, Canon Dual Pixel, Fuji EXR).\n\nV případě, že je pro demozajkování použita jiná metoda než Pixel Shift, tato volba určí, který podsnímek se použije.\n\nPokud je použita Pixel Shift metoda demozajkování na Pixel Shift raw soubory, budou použity všechny podsnímky a tato volba určí, který snímek bude použit pro pohyblivé části.
TP_RAW_LABEL;Demozajkování
TP_RAW_LMMSE;LMMSE
@@ -1924,7 +2016,7 @@ TP_RAW_SENSOR_XTRANS_DMETHOD_TOOLTIP;Tří průchodová dává lepší výsledky
TP_RAW_SENSOR_XTRANS_LABEL;Senzory s X-Trans maticí
TP_RAW_VNG4;VNG4
TP_RAW_XTRANS;X-Trans
-TP_RAW_XTRANSFAST;Fast X-Trans
+TP_RAW_XTRANSFAST;Rychlý X-Trans
TP_RESIZE_ALLOW_UPSCALING;Povolit zvětšení
TP_RESIZE_APPLIESTO;Aplikovat na:
TP_RESIZE_CROPPEDAREA;Oblast ořezu
@@ -2047,10 +2139,12 @@ TP_SHARPENING_EDRADIUS;Poloměr
TP_SHARPENING_EDTOLERANCE;Tolerance k hranám
TP_SHARPENING_HALOCONTROL;Omezení halo efektu
TP_SHARPENING_HCAMOUNT;Míra
+TP_SHARPENING_ITERCHECK;Automatické omezení průchodů
TP_SHARPENING_LABEL;Doostření
TP_SHARPENING_METHOD;Metoda
TP_SHARPENING_ONLYEDGES;Doostřit pouze hrany
TP_SHARPENING_RADIUS;Poloměr
+TP_SHARPENING_RADIUS_BOOST;Zvýšení poloměru rohu
TP_SHARPENING_RLD;RL Dekonvoluce
TP_SHARPENING_RLD_AMOUNT;Míra
TP_SHARPENING_RLD_DAMPING;Útlum
@@ -2110,11 +2204,17 @@ TP_WAVELET_BACKGROUND;Pozadí
TP_WAVELET_BACUR;Křivka
TP_WAVELET_BALANCE;Vyvážení kontrastu d/v-h
TP_WAVELET_BALANCE_TOOLTIP;Změní vyvážení mezi směry vlnky: svisle-vodorovně a úhlopříčně.\nPokud je aktivován kontrast, barevnost nebo zbytkové tónové mapování je efekt díky vyvážení zesílen.
-TP_WAVELET_BALCHRO;Vyvážení barev
+TP_WAVELET_BALCHRO;Vyvážení barevnosti
+TP_WAVELET_BALCHROM;Korekce odšumění Modrá-Červená
TP_WAVELET_BALCHRO_TOOLTIP;Pokud je povoleno, křivka nebo posuvníky "Vyvážení kontrastu" ovlivňují i vyvážení barev.
+TP_WAVELET_BALLUM;Korekce odšumění Bílá-Černá
TP_WAVELET_BANONE;Nic
TP_WAVELET_BASLI;Posuvník
TP_WAVELET_BATYPE;Metoda vyvážení kontrastu
+TP_WAVELET_BL;Úrovně rozmazání
+TP_WAVELET_BLCURVE;Rozmazání dle úrovní
+TP_WAVELET_BLURFRAME;Rozmazání
+TP_WAVELET_BLUWAV;Útlum
TP_WAVELET_CBENAB;Tónování a vyvážení barev
TP_WAVELET_CB_TOOLTIP;Pro silnější hodnoty barevného tónování s kombinováním nebo bez rozkladu na vrstvy tónování\nPro menší hodnoty můžete změnit vyvážení bílé na barvu pozadí (obloha, ...) bez změny předního plánu, obecně více kontrastní.
TP_WAVELET_CCURVE;Místní kontrast
@@ -2124,22 +2224,32 @@ TP_WAVELET_CH3;Propojit kontrast úrovní
TP_WAVELET_CHCU;Křivka
TP_WAVELET_CHR;Barevnost - kontrast síla propojení
TP_WAVELET_CHRO;Práh nasycené/pastelové
+TP_WAVELET_CHROFRAME;Odšumění Barevnosti
+TP_WAVELET_CHROMAFRAME;Barevnost
+TP_WAVELET_CHROMCO;Hrubá barevnost
+TP_WAVELET_CHROMFI;Jemná barevnost
TP_WAVELET_CHRO_TOOLTIP;Nastaví úroveň vlnky, která bude prahová pro syté a pastelové barvy.\n1-x: syté\nx-9: pastelové\n\nHodnota bude ignorována pokud přesáhne množství úrovní vlnky.
+TP_WAVELET_CHRWAV;Rozmazat barevnost
TP_WAVELET_CHR_TOOLTIP;Upraví barevnost jako funkci "Kontrast úrovní" a "Barevnost - kontrast síla propojení"
TP_WAVELET_CHSL;Posuvníky
TP_WAVELET_CHTYPE;Metoda barevnost
+TP_WAVELET_CLA;Čirost
+TP_WAVELET_CLARI;Ostrá maska a čirost
TP_WAVELET_COLORT;Neprůhlednost červená-zelená
TP_WAVELET_COMPCONT;Kontrast
TP_WAVELET_COMPGAMMA;Komprese gamy
TP_WAVELET_COMPGAMMA_TOOLTIP;Úprava gamy zůstatku obrázku vám umožní vyvážit data a histogram.
TP_WAVELET_COMPTM;Mapování tónů
TP_WAVELET_CONTEDIT;Křivka kontrastu 'Po'
+TP_WAVELET_CONTFRAME;Kontrast - komprese
TP_WAVELET_CONTR;Gamut
TP_WAVELET_CONTRA;Kontrast
+TP_WAVELET_CONTRASTEDIT;Jemnější - Hrubší úrovně
TP_WAVELET_CONTRAST_MINUS;Kontrast -
TP_WAVELET_CONTRAST_PLUS;Kontrast +
TP_WAVELET_CONTRA_TOOLTIP;Změní kontrast zůstatku obrazu.
TP_WAVELET_CTYPE;Ovládání barevnosti
+TP_WAVELET_CURVEEDITOR_BL_TOOLTIP;Zakázáno pokud je přiblížení přes 300%
TP_WAVELET_CURVEEDITOR_CC_TOOLTIP;Mění lokální kontrast jako funkci originálního lokálního kontrastu(úsečka).\nNízké hodnoty na úsečce představují malý lokální kontrast (skutečné hodnoty okolo 10..20).\n50% z úsečky představuje průměrný lokální kontrast (skutečné hodnoty okolo 100..300).\n66% z úsečky představuje představuje standardní odchylku lokálního kontrastu (skutečné hodnoty okolo 300..800).\n100% z úsečky představuje maximální lokální kontrast (skutečné hodnoty okolo 3000..8000).
TP_WAVELET_CURVEEDITOR_CH;Kontrast úrovní=f(Barevnost)
TP_WAVELET_CURVEEDITOR_CH_TOOLTIP;Mění kontrast každé úrovně jako funkci odstínu.\nDejte pozor, abyste nepřepsali změny udělané v podnástroji Gamut nástroje Odstín.\nZměny křivky se projeví pouze v případě, že posuvníky kontrastu úrovní vlnky nejsou nastaveny na nulu.
@@ -2155,10 +2265,13 @@ TP_WAVELET_DAUB6;D6 - standard plus
TP_WAVELET_DAUB10;D10 - střední
TP_WAVELET_DAUB14;D14 - Vysoká
TP_WAVELET_DAUB_TOOLTIP;Změní Daubechiesové koeficienty:\nD4 = Standard,\nD14 = Nejčastěji nejlepší výkon, ale o 10% delší zpracování .\n\nOvlivňuje detekci hran a obecnou kvalitu obrázku na prvních úrovních.Ovšem kvalita není striktně vázána na koeficienty a může se lišit v závislosti na obrázku a použití.
+TP_WAVELET_DIRFRAME;Směrový kontrast
TP_WAVELET_DONE;Svisle
TP_WAVELET_DTHR;Napříč
TP_WAVELET_DTWO;Vodorovně
TP_WAVELET_EDCU;Křivka
+TP_WAVELET_EDEFFECT;Útlum
+TP_WAVELET_EDEFFECT_TOOLTIP;Posuvník ovlivňuje rozsah hodnot kontrastu, které získají maximální efekt nástroje.\nMaximální hodnota (2.5) nástroj zakáže.
TP_WAVELET_EDGCONT;Místní kontrast
TP_WAVELET_EDGCONT_TOOLTIP;Posunutí bodů doleva snižuje kontrast a posunutí bodů doprava jej zvyšuje.\nRohy levý spodní, levý horní, pravý horní, pravý spodní postupně představují místní kontrast pro nízké hodnoty, průměr, průměr + stdev a maximum.
TP_WAVELET_EDGE;Doostření hran
@@ -2178,10 +2291,12 @@ TP_WAVELET_EDSL;Práh posuvníků
TP_WAVELET_EDTYPE;Metoda místního kontrastu
TP_WAVELET_EDVAL;Síla
TP_WAVELET_FINAL;Finální doladění
+TP_WAVELET_FINCFRAME;Finální místní kontrast
+TP_WAVELET_FINCOAR_TOOLTIP;Levá (pozitivní) část křivky působí na jemnější úrovně (navýšení).\nDva body na úsečce představují příslušné akční limity jemnějšía hrubší úrovně 5 a 6 (výchozí). Pravá (negativní) část křivky působí na hrubší úrovně (navýšení).\nVyvarujte se posouvání levé části křivky se zápornými hodnotami. Vyvarujte se posouvání pravé části křivky s kladnými hodnotami.
TP_WAVELET_FINEST;Nejjemnější
-TP_WAVELET_HIGHLIGHT;Zvýrazněný rozsah jasů
+TP_WAVELET_HIGHLIGHT;Jemnější úrovně rozsahu jasu
TP_WAVELET_HS1;Celý rozsah jasů
-TP_WAVELET_HS2;Stíny/Světla
+TP_WAVELET_HS2;Výběrový rozsah jasu
TP_WAVELET_HUESKIN;Odstín pleti
TP_WAVELET_HUESKIN_TOOLTIP;Spodní body nastaví začátek zóny přenosu a horní body její konec. Tam bude efekt největší.\n\nPokud potřebujete oblast výrazně změnit nebo se objevily artefakty, je nastavení vyvážení bílé nesprávné.
TP_WAVELET_HUESKY;Odstín oblohy
@@ -2192,9 +2307,9 @@ TP_WAVELET_LABEL;Úrovně vlnky
TP_WAVELET_LARGEST;Nejhrubší
TP_WAVELET_LEVCH;Barevnost
TP_WAVELET_LEVDIR_ALL;Všechny úrovně ve všech směrech
-TP_WAVELET_LEVDIR_INF;Méně nebo shodně s úrovní
+TP_WAVELET_LEVDIR_INF;Jemnější úrovně detailů s vybranou úrovní
TP_WAVELET_LEVDIR_ONE;Jedna úroveň
-TP_WAVELET_LEVDIR_SUP;Nad úrovní
+TP_WAVELET_LEVDIR_SUP;Hrubší úrovně detailů s vybranou úrovní
TP_WAVELET_LEVELS;Úrovně vlnky
TP_WAVELET_LEVELS_TOOLTIP;Vyberte počet úrovní detailu mezi které bude obrázek rozložen. Více úrovní potřebuje více paměti a zpracování trvá déle.
TP_WAVELET_LEVF;Kontrast
@@ -2205,57 +2320,89 @@ TP_WAVELET_LEVTWO;Úroveň 3
TP_WAVELET_LEVZERO;Úroveň 1
TP_WAVELET_LINKEDG;Spojit se sílou doostření hran
TP_WAVELET_LIPST;Vylepšený algoritmus
-TP_WAVELET_LOWLIGHT;Rozsah jasu a stínů
+TP_WAVELET_LOWLIGHT;Hrubší úrovně rozsahu jasu
+TP_WAVELET_LOWTHR_TOOLTIP;Zabraňuje zesílení šumu v jemných texturách
TP_WAVELET_MEDGREINF;První úroveň
TP_WAVELET_MEDI;Omezení artefaktů na modré obloze
TP_WAVELET_MEDILEV;Detekce hran
TP_WAVELET_MEDILEV_TOOLTIP;Pro povolení Detekce hran Vám doporučujeme:\n- zakázat úrovně s nízkým kontrastem pro vyhnutí se vzniku artefaktů,\n- použít vysoké hodnoty gradientu citlivosti.\n\nSílu můžete ovlivnit pomocí 'vylepšení' z Odšumění a vylepšení.
+TP_WAVELET_MERGEC;Sloučení barevnosti
+TP_WAVELET_MERGEL;Sloučení jasu
TP_WAVELET_NEUTRAL;Neutrální
TP_WAVELET_NOIS;Odšumění
TP_WAVELET_NOISE;Odšumění a vylepšení
+TP_WAVELET_NOISE_TOOLTIP;Pokud je čtvrtá úroveň jasu odšumění lepší než 20, použije se Agresivní režim.\nPokud je hrubší barevnost lepší než 20, použije se Agresívní režim.
TP_WAVELET_NPHIGH;Vysoká
TP_WAVELET_NPLOW;Nízká
TP_WAVELET_NPNONE;Nic
TP_WAVELET_NPTYPE;Sousední pixely
TP_WAVELET_NPTYPE_TOOLTIP;Tento algoritmus zkoumá blízkost pixelu a jeho osmi sousedů. V případě menšího rozdílu je hrana zesílena.
+TP_WAVELET_OFFSET_TOOLTIP;Posun změní vyvážení mezi světly a stíny.\nVyšší hodnoty zdůrazní změnu kontrastu světel, kdežto nižší hodnoty zdůrazní změnu kontrastu stínů.\nZároveň s nízkou hodnotou útlumu máte možnost si vybrat, které kontrasty budou zvýrazněny.
+TP_WAVELET_OLDSH;Algoritmus používá záporné hodnoty
TP_WAVELET_OPACITY;Neprůhlednost modrá-žlutá
TP_WAVELET_OPACITYW;Vyrovnání kontrastu d/v-h křivka
-TP_WAVELET_OPACITYWL;Finální místní kontrast
+TP_WAVELET_OPACITYWL;Místní kontrast
TP_WAVELET_OPACITYWL_TOOLTIP;Změní finální lokální kontrast na konci zpracování vlnky.\n\nLevá strana představuje nejmenší lokální kontrast a pravá strana zase největší lokální kontrast.
TP_WAVELET_PASTEL;Barevnost pastelů
TP_WAVELET_PROC;Zpracování
+TP_WAVELET_PROTAB;Ochrana
+TP_WAVELET_RADIUS;Poloměr Stíny - Světla
+TP_WAVELET_RANGEAB;Rozsah a a b %
TP_WAVELET_RE1;Zesílená
TP_WAVELET_RE2;Nezměněno
TP_WAVELET_RE3;Omezená
-TP_WAVELET_RESCHRO;Barevnost
+TP_WAVELET_RESBLUR;Jas rozmazání
+TP_WAVELET_RESBLURC;Barevnost rozmazání
+TP_WAVELET_RESBLUR_TOOLTIP;Zakázáno pokud je přiblížení přes 500%
+TP_WAVELET_RESCHRO;Intenzita
TP_WAVELET_RESCON;Stíny
TP_WAVELET_RESCONH;Světla
TP_WAVELET_RESID;Zůstatek obrazu
TP_WAVELET_SAT;Nasycená barevnost
TP_WAVELET_SETTINGS;Nastavení vlnky
+TP_WAVELET_SHA;Ostrá maska
+TP_WAVELET_SHFRAME;Stíny/Světla
+TP_WAVELET_SHOWMASK;Ukázat vlnkovou 'masku'
+TP_WAVELET_SIGMA;Útlum
+TP_WAVELET_SIGMAFIN;Útlum
+TP_WAVELET_SIGMA_TOOLTIP;Efekt posuvníků kontrastu je silnější u detailů se středními hodnotami kontrastu, a slabší u detailů s vysokými nebo nízkými hodnotami kontrastu.\n Tímto posuvníkem můžete kontrolovat jak rychle je potlačen efekt u extrémních hodnot kontrastu.\n Čím výše je posuvník nastaven, tím širší je rozsah hodnot kontrastů u kterých nastane silnější změna s větší šancí na vytvoření artefaktů.\n Čím je níže, tím přesněji bude účinek aplikován na úzkýrozsah hodnot kontrastu.
TP_WAVELET_SKIN;Ochrana: zaměření na pleťové tóny
TP_WAVELET_SKIN_TOOLTIP;Hodnota -100: zaměřeno na pleťové tóny.\nHodnota 0: se všemi tóny je zacházeno stejně.\nHodnota +100: pleťové tóny jsou chráněny zatímco všechny ostatní tóny jsou ovlivněny.
TP_WAVELET_SKY;Ochrana a zaměření na tóny oblohy
TP_WAVELET_SKY_TOOLTIP;Hodnota -100: zaměřeno na pleťové tóny.\nHodnota 0: se všemi tóny je zacházeno stejně.\nHodnota +100: pleťové tóny jsou chráněny zatímco všechny ostatní tóny jsou ovlivněny.
+TP_WAVELET_SOFTRAD;Jemný poloměr
TP_WAVELET_STREN;Síla
TP_WAVELET_STRENGTH;Síla
TP_WAVELET_SUPE;Extra
TP_WAVELET_THR;Práh stínů
-TP_WAVELET_THRESHOLD;Úrovně světel
-TP_WAVELET_THRESHOLD2;Úrovně stínů
-TP_WAVELET_THRESHOLD2_TOOLTIP;Pouze úrovně 9 a 9 mínus hodnota budou ovlivněny rozsahem stínů a jasů.Ostatní úrovně budou plně zpracovány. Maximální možná úroveň je omezena na nejvyšší hodnotu úrovně (9 - nejvyšší úroveň).
-TP_WAVELET_THRESHOLD_TOOLTIP;Pouze úrovně pod vybranou hodnotou budou ovlivněny rozsahem zvýraznění jasů.Ostatní úrovně budou plně zpracovány. Zde vybraná hodnota omezí nejvyšší možnou hodnotu úrovně stínů.
+TP_WAVELET_THRESHOLD;Jemnější úrovně
+TP_WAVELET_THRESHOLD2;Hrubší úrovně
+TP_WAVELET_THRESHOLD2_TOOLTIP;Pouze úrovně mezi 9 a 9 mínus hodnota budou ovlivněny rozsahem jasu stínů. Ostatní úrovně budou upraveny celé. Nejvyšší možná úroveň je omezena hodnotou zvýrazněné úrovně (9 mínus hodnota zvýrazněné úrovně). Pouze úrovně mezi vybranou hodnotou a úrovní 9/Extra budou ovlivněny Hrubým rozsahem úrovní.\nVšechny ostatní úrovně budou ovlivněny v celém rozsahu jasu, pokud nebudou omezeny nastavením Jemnými úrovněmi.\nNejnižší možná úroveň, kterou bude algoritmus zvažovat, je omezená hodnotou Jemných úrovní.
+TP_WAVELET_THRESHOLD_TOOLTIP;Pouze úrovně mimo vybranou hodnotu budou ovlivněny rozsahem jasu stínů. Ostatní úrovně budou upraveny celé. Zde vybraná hodnota omezuje nejvyšší možnou hodnotu úrovní stínů. Všechny úrovně od úrovně jedna až po vybranou úroveň ovlivněny Jemným rozsahem úrovní.\nVšechny ostatní úrovně budou ovlivněny v celém rozsahu jasu, pokud nebudou omezeny nastavením Hrubými úrovněmi.\nZde vybraná hodnota, se stane nejnižší možnou úrovní Hrubých úrovní.
+TP_WAVELET_THRESWAV;Práh vyvážení
TP_WAVELET_THRH;Práh světel
-TP_WAVELET_TILESBIG;Velké dlaždice
+TP_WAVELET_TILESBIG;Dlaždice
TP_WAVELET_TILESFULL;Celý obrázek
TP_WAVELET_TILESIZE;Metoda dlaždicování
TP_WAVELET_TILESLIT;Malé dlaždice
TP_WAVELET_TILES_TOOLTIP;Zpracování celého obrázku vede k lepší kvalitě a je doporučováno. Naproti tomu dlaždice jsou náhradní řešení pro uživatele s nedostatkem paměti. Paměťové nároky najdete na RawPedii.
+TP_WAVELET_TMEDGS;Zachování hran
+TP_WAVELET_TMSCALE;Měřítko
TP_WAVELET_TMSTRENGTH;Síla komprese
-TP_WAVELET_TMSTRENGTH_TOOLTIP;Ovládá sílu mapování tónů nebo kontrast komprese zůstatku obrázku. Pokud je hodnota rozdílná od nuly, budou posuvníky Síla a Gama v nástroji Mapování tónů v kartě Expozice neaktivní.
+TP_WAVELET_TMSTRENGTH_TOOLTIP;Kontroluje sílu tónového mapování nebo komprese kontrastu zůstatku obrazu.
TP_WAVELET_TMTYPE;Metoda komprese
TP_WAVELET_TON;Tónování
+TP_WAVELET_TONFRAME;Vyloučené barvy
+TP_WAVELET_USH;Nic
+TP_WAVELET_USHARP;Metoda čirosti
+TP_WAVELET_USHARP_TOOLTIP;Původní : zdrojovým souborem je soubor před Vlnkou.\nVlnka : zdrojovým souborem je soubor s aplikovanou Vlnkou.
+TP_WAVELET_USH_TOOLTIP;Pokud vyberete Ostrou masku, bude nastavení vlnky automaticky změněno na:\nPozadí=černá, zpracování=pod, úroveň=3 — ta může být změněna v rozmezí 1 až 4.\n\nPokud vyberete Čirost, bude nastavení vlnky automaticky změněno na:\nPozadí=zůstatek, zpracování=nad, úroveň=7 — ta může být změněna v rozmezí 5 až 10 úrovní vlnky.
+TP_WAVELET_WAVLOWTHR;Práh nízkého kontrastu
+TP_WAVELET_WAVOFFSET;Posun
TP_WBALANCE_AUTO;Automaticky
+TP_WBALANCE_AUTOITCGREEN;Teplotní korelace
+TP_WBALANCE_AUTOOLD;RGB šedé
+TP_WBALANCE_AUTO_HEADER;Automaticky
TP_WBALANCE_CAMERA;Fotoaparát
TP_WBALANCE_CLOUDY;Zataženo
TP_WBALANCE_CUSTOM;Vlastní
@@ -2297,6 +2444,8 @@ TP_WBALANCE_SOLUX41;Solux 4100K
TP_WBALANCE_SOLUX47;Solux 4700K (vendor)
TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery)
TP_WBALANCE_SPOTWB;Použijte pipetu pro nabrání vyvážení bílé z neutrální oblasti v náhledu.
+TP_WBALANCE_STUDLABEL;Faktor korelace: %1
+TP_WBALANCE_STUDLABEL_TOOLTIP;Zobrazí vypočítanou korelaci metody Student\nNižší hodnoty jsou lepší, přičemž pro <0.005 jsou výborné\n<0.01 jsou dobré a >0.5 jsou špatné.\nNízké hodnoty neznamenají, že je vyvážení bílé dobré: pro nestandardní světelné podmínky jsou výsledky nepředvídatelné.\nHodnota 1000 znamená, že byl použit předchozí výpočet a výsledky jsou pravděpodobně dobré.
TP_WBALANCE_TEMPBIAS;AVB - Zdůraznění teploty
TP_WBALANCE_TEMPBIAS_TOOLTIP;Dovolí ovlivnit výpočet "automatického vyvážení bílé"\nzdůrazněním teplejší nebo chladnější teploty. Toto zdůraznění\nje vyjádřeno v procentech vypočtené teploty a výsledek\nlze vyjádřit vzorcem "vypočtenáTeplota + vypočtenáTeplota * zdůraznění".
TP_WBALANCE_TEMPERATURE;Teplota
@@ -2311,42 +2460,3 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Přizpůsobit ořez obrazovce\nZkratka: f
ZOOMPANEL_ZOOMFITSCREEN;Přizpůsobit celý obrázek obrazovce\nZkratka: Alt-f
ZOOMPANEL_ZOOMIN;Přiblížit\nZkratka: +
ZOOMPANEL_ZOOMOUT;Oddálit\nZkratka: -
-
-!!!!!!!!!!!!!!!!!!!!!!!!!
-! Untranslated keys follow; remove the ! prefix after an entry is translated.
-!!!!!!!!!!!!!!!!!!!!!!!!!
-
-!FILEBROWSER_DELETEDIALOG_ALL;Are you sure you want to permanently delete all %1 files in trash?
-!FILEBROWSER_DELETEDIALOG_SELECTED;Are you sure you want to permanently delete the selected %1 files?
-!FILEBROWSER_DELETEDIALOG_SELECTEDINCLPROC;Are you sure you want to permanently delete the selected %1 files, including a queue-processed version?
-!FILEBROWSER_EMPTYTRASHHINT;Permanently delete all files in trash.
-!FILEBROWSER_POPUPREMOVE;Delete permanently
-!FILEBROWSER_POPUPREMOVEINCLPROC;Delete permanently, including queue-processed version
-!FILEBROWSER_SHOWNOTTRASHHINT;Show only images not in trash.
-!GENERAL_HELP;Help
-!HISTORY_MSG_494;Capture Sharpening
-!HISTORY_MSG_DEHAZE_LUMINANCE;Dehaze - Luminance only
-!HISTORY_MSG_FILMNEGATIVE_ENABLED;Film Negative
-!HISTORY_MSG_FILMNEGATIVE_VALUES;Film negative values
-!HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CS - Auto threshold
-!HISTORY_MSG_PDSHARPEN_AUTO_RADIUS;CS - Auto radius
-!HISTORY_MSG_PDSHARPEN_CHECKITER;CS - Auto limit iterations
-!HISTORY_MSG_PDSHARPEN_CONTRAST;CS - Contrast threshold
-!HISTORY_MSG_PDSHARPEN_ITERATIONS;CS - Iterations
-!HISTORY_MSG_PDSHARPEN_RADIUS;CS - Radius
-!HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;CS - Corner radius boost
-!HISTORY_MSG_TRANS_Method;Geometry - Method
-!PARTIALPASTE_FILMNEGATIVE;Film Negative
-!PREFERENCES_APPEARANCE_PSEUDOHIDPI;Pseudo-HiDPI mode
-!TP_DEHAZE_LUMINANCE;Luminance only
-!TP_FILMNEGATIVE_BLUE;Blue ratio
-!TP_FILMNEGATIVE_GREEN;Reference exponent (contrast)
-!TP_FILMNEGATIVE_GUESS_TOOLTIP;Automatically set the red and blue ratios by picking two patches which had a neutral hue (no color) in the original scene. The patches should differ in brightness. Set the white balance afterwards.
-!TP_FILMNEGATIVE_LABEL;Film Negative
-!TP_FILMNEGATIVE_PICK;Pick neutral spots
-!TP_FILMNEGATIVE_RED;Red ratio
-!TP_LENSGEOM_LIN;Linear
-!TP_LENSGEOM_LOG;Logarithmic
-!TP_PDSHARPENING_LABEL;Capture Sharpening
-!TP_SHARPENING_ITERCHECK;Auto limit iterations
-!TP_SHARPENING_RADIUS_BOOST;Corner radius boost
diff --git a/rtdata/languages/English (US) b/rtdata/languages/English (US)
index d8c497f16..1bd319e6f 100644
--- a/rtdata/languages/English (US)
+++ b/rtdata/languages/English (US)
@@ -937,7 +937,7 @@
!MAIN_TOOLTIP_BACKCOLOR0;Background color of the preview: theme-based\nShortcut: 9
!MAIN_TOOLTIP_BACKCOLOR1;Background color of the preview: black\nShortcut: 9
!MAIN_TOOLTIP_BACKCOLOR2;Background color of the preview: white\nShortcut: 9
-!MAIN_TOOLTIP_BACKCOLOR3;Background color of the preview: middle grey\nShortcut: 9
+!MAIN_TOOLTIP_BACKCOLOR3;Background color of the preview: middle gray\nShortcut: 9
!MAIN_TOOLTIP_BEFOREAFTERLOCK;Lock / Unlock the Before view\n\nLock: keep the Before view unchanged.\nUseful to evaluate the cumulative effect of multiple tools.\nAdditionally, comparisons can be made to any state in the History.\n\nUnlock: the Before view will follow the After view one step behind, showing the image before the effect of the currently used tool.
!MAIN_TOOLTIP_HIDEHP;Show/Hide the left panel (including the history).\nShortcut: l
!MAIN_TOOLTIP_INDCLIPPEDH;Clipped highlight indication.\nShortcut: >
@@ -2097,7 +2097,7 @@
!TP_WAVELET_APPLYTO;Apply To
!TP_WAVELET_AVOID;Avoid color shift
!TP_WAVELET_B0;Black
-!TP_WAVELET_B1;Grey
+!TP_WAVELET_B1;Gray
!TP_WAVELET_B2;Residual
!TP_WAVELET_BACKGROUND;Background
!TP_WAVELET_BACUR;Curve
diff --git a/rtdata/languages/Nederlands b/rtdata/languages/Nederlands
index c43f6314e..ba5880bfb 100644
--- a/rtdata/languages/Nederlands
+++ b/rtdata/languages/Nederlands
@@ -14,6 +14,7 @@
#14 2015-11-23 update by wim ter meer
#15 2016-07-21 update by wim ter meer
#16 2017-04-21 update by wim ter meer
+#17 2020-06-05 update by dheijl
ABOUT_TAB_BUILD;Versie
ABOUT_TAB_CREDITS;Credits
@@ -1962,367 +1963,367 @@ ZOOMPANEL_ZOOMOUT;Zoom uit\nSneltoets: -
! Untranslated keys follow; remove the ! prefix after an entry is translated.
!!!!!!!!!!!!!!!!!!!!!!!!!
-!ADJUSTER_RESET_TO_DEFAULT;Click - reset to default value.\nCtrl+click - reset to initial value.
-!CURVEEDITOR_CATMULLROM;Flexible
-!DONT_SHOW_AGAIN;Don't show this message again.
-!DYNPROFILEEDITOR_IMGTYPE_ANY;Any
-!DYNPROFILEEDITOR_IMGTYPE_HDR;HDR
-!DYNPROFILEEDITOR_IMGTYPE_PS;Pixel Shift
-!DYNPROFILEEDITOR_IMGTYPE_STD;Standard
-!EXIFFILTER_IMAGETYPE;Image type
-!EXIFPANEL_SHOWALL;Show all
-!FILEBROWSER_BROWSEPATHBUTTONHINT;Click to open specified path, reload folder and apply "find" keywords.
-!FILEBROWSER_CACHECLEARFROMFULL;Clear all including cached profiles
-!FILEBROWSER_CACHECLEARFROMPARTIAL;Clear all except cached profiles
-!FILEBROWSER_DELETEDIALOG_ALL;Are you sure you want to permanently delete all %1 files in trash?
-!FILEBROWSER_DELETEDIALOG_SELECTED;Are you sure you want to permanently delete the selected %1 files?
-!FILEBROWSER_DELETEDIALOG_SELECTEDINCLPROC;Are you sure you want to permanently delete the selected %1 files, including a queue-processed version?
-!FILEBROWSER_EMPTYTRASHHINT;Permanently delete all files in trash.
-!FILEBROWSER_POPUPREMOVE;Delete permanently
-!FILEBROWSER_POPUPREMOVEINCLPROC;Delete permanently, including queue-processed version
-!FILEBROWSER_SHOWNOTTRASHHINT;Show only images not in trash.
-!GENERAL_CURRENT;Current
-!GENERAL_HELP;Help
-!GENERAL_RESET;Reset
-!GENERAL_SAVE_AS;Save as...
-!GENERAL_SLIDER;Slider
-!GIMP_PLUGIN_INFO;Welcome to the RawTherapee GIMP plugin!\nOnce you are done editing, simply close the main RawTherapee window and the image will be automatically imported in GIMP.
-!HISTOGRAM_TOOLTIP_MODE;Toggle between linear, log-linear and log-log scaling of the histogram.
-!HISTORY_MSG_173;NR - Detail recovery
-!HISTORY_MSG_203;NR - Color space
-!HISTORY_MSG_235;B&W - CM - Auto
-!HISTORY_MSG_237;B&W - CM
-!HISTORY_MSG_256;NR - Median - Type
-!HISTORY_MSG_273;CT - Color Balance SMH
-!HISTORY_MSG_297;NR - Mode
-!HISTORY_MSG_392;W - Residual - Color Balance
-!HISTORY_MSG_441;Retinex - Gain transmission
-!HISTORY_MSG_475;PS - Equalize channel
-!HISTORY_MSG_476;CAM02 - Temp out
-!HISTORY_MSG_477;CAM02 - Green out
-!HISTORY_MSG_478;CAM02 - Yb out
-!HISTORY_MSG_479;CAM02 - CAT02 adaptation out
-!HISTORY_MSG_480;CAM02 - Automatic CAT02 out
-!HISTORY_MSG_481;CAM02 - Temp scene
-!HISTORY_MSG_482;CAM02 - Green scene
-!HISTORY_MSG_483;CAM02 - Yb scene
-!HISTORY_MSG_484;CAM02 - Auto Yb scene
-!HISTORY_MSG_485;Lens Correction
-!HISTORY_MSG_486;Lens Correction - Camera
-!HISTORY_MSG_487;Lens Correction - Lens
-!HISTORY_MSG_488;Dynamic Range Compression
-!HISTORY_MSG_489;DRC - Detail
-!HISTORY_MSG_490;DRC - Amount
-!HISTORY_MSG_491;White Balance
-!HISTORY_MSG_492;RGB Curves
-!HISTORY_MSG_493;L*a*b* Adjustments
-!HISTORY_MSG_494;Capture Sharpening
-!HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors
-!HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction
-!HISTORY_MSG_COLORTONING_LABREGION_AB;CT - Color correction
-!HISTORY_MSG_COLORTONING_LABREGION_CHANNEL;CT - Channel
-!HISTORY_MSG_COLORTONING_LABREGION_CHROMATICITYMASK;CT - region C mask
-!HISTORY_MSG_COLORTONING_LABREGION_HUEMASK;CT - H mask
-!HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESS;CT - Lightness
-!HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESSMASK;CT - L mask
-!HISTORY_MSG_COLORTONING_LABREGION_LIST;CT - List
-!HISTORY_MSG_COLORTONING_LABREGION_MASKBLUR;CT - region mask blur
-!HISTORY_MSG_COLORTONING_LABREGION_OFFSET;CT - region offset
-!HISTORY_MSG_COLORTONING_LABREGION_POWER;CT - region power
-!HISTORY_MSG_COLORTONING_LABREGION_SATURATION;CT - Saturation
-!HISTORY_MSG_COLORTONING_LABREGION_SHOWMASK;CT - region show mask
-!HISTORY_MSG_COLORTONING_LABREGION_SLOPE;CT - region slope
-!HISTORY_MSG_DEHAZE_DEPTH;Dehaze - Depth
-!HISTORY_MSG_DEHAZE_ENABLED;Haze Removal
-!HISTORY_MSG_DEHAZE_LUMINANCE;Dehaze - Luminance only
-!HISTORY_MSG_DEHAZE_SHOW_DEPTH_MAP;Dehaze - Show depth map
-!HISTORY_MSG_DEHAZE_STRENGTH;Dehaze - Strength
-!HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dual demosaic - Auto threshold
-!HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dual demosaic - Contrast threshold
-!HISTORY_MSG_FILMNEGATIVE_ENABLED;Film Negative
-!HISTORY_MSG_FILMNEGATIVE_VALUES;Film negative values
-!HISTORY_MSG_HISTMATCHING;Auto-matched tone curve
-!HISTORY_MSG_ICM_OUTPUT_PRIMARIES;Output - Primaries
-!HISTORY_MSG_ICM_OUTPUT_TEMP;Output - ICC-v4 illuminant D
-!HISTORY_MSG_ICM_OUTPUT_TYPE;Output - Type
-!HISTORY_MSG_ICM_WORKING_GAMMA;Working - Gamma
-!HISTORY_MSG_ICM_WORKING_SLOPE;Working - Slope
-!HISTORY_MSG_ICM_WORKING_TRC_METHOD;Working - TRC method
-!HISTORY_MSG_LOCALCONTRAST_AMOUNT;Local Contrast - Amount
-!HISTORY_MSG_LOCALCONTRAST_DARKNESS;Local Contrast - Darkness
-!HISTORY_MSG_LOCALCONTRAST_ENABLED;Local Contrast
-!HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Local Contrast - Lightness
-!HISTORY_MSG_LOCALCONTRAST_RADIUS;Local Contrast - Radius
-!HISTORY_MSG_METADATA_MODE;Metadata copy mode
-!HISTORY_MSG_MICROCONTRAST_CONTRAST;Microcontrast - Contrast threshold
-!HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CS - Auto threshold
-!HISTORY_MSG_PDSHARPEN_AUTO_RADIUS;CS - Auto radius
-!HISTORY_MSG_PDSHARPEN_CHECKITER;CS - Auto limit iterations
-!HISTORY_MSG_PDSHARPEN_CONTRAST;CS - Contrast threshold
-!HISTORY_MSG_PDSHARPEN_ITERATIONS;CS - Iterations
-!HISTORY_MSG_PDSHARPEN_RADIUS;CS - Radius
-!HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;CS - Corner radius boost
-!HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Demosaic method for motion
-!HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;Line noise filter direction
-!HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAF lines filter
-!HISTORY_MSG_PRSHARPEN_CONTRAST;PRS - Contrast threshold
-!HISTORY_MSG_RAWCACORR_AUTOIT;Raw CA Correction - Iterations
-!HISTORY_MSG_RAWCACORR_COLORSHIFT;Raw CA Correction - Avoid color shift
-!HISTORY_MSG_RAW_BORDER;Raw border
-!HISTORY_MSG_RESIZE_ALLOWUPSCALING;Resize - Allow upscaling
-!HISTORY_MSG_SHARPENING_BLUR;Sharpening - Blur radius
-!HISTORY_MSG_SHARPENING_CONTRAST;Sharpening - Contrast threshold
-!HISTORY_MSG_SH_COLORSPACE;S/H - Colorspace
-!HISTORY_MSG_SOFTLIGHT_ENABLED;Soft light
-!HISTORY_MSG_SOFTLIGHT_STRENGTH;Soft light - Strength
-!HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anchor
-!HISTORY_MSG_TRANS_Method;Geometry - Method
-!ICCPROFCREATOR_COPYRIGHT;Copyright:
-!ICCPROFCREATOR_COPYRIGHT_RESET_TOOLTIP;Reset to the default copyright, granted to "RawTherapee, CC0"
-!ICCPROFCREATOR_CUSTOM;Custom
-!ICCPROFCREATOR_DESCRIPTION;Description:
-!ICCPROFCREATOR_DESCRIPTION_ADDPARAM;Append gamma and slope values to the description
-!ICCPROFCREATOR_DESCRIPTION_TOOLTIP;Leave empty to set the default description.
-!ICCPROFCREATOR_GAMMA;Gamma
-!ICCPROFCREATOR_ICCVERSION;ICC version:
-!ICCPROFCREATOR_ILL;Illuminant:
-!ICCPROFCREATOR_ILL_41;D41
-!ICCPROFCREATOR_ILL_50;D50
-!ICCPROFCREATOR_ILL_55;D55
-!ICCPROFCREATOR_ILL_60;D60
-!ICCPROFCREATOR_ILL_65;D65
-!ICCPROFCREATOR_ILL_80;D80
-!ICCPROFCREATOR_ILL_DEF;Default
-!ICCPROFCREATOR_ILL_INC;StdA 2856K
-!ICCPROFCREATOR_ILL_TOOLTIP;You can only set the illuminant for ICC v4 profiles.
-!ICCPROFCREATOR_PRIMARIES;Primaries:
-!ICCPROFCREATOR_PRIM_ACESP0;ACES AP0
-!ICCPROFCREATOR_PRIM_ACESP1;ACES AP1
-!ICCPROFCREATOR_PRIM_ADOBE;Adobe RGB (1998)
-!ICCPROFCREATOR_PRIM_BEST;BestRGB
-!ICCPROFCREATOR_PRIM_BETA;BetaRGB
-!ICCPROFCREATOR_PRIM_BLUX;Blue X
-!ICCPROFCREATOR_PRIM_BLUY;Blue Y
-!ICCPROFCREATOR_PRIM_BRUCE;BruceRGB
-!ICCPROFCREATOR_PRIM_GREX;Green X
-!ICCPROFCREATOR_PRIM_GREY;Green Y
-!ICCPROFCREATOR_PRIM_PROPH;Prophoto
-!ICCPROFCREATOR_PRIM_REC2020;Rec2020
-!ICCPROFCREATOR_PRIM_REDX;Red X
-!ICCPROFCREATOR_PRIM_REDY;Red Y
-!ICCPROFCREATOR_PRIM_SRGB;sRGB
-!ICCPROFCREATOR_PRIM_TOOLTIP;You can only set custom primaries for ICC v4 profiles.
-!ICCPROFCREATOR_PRIM_WIDEG;Widegamut
-!ICCPROFCREATOR_PROF_V2;ICC v2
-!ICCPROFCREATOR_PROF_V4;ICC v4
-!ICCPROFCREATOR_SAVEDIALOG_TITLE;Save ICC profile as...
-!ICCPROFCREATOR_SLOPE;Slope
-!ICCPROFCREATOR_TRC_PRESET;Tone response curve:
-!MAIN_BUTTON_ICCPROFCREATOR;ICC Profile Creator
-!MAIN_FRAME_PLACES_DEL;Remove
-!MAIN_MSG_TOOMANYOPENEDITORS;Too many open editors.\nPlease close an editor to continue.
-!MAIN_TAB_ADVANCED;Advanced
-!MAIN_TAB_ADVANCED_TOOLTIP;Shortcut: Alt-a
-!MAIN_TAB_FAVORITES;Favorites
-!MAIN_TAB_FAVORITES_TOOLTIP;Shortcut: Alt-u
-!MAIN_TOOLTIP_BACKCOLOR3;Background color of the preview: middle grey\nShortcut: 9
-!MAIN_TOOLTIP_PREVIEWSHARPMASK;Preview the sharpening contrast mask.\nShortcut: p\n\nOnly works when sharpening is enabled and zoom >= 100%.
-!OPTIONS_BUNDLED_MISSING;The bundled profile "%1" could not be found!\n\nYour installation could be damaged.\n\nDefault internal values will be used instead.
-!OPTIONS_DEFIMG_MISSING;The default profile for non-raw photos could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\n"%1" will be used instead.
-!OPTIONS_DEFRAW_MISSING;The default profile for raw photos could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\n"%1" will be used instead.
-!PARTIALPASTE_ADVANCEDGROUP;Advanced Settings
-!PARTIALPASTE_DEHAZE;Haze removal
-!PARTIALPASTE_FILMNEGATIVE;Film Negative
-!PARTIALPASTE_LOCALCONTRAST;Local contrast
-!PARTIALPASTE_METADATA;Metadata mode
-!PARTIALPASTE_PREPROCESS_PDAFLINESFILTER;PDAF lines filter
-!PARTIALPASTE_RAWCACORR_AVOIDCOLORSHIFT;CA avoid color shift
-!PARTIALPASTE_RAW_BORDER;Raw border
-!PARTIALPASTE_SOFTLIGHT;Soft light
-!PARTIALPASTE_TM_FATTAL;Dynamic range compression
-!PREFERENCES_APPEARANCE;Appearance
-!PREFERENCES_APPEARANCE_COLORPICKERFONT;Color picker font
-!PREFERENCES_APPEARANCE_CROPMASKCOLOR;Crop mask color
-!PREFERENCES_APPEARANCE_MAINFONT;Main font
-!PREFERENCES_APPEARANCE_PSEUDOHIDPI;Pseudo-HiDPI mode
-!PREFERENCES_APPEARANCE_THEME;Theme
-!PREFERENCES_AUTOSAVE_TP_OPEN;Save tool collapsed/expanded state on exit
-!PREFERENCES_CACHECLEAR;Clear
-!PREFERENCES_CACHECLEAR_ALL;Clear all cached files:
-!PREFERENCES_CACHECLEAR_ALLBUTPROFILES;Clear all cached files except for cached processing profiles:
-!PREFERENCES_CACHECLEAR_ONLYPROFILES;Clear only cached processing profiles:
-!PREFERENCES_CACHECLEAR_SAFETY;Only files in the cache are cleared. Processing profiles stored alongside the source images are not touched.
-!PREFERENCES_CHUNKSIZES;Tiles per thread
-!PREFERENCES_CHUNKSIZE_RAW_AMAZE;AMaZE demosaic
-!PREFERENCES_CHUNKSIZE_RAW_CA;Raw CA correction
-!PREFERENCES_CHUNKSIZE_RAW_RCD;RCD demosaic
-!PREFERENCES_CHUNKSIZE_RAW_XT;Xtrans demosaic
-!PREFERENCES_CHUNKSIZE_RGB;RGB processing
-!PREFERENCES_CROP;Crop Editing
-!PREFERENCES_CROP_AUTO_FIT;Automatically zoom to fit the crop
-!PREFERENCES_CROP_GUIDES;Guides shown when not editing the crop
-!PREFERENCES_CROP_GUIDES_FRAME;Frame
-!PREFERENCES_CROP_GUIDES_FULL;Original
-!PREFERENCES_CROP_GUIDES_NONE;None
-!PREFERENCES_DIRECTORIES;Directories
-!PREFERENCES_EDITORCMDLINE;Custom command line
-!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Compact toolbars in File Browser
-!PREFERENCES_LANG;Language
-!PREFERENCES_PERFORMANCE_MEASURE;Measure
-!PREFERENCES_PERFORMANCE_MEASURE_HINT;Logs processing times in console
-!PREFERENCES_PERFORMANCE_THREADS;Threads
-!PREFERENCES_PERFORMANCE_THREADS_LABEL;Maximum number of threads for Noise Reduction and Wavelet Levels (0 = Automatic)
-!PREFERENCES_PROFILESAVEBOTH;Save processing profile both to the cache and next to the input file
-!PREFERENCES_PROFILESAVELOCATION;Processing profile saving location
-!PREFERENCES_SAVE_TP_OPEN_NOW;Save tool collapsed/expanded state now
-!PREFERENCES_TAB_PERFORMANCE;Performance
-!PREFERENCES_THUMBNAIL_INSPECTOR_JPEG;Embedded JPEG preview
-!PREFERENCES_THUMBNAIL_INSPECTOR_MODE;Image to show
-!PREFERENCES_THUMBNAIL_INSPECTOR_RAW;Neutral raw rendering
-!PREFERENCES_THUMBNAIL_INSPECTOR_RAW_IF_NO_JPEG_FULLSIZE;Embedded JPEG if fullsize, neutral raw otherwise
-!PROGRESSBAR_DECODING;Decoding...
-!PROGRESSBAR_GREENEQUIL;Green equilibration...
-!PROGRESSBAR_HLREC;Highlight reconstruction...
-!PROGRESSBAR_HOTDEADPIXELFILTER;Hot/dead pixel filter...
-!PROGRESSBAR_LINEDENOISE;Line noise filter...
-!PROGRESSBAR_RAWCACORR;Raw CA correction...
-!QINFO_FRAMECOUNT;%2 frames
-!QINFO_HDR;HDR / %2 frame(s)
-!QINFO_PIXELSHIFT;Pixel Shift / %2 frame(s)
-!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
-!SAMPLEFORMAT_1;8-bit unsigned
-!SAMPLEFORMAT_2;16-bit unsigned
-!SAMPLEFORMAT_4;24-bit LogLuv
-!SAMPLEFORMAT_8;32-bit LogLuv
-!SAMPLEFORMAT_16;16-bit floating-point
-!SAMPLEFORMAT_32;24-bit floating-point
-!SAMPLEFORMAT_64;32-bit floating-point
-!SAVEDLG_FILEFORMAT_FLOAT; floating-point
-!SOFTPROOF_GAMUTCHECK_TOOLTIP;Highlight pixels with out-of-gamut colors with respect to:\n- the printer profile, if one is set and soft-proofing is enabled,\n- the output profile, if a printer profile is not set and soft-proofing is enabled,\n- the monitor profile, if soft-proofing is disabled.
-!SOFTPROOF_TOOLTIP;Soft-proofing simulates the appearance of the image:\n- when printed, if a printer profile is set in Preferences > Color Management,\n- when viewed on a display that uses the current output profile, if a printer profile is not set.
-!TP_BWMIX_MIXC;Channel Mixer
-!TP_BWMIX_NEUTRAL;Reset
-!TP_COLORAPP_ABSOLUTELUMINANCE;Absolute luminance
-!TP_COLORAPP_CAT02ADAPTATION_TOOLTIP;When setting manually, values above 65 are recommended.
-!TP_COLORAPP_FREE;Free temp+green + CAT02 + [output]
-!TP_COLORAPP_MEANLUMINANCE;Mean luminance (Yb%)
-!TP_COLORAPP_NEUTRAL;Reset
-!TP_COLORAPP_NEUTRAL_TIP;Reset all sliders checkbox and curves to their default values
-!TP_COLORAPP_TEMP_TOOLTIP;To select an illuminant, always set Tint=1.\n\nA temp=2856\nD50 temp=5003\nD55 temp=5503\nD65 temp=6504\nD75 temp=7504
-!TP_COLORTONING_LABGRID;L*a*b* color correction grid
-!TP_COLORTONING_LABGRID_VALUES;HL: a=%1 b=%2\nS: a=%3 b=%4
-!TP_COLORTONING_LABREGIONS;Color correction regions
-!TP_COLORTONING_LABREGION_ABVALUES;a=%1 b=%2
-!TP_COLORTONING_LABREGION_CHANNEL;Channel
-!TP_COLORTONING_LABREGION_CHANNEL_ALL;All
-!TP_COLORTONING_LABREGION_CHANNEL_B;Blue
-!TP_COLORTONING_LABREGION_CHANNEL_G;Green
-!TP_COLORTONING_LABREGION_CHANNEL_R;Red
-!TP_COLORTONING_LABREGION_CHROMATICITYMASK;C
-!TP_COLORTONING_LABREGION_HUEMASK;H
-!TP_COLORTONING_LABREGION_LIGHTNESS;Lightness
-!TP_COLORTONING_LABREGION_LIGHTNESSMASK;L
-!TP_COLORTONING_LABREGION_LIST_TITLE;Correction
-!TP_COLORTONING_LABREGION_MASK;Mask
-!TP_COLORTONING_LABREGION_MASKBLUR;Mask Blur
-!TP_COLORTONING_LABREGION_OFFSET;Offset
-!TP_COLORTONING_LABREGION_POWER;Power
-!TP_COLORTONING_LABREGION_SATURATION;Saturation
-!TP_COLORTONING_LABREGION_SHOWMASK;Show mask
-!TP_COLORTONING_LABREGION_SLOPE;Slope
-!TP_CROP_PPI;PPI
-!TP_CROP_RESETCROP;Reset
-!TP_CROP_SELECTCROP;Select
-!TP_DEHAZE_DEPTH;Depth
-!TP_DEHAZE_LABEL;Haze Removal
-!TP_DEHAZE_LUMINANCE;Luminance only
-!TP_DEHAZE_SHOW_DEPTH_MAP;Show depth map
-!TP_DEHAZE_STRENGTH;Strength
-!TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;Increase (multiply) the value of all chrominance sliders.\nThis curve lets you adjust the strength of chromatic noise reduction as a function of chromaticity, for instance to increase the action in areas of low saturation and to decrease it in those of high saturation.
-!TP_DIRPYRDENOISE_LABEL;Noise Reduction
-!TP_EXPOSURE_CLAMPOOG;Clip out-of-gamut colors
-!TP_EXPOSURE_HISTMATCHING;Auto-Matched Tone Curve
-!TP_EXPOSURE_HISTMATCHING_TOOLTIP;Automatically adjust sliders and curves (except exposure compensation) to match the look of the embedded JPEG thumbnail.
-!TP_FILMNEGATIVE_BLUE;Blue ratio
-!TP_FILMNEGATIVE_GREEN;Reference exponent (contrast)
-!TP_FILMNEGATIVE_GUESS_TOOLTIP;Automatically set the red and blue ratios by picking two patches which had a neutral hue (no color) in the original scene. The patches should differ in brightness. Set the white balance afterwards.
-!TP_FILMNEGATIVE_LABEL;Film Negative
-!TP_FILMNEGATIVE_PICK;Pick neutral spots
-!TP_FILMNEGATIVE_RED;Red ratio
-!TP_ICM_WORKING_TRC;Tone response curve:
-!TP_ICM_WORKING_TRC_CUSTOM;Custom
-!TP_ICM_WORKING_TRC_GAMMA;Gamma
-!TP_ICM_WORKING_TRC_NONE;None
-!TP_ICM_WORKING_TRC_SLOPE;Slope
-!TP_ICM_WORKING_TRC_TOOLTIP;Only for built-in profiles.
-!TP_LENSGEOM_LIN;Linear
-!TP_LENSGEOM_LOG;Logarithmic
-!TP_LENSPROFILE_CORRECTION_AUTOMATCH;Automatically selected
-!TP_LENSPROFILE_CORRECTION_LCPFILE;LCP file
-!TP_LENSPROFILE_CORRECTION_MANUAL;Manually selected
-!TP_LENSPROFILE_LENS_WARNING;Warning: the crop factor used for lens profiling is larger than the crop factor of the camera, the results might be wrong.
-!TP_LENSPROFILE_MODE_HEADER;Lens Profile
-!TP_LENSPROFILE_USE_CA;Chromatic aberration
-!TP_LENSPROFILE_USE_GEOMETRIC;Geometric distortion
-!TP_LENSPROFILE_USE_HEADER;Correct
-!TP_LENSPROFILE_USE_VIGNETTING;Vignetting
-!TP_LOCALCONTRAST_AMOUNT;Amount
-!TP_LOCALCONTRAST_DARKNESS;Darkness level
-!TP_LOCALCONTRAST_LABEL;Local Contrast
-!TP_LOCALCONTRAST_LIGHTNESS;Lightness level
-!TP_LOCALCONTRAST_RADIUS;Radius
-!TP_METADATA_EDIT;Apply modifications
-!TP_METADATA_MODE;Metadata copy mode
-!TP_METADATA_STRIP;Strip all metadata
-!TP_METADATA_TUNNEL;Copy unchanged
-!TP_PDSHARPENING_LABEL;Capture Sharpening
-!TP_PREPROCESS_LINEDENOISE_DIRECTION;Direction
-!TP_PREPROCESS_LINEDENOISE_DIRECTION_BOTH;Both
-!TP_PREPROCESS_LINEDENOISE_DIRECTION_HORIZONTAL;Horizontal
-!TP_PREPROCESS_LINEDENOISE_DIRECTION_PDAF_LINES;Horizontal only on PDAF rows
-!TP_PREPROCESS_LINEDENOISE_DIRECTION_VERTICAL;Vertical
-!TP_PREPROCESS_PDAFLINESFILTER;PDAF lines filter
-!TP_RAWCACORR_AUTOIT;Iterations
-!TP_RAWCACORR_AUTOIT_TOOLTIP;This setting is available if "Auto-correction" is checked.\nAuto-correction is conservative, meaning that it often does not correct all chromatic aberration.\nTo correct the remaining chromatic aberration, you can use up to five iterations of automatic chromatic aberration correction.\nEach iteration will reduce the remaining chromatic aberration from the last iteration at the cost of additional processing time.
-!TP_RAWCACORR_AVOIDCOLORSHIFT;Avoid color shift
-!TP_RAW_2PASS;1-pass+fast
-!TP_RAW_4PASS;3-pass+fast
-!TP_RAW_AMAZEVNG4;AMaZE+VNG4
-!TP_RAW_BORDER;Border
-!TP_RAW_DCBVNG4;DCB+VNG4
-!TP_RAW_DUALDEMOSAICAUTOCONTRAST;Auto threshold
-!TP_RAW_DUALDEMOSAICAUTOCONTRAST_TOOLTIP;If the checkbox is checked (recommended), RawTherapee calculates an optimum value based on flat regions in the image.\nIf there is no flat region in the image or the image is too noisy, the value will be set to 0.\nTo set the value manually, uncheck the checkbox first (reasonable values depend on the image).
-!TP_RAW_DUALDEMOSAICCONTRAST;Contrast threshold
-!TP_RAW_IMAGENUM_SN;SN mode
-!TP_RAW_IMAGENUM_TOOLTIP;Some raw files consist of several sub-images (Pentax/Sony Pixel Shift, Pentax 3-in-1 HDR, Canon Dual Pixel, Fuji EXR).\n\nWhen using any demosaicing method other than Pixel Shift, this selects which sub-image is used.\n\nWhen using the Pixel Shift demosaicing method on a Pixel Shift raw, all sub-images are used, and this selects which sub-image should be used for moving parts.
-!TP_RAW_PIXELSHIFTDMETHOD;Demosaic method for motion
-!TP_RAW_PIXELSHIFTEPERISO;Sensitivity
-!TP_RAW_PIXELSHIFTEPERISO_TOOLTIP;The default value of 0 should work fine for base ISO.\nHigher values increase sensitivity of motion detection.\nChange in small steps and watch the motion mask while changing.\nIncrease sensitivity for underexposed or high ISO images.
-!TP_RAW_PIXELSHIFTEQUALBRIGHTCHANNEL;Equalize per channel
-!TP_RAW_PIXELSHIFTEQUALBRIGHTCHANNEL_TOOLTIP;Enabled: Equalize the RGB channels individually.\nDisabled: Use same equalization factor for all channels.
-!TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP;Overlays the image with a green mask showing the regions with motion.
-!TP_RAW_RCD;RCD
-!TP_RAW_RCDVNG4;RCD+VNG4
-!TP_RAW_XTRANS;X-Trans
-!TP_RAW_XTRANSFAST;Fast X-Trans
-!TP_RESIZE_ALLOW_UPSCALING;Allow Upscaling
-!TP_RETINEX_CONTEDIT_MAP;Equalizer
-!TP_RETINEX_GAINOFFS;Gain and Offset (brightness)
-!TP_RETINEX_GAINTRANSMISSION;Gain transmission
-!TP_RETINEX_GAINTRANSMISSION_TOOLTIP;Amplify or reduce the transmission map to achieve the desired luminance.\nThe x-axis is the transmission.\nThe y-axis is the gain.
-!TP_RETINEX_MAP;Method
-!TP_SHARPENING_BLUR;Blur radius
-!TP_SHARPENING_CONTRAST;Contrast threshold
-!TP_SHARPENING_ITERCHECK;Auto limit iterations
-!TP_SHARPENING_RADIUS_BOOST;Corner radius boost
-!TP_SHARPENMICRO_CONTRAST;Contrast threshold
-!TP_SOFTLIGHT_LABEL;Soft Light
-!TP_SOFTLIGHT_STRENGTH;Strength
-!TP_TM_FATTAL_AMOUNT;Amount
-!TP_TM_FATTAL_ANCHOR;Anchor
-!TP_TM_FATTAL_LABEL;Dynamic Range Compression
-!TP_TM_FATTAL_THRESHOLD;Detail
-!TP_WAVELET_CB_TOOLTIP;For strong values product color-toning by combining it or not with levels decomposition 'toning'\nFor low values you can change the white balance of the background (sky, ...) without changing that of the front plane, generally more contrasted
-!TP_WBALANCE_PICKER;Pick
+ADJUSTER_RESET_TO_DEFAULT;Klik - terug naar standaardwaarde.\nCtrl+klik - terug naar laatst opgeslagen waarde.
+CURVEEDITOR_CATMULLROM;Flexibel
+DONT_SHOW_AGAIN;Dit bericht niet meer tonen
+DYNPROFILEEDITOR_IMGTYPE_ANY;Alles
+DYNPROFILEEDITOR_IMGTYPE_HDR;HDR
+DYNPROFILEEDITOR_IMGTYPE_PS;Pixel Shift
+DYNPROFILEEDITOR_IMGTYPE_STD;Standaard
+EXIFFILTER_IMAGETYPE;Type afbeelding
+EXIFPANEL_SHOWALL;Toon alles
+FILEBROWSER_BROWSEPATHBUTTONHINT;Klik om de opgegeven map te laden, en het zoekfilter opnieuw toe te passen.
+FILEBROWSER_CACHECLEARFROMFULL;Wis alles inclusief opgeslagen profielen
+FILEBROWSER_CACHECLEARFROMPARTIAL;Wis alles behalve opgeslagen profielen
+FILEBROWSER_DELETEDIALOG_ALL;Alle %1 bestanden in de prullenbak definitief verwijderen?
+FILEBROWSER_DELETEDIALOG_SELECTED;Geselecteerde %1 bestanden definitief verwijderen?
+FILEBROWSER_DELETEDIALOG_SELECTEDINCLPROC;Geselecteerde %1 bestanden inclusief een versie die door de verwerkingsrij is gemaakt verwijderen?
+FILEBROWSER_EMPTYTRASHHINT;Alle bestanden in de prullenbak permanent verwijderen
+FILEBROWSER_POPUPREMOVE;Permanent verwijderen
+FILEBROWSER_POPUPREMOVEINCLPROC;Verwijder definitief, inclusief met uitvoer in de verwerkingsrij
+FILEBROWSER_SHOWNOTTRASHHINT;Toon alleen niet-verwijderde afbeeldingen.
+GENERAL_CURRENT;Huidig
+GENERAL_HELP;Help
+GENERAL_RESET;Terugzetten
+GENERAL_SAVE_AS;Bewaren als...
+GENERAL_SLIDER;Schuifregelaar
+GIMP_PLUGIN_INFO;Welkom bij de RawTherapee GIMP plug-in!\nAls uw bewerking gereed is, sluit dan het hoofdvenster van RawTherapee en uw afbeelding wordt automatisch in GIMP geladen.
+HISTOGRAM_TOOLTIP_MODE;Wissel tussen lineair, log-lineair and log-log schalen van het histogram.
+HISTORY_MSG_173;NR - Detailbehoud
+HISTORY_MSG_203;NR - Kleurruimte
+HISTORY_MSG_235;B&W - CM - Auto
+HISTORY_MSG_237;B&W - CM
+HISTORY_MSG_256;NR - Mediaan - Type
+HISTORY_MSG_273;CT - Kleurbalans SMH
+HISTORY_MSG_297;NR - Modus
+HISTORY_MSG_392;W - Overblijvend - Kleurbalans
+HISTORY_MSG_441;Retinex - Toename transmissie
+HISTORY_MSG_475;PS - Kanaalbalans
+HISTORY_MSG_476;CAM02 - Temp uit
+HISTORY_MSG_477;CAM02 - Groen uit
+HISTORY_MSG_478;CAM02 - Yb uit
+HISTORY_MSG_479;CAM02 - CAT02 aanpassing uit
+HISTORY_MSG_480;CAM02 - Automatische CAT02 uit
+HISTORY_MSG_481;CAM02 - Temp scène
+HISTORY_MSG_482;CAM02 - Groen scène
+HISTORY_MSG_483;CAM02 - Yb scène
+HISTORY_MSG_484;CAM02 - Auto Yb scène
+HISTORY_MSG_485;Lenscorrectie
+HISTORY_MSG_486;Lenscorrectie - Camera
+HISTORY_MSG_487;Lenscorrectie - Lens
+HISTORY_MSG_488;Compressie Dynamisch Bereik
+HISTORY_MSG_489;DRC - Detail
+HISTORY_MSG_490;DRC - Hoeveelheid
+HISTORY_MSG_491;Witbalans
+HISTORY_MSG_492;RGB Curven
+HISTORY_MSG_493;L*a*b* Adjustments
+HISTORY_MSG_494;verscherpen
+HISTORY_MSG_CLAMPOOG;Kleuren afkappen die buiten het gamma vallen
+HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Kleurcorrectie
+HISTORY_MSG_COLORTONING_LABREGION_AB;CT - Kleurcorrectie
+HISTORY_MSG_COLORTONING_LABREGION_CHANNEL;CT - Kanaal
+HISTORY_MSG_COLORTONING_LABREGION_CHROMATICITYMASK;CT - gebied C masker
+HISTORY_MSG_COLORTONING_LABREGION_HUEMASK;CT - H masker
+HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESS;CT - Licht
+HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESSMASK;CT - L masker
+HISTORY_MSG_COLORTONING_LABREGION_LIST;CT - Lijst
+HISTORY_MSG_COLORTONING_LABREGION_MASKBLUR;CT - verzachtingsmasker gebied
+HISTORY_MSG_COLORTONING_LABREGION_OFFSET;CT - offset gebied
+HISTORY_MSG_COLORTONING_LABREGION_POWER;CT - sterkte gebied
+HISTORY_MSG_COLORTONING_LABREGION_SATURATION;CT - Verzadiging
+HISTORY_MSG_COLORTONING_LABREGION_SHOWMASK;CT - toon gebiedsmasker
+HISTORY_MSG_COLORTONING_LABREGION_SLOPE;CT - hellingsgebied
+HISTORY_MSG_DEHAZE_DEPTH;Nevelvermindering - Diepte
+HISTORY_MSG_DEHAZE_ENABLED;Nevelvermindering
+HISTORY_MSG_DEHAZE_LUMINANCE;Nevelvermindering - Alleen Luminantie
+HISTORY_MSG_DEHAZE_SHOW_DEPTH_MAP;Nevelvermindering - Toon dieptemap
+HISTORY_MSG_DEHAZE_STRENGTH;Nevelvermindering - Sterkte
+HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dual-demozaïek - auto-drempel
+HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dual-demozaïek - Contrastdrempel
+HISTORY_MSG_FILMNEGATIVE_ENABLED;Filmnegatief
+HISTORY_MSG_FILMNEGATIVE_VALUES;Filmnegatief waarden
+HISTORY_MSG_HISTMATCHING;Auto-matched tooncurve
+HISTORY_MSG_ICM_OUTPUT_PRIMARIES;Uitvoer - Primaries
+HISTORY_MSG_ICM_OUTPUT_TEMP;Uitvoer - ICC-v4 illuminant D
+HISTORY_MSG_ICM_OUTPUT_TYPE;Uitvoer - Type
+HISTORY_MSG_ICM_WORKING_GAMMA;Working - Gamma
+HISTORY_MSG_ICM_WORKING_SLOPE;Working - Helling
+HISTORY_MSG_ICM_WORKING_TRC_METHOD;Working - TRC methode
+HISTORY_MSG_LOCALCONTRAST_AMOUNT;Local Contrast - Hoeveelheid
+HISTORY_MSG_LOCALCONTRAST_DARKNESS;Local Contrast - Donker
+HISTORY_MSG_LOCALCONTRAST_ENABLED;Lokaal Contrast
+HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Lokaal Contrast - Licht
+HISTORY_MSG_LOCALCONTRAST_RADIUS;Lokaal Contrast - Radius
+HISTORY_MSG_METADATA_MODE;Metadata kopieermodus
+HISTORY_MSG_MICROCONTRAST_CONTRAST;Microcontrast - Contrastdrempel
+HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CS - Auto drempel
+HISTORY_MSG_PDSHARPEN_AUTO_RADIUS;CS - Auto radius
+HISTORY_MSG_PDSHARPEN_CHECKITER;CS - Auto limiet herhalingen
+HISTORY_MSG_PDSHARPEN_CONTRAST;CS - Contrastdrempel
+HISTORY_MSG_PDSHARPEN_ITERATIONS;CS - Herhalingen
+HISTORY_MSG_PDSHARPEN_RADIUS;CS - Radius
+HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;CS - Toename hoekradius
+HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Demozaïekmethode voor beweging
+HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;lijnruisfilter richting
+HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAF lijnfilter
+HISTORY_MSG_PRSHARPEN_CONTRAST;PRS - Contrastdrempel
+HISTORY_MSG_RAWCACORR_AUTOIT;Raw CA Correctie - Herhalingen
+HISTORY_MSG_RAWCACORR_COLORSHIFT;Raw CA Correctie - Vermijd kleurverschuiving
+HISTORY_MSG_RAW_BORDER;Raw rand
+HISTORY_MSG_RESIZE_ALLOWUPSCALING;Schalen - sta vergroting toe
+HISTORY_MSG_SHARPENING_BLUR;Verscherpen - Vervagingsradius
+HISTORY_MSG_SHARPENING_CONTRAST;Verscherpen - Contrastdrempel
+HISTORY_MSG_SH_COLORSPACE;S/H - Kleurruimte
+HISTORY_MSG_SOFTLIGHT_ENABLED;Zacht licht
+HISTORY_MSG_SOFTLIGHT_STRENGTH;Zacht licht - Sterkte
+HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anker
+HISTORY_MSG_TRANS_Method;Geometrie - Methode
+ICCPROFCREATOR_COPYRIGHT;Copyright:
+ICCPROFCREATOR_COPYRIGHT_RESET_TOOLTIP;Zet terug naar standaard copyright, verleend aan "RawTherapee, CC0"
+ICCPROFCREATOR_CUSTOM;Handmatig
+ICCPROFCREATOR_DESCRIPTION;Beschriiving:
+ICCPROFCREATOR_DESCRIPTION_ADDPARAM;Voeg gamma- en hellingwaarden toe aan de beschrijving
+ICCPROFCREATOR_DESCRIPTION_TOOLTIP;Laat leeg voor de standaard beschrijving.
+ICCPROFCREATOR_GAMMA;Gamma
+ICCPROFCREATOR_ICCVERSION;ICC versie:
+ICCPROFCREATOR_ILL;Illuminant:
+ICCPROFCREATOR_ILL_41;D41
+ICCPROFCREATOR_ILL_50;D50
+ICCPROFCREATOR_ILL_55;D55
+ICCPROFCREATOR_ILL_60;D60
+ICCPROFCREATOR_ILL_65;D65
+ICCPROFCREATOR_ILL_80;D80
+ICCPROFCREATOR_ILL_DEF;Standaard
+ICCPROFCREATOR_ILL_INC;StdA 2856K
+ICCPROFCREATOR_ILL_TOOLTIP;U kunt alleen de illuminant instellen voor ICC v4-profielen.
+ICCPROFCREATOR_PRIMARIES;Primaire kleuren:
+ICCPROFCREATOR_PRIM_ACESP0;ACES AP0
+ICCPROFCREATOR_PRIM_ACESP1;ACES AP1
+ICCPROFCREATOR_PRIM_ADOBE;Adobe RGB (1998)
+ICCPROFCREATOR_PRIM_BEST;BestRGB
+ICCPROFCREATOR_PRIM_BETA;BetaRGB
+ICCPROFCREATOR_PRIM_BLUX;Blauw X
+ICCPROFCREATOR_PRIM_BLUY;Blauw Y
+ICCPROFCREATOR_PRIM_BRUCE;BruceRGB
+ICCPROFCREATOR_PRIM_GREX;Groen X
+ICCPROFCREATOR_PRIM_GREY;Groen Y
+ICCPROFCREATOR_PRIM_PROPH;Prophoto
+ICCPROFCREATOR_PRIM_REC2020;Rec2020
+ICCPROFCREATOR_PRIM_REDX;Rood X
+ICCPROFCREATOR_PRIM_REDY;Rood Y
+ICCPROFCREATOR_PRIM_SRGB;sRGB
+ICCPROFCREATOR_PRIM_TOOLTIP;U kunt alleen aangepaste primaries voor ICC v4-profielen instellen.
+ICCPROFCREATOR_PRIM_WIDEG;Widegamut
+ICCPROFCREATOR_PROF_V2;ICC v2
+ICCPROFCREATOR_PROF_V4;ICC v4
+ICCPROFCREATOR_SAVEDIALOG_TITLE;Bewaar ICC profiel als...
+ICCPROFCREATOR_SLOPE;Helling
+ICCPROFCREATOR_TRC_PRESET;Toonresponscurve:
+MAIN_BUTTON_ICCPROFCREATOR;ICC Profielmaker
+MAIN_FRAME_PLACES_DEL;Verwijderen
+MAIN_MSG_TOOMANYOPENEDITORS;Teveel open fotobewerkers.\nSluit er een om verder te kunnen.
+MAIN_TAB_ADVANCED;Geavanceerd
+MAIN_TAB_ADVANCED_TOOLTIP;Sneltoets: Alt-a
+MAIN_TAB_FAVORITES;Favorieten
+MAIN_TAB_FAVORITES_TOOLTIP;Sneltoets: Alt-u
+MAIN_TOOLTIP_BACKCOLOR3;Achtergrondkleur van het voorbeeld: middelgrijs\nSneltoets: 9
+MAIN_TOOLTIP_PREVIEWSHARPMASK;Bekijk het scherptecontrastmasker.\nSneltoets: p\nWerkt alleen als verscherping is geactiveerd en het zoomniveau >= 100%.
+OPTIONS_BUNDLED_MISSING;Het gebundelde profiel "%1" werd niet gevonden!\n\nUw installatie kan beschadigd zijn.\n\nDaarom worden interne standaardwaarden gebruikt.
+OPTIONS_DEFIMG_MISSING;Het standaardprofiel voor niet-raw- foto's werd niet gevonden of is niet ingesteld.\n\nControleer de profielenmap, het kan ontbreken of beschadigd zijn.\n\n"%1" wordt daarom gebruikt.
+OPTIONS_DEFRAW_MISSING;Het standaardprofiel voor raw-foto's werd niet gevonden of is niet ingesteld.\n\nControleer de profielenmap, het kan ontbreken of beschadigd zijn.\n\n"%1" wordt daarom gebruikt.
+PARTIALPASTE_ADVANCEDGROUP;Geavanceerd
+PARTIALPASTE_DEHAZE;Nevel verminderen
+PARTIALPASTE_FILMNEGATIVE;Film Negatief
+PARTIALPASTE_LOCALCONTRAST;Lokaal contrast
+PARTIALPASTE_METADATA;Metadata modus
+PARTIALPASTE_PREPROCESS_PDAFLINESFILTER;PDAF lijnfilter
+PARTIALPASTE_RAWCACORR_AVOIDCOLORSHIFT;CA vermijd kleurverschuiving
+PARTIALPASTE_RAW_BORDER;Raw rand
+PARTIALPASTE_SOFTLIGHT;Zacht licht
+PARTIALPASTE_TM_FATTAL;Compressie dynamisch bereik
+PREFERENCES_APPEARANCE;Uiterlijk
+PREFERENCES_APPEARANCE_COLORPICKERFONT;Lettertype kleurenkiezer
+PREFERENCES_APPEARANCE_CROPMASKCOLOR;Kleur bijsnijdmasker
+PREFERENCES_APPEARANCE_MAINFONT;Standaard lettertype
+PREFERENCES_APPEARANCE_PSEUDOHIDPI;Pseudo-HiDPI modus
+PREFERENCES_APPEARANCE_THEME;Thema
+PREFERENCES_AUTOSAVE_TP_OPEN;Bewaar positie gereedschappen (open/dicht) bij afsluiten
+PREFERENCES_CACHECLEAR;Wissen
+PREFERENCES_CACHECLEAR_ALL;Wis alle bestanden in de cache:
+PREFERENCES_CACHECLEAR_ALLBUTPROFILES;Wis alle bestanden in de cache behalve verwerkingsprofielen:
+PREFERENCES_CACHECLEAR_ONLYPROFILES;Wis alleen verwerkingsprofielen in de cache:
+PREFERENCES_CACHECLEAR_SAFETY;Alleen bestanden in de cache worden gewist. Verwerkingsprofielen van de oorspronkelijke afbeeldingen blijven ongemoeid.
+PREFERENCES_CHUNKSIZES;Tegels per thread
+PREFERENCES_CHUNKSIZE_RAW_AMAZE;AMaZE demosaïek
+PREFERENCES_CHUNKSIZE_RAW_CA;Raw CA correctie
+PREFERENCES_CHUNKSIZE_RAW_RCD;RCD demosaïek
+PREFERENCES_CHUNKSIZE_RAW_XT;Xtrans demosaïek
+PREFERENCES_CHUNKSIZE_RGB;RGB verwerking
+PREFERENCES_CROP;Uitsnijden
+PREFERENCES_CROP_AUTO_FIT;Automatisch zoomen tot de uitsnede
+PREFERENCES_CROP_GUIDES;Getoonde hulplijnen als uitsnede niet bewerkt wordt
+PREFERENCES_CROP_GUIDES_FRAME;Frame
+PREFERENCES_CROP_GUIDES_FULL;Origineel
+PREFERENCES_CROP_GUIDES_NONE;Geen
+PREFERENCES_DIRECTORIES;Mappen
+PREFERENCES_EDITORCMDLINE;Aangepaste opdrachtregel
+PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Compacte gereedschapsbalken in bestandsnavigator
+PREFERENCES_LANG;Taal
+PREFERENCES_PERFORMANCE_MEASURE;Meting
+PREFERENCES_PERFORMANCE_MEASURE_HINT;Log verwerkingstijden in de console
+PREFERENCES_PERFORMANCE_THREADS;Threads
+PREFERENCES_PERFORMANCE_THREADS_LABEL;Maximaal aantal threads voor ruisvermindering and Wavelet Niveaus (0 = Automatisch)
+PREFERENCES_PROFILESAVEBOTH;Bewaar verwerkingsprofielen zowel in de cache als naast het invoerbestand
+PREFERENCES_PROFILESAVELOCATION;Opslaglocatie profielen
+PREFERENCES_SAVE_TP_OPEN_NOW;Bewaar open/dicht-status van de gereedschappen nu
+PREFERENCES_TAB_PERFORMANCE;Performantie
+PREFERENCES_THUMBNAIL_INSPECTOR_JPEG;Ingesloten JPEG voorbeeld
+PREFERENCES_THUMBNAIL_INSPECTOR_MODE;Te tonen foto
+PREFERENCES_THUMBNAIL_INSPECTOR_RAW;Neutrale raw rendering
+PREFERENCES_THUMBNAIL_INSPECTOR_RAW_IF_NO_JPEG_FULLSIZE;Ingesloten JPEG indien vol formaat, anders neutrale raw
+PROGRESSBAR_DECODING;Decoderen...
+PROGRESSBAR_GREENEQUIL;Groen blancering...
+PROGRESSBAR_HLREC;Reconstructie hoge lichten...
+PROGRESSBAR_HOTDEADPIXELFILTER;Hot/dead pixel filter...
+PROGRESSBAR_LINEDENOISE;Lijnruis filter...
+PROGRESSBAR_RAWCACORR;Raw CA correctie...
+QINFO_FRAMECOUNT;%2 frames
+QINFO_HDR;HDR / %2 frame(s)
+QINFO_PIXELSHIFT;Pixel Shift / %2 frame(s)
+QUEUE_LOCATION_TITLE;Uitvoerlocatie
+QUEUE_STARTSTOP_TOOLTIP;;Start of stop de verwerking van foto's in de rij.\n\nSneltoets: Ctrl+s
+SAMPLEFORMAT_0;onbekend data formaat
+SAMPLEFORMAT_1;8-bit unsigned
+SAMPLEFORMAT_2;16-bit unsigned
+SAMPLEFORMAT_4;24-bit LogLuv
+SAMPLEFORMAT_8;32-bit LogLuv
+SAMPLEFORMAT_16;16-bit drijvendekomma
+SAMPLEFORMAT_32;24-bit drijvendekomma
+SAMPLEFORMAT_64;32-bit drijvendekomma
+SAVEDLG_FILEFORMAT_FLOAT; drijvendekomma
+SOFTPROOF_GAMUTCHECK_TOOLTIP;Markeer pixels waarvan de kleuren buiten het kleurgamma vallen, relatief aan:\n- het printerprofiel, indien opgegeven en soft-proofing is ingeschakeld,\n- het uitvoerprofiel, indien geen printerprofiel is gekozen en soft-proofing is ingeschakeld,\n- het beeldschermprofiel, indien soft-proofing is uitgeschakeld.
+SOFTPROOF_TOOLTIP;Soft-proofing simuleert hoe een foto wordt getoond:\n- als deze wordt afgedrukt, indien een printerprofiel is opgegeven in Voorkeuren > Kleurbeheer,\n- als de foto getoond wordt op een beeldscherm dat het huidige uitvoerprofiel gebruikt en een printerprofiel niet is opgegeven.
+TP_BWMIX_MIXC;Kanaal Mixer
+TP_BWMIX_NEUTRAL;Terugzetten
+TP_COLORAPP_ABSOLUTELUMINANCE;Absolute luminantie
+TP_COLORAPP_CAT02ADAPTATION_TOOLTIP;Bij manuele aanpassing worden waardon boven 65 aanbevolen.
+TP_COLORAPP_FREE;Vrije temp+groen + CAT02 + [uitvoer]
+TP_COLORAPP_MEANLUMINANCE;Gemiddelde luminantie (Yb%)
+TP_COLORAPP_NEUTRAL;Terugzetten
+TP_COLORAPP_NEUTRAL_TIP;Zet alle regelaars, vinkjes en curves terug naar hun standaardwaarde
+TP_COLORAPP_TEMP_TOOLTIP;Zet altijd Tint=1 om een lichtbron te selecteren.\n\nA temp=2856\nD50 temp=5003\nD55 temp=5503\nD65 temp=6504\nD75 temp=7504
+TP_COLORTONING_LABGRID;L*a*b* kleurcorrectie raster
+TP_COLORTONING_LABGRID_VALUES;HL: a=%1 b=%2\nS: a=%3 b=%4
+TP_COLORTONING_LABREGIONS;Kleurcorrectie gebieden
+TP_COLORTONING_LABREGION_ABVALUES;a=%1 b=%2
+TP_COLORTONING_LABREGION_CHANNEL;Kanaal
+TP_COLORTONING_LABREGION_CHANNEL_ALL;Alle
+TP_COLORTONING_LABREGION_CHANNEL_B;Blauw
+TP_COLORTONING_LABREGION_CHANNEL_G;Groen
+TP_COLORTONING_LABREGION_CHANNEL_R;Rood
+TP_COLORTONING_LABREGION_CHROMATICITYMASK;C
+TP_COLORTONING_LABREGION_HUEMASK;H
+TP_COLORTONING_LABREGION_LIGHTNESS;Helderheid(L)
+TP_COLORTONING_LABREGION_LIGHTNESSMASK;L
+TP_COLORTONING_LABREGION_LIST_TITLE;Correctie
+TP_COLORTONING_LABREGION_MASK;Masker
+TP_COLORTONING_LABREGION_MASKBLUR;Verzachtingsmasker
+TP_COLORTONING_LABREGION_OFFSET;Offset
+TP_COLORTONING_LABREGION_POWER;Kracht
+TP_COLORTONING_LABREGION_SATURATION;Verzadiging
+TP_COLORTONING_LABREGION_SHOWMASK;Toon masker
+TP_COLORTONING_LABREGION_SLOPE;Helling
+TP_CROP_PPI;PPI
+TP_CROP_RESETCROP;Terugzetten
+TP_CROP_SELECTCROP;Selecteer
+TP_DEHAZE_DEPTH;Diepte
+TP_DEHAZE_LABEL;Nevel vermindering
+TP_DEHAZE_LUMINANCE;Luminantie alleen
+TP_DEHAZE_SHOW_DEPTH_MAP;Toon de dieptemap
+TP_DEHAZE_STRENGTH;Sterkte
+TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;Verhoog (vermenigvuldig) de waarde van alle chrominantie regelaars.\nDeze curve regelt de sterkte van de chromatische ruisvermindering als een functie van de chromaticiteit, om bijvoorbeeld het effect te vergroten in gebieden met lage verzadiging en te verminderen in deze met lage verzadiging.
+TP_DIRPYRDENOISE_LABEL;Ruisvermindering
+TP_EXPOSURE_CLAMPOOG;Knip kleuren die buiten het gamma vallen
+TP_EXPOSURE_HISTMATCHING;Automatische Tooncurve
+TP_EXPOSURE_HISTMATCHING_TOOLTIP;Pas automatisch de curves en schuifregelaars aan (behalve belichtingscompensatie) om overeen te komen met de ingesloten JPEG miniatuur.
+TP_FILMNEGATIVE_BLUE;Blauw verhouding
+TP_FILMNEGATIVE_GREEN;Referentie exponent (contrast)
+TP_FILMNEGATIVE_GUESS_TOOLTIP;Zet automatisch de rood/groen verhouding door 2 gebieden te kiezen met een neutrale tint (geen kleur) in het origineel. De gebieden moeten verschillen in helderheid. Zet de witbalans nadien.
+TP_FILMNEGATIVE_LABEL;Film Negatief
+TP_FILMNEGATIVE_PICK;Kies neutrale punten
+TP_FILMNEGATIVE_RED;Rood verhouding
+TP_ICM_WORKING_TRC;Tooncurve:
+TP_ICM_WORKING_TRC_CUSTOM;Gebruiker gedefinieerd
+TP_ICM_WORKING_TRC_GAMMA;Gamma
+TP_ICM_WORKING_TRC_NONE;Geen
+TP_ICM_WORKING_TRC_SLOPE;Helling
+TP_ICM_WORKING_TRC_TOOLTIP;Enkel voor ingebouwde profielen.
+TP_LENSGEOM_LIN;Lineair
+TP_LENSGEOM_LOG;Logarithmisch
+TP_LENSPROFILE_CORRECTION_AUTOMATCH;Automatische selectie
+TP_LENSPROFILE_CORRECTION_LCPFILE;LCP bestand
+TP_LENSPROFILE_CORRECTION_MANUAL;Manuele selectie
+TP_LENSPROFILE_LENS_WARNING;Waarschuwing: de gebruikte lens profiel crop factor komt niet overeen met de camera crop factor, de resultaten kunnen verkeerd zijn.
+TP_LENSPROFILE_MODE_HEADER;Lens Profiel
+TP_LENSPROFILE_USE_CA;Chromatische afwijking
+TP_LENSPROFILE_USE_GEOMETRIC;Geometrische vervorming
+TP_LENSPROFILE_USE_HEADER;Lenscorrecties
+TP_LENSPROFILE_USE_VIGNETTING;Vignettering
+TP_LOCALCONTRAST_AMOUNT;Hoeveelheid
+TP_LOCALCONTRAST_DARKNESS;Donker niveau
+TP_LOCALCONTRAST_LABEL;Lokaal Contrast
+TP_LOCALCONTRAST_LIGHTNESS;helderheidsniveau
+TP_LOCALCONTRAST_RADIUS;Straal
+TP_METADATA_EDIT;Pas wijzigingen toe
+TP_METADATA_MODE;Metadata kopieermodus
+TP_METADATA_STRIP;Strip alle metadata
+TP_METADATA_TUNNEL;Kopieer ongewijzigd
+TP_PDSHARPENING_LABEL;Verscherpen
+TP_PREPROCESS_LINEDENOISE_DIRECTION;Richting
+TP_PREPROCESS_LINEDENOISE_DIRECTION_BOTH;Beide
+TP_PREPROCESS_LINEDENOISE_DIRECTION_HORIZONTAL;Horizontaal
+TP_PREPROCESS_LINEDENOISE_DIRECTION_PDAF_LINES;Horizontaal enkel op PDAF-rijen
+TP_PREPROCESS_LINEDENOISE_DIRECTION_VERTICAL;Verticaal
+TP_PREPROCESS_PDAFLINESFILTER;PDAF lijnfilter
+TP_RAWCACORR_AUTOIT;Herhalingen
+TP_RAWCACORR_AUTOIT_TOOLTIP;Deze schuif is alleen actief als Automatische CA-correctie is aangevinkt.\nAuto-correctie werkt conservatief en corrigeert meestal niet alle chromatische aberratie.\nOm de resterende CA te corrigeren, kunt u dit proces tot vijf keer herhalen.\nElke herhaling vermindert de CA van de vorige herhaling, maar gaat wel ten koste van extra rekentijd.
+TP_RAWCACORR_AVOIDCOLORSHIFT;Vermijd kleurverschuiving
+TP_RAW_2PASS;1-pass+snel
+TP_RAW_4PASS;3-pass+snel
+TP_RAW_AMAZEVNG4;AMaZE+VNG4
+TP_RAW_BORDER;Rand
+TP_RAW_DCBVNG4;DCB+VNG4
+TP_RAW_DUALDEMOSAICAUTOCONTRAST;Auto drempel
+TP_RAW_DUALDEMOSAICAUTOCONTRAST_TOOLTIP;Als checkbox is aangevinkt (aanbevolen), berekent RT een optimale waarde gebaseerd op vlakke gebieden in de foto.\nIndien die niet gevonden worden of de foto bevat veel ruis, wordt de waarde op 0 gezet.\nOm de waarde handmatig in te voeren moet u eerst de checkbox uitvinken (redelijke waarden zijn afhankelijk van het soort foto).
+TP_RAW_DUALDEMOSAICCONTRAST;Contrast drempel
+TP_RAW_IMAGENUM_SN;SN modus
+TP_RAW_IMAGENUM_TOOLTIP;Sommige raw bestanden bestaan uit verschillende sub-afbeeldingen (Pentax/Sony Pixel Shift, Pentax 3-in-1 HDR, Canon Dual Pixel, Fuji EXR).\n\Als een andere demozaïek methode dan Pixel Shift gebruikt wordt, selecteert dit de gebruikte sub-afbeelding.\n\nBij gebruik van de Pixel Shift demozaïek methode op een Pixel Shift raw, worden alle sub-afbeeldingen gebruikt, and dit selecteert de subafbeeldijg die gebruikt wordt voor bewegende moving gebieden.
+TP_RAW_PIXELSHIFTDMETHOD;Demozaïek voor beweging
+TP_RAW_PIXELSHIFTEPERISO;Gevoeligheid
+TP_RAW_PIXELSHIFTEPERISO_TOOLTIP;De standaardwaarde 0 werkt goed voor lage ISO-waarden.\nHogere waarden vergroten de gevoeligheid van bewegingsdetectie.\nWijzig in kleine stappen en controleer het bewegingsmasker.\nVerhoog gevoeligheid voor onderbelichte foto's of foto's met hoge ISO-waarden.
+TP_RAW_PIXELSHIFTEQUALBRIGHTCHANNEL;Balanceer per kanaal
+TP_RAW_PIXELSHIFTEQUALBRIGHTCHANNEL_TOOLTIP;Ingeschakeld: Balanceer elk RGB kanaal afzonderlijk.\nUitgeschakeld: Balanceer alle kanalen evenveel.
+TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP;Toont de foto met een groen masker dat de bewegingsgebieden toont.
+TP_RAW_RCD;RCD
+TP_RAW_RCDVNG4;RCD+VNG4
+TP_RAW_XTRANS;X-Trans
+TP_RAW_XTRANSFAST;Snelle X-Trans
+TP_RESIZE_ALLOW_UPSCALING;Sta opschalen toe
+TP_RETINEX_CONTEDIT_MAP;Equalizer
+TP_RETINEX_GAINOFFS;Versterking en Offset (helderheid)
+TP_RETINEX_GAINTRANSMISSION;Transmissie versterking
+TP_RETINEX_GAINTRANSMISSION_TOOLTIP;Versterk of verzwak de transmssiemap om de gewenste luminantie te bekomen.\nThe x-as is the transmissie.\nThe y-as is the versterking.
+TP_RETINEX_MAP;Methode
+TP_SHARPENING_BLUR;Vervagen straal
+TP_SHARPENING_CONTRAST;Contrast drempel
+TP_SHARPENING_ITERCHECK;Automatische limiet herhalingen
+TP_SHARPENING_RADIUS_BOOST;Straalvergroting
+TP_SHARPENMICRO_CONTRAST;Contrast drempel
+TP_SOFTLIGHT_LABEL;Zacht licht
+TP_SOFTLIGHT_STRENGTH;Sterkte
+TP_TM_FATTAL_AMOUNT;Hoeveelheid
+TP_TM_FATTAL_ANCHOR;Anker
+TP_TM_FATTAL_LABEL;Dynamisch bereik compressie
+TP_TM_FATTAL_THRESHOLD;Detail
+TP_WAVELET_CB_TOOLTIP;Voor hoge waarden: kleurcorrectie door al of niet te combineren met niveau decompositie 'toning'\nVoor lage waarden de witbalans van de achtergrond (hemel, ...) wijzigen zonder die van de voorgrond, meestal meer contrastrijk
+TP_WBALANCE_PICKER;Kies
diff --git a/rtdata/languages/default b/rtdata/languages/default
index b30e7dd32..e910d359a 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -22,7 +22,7 @@ CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click
CURVEEDITOR_HIGHLIGHTS;Highlights
CURVEEDITOR_LIGHTS;Lights
CURVEEDITOR_LINEAR;Linear
-CURVEEDITOR_LOADDLGLABEL;Load curve...
+CURVEEDITOR_LOADDLGLABEL;Load curve
CURVEEDITOR_MINMAXCPOINTS;Equalizer
CURVEEDITOR_NURBS;Control cage
CURVEEDITOR_PARAMETRIC;Parametric
@@ -694,21 +694,37 @@ HISTORY_MSG_441;Retinex - Gain transmission
HISTORY_MSG_442;Retinex - Scale
HISTORY_MSG_443;Output black point compensation
HISTORY_MSG_444;WB - Temp bias
-HISTORY_MSG_445;Raw sub-image
-HISTORY_MSG_449;PS - ISO adaption
-HISTORY_MSG_452;PS - Show motion
-HISTORY_MSG_453;PS - Show mask only
-HISTORY_MSG_457;PS - Check red/blue
-HISTORY_MSG_462;PS - Check green
-HISTORY_MSG_464;PS - Blur motion mask
-HISTORY_MSG_465;PS - Blur radius
-HISTORY_MSG_468;PS - Fill holes
-HISTORY_MSG_469;PS - Median
-HISTORY_MSG_471;PS - Motion correction
-HISTORY_MSG_472;PS - Smooth transitions
-HISTORY_MSG_473;PS - Use LMMSE
-HISTORY_MSG_474;PS - Equalize
-HISTORY_MSG_475;PS - Equalize channel
+HISTORY_MSG_445;Raw Sub-Image
+HISTORY_MSG_446;EvPixelShiftMotion
+HISTORY_MSG_447;EvPixelShiftMotionCorrection
+HISTORY_MSG_448;EvPixelShiftStddevFactorGreen
+HISTORY_MSG_449;PS ISO adaption
+HISTORY_MSG_450;EvPixelShiftNreadIso
+HISTORY_MSG_451;EvPixelShiftPrnu
+HISTORY_MSG_452;PS Show motion
+HISTORY_MSG_453;PS Show mask only
+HISTORY_MSG_454;EvPixelShiftAutomatic
+HISTORY_MSG_455;EvPixelShiftNonGreenHorizontal
+HISTORY_MSG_456;EvPixelShiftNonGreenVertical
+HISTORY_MSG_457;PS Check red/blue
+HISTORY_MSG_458;EvPixelShiftStddevFactorRed
+HISTORY_MSG_459;EvPixelShiftStddevFactorBlue
+HISTORY_MSG_460;EvPixelShiftGreenAmaze
+HISTORY_MSG_461;EvPixelShiftNonGreenAmaze
+HISTORY_MSG_462;PS Check green
+HISTORY_MSG_463;EvPixelShiftRedBlueWeight
+HISTORY_MSG_464;PS Blur motion mask
+HISTORY_MSG_465;PS Blur radius
+HISTORY_MSG_466;EvPixelShiftSum
+HISTORY_MSG_467;EvPixelShiftExp0
+HISTORY_MSG_468;PS Fill holes
+HISTORY_MSG_469;PS Median
+HISTORY_MSG_470;EvPixelShiftMedian3
+HISTORY_MSG_471;PS Motion correction
+HISTORY_MSG_472;PS Smooth transitions
+HISTORY_MSG_473;PS Use lmmse
+HISTORY_MSG_474;PS Equalize
+HISTORY_MSG_475;PS Equalize channel
HISTORY_MSG_476;CAM02 - Temp out
HISTORY_MSG_477;CAM02 - Green out
HISTORY_MSG_478;CAM02 - Yb out
@@ -726,8 +742,429 @@ HISTORY_MSG_489;DRC - Detail
HISTORY_MSG_490;DRC - Amount
HISTORY_MSG_491;White Balance
HISTORY_MSG_492;RGB Curves
-HISTORY_MSG_493;L*a*b* Adjustments
+HISTORY_MSG_493;Local Adjustments
HISTORY_MSG_494;Capture Sharpening
+HISTORY_MSG_496;Local Spot deleted
+HISTORY_MSG_497;Local Spot selected
+HISTORY_MSG_498;Local Spot name
+HISTORY_MSG_499;Local Spot visibility
+HISTORY_MSG_500;Local Spot shape
+HISTORY_MSG_501;Local Spot method
+HISTORY_MSG_502;Local Spot shape method
+HISTORY_MSG_503;Local Spot locX
+HISTORY_MSG_504;Local Spot locXL
+HISTORY_MSG_505;Local Spot locY
+HISTORY_MSG_506;Local Spot locYT
+HISTORY_MSG_507;Local Spot center
+HISTORY_MSG_508;Local Spot circrad
+HISTORY_MSG_509;Local Spot quality method
+HISTORY_MSG_510;Local Spot transition
+HISTORY_MSG_511;Local Spot thresh
+HISTORY_MSG_512;Local Spot ΔE -weakening
+HISTORY_MSG_513;Local Spot scope
+HISTORY_MSG_514;Local Spot structure
+HISTORY_MSG_515;Local Adjustments
+HISTORY_MSG_516;Local - Color and light
+HISTORY_MSG_517;Local - Enable super
+HISTORY_MSG_518;Local - Lightness
+HISTORY_MSG_519;Local - Contrast
+HISTORY_MSG_520;Local - Chrominance
+HISTORY_MSG_521;Local - Scope
+HISTORY_MSG_522;Local - curve method
+HISTORY_MSG_523;Local - LL Curve
+HISTORY_MSG_524;Local - CC curve
+HISTORY_MSG_525;Local - LH Curve
+HISTORY_MSG_526;Local - H curve
+HISTORY_MSG_527;Local - Color Inverse
+HISTORY_MSG_528;Local - Exposure
+HISTORY_MSG_529;Local - Exp Compensation
+HISTORY_MSG_530;Local - Exp Hlcompr
+HISTORY_MSG_531;Local - Exp hlcomprthresh
+HISTORY_MSG_532;Local - Exp black
+HISTORY_MSG_533;Local - Exp Shcompr
+HISTORY_MSG_534;Local - Warm Cool
+HISTORY_MSG_535;Local - Exp Scope
+HISTORY_MSG_536;Local - Exp Contrast curve
+HISTORY_MSG_537;Local - Vibrance
+HISTORY_MSG_538;Local - Vib Saturated
+HISTORY_MSG_539;Local - Vib Pastel
+HISTORY_MSG_540;Local - Vib Threshold
+HISTORY_MSG_541;Local - Vib Protect skin tones
+HISTORY_MSG_542;Local - Vib avoid colorshift
+HISTORY_MSG_543;Local - Vib link
+HISTORY_MSG_544;Local - Vib Scope
+HISTORY_MSG_545;Local - Vib H curve
+HISTORY_MSG_546;Local - Blur and noise
+HISTORY_MSG_547;Local - Radius
+HISTORY_MSG_548;Local - Noise
+HISTORY_MSG_549;Local - Blur scope
+HISTORY_MSG_550;Local - Blur method
+HISTORY_MSG_551;Local - Blur Luminance only
+HISTORY_MSG_552;Local - Tone mapping
+HISTORY_MSG_553;Local - TM compression strength
+HISTORY_MSG_554;Local - TM gamma
+HISTORY_MSG_555;Local - TM edge stopping
+HISTORY_MSG_556;Local - TM scale
+HISTORY_MSG_557;Local - TM Reweighting
+HISTORY_MSG_558;Local - TM scope
+HISTORY_MSG_559;Local - Retinex
+HISTORY_MSG_560;Local - Retinex method
+HISTORY_MSG_561;Local - Retinex strength
+HISTORY_MSG_562;Local - Retinex chroma
+HISTORY_MSG_563;Local - Retinex radius
+HISTORY_MSG_564;Local - Retinex contrast
+HISTORY_MSG_565;Local - scope
+HISTORY_MSG_566;Local - Retinex Gain curve
+HISTORY_MSG_567;Local - Retinex Inverse
+HISTORY_MSG_568;Local - Sharpening
+HISTORY_MSG_569;Local - Sh Radius
+HISTORY_MSG_570;Local - Sh Amount
+HISTORY_MSG_571;Local - Sh Damping
+HISTORY_MSG_572;Local - Sh Iterations
+HISTORY_MSG_573;Local - Sh Scope
+HISTORY_MSG_574;Local - Sh Inverse
+HISTORY_MSG_575;Local - CBDL
+HISTORY_MSG_576;Local - cbdl mult
+HISTORY_MSG_577;Local - cbdl chroma
+HISTORY_MSG_578;Local - cbdl threshold
+HISTORY_MSG_579;Local - cbdl scope
+HISTORY_MSG_580;Local - Denoise
+HISTORY_MSG_581;Local - deNoise lum f 1
+HISTORY_MSG_582;Local - deNoise lum c
+HISTORY_MSG_583;Local - deNoise lum detail
+HISTORY_MSG_584;Local - deNoise Equalizer white-black
+HISTORY_MSG_585;Local - deNoise chro f
+HISTORY_MSG_586;Local - deNoise chro c
+HISTORY_MSG_587;Local - deNoise chro detail
+HISTORY_MSG_588;Local - deNoise Equalizer blue-red
+HISTORY_MSG_589;Local - deNoise bilateral
+HISTORY_MSG_590;Local - deNoise Scope
+HISTORY_MSG_591;Local - Avoid color shift
+HISTORY_MSG_592;Local - Sh Contrast
+HISTORY_MSG_593;Local - Local contrast
+HISTORY_MSG_594;Local - Local contrast radius
+HISTORY_MSG_595;Local - Local contrast amount
+HISTORY_MSG_596;Local - Local contrast darkness
+HISTORY_MSG_597;Local - Local contrast lightness
+HISTORY_MSG_598;Local - Local contrast scope
+HISTORY_MSG_599;Local - Retinex dehaze
+HISTORY_MSG_600;Local - Soft Light enable
+HISTORY_MSG_601;Local - Soft Light strength
+HISTORY_MSG_602;Local - Soft Light scope
+HISTORY_MSG_603;Local - Sh Blur radius
+HISTORY_MSG_605;Local - Mask preview choice
+HISTORY_MSG_606;Local Spot selected
+HISTORY_MSG_607;Local - Color Mask C
+HISTORY_MSG_608;Local - Color Mask L
+HISTORY_MSG_609;Local - Exp Mask C
+HISTORY_MSG_610;Local - Exp Mask L
+HISTORY_MSG_611;Local - Color Mask H
+HISTORY_MSG_612;Local - Color Structure
+HISTORY_MSG_613;Local - Exp Structure
+HISTORY_MSG_614;Local - Exp Mask H
+HISTORY_MSG_615;Local - Blend color
+HISTORY_MSG_616;Local - Blend Exp
+HISTORY_MSG_617;Local - Blur Exp
+HISTORY_MSG_618;Local - Use Color Mask
+HISTORY_MSG_619;Local - Use Exp Mask
+HISTORY_MSG_620;Local - Blur col
+HISTORY_MSG_621;Local - Exp inverse
+HISTORY_MSG_622;Local - Exclude structure
+HISTORY_MSG_623;Local - Exp Chroma compensation
+HISTORY_MSG_624;Local - Color correction grid
+HISTORY_MSG_625;Local - Color correction strength
+HISTORY_MSG_626;Local - Color correction Method
+HISTORY_MSG_627;Local - Shadow Highlight
+HISTORY_MSG_628;Local - SH Highlight
+HISTORY_MSG_629;Local - SH H tonalwidth
+HISTORY_MSG_630;Local - SH Shadows
+HISTORY_MSG_631;Local - SH S tonalwidth
+HISTORY_MSG_632;Local - SH radius
+HISTORY_MSG_633;Local - SH Scope
+HISTORY_MSG_634;Local - radius color
+HISTORY_MSG_635;Local - radius Exp
+HISTORY_MSG_636;Local - Tool added
+HISTORY_MSG_637;Local - SH Mask C
+HISTORY_MSG_638;Local - SH Mask L
+HISTORY_MSG_639;Local - SH Mask H
+HISTORY_MSG_640;Local - SH blend
+HISTORY_MSG_641;Local - Use SH mask
+HISTORY_MSG_642;Local - radius SH
+HISTORY_MSG_643;Local - Blur SH
+HISTORY_MSG_644;Local - inverse SH
+HISTORY_MSG_645;Local - balance ΔE ab-L
+HISTORY_MSG_646;Local - Exp mask chroma
+HISTORY_MSG_647;Local - Exp mask gamma
+HISTORY_MSG_648;Local - Exp mask slope
+HISTORY_MSG_649;Local - Exp soft radius
+HISTORY_MSG_650;Local - Color mask chroma
+HISTORY_MSG_651;Local - Color mask gamma
+HISTORY_MSG_652;Local - Color mask slope
+HISTORY_MSG_653;Local - SH mask chroma
+HISTORY_MSG_654;Local - SH mask gamma
+HISTORY_MSG_655;Local - SH mask slope
+HISTORY_MSG_656;Local - Color soft radius
+HISTORY_MSG_657;Local - Retinex Reduce artifacts
+HISTORY_MSG_658;Local - CBDL soft radius
+HISTORY_MSG_659;Local Spot transition-weakening
+HISTORY_MSG_660;Local - cbdl clarity
+HISTORY_MSG_661;Local - cbdl contrast residual
+HISTORY_MSG_662;Local - deNoise lum f 0
+HISTORY_MSG_663;Local - deNoise lum f 2
+HISTORY_MSG_664;Local - cbdl Blur
+HISTORY_MSG_665;Local - cbdl mask Blend
+HISTORY_MSG_666;Local - cbdl mask radius
+HISTORY_MSG_667;Local - cbdl mask chroma
+HISTORY_MSG_668;Local - cbdl mask gamma
+HISTORY_MSG_669;Local - cbdl mask slope
+HISTORY_MSG_670;Local - cbdl mask C
+HISTORY_MSG_671;Local - cbdl mask L
+HISTORY_MSG_672;Local - cbdl mask CL
+HISTORY_MSG_673;Local - Use cbdl mask
+HISTORY_MSG_674;Local - Tool removed
+HISTORY_MSG_675;Local - TM soft radius
+HISTORY_MSG_676;Local Spot transition-differentiation
+HISTORY_MSG_677;Local - TM amount
+HISTORY_MSG_678;Local - TM saturation
+HISTORY_MSG_679;Local - Retinex mask C
+HISTORY_MSG_680;Local - Retinex mask L
+HISTORY_MSG_681;Local - Retinex mask CL
+HISTORY_MSG_682;Local - Retinex mask
+HISTORY_MSG_683;Local - Retinex mask Blend
+HISTORY_MSG_684;Local - Retinex mask radius
+HISTORY_MSG_685;Local - Retinex mask chroma
+HISTORY_MSG_686;Local - Retinex mask gamma
+HISTORY_MSG_687;Local - Retinex mask slope
+HISTORY_MSG_688;Local - Tool removed
+HISTORY_MSG_689;Local - Retinex mask transmission map
+HISTORY_MSG_690;Local - Retinex scale
+HISTORY_MSG_691;Local - Retinex darkness
+HISTORY_MSG_692;Local - Retinex lightness
+HISTORY_MSG_693;Local - Retinex threshold
+HISTORY_MSG_694;Local - Retinex Laplacian threshold
+HISTORY_MSG_695;Local - Soft method
+HISTORY_MSG_696;Local - Retinex Normalize
+HISTORY_MSG_697;Local - TM Normalize
+HISTORY_MSG_698;Local - Local contrast Fast Fourier
+HISTORY_MSG_699;Local - Retinex Fast Fourier
+HISTORY_MSG_701;Local - Exp Shadows
+HISTORY_MSG_702;Local - Exp Method
+HISTORY_MSG_703;Local - Exp Laplacian threshold
+HISTORY_MSG_704;Local - Exp PDE balance
+HISTORY_MSG_705;Local - Exp linearity
+HISTORY_MSG_706;Local - TM mask C
+HISTORY_MSG_707;Local - TM mask L
+HISTORY_MSG_708;Local - TM mask CL
+HISTORY_MSG_709;Local - use TM mask
+HISTORY_MSG_710;Local - TM mask Blend
+HISTORY_MSG_711;Local - TM mask radius
+HISTORY_MSG_712;Local - TM mask chroma
+HISTORY_MSG_713;Local - TM mask gamma
+HISTORY_MSG_714;Local - TM mask slope
+HISTORY_MSG_716;Local - Local method
+HISTORY_MSG_717;Local - Local contrast
+HISTORY_MSG_718;Local - Local contrast levels
+HISTORY_MSG_719;Local - Local contrast residual L
+HISTORY_MSG_720;Local - Blur mask C
+HISTORY_MSG_721;Local - Blur mask L
+HISTORY_MSG_722;Local - Blur mask CL
+HISTORY_MSG_723;Local - use Blur mask
+HISTORY_MSG_725;Local - Blur mask Blend
+HISTORY_MSG_726;Local - Blur mask radius
+HISTORY_MSG_727;Local - Blur mask chroma
+HISTORY_MSG_728;Local - Blur mask gamma
+HISTORY_MSG_729;Local - Blur mask slope
+HISTORY_MSG_730;Local - Blur method
+HISTORY_MSG_731;Local - median method
+HISTORY_MSG_732;Local - median iterations
+HISTORY_MSG_733;Local - soft radius
+HISTORY_MSG_734;Local - detail
+HISTORY_MSG_738;Local - Local contrast Merge L
+HISTORY_MSG_739;Local - Local contrast Soft radius
+HISTORY_MSG_740;Local - Local contrast Merge C
+HISTORY_MSG_741;Local - Local contrast Residual C
+HISTORY_MSG_742;Local - Exp Laplacian gamma
+HISTORY_MSG_743;Local - Exp Fattal Amount
+HISTORY_MSG_744;Local - Exp Fattal Detail
+HISTORY_MSG_745;Local - Exp Fattal Offset
+HISTORY_MSG_746;Local - Exp Fattal Sigma
+HISTORY_MSG_747;Local Spot created
+HISTORY_MSG_748;Local - Exp Denoise
+HISTORY_MSG_749;Local - Reti Depth
+HISTORY_MSG_750;Local - Reti Mode log - lin
+HISTORY_MSG_751;Local - Reti Dehaze luminance
+HISTORY_MSG_752;Local - Reti Offset
+HISTORY_MSG_753;Local - Reti Transmission map
+HISTORY_MSG_754;Local - Reti Clip
+HISTORY_MSG_755;Local - TM use tm mask
+HISTORY_MSG_756;Local - Exp use algo exposure mask
+HISTORY_MSG_757;Local - Exp Laplacian mask
+HISTORY_MSG_758;Local - Reti Laplacian mask
+HISTORY_MSG_759;Local - Exp Laplacian mask
+HISTORY_MSG_760;Local - Color Laplacian mask
+HISTORY_MSG_761;Local - SH Laplacian mask
+HISTORY_MSG_762;Local - cbdl Laplacian mask
+HISTORY_MSG_763;Local - Blur Laplacian mask
+HISTORY_MSG_764;Local - Solve PDE Laplacian mask
+HISTORY_MSG_765;Local - deNoise Detail threshold
+HISTORY_MSG_766;Local - Blur Fast Fourier
+HISTORY_MSG_767;Local - Grain Iso
+HISTORY_MSG_768;Local - Grain Strength
+HISTORY_MSG_769;Local - Grain Scale
+HISTORY_MSG_770;Local - Color Mask contrast curve
+HISTORY_MSG_771;Local - Exp Mask contrast curve
+HISTORY_MSG_772;Local - SH Mask contrast curve
+HISTORY_MSG_773;Local - TM Mask contrast curve
+HISTORY_MSG_774;Local - Reti Mask contrast curve
+HISTORY_MSG_775;Local - CBDL Mask contrast curve
+HISTORY_MSG_776;Local - Blur Denoise Mask contrast curve
+HISTORY_MSG_777;Local - Blur Mask local contrast curve
+HISTORY_MSG_778;Local - Mask highlights
+HISTORY_MSG_779;Local - Color Mask local contrast curve
+HISTORY_MSG_780;Local - Color Mask shadows
+HISTORY_MSG_781;Local - Contrast Mask Wavelet level
+HISTORY_MSG_782;Local - Blur Denoise Mask Wavelet levels
+HISTORY_MSG_783;Local - Color Wavelet levels
+HISTORY_MSG_784;Local - Mask ΔE
+HISTORY_MSG_785;Local - Mask Scope ΔE
+HISTORY_MSG_786;Local - SH method
+HISTORY_MSG_787;Local - Equalizer multiplier
+HISTORY_MSG_788;Local - Equalizer detail
+HISTORY_MSG_789;Local - SH mask amount
+HISTORY_MSG_790;Local - SH mask anchor
+HISTORY_MSG_791;Local - Mask Short L curves
+HISTORY_MSG_792;Local - Mask Luminance Background
+HISTORY_MSG_793;Local - SH TRC gamma
+HISTORY_MSG_794;Local - SH TRC slope
+HISTORY_MSG_795;Local - Mask save restore image
+HISTORY_MSG_796;Local - Recursive references
+HISTORY_MSG_797;Local - Merge Original method
+HISTORY_MSG_798;Local - Opacity
+HISTORY_MSG_799;Local - Color RGB ToneCurve
+HISTORY_MSG_800;Local - Color ToneCurve Method
+HISTORY_MSG_801;Local - Color ToneCurve Special
+HISTORY_MSG_802;Local - Contrast threshold
+HISTORY_MSG_803;Local - Color Merge
+HISTORY_MSG_804;Local - Color mask Structure
+HISTORY_MSG_805;Local - Blur Noise mask Structure
+HISTORY_MSG_806;Local - Color mask Structure as tool
+HISTORY_MSG_807;Local - Blur Noise mask Structure as tool
+HISTORY_MSG_808;Local - Color mask curve H(H)
+HISTORY_MSG_809;Local - Vib mask curve C(C)
+HISTORY_MSG_810;Local - Vib mask curve L(L)
+HISTORY_MSG_811;Local - Vib mask curve LC(H)
+HISTORY_MSG_813;Local - Use Vib mask
+HISTORY_MSG_814;Local - Vib mask Blend
+HISTORY_MSG_815;Local - Vib mask radius
+HISTORY_MSG_816;Local - Vib mask chroma
+HISTORY_MSG_817;Local - Vib mask gamma
+HISTORY_MSG_818;Local - Vib mask slope
+HISTORY_MSG_819;Local - Vib mask laplacian
+HISTORY_MSG_820;Local - Vib mask contrast curve
+HISTORY_MSG_821;Local - color grid background
+HISTORY_MSG_822;Local - color background merge
+HISTORY_MSG_823;Local - color background luminance
+HISTORY_MSG_824;Local - Exp gradient mask strength
+HISTORY_MSG_825;Local - Exp gradient mask angle
+HISTORY_MSG_826;Local - Exp gradient strength
+HISTORY_MSG_827;Local - Exp gradient angle
+HISTORY_MSG_828;Local - SH gradient strength
+HISTORY_MSG_829;Local - SH gradient angle
+HISTORY_MSG_830;Local - Color gradient strength L
+HISTORY_MSG_831;Local - Color gradient angle
+HISTORY_MSG_832;Local - Color gradient strength C
+HISTORY_MSG_833;Local - Gradient feather
+HISTORY_MSG_834;Local - Color gradient strength H
+HISTORY_MSG_835;Local - Vib gradient strength L
+HISTORY_MSG_836;Local - Vib gradient angle
+HISTORY_MSG_837;Local - Vib gradient strength C
+HISTORY_MSG_838;Local - Vib gradient strength H
+HISTORY_MSG_839;Local - Software complexity
+HISTORY_MSG_840;Local - CL Curve
+HISTORY_MSG_841;Local - LC curve
+HISTORY_MSG_842;Local - Contrast Threshold Mask blur
+HISTORY_MSG_843;Local - Radius Mask blur
+HISTORY_MSG_844;Local - Color Mask fftw
+HISTORY_MSG_845;Local - Encoding log
+HISTORY_MSG_846;Local - Encoding auto
+HISTORY_MSG_847;Local - Source Gray
+HISTORY_MSG_848;Local - Source Gray auto
+HISTORY_MSG_849;Local - Auto Grayon
+HISTORY_MSG_850;Local - Black Ev
+HISTORY_MSG_851;Local - White Ev
+HISTORY_MSG_852;Local - Target Gray
+HISTORY_MSG_853;Local - Local contrast
+HISTORY_MSG_854;Local - Scope encoding log
+HISTORY_MSG_855;Local - Entire image
+HISTORY_MSG_856;Local - Base log
+HISTORY_MSG_857;Local - Contrast Blur Residual
+HISTORY_MSG_858;Local - Contrast Luminance only
+HISTORY_MSG_859;Local - Contrast Maximum Blur levels
+HISTORY_MSG_860;Local - Contrast Curve Blur levels
+HISTORY_MSG_861;Local - Contrast Curve Contrast levels
+HISTORY_MSG_862;Local - Contrast Sigma luminance
+HISTORY_MSG_863;Local - Contrast Merge Original
+HISTORY_MSG_864;Local - Directional sigma
+HISTORY_MSG_865;Local - Directional delta
+HISTORY_MSG_866;Local - Contrast Curve Compression
+HISTORY_MSG_867;Local - Contrast Amount residual
+HISTORY_MSG_868;Local - balance ΔE C-H
+HISTORY_MSG_869;Local - denoise curve luminance
+HISTORY_MSG_870;Local - LC mask curve LC(H)
+HISTORY_MSG_871;Local - LC mask curve C(C)
+HISTORY_MSG_872;Local - LC mask curve L(L)
+HISTORY_MSG_873;Local - LC mask enable
+HISTORY_MSG_875;Local - LC mask blend
+HISTORY_MSG_876;Local - LC mask radius
+HISTORY_MSG_877;Local - LC mask chroma
+HISTORY_MSG_878;Local - LC mask curve contrast
+HISTORY_MSG_879;Local - LC Chroma levels
+HISTORY_MSG_880;Local - LC Chroma blur levels
+HISTORY_MSG_881;Local - Contrast Offset Luminance
+HISTORY_MSG_882;Local - Contrast Blur
+HISTORY_MSG_883;Local - Contrast By Levels
+HISTORY_MSG_884;Local - Contrast Dynamic Range Laplacian
+HISTORY_MSG_885;Local - Contrast Dynamic Range Wavelet
+HISTORY_MSG_886;Local - Contrast Wavelet Curve Compression
+HISTORY_MSG_887;Local - Contrast Wavelet Compression Residual
+HISTORY_MSG_888;Local - Contrast Wavelet Balance Threshold
+HISTORY_MSG_889;Local - Contrast Wavelet Graduated Strength
+HISTORY_MSG_890;Local - Contrast Wavelet Graduated angle
+HISTORY_MSG_891;Local - Contrast Wavelet Graduated
+HISTORY_MSG_892;Local - Encoding log Graduated Strength
+HISTORY_MSG_893;Local - Encoding log Graduated angle
+HISTORY_MSG_894;Local - Color Preview dE
+HISTORY_MSG_897;Local - Contrast Wavelet ES strength
+HISTORY_MSG_898;Local - Contrast Wavelet ES radius
+HISTORY_MSG_899;Local - Contrast Wavelet ES detail
+HISTORY_MSG_900;Local - Contrast Wavelet ES gradient
+HISTORY_MSG_901;Local - Contrast Wavelet ES threshold low
+HISTORY_MSG_902;Local - Contrast Wavelet ES threshold high
+HISTORY_MSG_903;Local - Contrast Wavelet ES local contrast
+HISTORY_MSG_904;Local - Contrast Wavelet ES first level
+HISTORY_MSG_905;Local - Contrast Wavelet Edge Sharpness
+HISTORY_MSG_906;Local - Contrast Wavelet ES sensitivity
+HISTORY_MSG_907;Local - Contrast Wavelet ES amplification
+HISTORY_MSG_908;Local - Contrast Wavelet ES neighboring
+HISTORY_MSG_909;Local - Contrast Wavelet ES show
+HISTORY_MSG_910;Local - Wavelet Edge performance
+HISTORY_MSG_911;Local - Blur Chroma Luma
+HISTORY_MSG_912;Local - Blur Guide filter strength
+HISTORY_MSG_913;Local - Contrast Wavelet Sigma DR
+HISTORY_MSG_914;Local - Blur Wavelet Sigma BL
+HISTORY_MSG_915;Local - Edge Wavelet Sigma ED
+HISTORY_MSG_916;Local - Residual wavelet shadows
+HISTORY_MSG_917;Local - Residual wavelet shadows threshold
+HISTORY_MSG_918;Local - Residual wavelet highlights
+HISTORY_MSG_919;Local - Residual wavelet highlights threshold
+HISTORY_MSG_920;Local - Wavelet sigma LC
+HISTORY_MSG_921;Local - Wavelet Graduated sigma LC2
+HISTORY_MSG_922;Local - changes In Black and White
+HISTORY_MSG_923;Local - Tool complexity mode
+HISTORY_MSG_924;Local - Tool complexity mode
+HISTORY_MSG_925;Local - Scope color tools
+HISTORY_MSG_926;Local - Show mask type
+HISTORY_MSG_927;Local - Shadow mask
HISTORY_MSG_CAT02PRESET;Cat02 automatic preset
HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors
HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction
@@ -776,6 +1213,13 @@ HISTORY_MSG_PDSHARPEN_CONTRAST;CS - Contrast threshold
HISTORY_MSG_PDSHARPEN_ITERATIONS;CS - Iterations
HISTORY_MSG_PDSHARPEN_RADIUS;CS - Radius
HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;CS - Corner radius boost
+HISTORY_MSG_PERSP_CAM_ANGLE;Perspective - Camera
+HISTORY_MSG_PERSP_CAM_FL;Perspective - Camera
+HISTORY_MSG_PERSP_CAM_SHIFT;Perspective - Camera
+HISTORY_MSG_PERSP_METHOD;Perspective - Method
+HISTORY_MSG_PERSP_PROJ_ANGLE;Perspective - Recovery
+HISTORY_MSG_PERSP_PROJ_ROTATE;Perspective - PCA rotation
+HISTORY_MSG_PERSP_PROJ_SHIFT;Perspective - PCA
HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Demosaic method for motion
HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;Line noise filter direction
HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAF lines filter
@@ -804,12 +1248,12 @@ HISTORY_MSG_WAVMERGEL;Merge L
HISTORY_MSG_WAVRADIUS;Radius Shadows-Highlight
HISTORY_MSG_WAVSCALE;Scale
HISTORY_MSG_WAVSHOWMASK;Show wavelet mask
-HISTORY_MSG_WAVSIGMA;Damper
+HISTORY_MSG_WAVSIGMA;Attenuation Response
HISTORY_MSG_WAVSOFTRAD;Soft radius clarity
HISTORY_MSG_WAVSOFTRADEND;Soft radius final
HISTORY_MSG_WAVUSHAMET;Clarity method
HISTORY_MSG_THRESWAV;Balance threshold
-HISTORY_MSG_BLUWAV;Damper
+HISTORY_MSG_BLUWAV;Attenuation Response
HISTORY_MSG_WAVOLDSH;Old algorithm
HISTORY_MSG_WAVOFFSET;Offset
HISTORY_MSG_WAVLOWTHR;Threshold low contrast
@@ -817,11 +1261,11 @@ HISTORY_MSG_BLSHAPE;Blur by level
HISTORY_MSG_WAVBL;Blur levels
HISTORY_MSG_BLURWAV;Blur luminance
HISTORY_MSG_BLURCWAV;Blur chroma
-HISTORY_MSG_EDGEFFECT;Edge Damper
-HISTORY_MSG_SIGMAFIN;Final contrast Damper
-HISTORY_MSG_SIGMATON;Toning Damper
-HISTORY_MSG_SIGMACOL;Chroma Damper
-HISTORY_MSG_SIGMADIR;Dir Damper
+HISTORY_MSG_EDGEFFECT;Edge Attenuation Response
+HISTORY_MSG_SIGMAFIN;Final contrast Attenuation Response
+HISTORY_MSG_SIGMATON;Toning Attenuation Response
+HISTORY_MSG_SIGMACOL;Chroma Attenuation Response
+HISTORY_MSG_SIGMADIR;Dir Attenuation Response
HISTORY_MSG_RANGEAB;Range ab
HISTORY_MSG_PROTAB;Protection
HISTORY_NEWSNAPSHOT;Add
@@ -963,6 +1407,8 @@ MAIN_TAB_FAVORITES_TOOLTIP;Shortcut: Alt-u
MAIN_TAB_FILTER; Filter
MAIN_TAB_INSPECT; Inspect
MAIN_TAB_IPTC;IPTC
+MAIN_TAB_LOCALLAB;Local
+MAIN_TAB_LOCALLAB_TOOLTIP;Shortcut: Alt-o
MAIN_TAB_METADATA;Metadata
MAIN_TAB_METADATA_TOOLTIP;Shortcut: Alt-m
MAIN_TAB_RAW;Raw
@@ -1005,6 +1451,7 @@ NAVIGATOR_XY_NA;x: --, y: --
OPTIONS_BUNDLED_MISSING;The bundled profile "%1" could not be found!\n\nYour installation could be damaged.\n\nDefault internal values will be used instead.
OPTIONS_DEFIMG_MISSING;The default profile for non-raw photos could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\n"%1" will be used instead.
OPTIONS_DEFRAW_MISSING;The default profile for raw photos could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\n"%1" will be used instead.
+PARTIALPASTE_LOCALLABGROUP;Local Adjustments Settings
PARTIALPASTE_ADVANCEDGROUP;Advanced Settings
PARTIALPASTE_BASICGROUP;Basic Settings
PARTIALPASTE_CACORRECTION;Chromatic aberration correction
@@ -1045,6 +1492,8 @@ PARTIALPASTE_IMPULSEDENOISE;Impulse noise reduction
PARTIALPASTE_IPTCINFO;IPTC
PARTIALPASTE_LABCURVE;L*a*b* adjustments
PARTIALPASTE_LENSGROUP;Lens Related Settings
+PARTIALPASTE_LOCALLAB;Local Adjustments
+PARTIALPASTE_LOCGROUP;Local
PARTIALPASTE_LENSPROFILE;Profiled lens correction
PARTIALPASTE_LOCALCONTRAST;Local contrast
PARTIALPASTE_METADATA;Metadata mode
@@ -1121,6 +1570,9 @@ PREFERENCES_CLUTSCACHE;HaldCLUT Cache
PREFERENCES_CLUTSCACHE_LABEL;Maximum number of cached CLUTs
PREFERENCES_CLUTSDIR;HaldCLUT directory
PREFERENCES_CMMBPC;Black point compensation
+PREFERENCES_COMPLEXITYLOC;Default complexity for Local Adjustments
+PREFERENCES_COMPLEXITY_EXP;Expert
+PREFERENCES_COMPLEXITY_NORM;Normal
PREFERENCES_CROP;Crop Editing
PREFERENCES_CROP_AUTO_FIT;Automatically zoom to fit the crop
PREFERENCES_CROP_GUIDES;Guides shown when not editing the crop
@@ -1235,8 +1687,9 @@ PREFERENCES_SERIALIZE_TIFF_READ_TOOLTIP;Enabling this option when working with f
PREFERENCES_SET;Set
PREFERENCES_SHOWBASICEXIF;Show basic Exif info
PREFERENCES_SHOWDATETIME;Show date and time
-PREFERENCES_SHOWEXPOSURECOMPENSATION;Append exposure compensation
PREFERENCES_SHOWFILMSTRIPTOOLBAR;Show Filmstrip toolbar
+PREFERENCES_SHOWEXPOSURECOMPENSATION;Append exposure compensation
+PREFERENCES_SHOWTOOLTIP;Show Local Adjustments advice tooltips
PREFERENCES_SHTHRESHOLD;Threshold for clipped shadows
PREFERENCES_SINGLETAB;Single Editor Tab Mode
PREFERENCES_SINGLETABVERTAB;Single Editor Tab Mode, Vertical Tabs
@@ -1503,7 +1956,7 @@ TP_COLORAPP_TCMODE_LIGHTNESS;Lightness
TP_COLORAPP_TCMODE_SATUR;Saturation
TP_COLORAPP_TEMP_TOOLTIP;To select an illuminant, always set Tint=1.\n\nA temp=2856\nD41 temp=4100\nD50 temp=5003\nD55 temp=5503\nD60 temp=6000\nD65 temp=6504\nD75 temp=7504
TP_COLORAPP_TEMP2_TOOLTIP;Either symmetrical mode temp = White balance.\nEither select illuminant always set Tint=1.\n\nA temp=2856\nD41 temp=4100\nD50 temp=5003\nD55 temp=5503\nD60 temp=6000\nD65 temp=6504\nD75 temp=7504
-TP_COLORAPP_TEMPOUT_TOOLTIP;Disable to chnage temperature and tint
+TP_COLORAPP_TEMPOUT_TOOLTIP;Disable to change temperature and tint
TP_COLORAPP_TONECIE;Tone mapping using CIECAM02
TP_COLORAPP_TONECIE_TOOLTIP;If this option is disabled, tone mapping is done in L*a*b* space.\nIf this option is enabled, tone mapping is done using CIECAM02.\nThe Tone Mapping tool must be enabled for this setting to take effect.
TP_COLORAPP_VIEWING_ABSOLUTELUMINANCE_TOOLTIP;Absolute luminance of the viewing environment\n(usually 16 cd/m²).
@@ -1842,6 +2295,562 @@ TP_LENSPROFILE_USE_CA;Chromatic aberration
TP_LENSPROFILE_USE_GEOMETRIC;Geometric distortion
TP_LENSPROFILE_USE_HEADER;Correct
TP_LENSPROFILE_USE_VIGNETTING;Vignetting
+TP_LOCALLAB_ACTIV;Luminance only
+TP_LOCALLAB_ADJ;Equalizer Blue-yellow Red-green
+TP_LOCALLAB_ALL;All rubrics
+TP_LOCALLAB_AMOUNT;Amount
+TP_LOCALLAB_ARTIF;Shape detection
+TP_LOCALLAB_ARTIF_TOOLTIP;Threshold deltaE-scope increase the range of scope-deltaE - high values are for very wide gamut images.\nIncrease deltaE Weakening improve shape detection, but can reduce the scope of detection.
+TP_LOCALLAB_AUTOGRAY;Automatic
+TP_LOCALLAB_AVOID;Avoid color shift
+TP_LOCALLAB_BALAN;Balance ΔE ab-L
+TP_LOCALLAB_BALANH;Balance ΔE C-H
+TP_LOCALLAB_BALAN_TOOLTIP;Change algorithm ΔE parameter.\nMore or less ab-L, more or less C - H.\nNot for Denoise
+TP_LOCALLAB_BALANEXP;ΔØ PDE balance
+TP_LOCALLAB_BASELOG;Logarithm base
+TP_LOCALLAB_BILATERAL;Bilateral filter
+TP_LOCALLAB_BLACK_EV;Black Ev
+TP_LOCALLAB_BLENDMASKCOL;Blend
+TP_LOCALLAB_BLENDMASK_TOOLTIP;If blend = 0 only shape detection is improved.\nIf blend > 0 the mask is added to the image. If blend < 0 the mask is subtracted from the image
+TP_LOCALLAB_BLGUID;Guided Filter
+TP_LOCALLAB_BLINV;Inverse
+TP_LOCALLAB_BLCO;Chrominance only
+TP_LOCALLAB_BLLO;Luminance only
+TP_LOCALLAB_BLLC;Luminance & Chrominance
+TP_LOCALLAB_BLMED;Median
+TP_LOCALLAB_BLMETHOD_TOOLTIP;Normal - direct blur and noise with all settings.\nInverse blur and noise with all settings. Be careful some results may be curious
+TP_LOCALLAB_BLNORM;Normal
+TP_LOCALLAB_BLSYM;Symmetric
+TP_LOCALLAB_BLWH;All changes forced in Black and White
+TP_LOCALLAB_BLWH_TOOLTIP;Force color change composante "a" and "b" to zero.\nUsefull when the user chooses black and white processes, or film.
+TP_LOCALLAB_SPOTNAME;New Spot
+TP_LOCALLAB_BLUFR;Smooth - Blur - Grain - Denoise
+TP_LOCALLAB_BLUMETHOD_TOOLTIP;To blur the background and isolate the foreground:\n*Blur the background by a RT-spot fully covering the image (high values for scope and transition) - normal or inverse.\n*Isolate the foreground by one or more excluding RT-spot with the tools you want (increse scope).\n\nThis module can be used in additional noise reduction,including "median" and "Guided filter"
+TP_LOCALLAB_BLURMASK_TOOLTIP;Generate a blur mask, take into account the structure with the contrast threshold Mask Blur slider.
+TP_LOCALLAB_BLUR;Gaussian Blur - Noise - Grain
+TP_LOCALLAB_BLURCBDL;Blur levels 0-1-2-3-4
+TP_LOCALLAB_BLURCOL;Radius Mask Blur
+TP_LOCALLAB_BLURDE;Blur Shape detection
+TP_LOCALLAB_BLURLC;Luminance Only
+TP_LOCALLAB_BLURLEVELFRA;Blur Levels
+TP_LOCALLAB_BLURRESIDFRA;Blur Residual
+TP_LOCALLAB_BUTTON_ADD;Add
+TP_LOCALLAB_BUTTON_DEL;Delete
+TP_LOCALLAB_BUTTON_DUPL;Duplicate
+TP_LOCALLAB_BUTTON_REN;Rename
+TP_LOCALLAB_BUTTON_VIS;Show/Hide
+TP_LOCALLAB_CBDL;Contrast by detail levels - Defects
+TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Acts as a wavelet tools.\nThe first level (0) acts on 2x2 details.\nThe last level (5) acts on 64x64 details.
+TP_LOCALLAB_CBDLCLARI_TOOLTIP;Takes the midtones and enhance them.
+TP_LOCALLAB_CBDL_THRES_TOOLTIP;Prevent the sharpening of noise
+TP_LOCALLAB_CENTER_X;Center X
+TP_LOCALLAB_CENTER_Y;Center Y
+TP_LOCALLAB_CH;Curves CL - LC
+TP_LOCALLAB_CHROMA;Chrominance
+TP_LOCALLAB_CHROMACBDL;Chroma
+TP_LOCALLAB_CHROMACB_TOOLTIP;Acts as an amplifier-reducer action compare to sliders of luminance.\nUnder 100 reduce, above 100 amplifie
+TP_LOCALLAB_CHROMALEV;Chroma levels
+TP_LOCALLAB_CHROMABLU;Chroma levels
+TP_LOCALLAB_CHROMABLU_TOOLTIP;Acts as an amplifier-reducer action compare to settings of luma.\nUnder 1 reduce, above 1 amplifie
+TP_LOCALLAB_CHROMASKCOL;Chroma mask
+TP_LOCALLAB_CHROMASK_TOOLTIP;You can use this slider to desaturated background (inverse mask - curve near 0).\nAlso to attenuate or enhance the action of a mask on the chroma
+TP_LOCALLAB_CHRRT;Chroma
+TP_LOCALLAB_CIRCRADIUS;Spot size
+TP_LOCALLAB_CIRCRAD_TOOLTIP;Contains the references of RT-spot, useful for shape detection (hue, luma, chroma, Sobel).\nLow values may be useful for treating foliage.\nHigh values may be useful for treating skin
+TP_LOCALLAB_CLARICRES;Merge Chroma
+TP_LOCALLAB_CLARIFRA;Clarity & Sharp mask - Blend & Soft images
+TP_LOCALLAB_CLARILRES;Merge Luma
+TP_LOCALLAB_CLARISOFT;Soft radius
+TP_LOCALLAB_CLARISOFT_TOOLTIP;Enabled for Clarity and Sharp mask if Merge Luma different from zero.\n\nEnabled for all wavelets pyramid modules.\nDisabled if Soft radius = 0
+TP_LOCALLAB_CLARITYML;Clarity
+TP_LOCALLAB_CLARI_TOOLTIP;Under or equal level wavelet 4, 'Sharp mask' is enabled.\nAbove level wavelet 5 'Clarity' is enabled.\nUsefull if you use 'Level dynamic Range Compression'
+TP_LOCALLAB_CLIPTM;Clip Restored datas (gain)
+TP_LOCALLAB_COFR;Color & Light - Small defects
+TP_LOCALLAB_COL_NAME;Name
+TP_LOCALLAB_COL_VIS;Status
+TP_LOCALLAB_COLORDE;Color preview selection ΔE - Intensity
+TP_LOCALLAB_COLORDE_TOOLTIP;Show preview selection ΔE in blue if negative and in green if positive.\n\nMask and modifications (show modifications without mask): show real modifications if positive, show enhanced modifications (only luminance) with blue and yellow if negative.
+TP_LOCALLAB_COLORDEPREV_TOOLTIP;Button Preview ΔE needs that only one tool is enabled (expander).\nTo be able to have an Preview ΔE with several enable tools use Mask and modifications - Preview ΔE
+TP_LOCALLAB_COLORSCOPE;Scope Color Tools
+TP_LOCALLAB_COLORSCOPE_TOOLTIP;Use a common Scope for Color and light, Exposure (Standard), Shadows highlight, Vibrance.\nOthers tools have their specific scope.
+TP_LOCALLAB_COMPFRA;Levels Directional Contrast
+TP_LOCALLAB_COMPREFRA;Levels Dynamic Wavelet Range (un)Compression
+TP_LOCALLAB_COMPRESS_TOOLTIP;Use if necessary the module 'Clarity & Sharp mask and Blend & Soft Images' by adjusting 'Soft radius' to reduce artifacts.
+TP_LOCALLAB_COMPFRAME_TOOLTIP;Allows special effects. You can reduce artifacts with 'Clarity & Sharp mask - Blend & Soft Images".\nUses a lot of resources
+TP_LOCALLAB_COMPLEX_METHOD;Software Complexity
+TP_LOCALLAB_COMPLEX_TOOLTIP; Allow user to select Local adjustments rubrics.
+TP_LOCALLAB_CONTCOL;Contrast threshold Mask Blur
+TP_LOCALLAB_CONTFRA;Contrast by Level
+TP_LOCALLAB_CONTRAST;Contrast
+TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP;Main mask contrast control.
+TP_LOCALLAB_CONTRESID;Contrast
+TP_LOCALLAB_CONTTHR;Contrast Threshold
+TP_LOCALLAB_CSTHRESHOLD;Ψ Wavelets Levels
+TP_LOCALLAB_CSTHRESHOLDBLUR;Ψ Mask Wavelet level
+TP_LOCALLAB_CURV;Lightness - Contrast - Chrominance "Super"
+TP_LOCALLAB_CURVCURR;Normal
+TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP;If curves at the top, mask is completely black no transformation is made by the mask on the image.\nAs you go down the curve, the mask gradually more colorful and brilliant, the image is changing more and more.\n\nIt is recommended (not required) to position the top of the curves on the gray transition line which represents the references (chroma, luma, hue).
+TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP;To be active, you must enable combobox 'Curves type'
+TP_LOCALLAB_CURVEEDITOR_TONES_LABEL;Tone curve
+TP_LOCALLAB_CURVEEDITOR_TONES_TOOLTIP;L=f(L), can be used with L(H) in Color and Light
+TP_LOCALLAB_CURVEMETHOD_TOOLTIP;'Normal', the curve L=f(L) has the same algorithm than slider lightness.\n'Super' the curve L=f(L) has an new improved algorithm, which can leeds in some cases to artifacts.
+TP_LOCALLAB_CURVENCONTRAST;Super+Contrast threshold (experimental)
+TP_LOCALLAB_CURVENH;Super
+TP_LOCALLAB_CURVENHSU;Combined HueChroma (experimental)
+TP_LOCALLAB_CURVENSOB2;Combined HueChroma + Contrast threshold (experimental)
+TP_LOCALLAB_CURVNONE;Disable curves
+TP_LOCALLAB_DARKRETI;Darkness
+TP_LOCALLAB_DEHAFRA;Dehaze
+TP_LOCALLAB_DEHAZ;Strength
+TP_LOCALLAB_DEHAZ_TOOLTIP;Negative values adds haze
+TP_LOCALLAB_DELTAD;Delta balance
+TP_LOCALLAB_DELTAEC;Mask ΔE Image
+TP_LOCALLAB_DENOIS;Ψ Denoise
+TP_LOCALLAB_DENOI_TOOLTIP;This module can be used alone (at the end of process), or in complement of main denoise (at the beginning).\nScope allows to differentiate the action according to the color (deltaE).\nYou can complete the action with "median" or "Guided Filter" (Smooth Blur...).\nYou can complete the action with "Blur levels" "Wavelet pyramid"
+TP_LOCALLAB_DEPTH;Depth
+TP_LOCALLAB_DETAIL;Local contrast
+TP_LOCALLAB_DETAILSH;Details
+TP_LOCALLAB_DETAILTHR;Detail threshold Luminance Chroma (DCT ƒ)
+TP_LOCALLAB_DUPLSPOTNAME;Copy
+TP_LOCALLAB_EDGFRA;Edge Sharpness
+TP_LOCALLAB_EDGSHOW;Show all tools
+TP_LOCALLAB_ELI;Elipse
+TP_LOCALLAB_ENABLE_AFTER_MASK;Use Tone Mapping
+TP_LOCALLAB_ENABLE_MASK;Enable mask
+TP_LOCALLAB_ENABLE_MASKAFT;Use all algorithms Exposure
+TP_LOCALLAB_ENARETIMASKTMAP_TOOLTIP;If enabled Mask uses Restored Datas after Transmission Map instead of Original datas
+TP_LOCALLAB_ENH;Enhanced
+TP_LOCALLAB_ENHDEN;Enhanced + chroma denoise
+TP_LOCALLAB_EPSBL;Detail
+TP_LOCALLAB_EQUIL;Normalize Luminance
+TP_LOCALLAB_EQUILTM_TOOLTIP;Reconstruct luminance in such a way that the mean and the variance of the output image is identical to that of the original.
+TP_LOCALLAB_ESTOP;Edge stopping
+TP_LOCALLAB_EV_DUPL;Copy of
+TP_LOCALLAB_EV_NVIS;Hide
+TP_LOCALLAB_EV_NVIS_ALL;Hide all
+TP_LOCALLAB_EV_VIS;Show
+TP_LOCALLAB_EV_VIS_ALL;Show all
+TP_LOCALLAB_EXCLUF;Excluding
+TP_LOCALLAB_EXCLUF_TOOLTIP;Can be used to exclude this part of datas - move Scope to extend color.\n You can apply all settings to this RT-spot.
+TP_LOCALLAB_EXCLUTYPE;Spot method
+TP_LOCALLAB_EXCLUTYPE_TOOLTIP;Normal spot use recursive data.\n\nExcluding spot reinitialize data to origin.\nCan be used to totally or partially cancel a previous action or to perform a inverse mode
+TP_LOCALLAB_EXECLU;Excluding spot
+TP_LOCALLAB_EXNORM;Normal spot
+TP_LOCALLAB_EXPCBDL_TOOLTIP;In the case of contaminated sensor (type "grease"), and when the area is important or for a series of small defects.\n\na) Put the selection spot on a pronounced default (adapting its size if necessary), use a large spot enough to allow wavelet; b) choose a wide selection area to cover most of the area affected by the defects; c) Select a transition value (low) and transition weakening (high value); d) act on levels 2, 3, 4 or 5 or lower by reducing the contrast (values below 100) and by acting on the chroma slider if necessary. e)possibly act on "scope" to reduce the extent of the action.\n\nYou can also complete with Blur levels and Gaussian blur (Smooth Blur and noise)
+TP_LOCALLAB_EXPCHROMA;Chroma compensation
+TP_LOCALLAB_EXPCHROMA_TOOLTIP;Only in association with exposure compensation and PDE Ipol.\nAvoids desaturation of colors
+TP_LOCALLAB_EXPCOLOR_TOOLTIP;In the case of small defects.\n\nRed-eyes : red-centered circular selector, spot delimiters close to the eye, weak scope, "lightness" -100, "chrominance" -100.\n\nSpotIR :Circular selector centered on the defect, spot delimiters close to the default - reduce "chrominance", possibly act on "scope" to reduce the extent of the action.\n\nDust - grease (small) :Circular selector centered on the defect (adapt the size of the spot), spot delimiters not too close to the defect to allow an inconspicuous transition. a) "Transition" (low values) and "Transition weak" (high values); b) act on "lightness" and possibly on "chrominance" or "Color correction grid - direct" to approach the rendering of the polluted zone to that of the healthy zone; c) act moderately on "scope" to modulate the desired action.\n\nYou can also complete with Gaussian blur (Smooth Blur and noise)
+TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Avoid spots that are too small(< 32x32 pixels).\nUse low transition values and high weakening transition values and scope to simulate small RT-spot and deal wth defects.\nUse if necessary the module 'Clarity & Sharp mask and Blend images' by adjusting 'Soft radius' to reduce artifacts.
+TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;See the documentation of wavelet levels.\nHowever there are some differences: more tools and closer to the details.\nEx: Tone mapping for wavelet levels.
+TP_LOCALLAB_EXPCURV;Curves
+TP_LOCALLAB_EXPGRAD;Graduated Filter
+TP_LOCALLAB_EXPLAP_TOOLTIP;The more you act on this threshold slider, the greater the action of reducing contrast.
+TP_LOCALLAB_EXPLAPBAL_TOOLTIP;Balances the action between the original image and the Laplace transform.
+TP_LOCALLAB_EXPLAPLIN_TOOLTIP;Add linear exposure component before application Laplace transform
+TP_LOCALLAB_EXPLAPGAMM_TOOLTIP;Apply a gamma before and after Laplace transform
+TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;Allows various possibilities to blend image (as layers in Photosshop) : difference, multiply, soft light, overlay...with opacity...\nOriginal Image : merge current RT-spot with Original.\nPrevious spot : merge current Rt-spot with previous - if there is only one spot previous = original.\nBackground : merge current RT-spot with a color and luminance background (less possibilties)
+TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Apply a median before Laplace transform to prevent artifacts (noise).\nYou can also use "Denoise" tool.
+TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : use an algorithm similar as main Exposure but in L*a*b* and taking account of deltaE.\n\nLaplacian & PDE : use another algorithm also with deltaE and with Poisson equation to solve Laplacian in Fourier space.\nPDE IPOL, PDE Fattal and Standard can be combined.\nFFTW Fourier Transform is optimized in size to reduce processing time.\nPDE reduce artifacts and noise.
+TP_LOCALLAB_EXPOSE;Exposure - PDE algorithms
+TP_LOCALLAB_EXPOSURE_TOOLTIP;In some cases (strong shadows ..) you can use others modules "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"...
+TP_LOCALLAB_EXPRETITOOLS;Advanced Retinex Tools
+TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUse low transition values and high weakening transition values and scope to simulate small RT-spot.
+TP_LOCALLAB_EXPTOOL;Tools exposure
+TP_LOCALLAB_EXPTRC;Tone Response Curve - TRC
+TP_LOCALLAB_FATAMOUNT;Amount
+TP_LOCALLAB_FATANCHOR;Anchor
+TP_LOCALLAB_FATANCHORA;Offset
+TP_LOCALLAB_FATDETAIL;Detail
+TP_LOCALLAB_FATFRA;Dynamic Range Compression ƒ
+TP_LOCALLAB_FATFRAME_TOOLTIP;PDE Fattal - use Fattal Tone mapping algorithm.
+TP_LOCALLAB_FATLEVEL;Sigma
+TP_LOCALLAB_FATRES;Amount Residual Image
+TP_LOCALLAB_FATSHFRA;Dynamic Range Compression Mask ƒ
+TP_LOCALLAB_FEATH_TOOLTIP;Gradient width in percent of the Spot diagonal\nUsed by all Graduated filter in all tools.\nNo action if Graduated Filter are not used.
+TP_LOCALLAB_FEATVALUE;Feather gradient (Graduated Filters)
+TP_LOCALLAB_FFTCOL_MASK;FFTW ƒ
+TP_LOCALLAB_FFTW;ƒ - Use Fast Fourier Transform
+TP_LOCALLAB_FFTW2;ƒ - Use Fast Fourier Transform (TIF, JPG,..)
+TP_LOCALLAB_FFTWBLUR;ƒ - Always Use Fast Fourier Transform
+TP_LOCALLAB_FULLIMAGE;Calculate DarkEv - WhiteEv - Value on the entire image
+TP_LOCALLAB_GAM;Gamma
+TP_LOCALLAB_GAMFRA;Tone response curve (TRC)
+TP_LOCALLAB_GAMM;Gamma
+TP_LOCALLAB_GAMMASKCOL;Gamma mask
+TP_LOCALLAB_GAMSH;Gamma
+TP_LOCALLAB_GRADANG;Gradient angle
+TP_LOCALLAB_GRADANG_TOOLTIP;Rotation angle in degrees : -180 0 +180
+TP_LOCALLAB_GRADFRA;Graduated Filter Mask
+TP_LOCALLAB_GRADGEN_TOOLTIP;Graduated filter is supplied with Color and Light & merge file, Exposure & mask, Shadows Highlight, Vibrance, Encoding log.\n\nVibrance, Color and Light & merge file, are provided with GF luminance, chrominance, Hue.\nFeather is located in settings.
+TP_LOCALLAB_GRADLOGFRA;Graduated Filter Luminance
+TP_LOCALLAB_GRADSTR;Gradient strength
+TP_LOCALLAB_GRADSTRAB_TOOLTIP;Filter chroma strength
+TP_LOCALLAB_GRADSTRCHRO;Gradient strength Chrominance
+TP_LOCALLAB_GRADSTRHUE2;Gradient strength Hue
+TP_LOCALLAB_GRADSTRHUE;Gradient strength Hue
+TP_LOCALLAB_GRADSTRHUE_TOOLTIP;Filter Hue strength
+TP_LOCALLAB_GRADSTRLUM;Gradient strength Luminance
+TP_LOCALLAB_GRADSTR_TOOLTIP;Filter strength in stops
+TP_LOCALLAB_GRAINFRA;Film Grain 1:1
+TP_LOCALLAB_GRALWFRA;Graduated Filter Local contrast
+TP_LOCALLAB_GRIDFRAME_TOOLTIP;You can use this tool as a brush. Use small spot and adapt transition and transition weakening\nOnly mode NORMAL and eventually Hue, Saturation, Color, Luminosity are concerned by Merge background (ΔE)
+TP_LOCALLAB_GRIDONE;Color Toning
+TP_LOCALLAB_GRIDTWO;Direct
+TP_LOCALLAB_GUIDBL;Soft radius
+TP_LOCALLAB_GUIDFILTER;Guided filter radius
+TP_LOCALLAB_GUIDFILTER_TOOLTIP;Adapt this values according to images - can reduce or increase artifacts.
+TP_LOCALLAB_HIGHMASKCOL;Highlights mask
+TP_LOCALLAB_HHMASK_TOOLTIP;Fine hue adjustments for example for the skin.
+TP_LOCALLAB_HLH;Curves H
+TP_LOCALLAB_IND;Independent (mouse)
+TP_LOCALLAB_INDSL;Independent (mouse + sliders)
+TP_LOCALLAB_INVERS;Inverse
+TP_LOCALLAB_INVERS_TOOLTIP;If selected (inverse) less possibilities.\n\nAlternative\nFirst Spot:\n full image - delimiter outside preview\n Shape RT-spot area : rectangle. Transition 100\n\nSecond spot : Excluding spot
+TP_LOCALLAB_ISOGR;Coarseness (ISO)
+TP_LOCALLAB_LABEL;Local Adjustments
+TP_LOCALLAB_LABBLURM;Mask Blur
+TP_LOCALLAB_LABGRID;Color correction grid
+TP_LOCALLAB_LABGRIDMERG;Background
+TP_LOCALLAB_LABGRID_VALUES;High(a)=%1 High(b)=%2\nLow(a)=%3 Low(b)=%4
+TP_LOCALLAB_LABSTRUM;Mask Structure
+TP_LOCALLAB_LAPLACC;ΔØ Mask Laplacian solve PDE
+TP_LOCALLAB_LAP_MASK_TOOLTIP;Solve PDE for all Laplacian masks.\nIf enabled Laplacian threshold mask reduce artifacts and smooth result.\nIf disabled linear response.
+TP_LOCALLAB_LAPLACE;Δ - Laplacian threshold ΔE
+TP_LOCALLAB_LAPLACEXP;∆ - Laplacian threshold
+TP_LOCALLAB_LAPMASKCOL;∆ - Laplacian threshold mask
+TP_LOCALLAB_LAPRAD_TOOLTIP;Avoid using Radius and Laplace Threshold simultaneously.\nLaplacian threshold reduce contrast, artifacts, smooth result (if PDE settings enabled).
+TP_LOCALLAB_LC_FFTW_TOOLTIP;FFT improve quality and allow big radius, but increases the treatment time.\nThe treatment time depends on the surface to be treated.\nTo be used preferably for large radius.\n\nDimensions can be reduced by a few pixels to optimize FFTW.\nThis optimization can reduce the treatment time by a factor of 1.5 to 10.\n
+TP_LOCALLAB_LEVELBLUR;Maximum Blur levels
+TP_LOCALLAB_LEVELLOCCONTRAST_TOOLTIP;On the abscissa local contrast (near concept luminance). On the ordinate, amplification or reduction local contrast.
+TP_LOCALLAB_LEVELWAV;Ψ Wavelets Levels
+TP_LOCALLAB_LEVELWAV_TOOLTIP;The Level is automatically adapted to the size of the spot and the preview.\nFrom level 9 size max 512 to level 1 size max = 4
+TP_LOCALLAB_LIGHTNESS;Lightness
+TP_LOCALLAB_LIGHTN_TOOLTIP;In inverse mode: selection = -100 force luminance to zero
+TP_LOCALLAB_LIGHTRETI;Lightness
+TP_LOCALLAB_LINEAR;Linearity
+TP_LOCALLAB_LMASK_LEVEL_TOOLTIP;Give priority to action on midtones and high lights and by choosing the concerned wavelet levels
+TP_LOCALLAB_LMASK_LL_TOOLTIP;Give priority to action on midtones and high lights
+TP_LOCALLAB_LOCCONT;Unsharp Mask
+TP_LOCALLAB_LOC_CONTRAST;Local contrast -Wavelet - defects
+TP_LOCALLAB_LOC_CONTRASTPYR;Ψ Pyramid 1:
+TP_LOCALLAB_LOC_CONTRASTPYRLAB; Graduated Filter - Edge Sharpness - Blur
+TP_LOCALLAB_LOC_CONTRASTPYR2;Ψ Pyramid 2:
+TP_LOCALLAB_LOC_CONTRASTPYR2LAB; Contrast by Levels- Tone Mapping - Dir. Contrast
+TP_LOCALLAB_LOC_RESIDPYR;Residual Image Main
+TP_LOCALLAB_LOG;Encoding log
+TP_LOCALLAB_LOGAUTO;Automatic
+TP_LOCALLAB_LOGFRA;Source Gray Point
+TP_LOCALLAB_LOGLIN;Logarithm mode
+TP_LOCALLAB_LOGPFRA;Relative Exposure Levels
+TP_LOCALLAB_LOGENCOD_TOOLTIP;Allows Tone Mapping with Logarithm encoding (ACES).\nUsefull for underexposed pictures, or with high dynamic range.\n\nTwo steps in the process : 1) Calculate Dynamic Range 2) User adaptation
+TP_LOCALLAB_LOGFRAME_TOOLTIP;Calculate or use Exposure levels of the image early in the process:\n Black Ev, White Ev and Source Gray point.\n Take into account main exposure compensation.
+TP_LOCALLAB_LOGAUTO_TOOLTIP;Pressing this button will bring an evaluation of dynamic range and Source Gray point (if "Automatic" Source gray enabled).\nTo be able to touch up the automatic values, press the button again
+TP_LOCALLAB_LOGBLACKWHEV_TOOLTIP;Estimated values of Dynamic Range - Black Ev and White Ev
+TP_LOCALLAB_LOGSRCGREY_TOOLTIP;Estimated gray point value of the image, early in the process
+TP_LOCALLAB_LOGTARGGREY_TOOLTIP;You can change this value to adapt it to your taste.
+TP_LOCALLAB_LOGBASE_TOOLTIP;Default = 2.\nValues less than 2 reduce the action of the algorithm, the shadows are darker, the highlights are brighter.\nValues greater than 2 change the action of the algorithm, the shadows are grayer the highlights are more washed out
+TP_LOCALLAB_LUM;Curves LL - CC
+TP_LOCALLAB_LUMADARKEST;Darkest
+TP_LOCALLAB_LUMASK;Luminance Background Mask
+TP_LOCALLAB_LUMASK_TOOLTIP;Adjust the gray of the mask background in Show Mask (Mask and modifications)
+TP_LOCALLAB_LUMAWHITESEST;Whiteest
+TP_LOCALLAB_LUMONLY;Luminance only
+TP_LOCALLAB_MASFRAME;Mask and Merge
+TP_LOCALLAB_MASFRAME_TOOLTIP;For all masks.\nTake into account deltaE image to avoid retouching the selection area when sliders gamma mask, slope mask, chroma mask and curves contrast , levels contrasts, and mask blur, structure(if enabled tool) are used.\nDisabled in Inverse
+TP_LOCALLAB_MASK2;Contrast curve mask
+TP_LOCALLAB_MASK;Mask
+TP_LOCALLAB_MASKCOL;Mask Curves
+TP_LOCALLAB_MASKH;Hue curve mask
+TP_LOCALLAB_MASK_TOOLTIP;You can enable multiple masks for a single tool, this requires activating another tool (but without using the tool : sliders to 0,...) where is the mask you want to activate.\n\nYou can also duplicate the RT-spot and place it right next to each other,variations of references allow fine work on images.
+TP_LOCALLAB_MED;Medium
+TP_LOCALLAB_MEDIAN;Median Low
+TP_LOCALLAB_MEDNONE;None
+TP_LOCALLAB_MERCOL;Color
+TP_LOCALLAB_MERDCOL;Merge background (ΔE)
+TP_LOCALLAB_MERELE;Lighten only
+TP_LOCALLAB_MERFIV;Addition
+TP_LOCALLAB_MERFOR;Color Dodge
+TP_LOCALLAB_MERFOU;Multiply
+TP_LOCALLAB_MERGE1COLFRA;Merge with Original or Previous or Background
+TP_LOCALLAB_MERGECOLFRA;Mask: LCH & Structure
+TP_LOCALLAB_MERGEFIV;Previous Spot(Mask 7) + Mask LCH
+TP_LOCALLAB_MERGEFOU;Previous Spot(Mask 7)
+TP_LOCALLAB_MERGENONE;None
+TP_LOCALLAB_MERGEONE;Short Curves 'L' Mask
+TP_LOCALLAB_MERGEOPA_TOOLTIP;Opacity merge % current Spot with original or previous Spot.\nContrast threshold : adjust result in function of Original contrast
+TP_LOCALLAB_MERGETHR;Original(Mask 7) + Mask LCH
+TP_LOCALLAB_MERGETWO;Original(Mask 7)
+TP_LOCALLAB_MERGETYPE;Merge image and mask
+TP_LOCALLAB_MERGETYPE_TOOLTIP;None, use all mask in LCH mode.\nShort curves 'L' mask, use a short circuit for mask 2, 3, 4, 6, 7.\nOriginal mask 8, blend current image with original
+TP_LOCALLAB_MERGEMER_TOOLTIP;Take into account ΔE to merge files (equivalent of scope for this use)
+TP_LOCALLAB_MERHEI;Overlay
+TP_LOCALLAB_MERHUE;Hue
+TP_LOCALLAB_MERLUCOL;Luminance
+TP_LOCALLAB_MERLUM;Luminosity
+TP_LOCALLAB_MERNIN;Screen
+TP_LOCALLAB_MERONE;Normal
+TP_LOCALLAB_MERSAT;Saturation
+TP_LOCALLAB_MERSEV0;Soft Light Illusion
+TP_LOCALLAB_MERSEV1;Soft Light W3C
+TP_LOCALLAB_MERSEV2;Hard Light
+TP_LOCALLAB_MERSEV;Soft Light Photshop
+TP_LOCALLAB_MERSIX;Divide
+TP_LOCALLAB_MERTEN;Darken only
+TP_LOCALLAB_MERTHI;Color Burn
+TP_LOCALLAB_MERTHR;Difference
+TP_LOCALLAB_MERTWE;Exclusion
+TP_LOCALLAB_MERTWO;Substract
+TP_LOCALLAB_METHOD_TOOLTIP;'Enhanced + chroma denoise' significantly increases processing times.\nBut reduce artifacts.
+TP_LOCALLAB_MLABEL;Restored datas Min=%1 Max=%2 (Clip - Offset)
+TP_LOCALLAB_MLABEL_TOOLTIP;'Should be' near min=0 max=32768 (log mode) but others values are possible.\nYou can act on Clip Restored datas and Offset to normalize.\n\nRestored image with no mixture.
+TP_LOCALLAB_MODE_EXPERT;Expert
+TP_LOCALLAB_MODE_NORMAL;Normal
+TP_LOCALLAB_MRFIV;Background
+TP_LOCALLAB_MRFOU;Previous Spot
+TP_LOCALLAB_MRONE;None
+TP_LOCALLAB_MRTHR;Original Image
+TP_LOCALLAB_MRTWO;Short Curves 'L' Mask
+TP_LOCALLAB_MULTIPL_TOOLTIP;Allows the retouching of tones over a very wide range : -18EV +4EV. The first slider acts on very dark tones between -18EV and -6EV. The last slider acts light tones up to 4EV
+TP_LOCALLAB_NEIGH;Radius
+TP_LOCALLAB_NOISECHROCOARSE;Chroma coarse (Wav)
+TP_LOCALLAB_NOISECHROC_TOOLTIP;If superior to zero, high quality algorithm is enabled.\nCoarse is for slider >=0.02
+TP_LOCALLAB_NOISECHRODETAIL;Chroma detail recovery (DCT ƒ)
+TP_LOCALLAB_NOISECHROFINE;Chroma fine (Wav)
+TP_LOCALLAB_NOISEDETAIL_TOOLTIP;Disabled if slider = 100
+TP_LOCALLAB_NOISELEQUAL;Equalizer white-black
+TP_LOCALLAB_NOISELUMCOARSE;Luminance coarse (Wav)
+TP_LOCALLAB_NOISELUMDETAIL;Luminance detail recovery (DCT ƒ)
+TP_LOCALLAB_NOISELUMFINE;Luminance fine 1 (Wav)
+TP_LOCALLAB_NOISELUMFINETWO;Luminance fine 2 (Wav)
+TP_LOCALLAB_NOISELUMFINEZERO;Luminance fine 0 (Wav)
+TP_LOCALLAB_NOISEMETH;Denoise
+TP_LOCALLAB_NONENOISE;None
+TP_LOCALLAB_OFFS;Offset
+TP_LOCALLAB_OFFSETWAV;Offset
+TP_LOCALLAB_OPACOL;Opacity
+TP_LOCALLAB_ORIGLC;Merge only with original image
+TP_LOCALLAB_ORRETISTREN_TOOLTIP;Acts on the Laplacian threshold, the greater the action, the more the differences in contrast will be reduced
+TP_LOCALLAB_ORRETILAP_TOOLTIP;Acts on a second Laplacian threshold, to take into account ΔE to differentiate the action especially with the background (different from Scope)
+TP_LOCALLAB_PASTELS2;Vibrance
+TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Dynamic Range compression + Standard
+TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuator ƒ
+TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - personal algorithm adapted from IPOL to Rawtherapee: lead to very different results and needs differents settings that Standard (negative black, gamma < 1,...)\nMay be usefull for low exposure or high dynamic range.\n
+TP_LOCALLAB_PREVIEW;Preview ΔE
+TP_LOCALLAB_PROXI;ΔE weakening
+TP_LOCALLAB_QUALCURV_METHOD;Curves type
+TP_LOCALLAB_QUAL_METHOD;Global quality
+TP_LOCALLAB_RADIUS;Radius
+TP_LOCALLAB_RADIUS_TOOLTIP;Above Radius 30 Use Fast Fourier Transform
+TP_LOCALLAB_RADMASKCOL;Smooth Radius Mask
+TP_LOCALLAB_RECT;Rectangle
+TP_LOCALLAB_RECURS;Recursive references
+TP_LOCALLAB_RECURS_TOOLTIP;Recalculate references for hue, luma, chroma after each module and after each RT-spot.\nAlso useful for working with masks.
+TP_LOCALLAB_REFLABEL;Ref. (0..1) Chroma=%1 Luma=%2 Hue=%3
+TP_LOCALLAB_REN_DIALOG_LAB;Enter the new Control Spot name
+TP_LOCALLAB_REN_DIALOG_NAME;Renaming Control Spot
+TP_LOCALLAB_RESETSHOW;Reset All Show Modifications
+TP_LOCALLAB_RESID;Residual Image
+TP_LOCALLAB_RESIDBLUR;Blur Residual Image
+TP_LOCALLAB_RESIDCHRO;Residual image Chroma
+TP_LOCALLAB_RESIDCOMP;Compress Residual image
+TP_LOCALLAB_RESIDCONT;Residual image Contrast
+TP_LOCALLAB_RESIDHI;Highlights
+TP_LOCALLAB_RESIDHITHR;Highlights threshold
+TP_LOCALLAB_RESIDSHA;Shadows
+TP_LOCALLAB_RESIDSHATHR;Shadows threshold
+TP_LOCALLAB_RETI;Dehaze - Retinex Strong contrast
+TP_LOCALLAB_RETI_LIMDOFFS_TOOLTIP;Play on internal parameters to optimize response.\nLook at the "restored datas" indicators "near" min=0 and max=32768 (log mode), but others values are possible.
+TP_LOCALLAB_RETI_LIGHTDARK_TOOLTIP;Have no effect when the value "Lightness = 1" or "Darkness =2" is chosen.\nIn other cases, the last step of "Multiple scale Retinex" is applied an algorithm close to "local contrast", these 2 cursors, associated with "Strength" will allow to play upstream on the local contrast.
+TP_LOCALLAB_RETI_LOGLIN_TOOLTIP;Logarithm allows differenciation for haze or normal.\nLogarithm brings more contrast but will generate more halo.
+TP_LOCALLAB_RETI_SCALE_TOOLTIP;If scale=1, retinex behaves like local contrast with many more possibilities.\nThe greater the scale, the more intense the recursive action, the longer the calculation times
+TP_LOCALLAB_RETIFRA;Retinex
+TP_LOCALLAB_RETIM;Original Retinex
+TP_LOCALLAB_RETITOOLFRA;Retinex Tools
+TP_LOCALLAB_RETI_FFTW_TOOLTIP;FFT improve quality and allow big radius, but increases the treatment time.\nThe treatment time depends on the surface to be treated\nThe treatment time depends on the value of scale (be carefull to high values).\nTo be used preferably for large radius.\n\nDimensions can be reduced by a few pixels to optimize FFTW.\nThis optimization can reduce the treatment time by a factor of 1.5 to 10.\nOptimization not used in Preview
+TP_LOCALLAB_RETI_NEIGH_VART_TOOLTIP;Adapt these values according to images - if misty images and depending on whether you want to act on the front or the background
+TP_LOCALLAB_REWEI;Reweighting iterates
+TP_LOCALLAB_RGB;RGB Tone Curve
+TP_LOCALLAB_ROW_NVIS;Not visible
+TP_LOCALLAB_ROW_VIS;Visible
+TP_LOCALLAB_SATUR;Saturation
+TP_LOCALLAB_SAVREST;Save - Restore Current Image
+TP_LOCALLAB_SCALEGR;Scale
+TP_LOCALLAB_SCALERETI;Scale
+TP_LOCALLAB_SCALTM;Scale
+TP_LOCALLAB_SCOPEMASK;Scope Mask ΔE Image
+TP_LOCALLAB_SCOPEMASK_TOOLTIP;Enabled if Mask DeltaE Image is enabled.\nLow values avoid retouching selected area
+TP_LOCALLAB_SENSI;Scope
+TP_LOCALLAB_SENSIBN;Scope
+TP_LOCALLAB_SENSICB;Scope
+TP_LOCALLAB_SENSIDEN;Scope
+TP_LOCALLAB_SENSIEXCLU;Scope
+TP_LOCALLAB_SENSIEXCLU_TOOLTIP;Adjust color to include in exclusion!
+TP_LOCALLAB_SENSIH;Scope
+TP_LOCALLAB_SENSIH_TOOLTIP;Adjust scope of action:\nSmall values limit action to colors very similar to those under the center spot.\nHigh values let the tool act upon a wider range of colors.
+TP_LOCALLAB_SENSILOG;Scope
+TP_LOCALLAB_SENSIS;Scope
+TP_LOCALLAB_SENSIS_TOOLTIP;Adjust scope of action:\nSmall values limit action to colors very similar to those under the center spot.\nHigh values let the tool act upon a wider range of colors.\nValues smaller than 20 lead to a better algorithm.
+TP_LOCALLAB_SENSI_TOOLTIP;Adjust scope of action:\nSmall values limit action to colors very similar to those under the center spot.\nHigh values let the tool act upon a wider range of colors.\nValues smaller than 20 lead to a better algorithm.
+TP_LOCALLAB_SETTINGS;Settings
+TP_LOCALLAB_SH1;Shadows Highlights
+TP_LOCALLAB_SH2;Equalizer
+TP_LOCALLAB_SHADEX;Shadows
+TP_LOCALLAB_SHADEXCOMP;Shadows compression & tonal width
+TP_LOCALLAB_SHADHIGH;ShadowsHighlight - Tone equalizer
+TP_LOCALLAB_SHADOWHIGHLIGHT_TOOLTIP;Can be used instead - or in complement - of exposure module in difficult cases.\nThe use of Denoise may be necessary : lightening the shadows.\n\nCan be used as graduated filter (increase Scope)
+TP_LOCALLAB_SHAMASKCOL;Shadows mask
+TP_LOCALLAB_SHAPETYPE;Shape RT-spot area
+TP_LOCALLAB_SHAPE_TOOLTIP;Elipse is normal mode.\nRectangle can be used in some cases, for example to work in full image in conjonction with delimiters outside preview, transition = 100.\n\nPolygone - Beziers are waiting for GUI...
+TP_LOCALLAB_SHARAMOUNT;Amount
+TP_LOCALLAB_SHARBLUR;Blur radius
+TP_LOCALLAB_SHARDAMPING;Damping
+TP_LOCALLAB_SHARITER;Iterations
+TP_LOCALLAB_SHARFRAME;Modifications
+TP_LOCALLAB_SHARP;Sharpening
+TP_LOCALLAB_SHARRADIUS;Radius
+TP_LOCALLAB_SHORTC;Short Curves 'L' Mask
+TP_LOCALLAB_SHORTCMASK_TOOLTIP;Short circuit the 2 curves L(L) and L(H).\nAllows you to mix the current image with the original image modified by the mask job.\nUsable with masks 2, 3, 4, 6, 7
+TP_LOCALLAB_SHOWC1;Merge file
+TP_LOCALLAB_SHOWC;Mask and modifications
+TP_LOCALLAB_SHOWCB;Mask and modifications
+TP_LOCALLAB_SHOWDCT;Show process Fourier ƒ
+TP_LOCALLAB_SHOWE;Mask and modifications
+TP_LOCALLAB_SHOWFOURIER;Fourier ƒ(dct)
+TP_LOCALLAB_SHOWLAPLACE;∆ Laplacian (first)
+TP_LOCALLAB_SHOWLC;Mask and modifications
+TP_LOCALLAB_SHOWMASK;Show mask
+TP_LOCALLAB_SHOWMASKCOL_TOOLTIP;Display mask modifications.\nBeware, you can only view one tool mask at the same time.\n\nNote: Use Mask is before algorihtm shape detection.
+TP_LOCALLAB_SHOWMASKSOFT_TOOLTIP;Show process Fourier:\nShows the different stages of the process.\nLaplace - builds the second derivative according to the threshold (first step).\nFourier -shows the transformed Laplacian with DCT.\nPoisson - show solution of Poisson DCE.\nNormalize - show result without normalization luminance.
+TP_LOCALLAB_SHOWMASKTYP1;Blur & Noise
+TP_LOCALLAB_SHOWMASKTYP2;Denoise
+TP_LOCALLAB_SHOWMASKTYP3;Blur & Noise + Denoise
+TP_LOCALLAB_SHOWMASKTYP_TOOLTIP;Mask and modifications can be chosen.\nBlur and noise : in this case it is not used for 'denoise'.\nDenoise : in this case it is not used for 'blur and noise'.\n\nBlur and noise + denoise : mask is shared, be carefull to 'show modifications' and 'scope'
+TP_LOCALLAB_SHOWMNONE;None
+TP_LOCALLAB_SHOWMODIF;Show modifications without mask
+TP_LOCALLAB_SHOWMODIFMASK;Show modifications with mask
+TP_LOCALLAB_SHOWNORMAL;Normalize luminance (no)
+TP_LOCALLAB_SHOWPLUS;Mask and modifications - Smooth-Blur & Denoise
+TP_LOCALLAB_SHOWPOISSON;Poisson (pde ƒ)
+TP_LOCALLAB_SHOWR;Mask and modifications
+TP_LOCALLAB_SHOWS;Mask and modifications
+TP_LOCALLAB_SHOWREF;Preview ΔE
+TP_LOCALLAB_SHOWSTRUC;Show structure Spot
+TP_LOCALLAB_SHOWSTRUCEX;Show structure Spot
+TP_LOCALLAB_SHOWT;Mask and modifications
+TP_LOCALLAB_SHOWVI;Mask and modifications
+TP_LOCALLAB_SHRESFRA;Shadows/Highlights
+TP_LOCALLAB_SHTRC_TOOLTIP;Modifies the tones of the image by acting on a TRC (Tone Response Curve).\nGamma acts mainly on light tones.\nSlope acts mainly on dark tones
+TP_LOCALLAB_SIGMAWAV;Attenuation Response
+TP_LOCALLAB_SIM;Simple
+TP_LOCALLAB_SLOMASKCOL;Slope mask
+TP_LOCALLAB_SLOSH;Slope
+TP_LOCALLAB_SOFT;Soft Light - Original Retinex
+TP_LOCALLAB_SOFTM;Soft Light
+TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex is very different from others Retinex method.\nIts acts on grey and balance luminance.\nIt is an emulation of "Dodge" and "Burn"
+TP_LOCALLAB_SOFTRADIUSCOL;Soft radius
+TP_LOCALLAB_SOFTRETI;Reduce artifact ΔE
+TP_LOCALLAB_SOFTRETI_TOOLTIP;Take into account deltaE to improve Transmission map
+TP_LOCALLAB_SOURCE_GRAY;Value
+TP_LOCALLAB_SPECIAL;Special use of RGB curves
+TP_LOCALLAB_SPECIAL_TOOLTIP;Only for this RGB curve, disabled (or reduce effects) of Scope, mask...for example, if you want to have a negative effect.
+TP_LOCALLAB_SPECCASE; Specific cases
+TP_LOCALLAB_SPOTNAME;Control Spot #
+TP_LOCALLAB_STD;Standard
+TP_LOCALLAB_STR;Strength
+TP_LOCALLAB_STRBL;Strength
+TP_LOCALLAB_STREN;Compression Strength
+TP_LOCALLAB_STRENG;Strength
+TP_LOCALLAB_STRENGR;Strength
+TP_LOCALLAB_STRENGTH;Noise
+TP_LOCALLAB_STRGRID;Strength
+TP_LOCALLAB_STRRETI_TOOLTIP;if Strength Retinex < 0.2 only Dehaze is enabled.\nif Strength Retinex >= 0.1 Dehaze is in luminance mode.
+TP_LOCALLAB_STRUC;Structure
+TP_LOCALLAB_STRUCCOL1;Structure Spot
+TP_LOCALLAB_STRUCT_TOOLTIP;Use Sobel algorithm to take into account structure in shape detection.\nyou can have a preview by activating "mask and modifications - Show structure spot".\n\nCan be used in conjunction with masks (expert) structure, blur, wavelet to improve edge detection.\n\nNeeds maskless adjustments to be activated (lightness, exposure...)
+TP_LOCALLAB_STRUCCOL;Structure
+TP_LOCALLAB_STRUMASKCOL;Structure mask strength
+TP_LOCALLAB_STRUMASK_TOOLTIP;Generate a structure mask with difference between surface areas and reliefs.\nIf structure mask as tool is enabled, this mask is used in addition to the other tools (gamma, slope, contrast curve ...)
+TP_LOCALLAB_STYPE;Shape method
+TP_LOCALLAB_STYPE_TOOLTIP;You can choose between:\nSymmetrical - left handle linked to right, top handle linked to bottom.\nIndependent - all handles are independent.
+TP_LOCALLAB_SYM;Symmetrical (mouse)
+TP_LOCALLAB_SYMSL;Symmetrical (mouse + sliders)
+TP_LOCALLAB_TARGET_GRAY;Target Gray Point
+TP_LOCALLAB_THRES;Threshold structure
+TP_LOCALLAB_THRESDELTAE;Threshold ΔE-scope
+TP_LOCALLAB_THRESRETI;Threshold
+TP_LOCALLAB_THRESWAV;Balance Threshold
+TP_LOCALLAB_TLABEL2;TM Effective Tm=%1 TM=%2
+TP_LOCALLAB_TLABEL;TM Datas Min=%1 Max=%2 Mean=%3 Sigma=%4 (Threshold)
+TP_LOCALLAB_TLABEL_TOOLTIP;Transmission map result.\nMin and Max are used by Variance.\nTm=Min TM=Max of Transmission Map.\nYou can act on Threshold to normalize
+TP_LOCALLAB_TM;Tone Mapping - Texture
+TP_LOCALLAB_TM_MASK;Use transmission map
+TP_LOCALLAB_TONEMAPESTOP_TOOLTIP;This parameter affects sensitivity to edges.\n The greater it is the more likely an illumination change is to be considered an "edge".\n If set to zero tone mapping will have an effect similar to unsharp masking.
+TP_LOCALLAB_TONEMAPREWEI_TOOLTIP;In some cases tone mapping may result in a cartoonish appearance, and in some rare cases soft but wide halos may appear.\n Increasing the number of reweighting iterates will help fight some of these problems.
+TP_LOCALLAB_TONEMASCALE_TOOLTIP;This control gives meaning to the difference between "local" and "global" contrast.\nThe greater it is the larger a detail needs to be in order to be boosted
+TP_LOCALLAB_TONEMAPGAM_TOOLTIP;Gamma moves the action of tone-mapping to shadows or highlights.
+TP_LOCALLAB_TOOLCOL;Structure mask as tool
+TP_LOCALLAB_TONEMAP_TOOLTIP;Tone Mapping - main menu must be disabled
+TP_LOCALLAB_TOOLMASK;Tools
+TP_LOCALLAB_TRANSIT;Transition Gradient
+TP_LOCALLAB_TRANSITGRAD;Transition differentiation XY
+TP_LOCALLAB_TRANSITGRAD_TOOLTIP;Changes the transition of the abscissa to that of the ordinate
+TP_LOCALLAB_TRANSITVALUE;Transition value
+TP_LOCALLAB_TRANSITWEAK;Transition weakening
+TP_LOCALLAB_TRANSITWEAK_TOOLTIP;Adjust transition weakening : change smoothness process - 1 linear - 2 parabolic - 3 cubic - ^25.\nCan be used in conjunction with very low transition values to reduce defects (CBDL, Wavelet, Color & Light)
+TP_LOCALLAB_TRANSIT_TOOLTIP;Adjust smoothness of transition between affected and unaffected areas, as a percentage of the "radius"
+TP_LOCALLAB_TRANSMISSIONGAIN;Transmission gain
+TP_LOCALLAB_TRANSMISSIONMAP;Transmission map
+TP_LOCALLAB_TRANSMISSION_TOOLTIP;Transmission according to transmission.\nAbscissa: transmission from negative values (min), mean, and positives values (max).\nOrdinate: amplification or reduction.\nYou can act on this curve to change Transmission and reduce the artifacts
+TP_LOCALLAB_USEMASK;Use mask
+TP_LOCALLAB_VART;Variance (contrast)
+TP_LOCALLAB_VIBRANCE;Vibrance - Warm & Cool
+TP_LOCALLAB_VIS_TOOLTIP;Click to show/hide selected Control Spot.\nCtrl+click to show/hide all Control Spot.
+TP_LOCALLAB_LIST_NAME;Add tool to current spot...
+TP_LOCALLAB_LIST_TOOLTIP;Choose a tool and then its level of complexity "Normal" or "Expert".\nThe number reflects the place of the tool in the process of each RT-Spot
+TP_LOCALLAB_COLOR_TOOLNAME;Color&Light (Defects) - 11
+TP_LOCALLAB_EXP_TOOLNAME;Exposure - Dynamic Range Compression - 10
+TP_LOCALLAB_SH_TOOLNAME;Shadows Highlight & Tone Equalizer - 5
+TP_LOCALLAB_VIB_TOOLNAME;Vibrance - Warm & Cool - 3
+TP_LOCALLAB_SOFT_TOOLNAME;Soft Light & Original Retinex - 6
+TP_LOCALLAB_BLUR_TOOLNAME;Smooth Blur Gain & Denoise - 1
+TP_LOCALLAB_TONE_TOOLNAME;Tone Mapping - 4
+TP_LOCALLAB_RET_TOOLNAME;Dehaze & Retinex - 9
+TP_LOCALLAB_SHARP_TOOLNAME;Sharpening - 8
+TP_LOCALLAB_LC_TOOLNAME;Local Constrast & Wavelet (Defects) - 7
+TP_LOCALLAB_CBDL_TOOLNAME;CBDL (Defects) - 2
+TP_LOCALLAB_LOG_TOOLNAME;Encoding log - 0
+TP_LOCALLAB_WAMASKCOL;Ψ Mask Wavelet level
+TP_LOCALLAB_WARM;Warm - Cool & Color artifacts
+TP_LOCALLAB_WARM_TOOLTIP;This slider use Ciecam algorithm and acts as White Balance, it can warm or cool the area selected.\nIt can also in some cases reduce color artifacts.
+TP_LOCALLAB_WAV;Levels local contrast
+TP_LOCALLAB_WAVMASK_TOOLTIP;Allows fine work on mask levels contrasts (structure)
+TP_LOCALLAB_WAVBLUR_TOOLTIP;Performs a blur for each level of decomposition, as well as the residual image.
+TP_LOCALLAB_WAVEDG;Local contrast
+TP_LOCALLAB_WAVCOMP;Compression by Level
+TP_LOCALLAB_WAVCOMP_TOOLTIP;Achive local contrast in function of the direction wavelet decomposition : horizontal, vertical, diagonal
+TP_LOCALLAB_WAVCOMPRE;(un)Compression by Level
+TP_LOCALLAB_WAVCOMPRE_TOOLTIP;Achieve a Tone-mapping or reduction local contrast by levels.\nOn abscissa levels
+TP_LOCALLAB_WAVCON;Contrast by Level
+TP_LOCALLAB_WAVCONTF_TOOLTIP;Similar to Contrast By Detail Levels : on abscissa levels.
+TP_LOCALLAB_WAVDEN;Luminance denoise by level (0 1 2 + 3 and more)
+TP_LOCALLAB_WASDEN_TOOLTIP;Denoise luminance for the 3 first levels (fine).\nThe right limit of the curve correspond to coarse : level 3 and beyond
+TP_LOCALLAB_WAVE;Ψ Wavelet
+TP_LOCALLAB_WAVEEDG_TOOLTIP;Achieves a sharpness taking into account the notion of edges wavelet.\nRequires that at least the first 4 levels are usable
+TP_LOCALLAB_WAVGRAD_TOOLTIP;Graduated filter for Local contrast "luminance"
+TP_LOCALLAB_WAVHIGH;Ψ Wavelet high
+TP_LOCALLAB_WAVLEV;Blur by Level
+TP_LOCALLAB_WAVLOW;Ψ Wavelet low
+TP_LOCALLAB_WAVMASK;Ψ Mask Levels local contrast
+TP_LOCALLAB_WAVMED;Ψ Wavelet normal
+TP_LOCALLAB_WEDIANHI;Median Hi
+TP_LOCALLAB_WHITE_EV;White Ev
+TP_LOCALLAB_BLNOI_EXP;Blur & Noise
+TP_LOCALLAB_DENOI_EXP;Denoise
+TP_LOCAL_HEIGHT;Bottom
+TP_LOCAL_HEIGHT_T;Top
+TP_LOCAL_WIDTH;Right
+TP_LOCAL_WIDTH_L;Left
+TP_LOCRETI_METHOD_TOOLTIP;Low = Reinforce low light.\nUniform = Equalize action.\nHigh = Reinforce high light.\n
TP_LOCALCONTRAST_AMOUNT;Amount
TP_LOCALCONTRAST_DARKNESS;Darkness level
TP_LOCALCONTRAST_LABEL;Local Contrast
@@ -1861,8 +2870,26 @@ TP_PCVIGNETTE_ROUNDNESS_TOOLTIP;Roundness:\n0 = rectangle,\n50 = fitted ellipse,
TP_PCVIGNETTE_STRENGTH;Strength
TP_PCVIGNETTE_STRENGTH_TOOLTIP;Filter strength in stops (reached in corners).
TP_PDSHARPENING_LABEL;Capture Sharpening
+TP_PERSPECTIVE_CAMERA_CROP_FACTOR;Crop factor
+TP_PERSPECTIVE_CAMERA_FRAME;Correction
+TP_PERSPECTIVE_CAMERA_FOCAL_LENGTH;Focal length
+TP_PERSPECTIVE_CAMERA_PITCH;Vertical
+TP_PERSPECTIVE_CAMERA_ROLL;Rotation
+TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL;Horizontal shift
+TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL;Vertical shift
+TP_PERSPECTIVE_CAMERA_YAW;Horizontal
TP_PERSPECTIVE_HORIZONTAL;Horizontal
TP_PERSPECTIVE_LABEL;Perspective
+TP_PERSPECTIVE_METHOD;Method
+TP_PERSPECTIVE_METHOD_SIMPLE;Simple
+TP_PERSPECTIVE_METHOD_CAMERA_BASED;Camera-based
+TP_PERSPECTIVE_POST_CORRECTION_ADJUSTMENT_FRAME;Post-correction adjustment
+TP_PERSPECTIVE_PROJECTION_PITCH;Vertical
+TP_PERSPECTIVE_PROJECTION_ROTATE;Rotation
+TP_PERSPECTIVE_PROJECTION_SHIFT_HORIZONTAL;Horizontal shift
+TP_PERSPECTIVE_PROJECTION_SHIFT_VERTICAL;Vertical shift
+TP_PERSPECTIVE_PROJECTION_YAW;Horizontal
+TP_PERSPECTIVE_RECOVERY_FRAME;Recovery
TP_PERSPECTIVE_VERTICAL;Vertical
TP_PFCURVE_CURVEEDITOR_CH;Hue
TP_PFCURVE_CURVEEDITOR_CH_TOOLTIP;Controls defringe strength by color.\nHigher = more,\nLower = less.
@@ -2040,8 +3067,8 @@ TP_RETINEX_MAP_NONE;None
TP_RETINEX_MEDIAN;Transmission median filter
TP_RETINEX_METHOD;Method
TP_RETINEX_METHOD_TOOLTIP;Low = Reinforce low light.\nUniform = Equalize action.\nHigh = Reinforce high light.\nHighlights = Remove magenta in highlights.
-TP_RETINEX_MLABEL;Restored haze-free Min=%1 Max=%2
-TP_RETINEX_MLABEL_TOOLTIP;Should be near min=0 max=32768\nRestored image with no mixture.
+TP_RETINEX_MLABEL;Restored data Min=%1 Max=%2
+TP_RETINEX_MLABEL_TOOLTIP;'Should be' near min=0 max=32768 (log mode) but others values are possible\nRestored image with no mixture.
TP_RETINEX_NEIGHBOR;Radius
TP_RETINEX_NEUTRAL;Reset
TP_RETINEX_NEUTRAL_TIP;Reset all sliders and curves to their default values.
@@ -2054,11 +3081,11 @@ TP_RETINEX_SLOPE;Free gamma slope
TP_RETINEX_STRENGTH;Strength
TP_RETINEX_THRESHOLD;Threshold
TP_RETINEX_THRESHOLD_TOOLTIP;Limits in/out.\nIn = image source,\nOut = image gauss.
-TP_RETINEX_TLABEL;TM Min=%1 Max=%2 Mean=%3 Sigma=%4
-TP_RETINEX_TLABEL2;TM Tm=%1 TM=%2
-TP_RETINEX_TLABEL_TOOLTIP;Transmission map result.\nMin and Max are used by Variance.\nMean and Sigma.\nTm=Min TM=Max of transmission map.
+TP_RETINEX_TLABEL;TM Datas Min=%1 Max=%2 Mean=%3 Sigma=%4
+TP_RETINEX_TLABEL2;TM Effective Tm=%1 TM=%2
+TP_RETINEX_TLABEL_TOOLTIP;Transmission map result.\nMin and Max are used by Variance.\nTm=Min TM=Max of Transmission Map.
TP_RETINEX_TRANF;Transmission
-TP_RETINEX_TRANSMISSION;Transmission map
+TP_RETINEX_TRANSMISSION;Transmission map
TP_RETINEX_TRANSMISSION_TOOLTIP;Transmission according to transmission.\nAbscissa: transmission from negative values (min), mean, and positives values (max).\nOrdinate: amplification or reduction.
TP_RETINEX_UNIFORM;Uniform
TP_RETINEX_VARIANCE;Contrast
@@ -2119,7 +3146,7 @@ TP_SOFTLIGHT_LABEL;Soft Light
TP_SOFTLIGHT_STRENGTH;Strength
TP_TM_FATTAL_AMOUNT;Amount
TP_TM_FATTAL_ANCHOR;Anchor
-TP_TM_FATTAL_LABEL;Dynamic Range Compression
+TP_TM_FATTAL_LABEL;Dynamic Range Compression ƒ
TP_TM_FATTAL_THRESHOLD;Detail
TP_VIBRANCE_AVOIDCOLORSHIFT;Avoid color shift
TP_VIBRANCE_CURVEEDITOR_SKINTONES;HH
@@ -2165,14 +3192,14 @@ TP_WAVELET_BALANCE;Contrast balance d/v-h
TP_WAVELET_BALANCE_TOOLTIP;Alters the balance between the wavelet directions: vertical-horizontal and diagonal.\nIf contrast, chroma or residual tone mapping are activated, the effect due to balance is amplified.
TP_WAVELET_BALCHRO;Chrominance balance
TP_WAVELET_BALCHRO_TOOLTIP;If enabled, the 'Contrast balance' curve or slider also modifies chroma balance.
-TP_WAVELET_BALCHROM;Denoise Equalizer Blue-Red
+TP_WAVELET_BALCHROM;Denoise Equalizer Blue-yellow Red-green
TP_WAVELET_BALLUM;Denoise Equalizer White-Black
TP_WAVELET_BANONE;None
TP_WAVELET_BASLI;Slider
TP_WAVELET_BATYPE;Contrast balance method
TP_WAVELET_BLCURVE;Blur by levels
TP_WAVELET_BLURFRAME;Blur
-TP_WAVELET_BLUWAV;Damper
+TP_WAVELET_BLUWAV;Attenuation Response
TP_WAVELET_CBENAB;Toning and Color Balance
TP_WAVELET_CB_TOOLTIP;For strong values product color-toning by combining it or not with levels decomposition 'toning'\nFor low values you can change the white balance of the background (sky, ...) without changing that of the front plane, generally more contrasted
TP_WAVELET_CCURVE;Local contrast
@@ -2217,6 +3244,7 @@ TP_WAVELET_CURVEEDITOR_HH;HH
TP_WAVELET_CURVEEDITOR_HH_TOOLTIP;Modifies the residual image's hue as a function of hue.
TP_WAVELET_DALL;All directions
TP_WAVELET_DAUB;Edge performance
+TP_WAVELET_DAUBLOCAL;Wavelet Edge performance
TP_WAVELET_DAUB2;D2 - low
TP_WAVELET_DAUB4;D4 - standard
TP_WAVELET_DAUB6;D6 - standard plus
@@ -2237,7 +3265,7 @@ TP_WAVELET_EDGEDETECTTHR;Threshold low (noise)
TP_WAVELET_EDGEDETECTTHR2;Threshold high (detection)
TP_WAVELET_EDGEDETECTTHR_TOOLTIP;This adjuster lets you target edge detection for example to avoid applying edge sharpness to fine details, such as noise in the sky.
TP_WAVELET_EDGEDETECT_TOOLTIP;Moving the slider to the right increases edge sensitivity. This affects local contrast, edge settings and noise.
-TP_WAVELET_EDEFFECT;Damper
+TP_WAVELET_EDEFFECT;Attenuation Response
TP_WAVELET_EDEFFECT_TOOLTIP;This slider controls how wide the range of contrast values are that receive the maximum effect from the tool.\nMaximum value (2.5) disabled the tool
TP_WAVELET_EDGESENSI;Edge sensitivity
TP_WAVELET_EDGREINF_TOOLTIP;Reinforce or reduce the action of the first level, do the opposite to the second level, and leave the rest unchanged.
@@ -2320,9 +3348,9 @@ TP_WAVELET_SETTINGS;Wavelet Settings
TP_WAVELET_SHA;Sharp mask
TP_WAVELET_SHFRAME;Shadows/Highlights
TP_WAVELET_SHOWMASK;Show wavelet 'mask'
-TP_WAVELET_SIGMA;Damper
-TP_WAVELET_SIGMAFIN;Damper
-TP_WAVELET_SIGMA_TOOLTIP;The effect of the contrast sliders is stronger in medium contrast details, and weaker in high and low contrast details.\n With this slider you can control how quickly the effect dampens towards the extreme contrasts.\n The higher the slider is set, the wider the range of contrasts which will get a strong change, and the higher the risk to generate artifacts.\n The lower it is, the more pinpoint will the effect be applied to a narrow range of contrast values.
+TP_WAVELET_SIGMA;Attenuation Response
+TP_WAVELET_SIGMAFIN;Attenuation Response
+TP_WAVELET_SIGMA_TOOLTIP;The effect of the contrast sliders is stronger in medium contrast details, and weaker in high and low contrast details.\n With this slider you can control how quickly the effect dampens towards the extreme contrasts.\n The higher the slider is set, the wider the range of contrasts which will get a strong change, and the higher the risk to generate artifacts.\n .The lower it is, the more the effect will be pinpointed towards a narrow range of contrast values
TP_WAVELET_SKIN;Skin targetting/protection
TP_WAVELET_SKIN_TOOLTIP;At -100 skin-tones are targetted.\nAt 0 all tones are treated equally.\nAt +100 skin-tones are protected while all other tones are affected.
TP_WAVELET_SKY;Sky targetting/protection
@@ -2334,8 +3362,8 @@ TP_WAVELET_SUPE;Extra
TP_WAVELET_THR;Shadows threshold
TP_WAVELET_THRESHOLD;Finer levels
TP_WAVELET_THRESHOLD2;Coarser levels
-TP_WAVELET_THRESHOLD_TOOLTIP;Only levels beyond the chosen value will be affected by the highlight luminance range. Other levels will be fully treated. The chosen value here limits the highest possible value of the shadow levels. : All levels from level 1 up to the chosen value will only be affected within the Finer levels luminance range.\nAll other levels will have the whole range of luminances affected, unless the Coarser levels setting limits it.\nThe chosen value in this slider becomes the minimum possible value of the Coarser levels.
-TP_WAVELET_THRESHOLD2_TOOLTIP;Only levels between 9 and 9 minus the value will be affected by the shadow luminance range. Other levels will be fully treated. The highest level possible is limited by the highlight level value (9 minus highlight level value). : Only levels between the chosen value and level 9/Extra will be affected by the Coarser levels luminance range.\nAll other levels will have the whole range of luminances affected, unless the Finer levels setting limits it.\nThe lower level possible that will be considered by the algorithm is limited by the Finer levels value.
+TP_WAVELET_THRESHOLD_TOOLTIP;Only levels below and including the chosen value will be affected by the Highlight luminance range.
+TP_WAVELET_THRESHOLD2_TOOLTIP;Only levels from the chosen value to the selected number of ‘wavelet levels’ will be affected by the Shadow luminance range.
TP_WAVELET_THRESWAV;Balance Threshold
TP_WAVELET_THRH;Highlights threshold
TP_WAVELET_TILESBIG;Tiles
@@ -2358,7 +3386,7 @@ TP_WAVELET_USHARP_TOOLTIP;Origin : the source file is the file before Wavelet.\n
TP_WAVELET_USH_TOOLTIP;If you select Sharp-mask, wavelet settings will be automatically positioned :\nBackground=black, Process=below, level=3...you can change level between 1 and 4.\n\nIf you select Clarity, wavelet settings will be automatically positioned :\nBackground=residual, Process=above, level=7..you can change level between 5 and 10 and wavelet levels.
TP_WAVELET_WAVLOWTHR;Low contrast threshold
TP_WAVELET_WAVOFFSET;Offset
-TP_WAVELET_OFFSET_TOOLTIP;Offset modifies the balance between shadows and highlights.\nHigh values here will amplify the contrast change of the highlights, whereas low values will amplify the contrast change of the shadows.\nAlong with a low Damper value you will able to select the contrasts that will be enhanced.
+TP_WAVELET_OFFSET_TOOLTIP;Offset modifies the balance between shadows and highlights.\nHigh values here will amplify the contrast change of the highlights, whereas low values will amplify the contrast change of the shadows.\nAlong with a low Attenuation Response value you will able to select the contrasts that will be enhanced.
TP_WBALANCE_AUTO;Auto
TP_WBALANCE_AUTOITCGREEN;Temperature correlation
TP_WBALANCE_AUTOOLD;RGB grey
diff --git a/rtdata/themes/RawTherapee-GTK3-20_.css b/rtdata/themes/RawTherapee-GTK3-20_.css
index bdadc00db..76f0004ee 100644
--- a/rtdata/themes/RawTherapee-GTK3-20_.css
+++ b/rtdata/themes/RawTherapee-GTK3-20_.css
@@ -745,6 +745,7 @@ flowboxchild:selected {
background-color: #363636;
}
+#LocallabToolPanel frame,
#ExpanderBox frame,
#ExpanderBox2 frame,
#ExpanderBox3 frame {
@@ -761,18 +762,22 @@ flowboxchild:selected {
padding: 0.25em;
}
+#LocallabToolPanel frame > label, #LocallabToolPanel frame frame > label,
#ExpanderBox frame > label, #ExpanderBox frame frame > label,
#ExpanderBox2 frame > label, #ExpanderBox2 frame frame > label,
#ExpanderBox3 frame > label, #ExpanderBox3 frame frame > label {
margin-left: 7pt;
margin-top: 0;
}
+
+#LocallabToolPanel frame > box, #LocallabToolPanel frame frame > box, #LocallabToolPanel frame > grid, #LocallabToolPanel frame frame > grid,
#ExpanderBox frame > box, #ExpanderBox frame frame > box, #ExpanderBox frame > grid, #ExpanderBox frame frame > grid,
#ExpanderBox2 frame > box, #ExpanderBox2 frame frame > box, #ExpanderBox2 frame > grid, #ExpanderBox2 frame frame > grid,
#ExpanderBox3 frame > box, #ExpanderBox3 frame frame > box, #ExpanderBox3 frame > grid, #ExpanderBox3 frame frame > grid {
margin: 0.1666666666666666em;
}
+#LocallabToolPanel > box > checkbutton, #LocallabToolPanel > box > box, #LocallabToolPanel > grid > checkbutton, #LocallabToolPanel > box > grid, #LocallabToolPanel > grid > grid, #LocallabToolPanel frame > box > grid, #LocallabToolPanel frame > grid > grid, #LocallabToolPanel frame > grid > box,
#ExpanderBox > box > checkbutton, #ExpanderBox > box > box, #ExpanderBox > grid > checkbutton, #ExpanderBox > box > grid, #ExpanderBox > grid > grid, #ExpanderBox frame > box > grid, #ExpanderBox frame > grid > grid, #ExpanderBox frame > grid > box,
#ExpanderBox2 > box > checkbutton, #ExpanderBox2 > box > box, #ExpanderBox2 > grid > checkbutton, #ExpanderBox2 > box > grid, #ExpanderBox2 > grid > grid, #ExpanderBox2 frame > box > grid, #ExpanderBox2 frame > grid > grid, #ExpanderBox2 frame > grid > box,
#ExpanderBox3 > box > checkbutton, #ExpanderBox3 > box > box, #ExpanderBox3 > grid > checkbutton, #ExpanderBox3 > box > grid, #ExpanderBox3 > grid > grid, #ExpanderBox3 frame > box > grid, #ExpanderBox3 frame > grid > grid, #ExpanderBox3 frame > grid > box {
@@ -796,6 +801,7 @@ flowboxchild:selected {
}
/* Sub-tool (MyExpander) background */
+#LocallabToolPanel > box, #LocallabToolPanel > grid,
#ExpanderBox2 > box, #ExpanderBox2 > grid {
background-color: #3B3B3B;
border: 0.0833333333333333em solid #2A2A2A;
@@ -804,10 +810,12 @@ flowboxchild:selected {
padding: 0.25em;
}
+#LocallabToolPanel drawingarea,
#ExpanderBox2 drawingarea {
background-color: #3B3B3B;
}
+#LocallabToolPanel frame > border,
#ExpanderBox2 frame > border {
background-color: #414141;
border: 0.0833333333333333em solid #373737;
@@ -816,10 +824,12 @@ flowboxchild:selected {
padding: 0.25em;
}
+#LocallabToolPanel frame drawingarea,
#ExpanderBox2 frame drawingarea {
background-color: #414141;
}
+#LocallabToolPanel frame frame > border,
#ExpanderBox2 frame frame > border {
background-color: #474747;
border: 0.0833333333333333em solid #3D3D3D;
@@ -828,6 +838,7 @@ flowboxchild:selected {
padding: 0.25em;
}
+#LocallabToolPanel frame frame drawingarea,
#ExpanderBox2 frame frame drawingarea {
background-color: #474747;
}
@@ -854,7 +865,7 @@ flowboxchild:selected {
color: #D8D8D8;
}
-#ExpanderBox2 separator, #ExpanderBox3 separator {
+#LocallabToolPanel separator, #ExpanderBox2 separator, #ExpanderBox3 separator {
color: #292929;
}
diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt
index 0450ee29f..543cd4841 100644
--- a/rtengine/CMakeLists.txt
+++ b/rtengine/CMakeLists.txt
@@ -118,7 +118,9 @@ set(RTENGINESOURCEFILES
impulse_denoise.cc
init.cc
ipdehaze.cc
+ ipgrain.cc
iplab2rgb.cc
+ iplocallab.cc
iplabregions.cc
iplocalcontrast.cc
ipresize.cc
@@ -141,6 +143,7 @@ set(RTENGINESOURCEFILES
myfile.cc
panasonic_decoders.cc
pdaflinesfilter.cc
+ perspectivecorrection.cc
PF_correct_RT.cc
pipettebuffer.cc
pixelshift.cc
diff --git a/rtengine/EdgePreservingDecomposition.cc b/rtengine/EdgePreservingDecomposition.cc
index 04459e88a..34ac2440b 100644
--- a/rtengine/EdgePreservingDecomposition.cc
+++ b/rtengine/EdgePreservingDecomposition.cc
@@ -916,7 +916,7 @@ void EdgePreservingDecomposition::CompressDynamicRange(float *Source, float Scal
float temp;
if(DetailBoost > 0.f) {
- float betemp = expf(-(2.f - DetailBoost + 0.694f)) - 1.f; //0.694 = log(2)
+ float betemp = expf(-(2.f - DetailBoost + 0.693147f)) - 1.f; //0.694 = log(2)
temp = 1.2f * xlogf( -betemp);
} else {
temp = CompressionExponent - 1.0f;
@@ -939,7 +939,7 @@ void EdgePreservingDecomposition::CompressDynamicRange(float *Source, float Scal
cev = xexpf(LVFU(Source[i]) + LVFU(u[i]) * (tempv)) - epsv;
uev = xexpf(LVFU(u[i])) - epsv;
sourcev = xexpf(LVFU(Source[i])) - epsv;
- _mm_storeu_ps( &Source[i], cev + DetailBoostv * (sourcev - uev) );
+ _mm_storeu_ps( &Source[i], cev + DetailBoostv * (sourcev - uev));
}
}
diff --git a/rtengine/FTblockDN.cc b/rtengine/FTblockDN.cc
index 89e4c1b8d..fca26987f 100644
--- a/rtengine/FTblockDN.cc
+++ b/rtengine/FTblockDN.cc
@@ -43,7 +43,6 @@
#include "procparams.h"
#include "rt_math.h"
#include "sleef.h"
-
#include "../rtgui/threadutils.h"
#include "../rtgui/options.h"
@@ -483,11 +482,9 @@ enum nrquality {QUALITY_STANDARD, QUALITY_HIGH};
void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagefloat * dst, Imagefloat * calclum, float * ch_M, float *max_r, float *max_b, bool isRAW, const procparams::DirPyrDenoiseParams & dnparams, const double expcomp, const NoiseCurve & noiseLCurve, const NoiseCurve & noiseCCurve, float &nresi, float &highresi)
{
BENCHFUN
-//#ifdef _DEBUG
MyTime t1e, t2e;
t1e.set();
-//#endif
if (dnparams.luma == 0 && dnparams.chroma == 0 && !dnparams.median && !noiseLCurve && !noiseCCurve) {
//nothing to do; copy src to dst or do nothing in case src == dst
if (src != dst) {
@@ -955,13 +952,8 @@ BENCHFUN
labdn->b[i1][j1] = B_ < 65535.f ? gamcurve[B_] : Color::gammanf(B_ / 65535.f, gam) * 32768.f;
if (((i1 | j1) & 1) == 0) {
- if (numTries == 1) {
- noisevarlum[(i1 >> 1) * width2 + (j1 >> 1)] = useNoiseLCurve ? lumcalc[i >> 1][j >> 1] : noisevarL;
- noisevarchrom[(i1 >> 1) * width2 + (j1 >> 1)] = useNoiseCCurve ? maxNoiseVarab * ccalc[i >> 1][j >> 1] : 1.f;
- } else {
- noisevarlum[(i1 >> 1) * width2 + (j1 >> 1)] = lumcalc[i >> 1][j >> 1];
- noisevarchrom[(i1 >> 1) * width2 + (j1 >> 1)] = ccalc[i >> 1][j >> 1];
- }
+ noisevarlum[(i1 >> 1) * width2 + (j1 >> 1)] = useNoiseLCurve ? lumcalc[i >> 1][j >> 1] : noisevarL;
+ noisevarchrom[(i1 >> 1) * width2 + (j1 >> 1)] = useNoiseCCurve ? maxNoiseVarab * ccalc[i >> 1][j >> 1] : 1.f;
}
//end chroma
@@ -993,13 +985,8 @@ BENCHFUN
labdn->b[i1][j1] = (Y - Z);
if (((i1 | j1) & 1) == 0) {
- if (numTries == 1) {
- noisevarlum[(i1 >> 1)*width2 + (j1 >> 1)] = useNoiseLCurve ? lumcalc[i >> 1][j >> 1] : noisevarL;
- noisevarchrom[(i1 >> 1)*width2 + (j1 >> 1)] = useNoiseCCurve ? maxNoiseVarab * ccalc[i >> 1][j >> 1] : 1.f;
- } else {
- noisevarlum[(i1 >> 1)*width2 + (j1 >> 1)] = lumcalc[i >> 1][j >> 1];
- noisevarchrom[(i1 >> 1)*width2 + (j1 >> 1)] = ccalc[i >> 1][j >> 1];
- }
+ noisevarlum[(i1 >> 1)*width2 + (j1 >> 1)] = useNoiseLCurve ? lumcalc[i >> 1][j >> 1] : noisevarL;
+ noisevarchrom[(i1 >> 1)*width2 + (j1 >> 1)] = useNoiseCCurve ? maxNoiseVarab * ccalc[i >> 1][j >> 1] : 1.f;
}
}
}
@@ -1111,9 +1098,6 @@ BENCHFUN
}
if (execwavelet) {//gain time if user choose only median sliders L <=1 slider chrom master < 1
- wavelet_decomposition* Ldecomp;
- wavelet_decomposition* adecomp;
-
int levwav = 5;
float maxreal = max(realred, realblue);
@@ -1154,9 +1138,9 @@ BENCHFUN
levwav = min(maxlev2, levwav);
// if (settings->verbose) printf("levwavelet=%i noisevarA=%f noisevarB=%f \n",levwav, noisevarab_r, noisevarab_b);
- Ldecomp = new wavelet_decomposition(labdn->L[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels));
+ const std::unique_ptr Ldecomp(new wavelet_decomposition(labdn->L[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels)));
- if (Ldecomp->memoryAllocationFailed) {
+ if (Ldecomp->memory_allocation_failed()) {
memoryAllocationFailed = true;
}
@@ -1175,7 +1159,7 @@ BENCHFUN
int Wlvl_L = Ldecomp->level_W(lvl);
int Hlvl_L = Ldecomp->level_H(lvl);
- float ** WavCoeffs_L = Ldecomp->level_coeffs(lvl);
+ const float* const* WavCoeffs_L = Ldecomp->level_coeffs(lvl);
if (!denoiseMethodRgb) {
madL[lvl][dir - 1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L * Hlvl_L));
@@ -1192,9 +1176,9 @@ BENCHFUN
float chmaxresid = 0.f;
float chmaxresidtemp = 0.f;
- adecomp = new wavelet_decomposition(labdn->a[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels));
+ std::unique_ptr adecomp(new wavelet_decomposition(labdn->a[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels)));
- if (adecomp->memoryAllocationFailed) {
+ if (adecomp->memory_allocation_failed()) {
memoryAllocationFailed = true;
}
@@ -1226,12 +1210,12 @@ BENCHFUN
adecomp->reconstruct(labdn->a[0]);
}
- delete adecomp;
+ adecomp.reset();
if (!memoryAllocationFailed) {
- wavelet_decomposition* bdecomp = new wavelet_decomposition(labdn->b[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels));
+ std::unique_ptr bdecomp(new wavelet_decomposition(labdn->b[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels)));
- if (bdecomp->memoryAllocationFailed) {
+ if (bdecomp->memory_allocation_failed()) {
memoryAllocationFailed = true;
}
@@ -1266,7 +1250,7 @@ BENCHFUN
bdecomp->reconstruct(labdn->b[0]);
}
- delete bdecomp;
+ bdecomp.reset();
if (!memoryAllocationFailed) {
if (denoiseLuminance) {
@@ -1306,8 +1290,6 @@ BENCHFUN
}
}
}
-
- delete Ldecomp;
}
if (!memoryAllocationFailed) {
@@ -2035,14 +2017,10 @@ BENCHFUN
delete[] ccalc;
}
-//#ifdef _DEBUG
if (settings->verbose) {
t2e.set();
printf("Denoise performed in %d usec:\n", t2e.etime(t1e));
}
-
-//#endif
-
}//end of main RGB_denoise
@@ -2194,7 +2172,7 @@ void ImProcFunctions::Noise_residualAB(const wavelet_decomposition &WaveletCoeff
const int Wlvl_ab = WaveletCoeffs_ab.level_W(lvl);
const int Hlvl_ab = WaveletCoeffs_ab.level_H(lvl);
- float ** WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl);
+ const float* const* WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl);
const float madC = SQR(denoiseMethodRgb ? MadRgb(WavCoeffs_ab[dir], Wlvl_ab * Hlvl_ab) : Mad(WavCoeffs_ab[dir], Wlvl_ab * Hlvl_ab));
resid += madC;
@@ -2209,7 +2187,7 @@ void ImProcFunctions::Noise_residualAB(const wavelet_decomposition &WaveletCoeff
chmaxresid = maxresid;
}
-bool ImProcFunctions::WaveletDenoiseAll_BiShrinkL(const wavelet_decomposition &WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels)
+bool ImProcFunctions::WaveletDenoiseAll_BiShrinkL(wavelet_decomposition& WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels)
{
int maxlvl = min(WaveletCoeffs_L.maxlevel(), 5);
const float eps = 0.01f;
@@ -2260,7 +2238,7 @@ bool ImProcFunctions::WaveletDenoiseAll_BiShrinkL(const wavelet_decomposition &W
int Wlvl_L = WaveletCoeffs_L.level_W(lvl);
int Hlvl_L = WaveletCoeffs_L.level_H(lvl);
- float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl);
+ float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl);
if (lvl == maxlvl - 1) {
// int edge = 0;
@@ -2392,8 +2370,7 @@ bool ImProcFunctions::WaveletDenoiseAll_BiShrinkL(const wavelet_decomposition &W
}
-bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(const wavelet_decomposition &WaveletCoeffs_L, const wavelet_decomposition &WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float *variC, int local, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, int denoiseNestedLevels)
-
+bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(wavelet_decomposition& WaveletCoeffs_L, wavelet_decomposition& WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float *variC, int local, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, int denoiseNestedLevels)
{
int maxlvl = WaveletCoeffs_L.maxlevel();
@@ -2449,7 +2426,7 @@ bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(const wavelet_decomposition &
// compute median absolute deviation (MAD) of detail coefficients as robust noise estimator
int Wlvl_ab = WaveletCoeffs_ab.level_W(lvl);
int Hlvl_ab = WaveletCoeffs_ab.level_H(lvl);
- float ** WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl);
+ const float* const* WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl);
if (!denoiseMethodRgb) {
madab[lvl][dir - 1] = SQR(Mad(WavCoeffs_ab[dir], Wlvl_ab * Hlvl_ab));
@@ -2468,8 +2445,8 @@ bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(const wavelet_decomposition &
int Wlvl_ab = WaveletCoeffs_ab.level_W(lvl);
int Hlvl_ab = WaveletCoeffs_ab.level_H(lvl);
- float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl);
- float ** WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl);
+ float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl);
+ float* const* WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl);
if (lvl == maxlvl - 1) {
ShrinkAllAB(WaveletCoeffs_L, WaveletCoeffs_ab, buffer, lvl, dir, noisevarchrom, noisevar_ab, useNoiseCCurve, autoch, denoiseMethodRgb, madL[lvl], nullptr, 0, madab[lvl], true);
@@ -2563,7 +2540,7 @@ bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(const wavelet_decomposition &
}
-bool ImProcFunctions::WaveletDenoiseAllL(const wavelet_decomposition &WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels)//mod JD
+bool ImProcFunctions::WaveletDenoiseAllL(wavelet_decomposition& WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels)//mod JD
{
@@ -2624,7 +2601,7 @@ bool ImProcFunctions::WaveletDenoiseAllL(const wavelet_decomposition &WaveletCoe
}
-bool ImProcFunctions::WaveletDenoiseAllAB(const wavelet_decomposition &WaveletCoeffs_L, const wavelet_decomposition &WaveletCoeffs_ab,
+bool ImProcFunctions::WaveletDenoiseAllAB(wavelet_decomposition& WaveletCoeffs_L, wavelet_decomposition& WaveletCoeffs_ab,
float *noisevarchrom, float madL[8][3], float *variC, int local, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, int denoiseNestedLevels)//mod JD
{
@@ -2688,7 +2665,7 @@ bool ImProcFunctions::WaveletDenoiseAllAB(const wavelet_decomposition &WaveletCo
-void ImProcFunctions::ShrinkAllL(const wavelet_decomposition &WaveletCoeffs_L, float **buffer, int level, int dir,
+void ImProcFunctions::ShrinkAllL(wavelet_decomposition& WaveletCoeffs_L, float **buffer, int level, int dir,
float *noisevarlum, float * madL, float * vari, int edge)
{
@@ -2702,7 +2679,7 @@ void ImProcFunctions::ShrinkAllL(const wavelet_decomposition &WaveletCoeffs_L, f
const int W_L = WaveletCoeffs_L.level_W(level);
const int H_L = WaveletCoeffs_L.level_H(level);
- float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(level);
+ float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(level);
const float mad_L = madL[dir - 1] ;
const float levelFactor = mad_L * 5.f / static_cast(level + 1);
@@ -2779,7 +2756,7 @@ void ImProcFunctions::ShrinkAllL(const wavelet_decomposition &WaveletCoeffs_L, f
}
-void ImProcFunctions::ShrinkAllAB(const wavelet_decomposition & WaveletCoeffs_L, const wavelet_decomposition & WaveletCoeffs_ab, float **buffer, int level, int dir,
+void ImProcFunctions::ShrinkAllAB(wavelet_decomposition& WaveletCoeffs_L, wavelet_decomposition& WaveletCoeffs_ab, float **buffer, int level, int dir,
float * noisevarchrom, float noisevar_ab, const bool useNoiseCCurve, bool autoch,
bool denoiseMethodRgb, float * madL, float * variC, int local, float * madaab, bool madCalculated)
@@ -2798,8 +2775,8 @@ void ImProcFunctions::ShrinkAllAB(const wavelet_decomposition & WaveletCoeffs_L,
int W_ab = WaveletCoeffs_ab.level_W(level);
int H_ab = WaveletCoeffs_ab.level_H(level);
- float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(level);
- float ** WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(level);
+ float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(level);
+ float* const* WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(level);
float madab;
float mad_L = madL[dir - 1];
@@ -2918,7 +2895,7 @@ void ImProcFunctions::ShrinkAllAB(const wavelet_decomposition & WaveletCoeffs_L,
delete [] nvc;
}
-void ImProcFunctions::ShrinkAll_info(float ** WavCoeffs_a, float ** WavCoeffs_b,
+void ImProcFunctions::ShrinkAll_info(const float* const* WavCoeffs_a, const float* const* WavCoeffs_b,
int W_ab, int H_ab, float **noisevarlum, float **noisevarchrom, float **noisevarhue, float & chaut, int &Nb, float & redaut, float & blueaut,
float & maxredaut, float & maxblueaut, float & minredaut, float & minblueaut, int schoice, int lvl, float & chromina, float & sigma, float & lumema, float & sigma_L, float & redyel, float & skinc, float & nsknc,
float & maxchred, float & maxchblue, float & minchred, float & minchblue, int &nb, float & chau, float & chred, float & chblue, bool denoiseMethodRgb)
@@ -3048,8 +3025,8 @@ void ImProcFunctions::WaveletDenoiseAll_info(int levwav, const wavelet_decomposi
int Wlvl_ab = WaveletCoeffs_a.level_W(lvl);
int Hlvl_ab = WaveletCoeffs_a.level_H(lvl);
- float ** WavCoeffs_a = WaveletCoeffs_a.level_coeffs(lvl);
- float ** WavCoeffs_b = WaveletCoeffs_b.level_coeffs(lvl);
+ const float* const* WavCoeffs_a = WaveletCoeffs_a.level_coeffs(lvl);
+ const float* const* WavCoeffs_b = WaveletCoeffs_b.level_coeffs(lvl);
ShrinkAll_info(WavCoeffs_a, WavCoeffs_b, Wlvl_ab, Hlvl_ab,
noisevarlum, noisevarchrom, noisevarhue, chaut, Nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut,
diff --git a/rtengine/ashift_dt.c b/rtengine/ashift_dt.c
new file mode 100644
index 000000000..3894a1c1b
--- /dev/null
+++ b/rtengine/ashift_dt.c
@@ -0,0 +1,5127 @@
+/* -*- C++ -*-
+ *
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2019 Alberto Griggio
+ *
+ * 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 .
+ */
+
+using namespace std;
+
+// taken from darktable (src/iop/ashift.c)
+/*
+ This file is part of darktable,
+ copyright (c) 2016 Ulrich Pegelow.
+
+ darktable 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.
+
+ darktable 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 darktable. If not, see .
+*/
+
+// Inspiration to this module comes from the program ShiftN (http://www.shiftn.de) by
+// Marcus Hebel.
+
+// Thanks to Marcus for his support when implementing part of the ShiftN functionality
+// to darktable.
+
+#define ROTATION_RANGE 10 // allowed min/max default range for rotation parameter
+#define ROTATION_RANGE_SOFT 20 // allowed min/max range for rotation parameter with manual adjustment
+#define LENSSHIFT_RANGE 0.5 // allowed min/max default range for lensshift parameters
+#define LENSSHIFT_RANGE_SOFT 1 // allowed min/max range for lensshift parameters with manual adjustment
+#define SHEAR_RANGE 0.2 // allowed min/max range for shear parameter
+#define SHEAR_RANGE_SOFT 0.5 // allowed min/max range for shear parameter with manual adjustment
+#define CAMERA_ANGLE_RANGE_SOFT 80
+#define MIN_LINE_LENGTH 5 // the minimum length of a line in pixels to be regarded as relevant
+#define MAX_TANGENTIAL_DEVIATION 30 // by how many degrees a line may deviate from the +/-180 and +/-90 to be regarded as relevant
+#define LSD_SCALE 0.99 // LSD: scaling factor for line detection
+#define LSD_SIGMA_SCALE 0.6 // LSD: sigma for Gaussian filter is computed as sigma = sigma_scale/scale
+#define LSD_QUANT 2.0 // LSD: bound to the quantization error on the gradient norm
+#define LSD_ANG_TH 22.5 // LSD: gradient angle tolerance in degrees
+#define LSD_LOG_EPS 0.0 // LSD: detection threshold: -log10(NFA) > log_eps
+#define LSD_DENSITY_TH 0.7 // LSD: minimal density of region points in rectangle
+#define LSD_N_BINS 1024 // LSD: number of bins in pseudo-ordering of gradient modulus
+#define LSD_GAMMA 0.45 // gamma correction to apply on raw images prior to line detection
+#define RANSAC_RUNS 400 // how many iterations to run in ransac
+#define RANSAC_EPSILON 2 // starting value for ransac epsilon (in -log10 units)
+#define RANSAC_EPSILON_STEP 1 // step size of epsilon optimization (log10 units)
+#define RANSAC_ELIMINATION_RATIO 60 // percentage of lines we try to eliminate as outliers
+#define RANSAC_OPTIMIZATION_STEPS 5 // home many steps to optimize epsilon
+#define RANSAC_OPTIMIZATION_DRY_RUNS 50 // how man runs per optimization steps
+#define RANSAC_HURDLE 5 // hurdle rate: the number of lines below which we do a complete permutation instead of random sampling
+#define MINIMUM_FITLINES 4 // minimum number of lines needed for automatic parameter fit
+#define NMS_EPSILON 1e-3 // break criterion for Nelder-Mead simplex
+#define NMS_SCALE 1.0 // scaling factor for Nelder-Mead simplex
+#define NMS_ITERATIONS 400 // number of iterations for Nelder-Mead simplex
+#define NMS_CROP_EPSILON 100.0 // break criterion for Nelder-Mead simplex on crop fitting
+#define NMS_CROP_SCALE 0.5 // scaling factor for Nelder-Mead simplex on crop fitting
+#define NMS_CROP_ITERATIONS 100 // number of iterations for Nelder-Mead simplex on crop fitting
+#define NMS_ALPHA 1.0 // reflection coefficient for Nelder-Mead simplex
+#define NMS_BETA 0.5 // contraction coefficient for Nelder-Mead simplex
+#define NMS_GAMMA 2.0 // expansion coefficient for Nelder-Mead simplex
+#define DEFAULT_F_LENGTH 28.0 // focal length we assume if no exif data are available
+
+/* // define to get debugging output */
+/* #undef ASHIFT_DEBUG */
+
+#define SQR(a) ((a) * (a))
+
+// For line detection we use the LSD algorithm as published by Rafael Grompone:
+//
+// "LSD: a Line Segment Detector" by Rafael Grompone von Gioi,
+// Jeremie Jakubowicz, Jean-Michel Morel, and Gregory Randall,
+// Image Processing On Line, 2012. DOI:10.5201/ipol.2012.gjmr-lsd
+// http://dx.doi.org/10.5201/ipol.2012.gjmr-lsd
+#include "ashift_lsd.c"
+
+// For parameter optimization we are using the Nelder-Mead simplex method
+// implemented by Michael F. Hutt.
+#include "ashift_nmsimplex.c"
+
+#include "homogeneouscoordinates.h"
+
+
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+DT_MODULE_INTROSPECTION(4, dt_iop_ashift_params_t)
+
+
+const char *name()
+{
+ return _("perspective correction");
+}
+
+int flags()
+{
+ return IOP_FLAGS_ALLOW_TILING | IOP_FLAGS_TILING_FULL_ROI | IOP_FLAGS_ONE_INSTANCE;
+}
+
+int groups()
+{
+ return dt_iop_get_group("perspective correction", IOP_GROUP_CORRECT);
+}
+
+int operation_tags()
+{
+ return IOP_TAG_DISTORT;
+}
+
+int operation_tags_filter()
+{
+ // switch off clipping and decoration, we want to see the full image.
+ return IOP_TAG_DECORATION | IOP_TAG_CLIPPING;
+}
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+typedef enum dt_iop_ashift_homodir_t
+{
+ ASHIFT_HOMOGRAPH_FORWARD,
+ ASHIFT_HOMOGRAPH_INVERTED
+} dt_iop_ashift_homodir_t;
+
+//typedef enum dt_iop_ashift_linetype_t
+enum
+{
+ ASHIFT_LINE_IRRELEVANT = 0, // the line is found to be not interesting
+ // eg. too short, or not horizontal or vertical
+ ASHIFT_LINE_RELEVANT = 1 << 0, // the line is relevant for us
+ ASHIFT_LINE_DIRVERT = 1 << 1, // the line is (mostly) vertical, else (mostly) horizontal
+ ASHIFT_LINE_SELECTED = 1 << 2, // the line is selected for fitting
+ ASHIFT_LINE_VERTICAL_NOT_SELECTED = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_DIRVERT,
+ ASHIFT_LINE_HORIZONTAL_NOT_SELECTED = ASHIFT_LINE_RELEVANT,
+ ASHIFT_LINE_VERTICAL_SELECTED = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_DIRVERT | ASHIFT_LINE_SELECTED,
+ ASHIFT_LINE_HORIZONTAL_SELECTED = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED,
+ ASHIFT_LINE_MASK = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_DIRVERT | ASHIFT_LINE_SELECTED
+}; //dt_iop_ashift_linetype_t;
+typedef unsigned int dt_iop_ashift_linetype_t;
+
+typedef enum dt_iop_ashift_linecolor_t
+{
+ ASHIFT_LINECOLOR_GREY = 0,
+ ASHIFT_LINECOLOR_GREEN = 1,
+ ASHIFT_LINECOLOR_RED = 2,
+ ASHIFT_LINECOLOR_BLUE = 3,
+ ASHIFT_LINECOLOR_YELLOW = 4
+} dt_iop_ashift_linecolor_t;
+
+//typedef enum dt_iop_ashift_fitaxis_t
+enum
+{
+ ASHIFT_FIT_NONE = 0, // none
+ ASHIFT_FIT_ROTATION = 1 << 0, // flag indicates to fit rotation angle
+ ASHIFT_FIT_LENS_VERT = 1 << 1, // flag indicates to fit vertical lens shift
+ ASHIFT_FIT_LENS_HOR = 1 << 2, // flag indicates to fit horizontal lens shift
+ ASHIFT_FIT_SHEAR = 1 << 3, // flag indicates to fit shear parameter
+ ASHIFT_FIT_LINES_VERT = 1 << 4, // use vertical lines for fitting
+ ASHIFT_FIT_LINES_HOR = 1 << 5, // use horizontal lines for fitting
+ ASHIFT_FIT_LENS_BOTH = ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LENS_HOR,
+ ASHIFT_FIT_LINES_BOTH = ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR,
+ ASHIFT_FIT_VERTICALLY = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LINES_VERT,
+ ASHIFT_FIT_HORIZONTALLY = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LENS_HOR | ASHIFT_FIT_LINES_HOR,
+ ASHIFT_FIT_BOTH = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LENS_HOR |
+ ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR,
+ ASHIFT_FIT_VERTICALLY_NO_ROTATION = ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LINES_VERT,
+ ASHIFT_FIT_HORIZONTALLY_NO_ROTATION = ASHIFT_FIT_LENS_HOR | ASHIFT_FIT_LINES_HOR,
+ ASHIFT_FIT_BOTH_NO_ROTATION = ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LENS_HOR |
+ ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR,
+ ASHIFT_FIT_BOTH_SHEAR = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LENS_HOR |
+ ASHIFT_FIT_SHEAR | ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR,
+ ASHIFT_FIT_ROTATION_VERTICAL_LINES = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LINES_VERT,
+ ASHIFT_FIT_ROTATION_HORIZONTAL_LINES = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LINES_HOR,
+ ASHIFT_FIT_ROTATION_BOTH_LINES = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR,
+ ASHIFT_FIT_FLIP = ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LENS_HOR | ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR
+}; //dt_iop_ashift_fitaxis_t;
+typedef unsigned int dt_iop_ashift_fitaxis_t;
+
+typedef enum dt_iop_ashift_nmsresult_t
+{
+ NMS_SUCCESS = 0,
+ NMS_NOT_ENOUGH_LINES = 1,
+ NMS_DID_NOT_CONVERGE = 2,
+ NMS_INSANE = 3
+} dt_iop_ashift_nmsresult_t;
+
+typedef enum dt_iop_ashift_enhance_t
+{
+ ASHIFT_ENHANCE_NONE = 0,
+ ASHIFT_ENHANCE_EDGES = 1 << 0,
+ ASHIFT_ENHANCE_DETAIL = 1 << 1,
+ ASHIFT_ENHANCE_HORIZONTAL = 0x100,
+ ASHIFT_ENHANCE_VERTICAL = 0x200
+} dt_iop_ashift_enhance_t;
+
+typedef enum dt_iop_ashift_mode_t
+{
+ ASHIFT_MODE_GENERIC = 0,
+ ASHIFT_MODE_SPECIFIC = 1
+} dt_iop_ashift_mode_t;
+
+typedef enum dt_iop_ashift_crop_t
+{
+ ASHIFT_CROP_OFF = 0,
+ ASHIFT_CROP_LARGEST = 1,
+ ASHIFT_CROP_ASPECT = 2
+} dt_iop_ashift_crop_t;
+
+typedef enum dt_iop_ashift_bounding_t
+{
+ ASHIFT_BOUNDING_OFF = 0,
+ ASHIFT_BOUNDING_SELECT = 1,
+ ASHIFT_BOUNDING_DESELECT = 2
+} dt_iop_ashift_bounding_t;
+
+typedef enum dt_iop_ashift_jobcode_t
+{
+ ASHIFT_JOBCODE_NONE = 0,
+ ASHIFT_JOBCODE_GET_STRUCTURE = 1,
+ ASHIFT_JOBCODE_FIT = 2
+} dt_iop_ashift_jobcode_t;
+
+typedef struct dt_iop_ashift_params1_t
+{
+ float rotation;
+ float lensshift_v;
+ float lensshift_h;
+ int toggle;
+} dt_iop_ashift_params1_t;
+
+typedef struct dt_iop_ashift_params2_t
+{
+ float rotation;
+ float lensshift_v;
+ float lensshift_h;
+ float f_length;
+ float crop_factor;
+ float orthocorr;
+ float aspect;
+ dt_iop_ashift_mode_t mode;
+ int toggle;
+} dt_iop_ashift_params2_t;
+
+typedef struct dt_iop_ashift_params3_t
+{
+ float rotation;
+ float lensshift_v;
+ float lensshift_h;
+ float f_length;
+ float crop_factor;
+ float orthocorr;
+ float aspect;
+ dt_iop_ashift_mode_t mode;
+ int toggle;
+ dt_iop_ashift_crop_t cropmode;
+ float cl;
+ float cr;
+ float ct;
+ float cb;
+} dt_iop_ashift_params3_t;
+
+typedef struct dt_iop_ashift_params_t
+{
+ float rotation;
+ float lensshift_v;
+ float lensshift_h;
+ float shear;
+ float f_length;
+ float crop_factor;
+ float orthocorr;
+ float aspect;
+ dt_iop_ashift_mode_t mode;
+ int toggle;
+ dt_iop_ashift_crop_t cropmode;
+ float cl;
+ float cr;
+ float ct;
+ float cb;
+ float camera_pitch;
+ float camera_yaw;
+} dt_iop_ashift_params_t;
+
+typedef struct dt_iop_ashift_line_t
+{
+ float p1[3];
+ float p2[3];
+ float length;
+ float width;
+ float weight;
+ dt_iop_ashift_linetype_t type;
+ // homogeneous coordinates:
+ float L[3];
+} dt_iop_ashift_line_t;
+
+typedef struct dt_iop_ashift_points_idx_t
+{
+ size_t offset;
+ int length;
+ int near;
+ int bounded;
+ dt_iop_ashift_linetype_t type;
+ dt_iop_ashift_linecolor_t color;
+ // bounding box:
+ float bbx, bby, bbX, bbY;
+} dt_iop_ashift_points_idx_t;
+
+typedef struct dt_iop_ashift_fit_params_t
+{
+ int params_count;
+ dt_iop_ashift_linetype_t linetype;
+ dt_iop_ashift_linetype_t linemask;
+ dt_iop_ashift_line_t *lines;
+ int lines_count;
+ int width;
+ int height;
+ float weight;
+ float f_length_kb;
+ float orthocorr;
+ float aspect;
+ float rotation;
+ float lensshift_v;
+ float lensshift_h;
+ float shear;
+ float camera_pitch;
+ float camera_yaw;
+ float rotation_range;
+ float lensshift_v_range;
+ float lensshift_h_range;
+ float shear_range;
+ float camera_pitch_range;
+ float camera_yaw_range;
+} dt_iop_ashift_fit_params_t;
+
+typedef struct dt_iop_ashift_cropfit_params_t
+{
+ int width;
+ int height;
+ float x;
+ float y;
+ float alpha;
+ float homograph[3][3];
+ float edges[4][3];
+} dt_iop_ashift_cropfit_params_t;
+
+typedef struct dt_iop_ashift_gui_data_t
+{
+ /* GtkWidget *rotation; */
+ /* GtkWidget *lensshift_v; */
+ /* GtkWidget *lensshift_h; */
+ /* GtkWidget *shear; */
+ /* GtkWidget *guide_lines; */
+ /* GtkWidget *cropmode; */
+ /* GtkWidget *mode; */
+ /* GtkWidget *f_length; */
+ /* GtkWidget *crop_factor; */
+ /* GtkWidget *orthocorr; */
+ /* GtkWidget *aspect; */
+ /* GtkWidget *fit_v; */
+ /* GtkWidget *fit_h; */
+ /* GtkWidget *fit_both; */
+ /* GtkWidget *structure; */
+ /* GtkWidget *clean; */
+ /* GtkWidget *eye; */
+ int lines_suppressed;
+ int fitting;
+ int isflipped;
+ int show_guides;
+ int isselecting;
+ int isdeselecting;
+ dt_iop_ashift_bounding_t isbounding;
+ float near_delta;
+ int selecting_lines_version;
+ float rotation_range;
+ float lensshift_v_range;
+ float lensshift_h_range;
+ float shear_range;
+ float camera_pitch_range;
+ float camera_yaw_range;
+ dt_iop_ashift_line_t *lines;
+ int lines_in_width;
+ int lines_in_height;
+ int lines_x_off;
+ int lines_y_off;
+ int lines_count;
+ int vertical_count;
+ int horizontal_count;
+ int lines_version;
+ float vertical_weight;
+ float horizontal_weight;
+ float *points;
+ dt_iop_ashift_points_idx_t *points_idx;
+ int points_lines_count;
+ int points_version;
+ float *buf;
+ int buf_width;
+ int buf_height;
+ int buf_x_off;
+ int buf_y_off;
+ float buf_scale;
+ uint64_t lines_hash;
+ uint64_t grid_hash;
+ uint64_t buf_hash;
+ dt_iop_ashift_fitaxis_t lastfit;
+ float lastx;
+ float lasty;
+ float crop_cx;
+ float crop_cy;
+ dt_iop_ashift_jobcode_t jobcode;
+ int jobparams;
+ /* dt_pthread_mutex_t lock; */
+ MyMutex lock;
+ gboolean adjust_crop;
+} dt_iop_ashift_gui_data_t;
+
+typedef struct dt_iop_ashift_data_t
+{
+ float rotation;
+ float lensshift_v;
+ float lensshift_h;
+ float shear;
+ float f_length_kb;
+ float orthocorr;
+ float aspect;
+ float cl;
+ float cr;
+ float ct;
+ float cb;
+} dt_iop_ashift_data_t;
+
+typedef struct dt_iop_ashift_global_data_t
+{
+ int kernel_ashift_bilinear;
+ int kernel_ashift_bicubic;
+ int kernel_ashift_lanczos2;
+ int kernel_ashift_lanczos3;
+} dt_iop_ashift_global_data_t;
+
+typedef struct dt_iop_module_t
+{
+ dt_iop_ashift_gui_data_t *gui_data;
+ int is_raw;
+} dt_iop_module_t;
+
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
+ void *new_params, const int new_version)
+{
+ if(old_version == 1 && new_version == 4)
+ {
+ const dt_iop_ashift_params1_t *old = old_params;
+ dt_iop_ashift_params_t *new = new_params;
+ new->rotation = old->rotation;
+ new->lensshift_v = old->lensshift_v;
+ new->lensshift_h = old->lensshift_h;
+ new->shear = 0.0f;
+ new->toggle = old->toggle;
+ new->f_length = DEFAULT_F_LENGTH;
+ new->crop_factor = 1.0f;
+ new->orthocorr = 100.0f;
+ new->aspect = 1.0f;
+ new->mode = ASHIFT_MODE_GENERIC;
+ new->cropmode = ASHIFT_CROP_OFF;
+ new->cl = 0.0f;
+ new->cr = 1.0f;
+ new->ct = 0.0f;
+ new->cb = 1.0f;
+ return 0;
+ }
+ if(old_version == 2 && new_version == 4)
+ {
+ const dt_iop_ashift_params2_t *old = old_params;
+ dt_iop_ashift_params_t *new = new_params;
+ new->rotation = old->rotation;
+ new->lensshift_v = old->lensshift_v;
+ new->lensshift_h = old->lensshift_h;
+ new->shear = 0.0f;
+ new->toggle = old->toggle;
+ new->f_length = old->f_length;
+ new->crop_factor = old->crop_factor;
+ new->orthocorr = old->orthocorr;
+ new->aspect = old->aspect;
+ new->mode = old->mode;
+ new->cropmode = ASHIFT_CROP_OFF;
+ new->cl = 0.0f;
+ new->cr = 1.0f;
+ new->ct = 0.0f;
+ new->cb = 1.0f;
+ return 0;
+ }
+ if(old_version == 3 && new_version == 4)
+ {
+ const dt_iop_ashift_params3_t *old = old_params;
+ dt_iop_ashift_params_t *new = new_params;
+ new->rotation = old->rotation;
+ new->lensshift_v = old->lensshift_v;
+ new->lensshift_h = old->lensshift_h;
+ new->shear = 0.0f;
+ new->toggle = old->toggle;
+ new->f_length = old->f_length;
+ new->crop_factor = old->crop_factor;
+ new->orthocorr = old->orthocorr;
+ new->aspect = old->aspect;
+ new->mode = old->mode;
+ new->cropmode = old->cropmode;
+ new->cl = old->cl;
+ new->cr = old->cr;
+ new->ct = old->ct;
+ new->cb = old->cb;
+ return 0;
+ }
+
+ return 1;
+}
+
+void init_key_accels(dt_iop_module_so_t *self)
+{
+ dt_accel_register_slider_iop(self, FALSE, NC_("accel", "rotation"));
+ dt_accel_register_slider_iop(self, FALSE, NC_("accel", "lens shift (v)"));
+ dt_accel_register_slider_iop(self, FALSE, NC_("accel", "lens shift (h)"));
+ dt_accel_register_slider_iop(self, FALSE, NC_("accel", "shear"));
+}
+
+void connect_key_accels(dt_iop_module_t *self)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ dt_accel_connect_slider_iop(self, "rotation", GTK_WIDGET(g->rotation));
+ dt_accel_connect_slider_iop(self, "lens shift (v)", GTK_WIDGET(g->lensshift_v));
+ dt_accel_connect_slider_iop(self, "lens shift (h)", GTK_WIDGET(g->lensshift_h));
+ dt_accel_connect_slider_iop(self, "shear", GTK_WIDGET(g->shear));
+}
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+// multiply 3x3 matrix with 3x1 vector
+// dst needs to be different from v
+static inline void mat3mulv(float *dst, const float *const mat, const float *const v)
+{
+ for(int k = 0; k < 3; k++)
+ {
+ float x = 0.0f;
+ for(int i = 0; i < 3; i++) x += mat[3 * k + i] * v[i];
+ dst[k] = x;
+ }
+}
+
+// multiply two 3x3 matrices
+// dst needs to be different from m1 and m2
+static inline void mat3mul(float *dst, const float *const m1, const float *const m2)
+{
+ for(int k = 0; k < 3; k++)
+ {
+ for(int i = 0; i < 3; i++)
+ {
+ float x = 0.0f;
+ for(int j = 0; j < 3; j++) x += m1[3 * k + j] * m2[3 * j + i];
+ dst[3 * k + i] = x;
+ }
+ }
+}
+
+// normalized product of two 3x1 vectors
+// dst needs to be different from v1 and v2
+static inline void vec3prodn(float *dst, const float *const v1, const float *const v2)
+{
+ const float l1 = v1[1] * v2[2] - v1[2] * v2[1];
+ const float l2 = v1[2] * v2[0] - v1[0] * v2[2];
+ const float l3 = v1[0] * v2[1] - v1[1] * v2[0];
+
+ // normalize so that l1^2 + l2^2 + l3^3 = 1
+ const float sq = sqrt(l1 * l1 + l2 * l2 + l3 * l3);
+
+ const float f = sq > 0.0f ? 1.0f / sq : 1.0f;
+
+ dst[0] = l1 * f;
+ dst[1] = l2 * f;
+ dst[2] = l3 * f;
+}
+
+// normalize a 3x1 vector so that x^2 + y^2 + z^2 = 1
+// dst and v may be the same
+static inline void vec3norm(float *dst, const float *const v)
+{
+ const float sq = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+
+ // special handling for an all-zero vector
+ const float f = sq > 0.0f ? 1.0f / sq : 1.0f;
+
+ dst[0] = v[0] * f;
+ dst[1] = v[1] * f;
+ dst[2] = v[2] * f;
+}
+
+// normalize a 3x1 vector so that x^2 + y^2 = 1; a useful normalization for
+// lines in homogeneous coordinates
+// dst and v may be the same
+static inline void vec3lnorm(float *dst, const float *const v)
+{
+ const float sq = sqrt(v[0] * v[0] + v[1] * v[1]);
+
+ // special handling for a point vector of the image center
+ const float f = sq > 0.0f ? 1.0f / sq : 1.0f;
+
+ dst[0] = v[0] * f;
+ dst[1] = v[1] * f;
+ dst[2] = v[2] * f;
+}
+
+
+// scalar product of two 3x1 vectors
+static inline float vec3scalar(const float *const v1, const float *const v2)
+{
+ return (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]);
+}
+
+// check if 3x1 vector is (very close to) null
+static inline int vec3isnull(const float *const v)
+{
+ const float eps = 1e-10f;
+ return (fabs(v[0]) < eps && fabs(v[1]) < eps && fabs(v[2]) < eps);
+}
+
+#ifdef ASHIFT_DEBUG
+static void print_roi(const dt_iop_roi_t *roi, const char *label)
+{
+ printf("{ %5d %5d %5d %5d %.6f } %s\n", roi->x, roi->y, roi->width, roi->height, roi->scale, label);
+}
+#endif
+
+#define MAT3SWAP(a, b) { float (*tmp)[3] = (a); (a) = (b); (b) = tmp; }
+
+/*
+static void homography(float *homograph, const float angle, const float shift_v, const float shift_h,
+ const float shear, const float f_length_kb, const float orthocorr, const float aspect,
+ const int width, const int height, dt_iop_ashift_homodir_t dir)
+*/
+static void homography(float *homograph, const float angle, const float shift_v,
+ const float shift_h, const float shear, const float camera_pitch, const
+ float camera_yaw, const float f_length_kb, const float orthocorr, const
+ float aspect, const int width, const int height, dt_iop_ashift_homodir_t
+ dir)
+{
+ // calculate homograph that combines all translations, rotations
+ // and warping into one single matrix operation.
+ // this is heavily leaning on ShiftN where the homographic matrix expects
+ // input in (y : x : 1) format. in the darktable world we want to keep the
+ // (x : y : 1) convention. therefore we need to flip coordinates first and
+ // make sure that output is in correct format after corrections are applied.
+
+ const float u = width;
+ const float v = height;
+ const float rot = -M_PI * angle / 180.0f;
+ const float pitch = M_PI * camera_pitch / 180.0f;
+ const float yaw = M_PI * camera_yaw / 180.0f;
+
+ /*
+ const float phi = M_PI * angle / 180.0f;
+ const float cosi = cos(phi);
+ const float sini = sin(phi);
+ const float ascale = sqrt(aspect);
+
+ // most of this comes from ShiftN
+ const float f_global = f_length_kb;
+ const float horifac = 1.0f - orthocorr / 100.0f;
+ const float exppa_v = exp(shift_v);
+ const float fdb_v = f_global / (14.4f + (v / u - 1) * 7.2f);
+ const float rad_v = fdb_v * (exppa_v - 1.0f) / (exppa_v + 1.0f);
+ const float alpha_v = CLAMP(atan(rad_v), -1.5f, 1.5f);
+ const float rt_v = sin(0.5f * alpha_v);
+ const float r_v = fmax(0.1f, 2.0f * (horifac - 1.0f) * rt_v * rt_v + 1.0f);
+
+ const float vertifac = 1.0f - orthocorr / 100.0f;
+ const float exppa_h = exp(shift_h);
+ const float fdb_h = f_global / (14.4f + (u / v - 1) * 7.2f);
+ const float rad_h = fdb_h * (exppa_h - 1.0f) / (exppa_h + 1.0f);
+ const float alpha_h = CLAMP(atan(rad_h), -1.5f, 1.5f);
+ const float rt_h = sin(0.5f * alpha_h);
+ const float r_h = fmax(0.1f, 2.0f * (vertifac - 1.0f) * rt_h * rt_h + 1.0f);
+ */
+
+ const float f = f_length_kb * (sqrt(u*u + v*v) / sqrt(36.0*36.0 + 24.0*24.0));
+
+ // three intermediate buffers for matrix calculation ...
+ float m1[3][3]/*, m2[3][3]*/, m3[3][3];
+
+ // ... and some pointers to handle them more intuitively
+ float (*mwork)[3] = m1;
+ //float (*minput)[3] = m2;
+ float (*moutput)[3] = m3;
+
+ /*
+ // Step 1: flip x and y coordinates (see above)
+ memset(minput, 0, 9 * sizeof(float));
+ minput[0][1] = 1.0f;
+ minput[1][0] = 1.0f;
+ minput[2][2] = 1.0f;
+
+
+ // Step 2: rotation of image around its center
+ memset(mwork, 0, 9 * sizeof(float));
+ mwork[0][0] = cosi;
+ mwork[0][1] = -sini;
+ mwork[1][0] = sini;
+ mwork[1][1] = cosi;
+ mwork[0][2] = -0.5f * v * cosi + 0.5f * u * sini + 0.5f * v;
+ mwork[1][2] = -0.5f * v * sini - 0.5f * u * cosi + 0.5f * u;
+ mwork[2][2] = 1.0f;
+
+ // multiply mwork * minput -> moutput
+ mat3mul((float *)moutput, (float *)mwork, (float *)minput);
+
+
+ // Step 3: apply shearing
+ memset(mwork, 0, 9 * sizeof(float));
+ mwork[0][0] = 1.0f;
+ mwork[0][1] = shear;
+ mwork[1][1] = 1.0f;
+ mwork[1][0] = shear;
+ mwork[2][2] = 1.0f;
+
+ // moutput (of last calculation) -> minput
+ MAT3SWAP(minput, moutput);
+ // multiply mwork * minput -> moutput
+ mat3mul((float *)moutput, (float *)mwork, (float *)minput);
+
+
+ // Step 4: apply vertical lens shift effect
+ memset(mwork, 0, 9 * sizeof(float));
+ mwork[0][0] = exppa_v;
+ mwork[1][0] = 0.5f * ((exppa_v - 1.0f) * u) / v;
+ mwork[1][1] = 2.0f * exppa_v / (exppa_v + 1.0f);
+ mwork[1][2] = -0.5f * ((exppa_v - 1.0f) * u) / (exppa_v + 1.0f);
+ mwork[2][0] = (exppa_v - 1.0f) / v;
+ mwork[2][2] = 1.0f;
+
+ // moutput (of last calculation) -> minput
+ MAT3SWAP(minput, moutput);
+ // multiply mwork * minput -> moutput
+ mat3mul((float *)moutput, (float *)mwork, (float *)minput);
+
+
+ // Step 5: horizontal compression
+ memset(mwork, 0, 9 * sizeof(float));
+ mwork[0][0] = 1.0f;
+ mwork[1][1] = r_v;
+ mwork[1][2] = 0.5f * u * (1.0f - r_v);
+ mwork[2][2] = 1.0f;
+
+ // moutput (of last calculation) -> minput
+ MAT3SWAP(minput, moutput);
+ // multiply mwork * minput -> moutput
+ mat3mul((float *)moutput, (float *)mwork, (float *)minput);
+
+
+ // Step 6: flip x and y back again
+ memset(mwork, 0, 9 * sizeof(float));
+ mwork[0][1] = 1.0f;
+ mwork[1][0] = 1.0f;
+ mwork[2][2] = 1.0f;
+
+ // moutput (of last calculation) -> minput
+ MAT3SWAP(minput, moutput);
+ // multiply mwork * minput -> moutput
+ mat3mul((float *)moutput, (float *)mwork, (float *)minput);
+
+
+ // from here output vectors would be in (x : y : 1) format
+
+ // Step 7: now we can apply horizontal lens shift with the same matrix format as above
+ memset(mwork, 0, 9 * sizeof(float));
+ mwork[0][0] = exppa_h;
+ mwork[1][0] = 0.5f * ((exppa_h - 1.0f) * v) / u;
+ mwork[1][1] = 2.0f * exppa_h / (exppa_h + 1.0f);
+ mwork[1][2] = -0.5f * ((exppa_h - 1.0f) * v) / (exppa_h + 1.0f);
+ mwork[2][0] = (exppa_h - 1.0f) / u;
+ mwork[2][2] = 1.0f;
+
+ // moutput (of last calculation) -> minput
+ MAT3SWAP(minput, moutput);
+ // multiply mwork * minput -> moutput
+ mat3mul((float *)moutput, (float *)mwork, (float *)minput);
+
+
+ // Step 8: vertical compression
+ memset(mwork, 0, 9 * sizeof(float));
+ mwork[0][0] = 1.0f;
+ mwork[1][1] = r_h;
+ mwork[1][2] = 0.5f * v * (1.0f - r_h);
+ mwork[2][2] = 1.0f;
+
+ // moutput (of last calculation) -> minput
+ MAT3SWAP(minput, moutput);
+ // multiply mwork * minput -> moutput
+ mat3mul((float *)moutput, (float *)mwork, (float *)minput);
+
+
+ // Step 9: apply aspect ratio scaling
+ memset(mwork, 0, 9 * sizeof(float));
+ mwork[0][0] = 1.0f * ascale;
+ mwork[1][1] = 1.0f / ascale;
+ mwork[2][2] = 1.0f;
+
+ // moutput (of last calculation) -> minput
+ MAT3SWAP(minput, moutput);
+ // multiply mwork * minput -> moutput
+ mat3mul((float *)moutput, (float *)mwork, (float *)minput);
+ */
+
+ rtengine::homogeneous::Vector center;
+ center[0] = 0.0f;
+ center[1] = 0.0f;
+ center[2] = f;
+ center[3] = 1.0f;
+
+ using rtengine::operator*;
+
+ // Location of image center after rotations.
+ const rtengine::homogeneous::Vector camera_center_yaw_pitch =
+ rtengine::homogeneous::rotationMatrix(pitch, rtengine::homogeneous::Axis::X) *
+ rtengine::homogeneous::rotationMatrix(yaw, rtengine::homogeneous::Axis::Y) *
+ center;
+
+ const rtengine::homogeneous::Matrix matrix =
+ // Perspective correction.
+ rtengine::homogeneous::projectionMatrix(camera_center_yaw_pitch[2], rtengine::homogeneous::Axis::Z) *
+ rtengine::homogeneous::rotationMatrix(yaw, rtengine::homogeneous::Axis::Y) *
+ rtengine::homogeneous::rotationMatrix(pitch, rtengine::homogeneous::Axis::X) *
+ // Rotation.
+ rtengine::homogeneous::rotationMatrix(rot, rtengine::homogeneous::Axis::Z) *
+ // Lens/sensor shift and move to z == camera_focal_length.
+ rtengine::homogeneous::translationMatrix((0.01f * shift_h - 0.5f) * u, (-0.01f * shift_v - 0.5f) * v, f);
+
+ m3[0][0] = matrix[0][0];
+ m3[0][1] = matrix[0][1];
+ m3[0][2] = matrix[0][3];
+ m3[1][0] = matrix[1][0];
+ m3[1][1] = matrix[1][1];
+ m3[1][2] = matrix[1][3];
+ m3[2][0] = matrix[3][0];
+ m3[2][1] = matrix[3][1];
+ m3[2][2] = matrix[3][3];
+
+ /*
+ // Step 10: find x/y offsets and apply according correction so that
+ // no negative coordinates occur in output vector
+ float umin = FLT_MAX, vmin = FLT_MAX;
+ // visit all four corners
+ for(int y = 0; y < height; y += height - 1)
+ for(int x = 0; x < width; x += width - 1)
+ {
+ float pi[3], po[3];
+ pi[0] = x;
+ pi[1] = y;
+ pi[2] = 1.0f;
+ // moutput expects input in (x:y:1) format and gives output as (x:y:1)
+ mat3mulv(po, (float *)moutput, pi);
+ umin = fmin(umin, po[0] / po[2]);
+ vmin = fmin(vmin, po[1] / po[2]);
+ }
+
+ memset(mwork, 0, 9 * sizeof(float));
+ mwork[0][0] = 1.0f;
+ mwork[1][1] = 1.0f;
+ mwork[2][2] = 1.0f;
+ mwork[0][2] = -umin;
+ mwork[1][2] = -vmin;
+
+ // moutput (of last calculation) -> minput
+ MAT3SWAP(minput, moutput);
+ // multiply mwork * minput -> moutput
+ mat3mul((float *)moutput, (float *)mwork, (float *)minput);
+ */
+
+
+ // on request we either keep the final matrix for forward conversions
+ // or produce an inverted matrix for backward conversions
+ if(dir == ASHIFT_HOMOGRAPH_FORWARD)
+ {
+ // we have what we need -> copy it to the right place
+ memcpy(homograph, moutput, 9 * sizeof(float));
+ }
+ else
+ {
+ // generate inverted homograph (mat3inv function defined in colorspaces.c)
+ if(mat3inv((float *)homograph, (float *)moutput))
+ {
+ // in case of error we set to unity matrix
+ memset(mwork, 0, 9 * sizeof(float));
+ mwork[0][0] = 1.0f;
+ mwork[1][1] = 1.0f;
+ mwork[2][2] = 1.0f;
+ memcpy(homograph, mwork, 9 * sizeof(float));
+ }
+ }
+}
+#undef MAT3SWAP
+
+
+// check if module parameters are set to all neutral values in which case the module's
+// output is identical to its input
+static inline int isneutral(dt_iop_ashift_data_t *data)
+{
+ // values lower than this have no visible effect
+ const float eps = 1.0e-4f;
+
+ return(fabs(data->rotation) < eps &&
+ fabs(data->lensshift_v) < eps &&
+ fabs(data->lensshift_h) < eps &&
+ fabs(data->shear) < eps &&
+ fabs(data->aspect - 1.0f) < eps &&
+ data->cl < eps &&
+ 1.0f - data->cr < eps &&
+ data->ct < eps &&
+ 1.0f - data->cb < eps);
+}
+
+
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+int distort_transform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *points, size_t points_count)
+{
+ dt_iop_ashift_data_t *data = (dt_iop_ashift_data_t *)piece->data;
+
+ // nothing to be done if parameters are set to neutral values
+ if(isneutral(data)) return 1;
+
+ float homograph[3][3];
+ homography((float *)homograph, data->rotation, data->lensshift_v, data->lensshift_h, data->shear, data->f_length_kb,
+ data->orthocorr, data->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_FORWARD);
+
+ // clipping offset
+ const float fullwidth = (float)piece->buf_out.width / (data->cr - data->cl);
+ const float fullheight = (float)piece->buf_out.height / (data->cb - data->ct);
+ const float cx = fullwidth * data->cl;
+ const float cy = fullheight * data->ct;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(static) shared(points, points_count, homograph)
+#endif
+ for(size_t i = 0; i < points_count * 2; i += 2)
+ {
+ float pi[3] = { points[i], points[i + 1], 1.0f };
+ float po[3];
+ mat3mulv(po, (float *)homograph, pi);
+ points[i] = po[0] / po[2] - cx;
+ points[i + 1] = po[1] / po[2] - cy;
+ }
+
+ return 1;
+}
+
+
+int distort_backtransform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *points,
+ size_t points_count)
+{
+ dt_iop_ashift_data_t *data = (dt_iop_ashift_data_t *)piece->data;
+
+ // nothing to be done if parameters are set to neutral values
+ if(isneutral(data)) return 1;
+
+ float ihomograph[3][3];
+ homography((float *)ihomograph, data->rotation, data->lensshift_v, data->lensshift_h, data->shear, data->f_length_kb,
+ data->orthocorr, data->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_INVERTED);
+
+ // clipping offset
+ const float fullwidth = (float)piece->buf_out.width / (data->cr - data->cl);
+ const float fullheight = (float)piece->buf_out.height / (data->cb - data->ct);
+ const float cx = fullwidth * data->cl;
+ const float cy = fullheight * data->ct;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(static) shared(points, points_count, ihomograph)
+#endif
+ for(size_t i = 0; i < points_count * 2; i += 2)
+ {
+ float pi[3] = { points[i] + cx, points[i + 1] + cy, 1.0f };
+ float po[3];
+ mat3mulv(po, (float *)ihomograph, pi);
+ points[i] = po[0] / po[2];
+ points[i + 1] = po[1] / po[2];
+ }
+
+ return 1;
+}
+
+void modify_roi_out(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
+ const dt_iop_roi_t *roi_in)
+{
+ dt_iop_ashift_data_t *data = (dt_iop_ashift_data_t *)piece->data;
+ *roi_out = *roi_in;
+
+ // nothing more to be done if parameters are set to neutral values
+ if(isneutral(data)) return;
+
+ float homograph[3][3];
+ homography((float *)homograph, data->rotation, data->lensshift_v, data->lensshift_h, data->shear, data->f_length_kb,
+ data->orthocorr, data->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_FORWARD);
+
+ float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
+
+ // go through all four vertices of input roi and convert coordinates to output
+ for(int y = 0; y < roi_in->height; y += roi_in->height - 1)
+ {
+ for(int x = 0; x < roi_in->width; x += roi_in->width - 1)
+ {
+ float pin[3], pout[3];
+
+ // convert from input coordinates to original image coordinates
+ pin[0] = roi_in->x + x;
+ pin[1] = roi_in->y + y;
+ pin[0] /= roi_in->scale;
+ pin[1] /= roi_in->scale;
+ pin[2] = 1.0f;
+
+ // apply homograph
+ mat3mulv(pout, (float *)homograph, pin);
+
+ // convert to output image coordinates
+ pout[0] /= pout[2];
+ pout[1] /= pout[2];
+ pout[0] *= roi_out->scale;
+ pout[1] *= roi_out->scale;
+ xm = MIN(xm, pout[0]);
+ xM = MAX(xM, pout[0]);
+ ym = MIN(ym, pout[1]);
+ yM = MAX(yM, pout[1]);
+ }
+ }
+ float width = xM - xm + 1;
+ float height = yM - ym + 1;
+
+ // clipping adjustments
+ width *= data->cr - data->cl;
+ height *= data->cb - data->ct;
+
+ roi_out->width = floorf(width);
+ roi_out->height = floorf(height);
+
+#ifdef ASHIFT_DEBUG
+ print_roi(roi_in, "roi_in (going into modify_roi_out)");
+ print_roi(roi_out, "roi_out (after modify_roi_out)");
+#endif
+}
+
+void modify_roi_in(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
+ const dt_iop_roi_t *const roi_out, dt_iop_roi_t *roi_in)
+{
+ dt_iop_ashift_data_t *data = (dt_iop_ashift_data_t *)piece->data;
+ *roi_in = *roi_out;
+
+ // nothing more to be done if parameters are set to neutral values
+ if(isneutral(data)) return;
+
+ float ihomograph[3][3];
+ homography((float *)ihomograph, data->rotation, data->lensshift_v, data->lensshift_h, data->shear, data->f_length_kb,
+ data->orthocorr, data->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_INVERTED);
+
+ const float orig_w = roi_in->scale * piece->buf_in.width;
+ const float orig_h = roi_in->scale * piece->buf_in.height;
+
+ // clipping offset
+ const float fullwidth = (float)piece->buf_out.width / (data->cr - data->cl);
+ const float fullheight = (float)piece->buf_out.height / (data->cb - data->ct);
+ const float cx = roi_out->scale * fullwidth * data->cl;
+ const float cy = roi_out->scale * fullheight * data->ct;
+
+ float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
+
+ // go through all four vertices of output roi and convert coordinates to input
+ for(int y = 0; y < roi_out->height; y += roi_out->height - 1)
+ {
+ for(int x = 0; x < roi_out->width; x += roi_out->width - 1)
+ {
+ float pin[3], pout[3];
+
+ // convert from output image coordinates to original image coordinates
+ pout[0] = roi_out->x + x + cx;
+ pout[1] = roi_out->y + y + cy;
+ pout[0] /= roi_out->scale;
+ pout[1] /= roi_out->scale;
+ pout[2] = 1.0f;
+
+ // apply homograph
+ mat3mulv(pin, (float *)ihomograph, pout);
+
+ // convert to input image coordinates
+ pin[0] /= pin[2];
+ pin[1] /= pin[2];
+ pin[0] *= roi_in->scale;
+ pin[1] *= roi_in->scale;
+ xm = MIN(xm, pin[0]);
+ xM = MAX(xM, pin[0]);
+ ym = MIN(ym, pin[1]);
+ yM = MAX(yM, pin[1]);
+ }
+ }
+
+ const struct dt_interpolation *interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF);
+ roi_in->x = fmaxf(0.0f, xm - interpolation->width);
+ roi_in->y = fmaxf(0.0f, ym - interpolation->width);
+ roi_in->width = fminf(ceilf(orig_w) - roi_in->x, xM - roi_in->x + 1 + interpolation->width);
+ roi_in->height = fminf(ceilf(orig_h) - roi_in->y, yM - roi_in->y + 1 + interpolation->width);
+
+ // sanity check.
+ roi_in->x = CLAMP(roi_in->x, 0, (int)floorf(orig_w));
+ roi_in->y = CLAMP(roi_in->y, 0, (int)floorf(orig_h));
+ roi_in->width = CLAMP(roi_in->width, 1, (int)floorf(orig_w) - roi_in->x);
+ roi_in->height = CLAMP(roi_in->height, 1, (int)floorf(orig_h) - roi_in->y);
+#ifdef ASHIFT_DEBUG
+ print_roi(roi_out, "roi_out (going into modify_roi_in)");
+ print_roi(roi_in, "roi_in (after modify_roi_in)");
+#endif
+}
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+// simple conversion of rgb image into greyscale variant suitable for line segment detection
+// the lsd routines expect input as *double, roughly in the range [0.0; 256.0]
+static void rgb2grey256(const float *in, double *out, const int width, const int height)
+{
+ const int ch = 4;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(static) shared(in, out)
+#endif
+ for(int j = 0; j < height; j++)
+ {
+ const float *inp = in + (size_t)ch * j * width;
+ double *outp = out + (size_t)j * width;
+ for(int i = 0; i < width; i++, inp += ch, outp++)
+ {
+ *outp = (0.3f * inp[0] + 0.59f * inp[1] + 0.11f * inp[2]) * 256.0;
+ }
+ }
+}
+
+// sobel edge enhancement in one direction
+static void edge_enhance_1d(const double *in, double *out, const int width, const int height,
+ dt_iop_ashift_enhance_t dir)
+{
+ // Sobel kernels for both directions
+ const double hkernel[3][3] = { { 1.0, 0.0, -1.0 }, { 2.0, 0.0, -2.0 }, { 1.0, 0.0, -1.0 } };
+ const double vkernel[3][3] = { { 1.0, 2.0, 1.0 }, { 0.0, 0.0, 0.0 }, { -1.0, -2.0, -1.0 } };
+ const int kwidth = 3;
+ const int khwidth = kwidth / 2;
+
+ // select kernel
+ const double *kernel = (dir == ASHIFT_ENHANCE_HORIZONTAL) ? (const double *)hkernel : (const double *)vkernel;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(static) shared(in, out, kernel)
+#endif
+ // loop over image pixels and perform sobel convolution
+ for(int j = khwidth; j < height - khwidth; j++)
+ {
+ const double *inp = in + (size_t)j * width + khwidth;
+ double *outp = out + (size_t)j * width + khwidth;
+ for(int i = khwidth; i < width - khwidth; i++, inp++, outp++)
+ {
+ double sum = 0.0f;
+ for(int jj = 0; jj < kwidth; jj++)
+ {
+ const int k = jj * kwidth;
+ const int l = (jj - khwidth) * width;
+ for(int ii = 0; ii < kwidth; ii++)
+ {
+ sum += inp[l + ii - khwidth] * kernel[k + ii];
+ }
+ }
+ *outp = sum;
+ }
+ }
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(static) shared(out)
+#endif
+ // border fill in output buffer, so we don't get pseudo lines at image frame
+ for(int j = 0; j < height; j++)
+ for(int i = 0; i < width; i++)
+ {
+ double val = out[j * width + i];
+
+ if(j < khwidth)
+ val = out[(khwidth - j) * width + i];
+ else if(j >= height - khwidth)
+ val = out[(j - khwidth) * width + i];
+ else if(i < khwidth)
+ val = out[j * width + (khwidth - i)];
+ else if(i >= width - khwidth)
+ val = out[j * width + (i - khwidth)];
+
+ out[j * width + i] = val;
+
+ // jump over center of image
+ if(i == khwidth && j >= khwidth && j < height - khwidth) i = width - khwidth;
+ }
+}
+
+// edge enhancement in both directions
+static int edge_enhance(const double *in, double *out, const int width, const int height)
+{
+ double *Gx = NULL;
+ double *Gy = NULL;
+
+ Gx = (double *)malloc((size_t)width * height * sizeof(double));
+ if(Gx == NULL) goto error;
+
+ Gy = (double *)malloc((size_t)width * height * sizeof(double));
+ if(Gy == NULL) goto error;
+
+ // perform edge enhancement in both directions
+ edge_enhance_1d(in, Gx, width, height, ASHIFT_ENHANCE_HORIZONTAL);
+ edge_enhance_1d(in, Gy, width, height, ASHIFT_ENHANCE_VERTICAL);
+
+// calculate absolute values
+#ifdef _OPENMP
+#pragma omp parallel for schedule(static) shared(Gx, Gy, out)
+#endif
+ for(size_t k = 0; k < (size_t)width * height; k++)
+ {
+ out[k] = sqrt(Gx[k] * Gx[k] + Gy[k] * Gy[k]);
+ }
+
+ free(Gx);
+ free(Gy);
+ return TRUE;
+
+error:
+ if(Gx) free(Gx);
+ if(Gy) free(Gy);
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+// XYZ -> sRGB matrix
+static void XYZ_to_sRGB(const float *XYZ, float *sRGB)
+{
+ sRGB[0] = 3.1338561f * XYZ[0] - 1.6168667f * XYZ[1] - 0.4906146f * XYZ[2];
+ sRGB[1] = -0.9787684f * XYZ[0] + 1.9161415f * XYZ[1] + 0.0334540f * XYZ[2];
+ sRGB[2] = 0.0719453f * XYZ[0] - 0.2289914f * XYZ[1] + 1.4052427f * XYZ[2];
+}
+
+// sRGB -> XYZ matrix
+static void sRGB_to_XYZ(const float *sRGB, float *XYZ)
+{
+ XYZ[0] = 0.4360747f * sRGB[0] + 0.3850649f * sRGB[1] + 0.1430804f * sRGB[2];
+ XYZ[1] = 0.2225045f * sRGB[0] + 0.7168786f * sRGB[1] + 0.0606169f * sRGB[2];
+ XYZ[2] = 0.0139322f * sRGB[0] + 0.0971045f * sRGB[1] + 0.7141733f * sRGB[2];
+}
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+// detail enhancement via bilateral grid (function arguments in and out may represent identical buffers)
+static int detail_enhance(const float *in, float *out, const int width, const int height)
+{
+ return TRUE;
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+ const float sigma_r = 5.0f;
+ const float sigma_s = fminf(width, height) * 0.02f;
+ const float detail = 10.0f;
+
+ int success = TRUE;
+
+ // we need to convert from RGB to Lab first;
+ // as colors don't matter we are safe to assume data to be sRGB
+
+ // convert RGB input to Lab, use output buffer for intermediate storage
+#ifdef _OPENMP
+#pragma omp parallel for schedule(static) shared(in, out)
+#endif
+ for(int j = 0; j < height; j++)
+ {
+ const float *inp = in + (size_t)4 * j * width;
+ float *outp = out + (size_t)4 * j * width;
+ for(int i = 0; i < width; i++, inp += 4, outp += 4)
+ {
+ float XYZ[3];
+ sRGB_to_XYZ(inp, XYZ);
+ dt_XYZ_to_Lab(XYZ, outp);
+ }
+ }
+
+ // bilateral grid detail enhancement
+ dt_bilateral_t *b = dt_bilateral_init(width, height, sigma_s, sigma_r);
+
+ if(b != NULL)
+ {
+ dt_bilateral_splat(b, out);
+ dt_bilateral_blur(b);
+ dt_bilateral_slice_to_output(b, out, out, detail);
+ dt_bilateral_free(b);
+ }
+ else
+ success = FALSE;
+
+ // convert resulting Lab to RGB output
+#ifdef _OPENMP
+#pragma omp parallel for schedule(static) shared(out)
+#endif
+ for(int j = 0; j < height; j++)
+ {
+ float *outp = out + (size_t)4 * j * width;
+ for(int i = 0; i < width; i++, outp += 4)
+ {
+ float XYZ[3];
+ dt_Lab_to_XYZ(outp, XYZ);
+ XYZ_to_sRGB(XYZ, outp);
+ }
+ }
+
+ return success;
+#endif // if 0
+//-----------------------------------------------------------------------------
+}
+
+// apply gamma correction to RGB buffer (function arguments in and out may represent identical buffers)
+static void gamma_correct(const float *in, float *out, const int width, const int height)
+{
+#ifdef _OPENMP
+#pragma omp parallel for schedule(static) shared(in, out)
+#endif
+ for(int j = 0; j < height; j++)
+ {
+ const float *inp = in + (size_t)4 * j * width;
+ float *outp = out + (size_t)4 * j * width;
+ for(int i = 0; i < width; i++, inp += 4, outp += 4)
+ {
+ for(int c = 0; c < 3; c++)
+ outp[c] = powf(inp[c], LSD_GAMMA);
+ }
+ }
+}
+
+// do actual line_detection based on LSD algorithm and return results according
+// to this module's conventions
+static int line_detect(float *in, const int width, const int height, const int x_off, const int y_off,
+ const float scale, dt_iop_ashift_line_t **alines, int *lcount, int *vcount, int *hcount,
+ float *vweight, float *hweight, dt_iop_ashift_enhance_t enhance, const int is_raw)
+{
+ double *greyscale = NULL;
+ double *lsd_lines = NULL;
+ dt_iop_ashift_line_t *ashift_lines = NULL;
+
+ int vertical_count = 0;
+ int horizontal_count = 0;
+ float vertical_weight = 0.0f;
+ float horizontal_weight = 0.0f;
+ //
+ int lines_count;
+ // we count the lines that we really want to use
+ int lct = 0;
+
+ // apply gamma correction if image is raw
+ if(is_raw)
+ {
+ gamma_correct(in, in, width, height);
+ }
+
+ // if requested perform an additional detail enhancement step
+ if(enhance & ASHIFT_ENHANCE_DETAIL)
+ {
+ (void)detail_enhance(in, in, width, height);
+ }
+
+ // allocate intermediate buffers
+ greyscale = (double *)malloc((size_t)width * height * sizeof(double));
+ if(greyscale == NULL) goto error;
+
+ // convert to greyscale image
+ rgb2grey256(in, greyscale, width, height);
+
+ // if requested perform an additional edge enhancement step
+ if(enhance & ASHIFT_ENHANCE_EDGES)
+ {
+ (void)edge_enhance(greyscale, greyscale, width, height);
+ }
+
+ // call the line segment detector LSD;
+ // LSD stores the number of found lines in lines_count.
+ // it returns structural details as vector 'double lines[7 * lines_count]'
+ lsd_lines = LineSegmentDetection(&lines_count, greyscale, width, height,
+ LSD_SCALE, LSD_SIGMA_SCALE, LSD_QUANT,
+ LSD_ANG_TH, LSD_LOG_EPS, LSD_DENSITY_TH,
+ LSD_N_BINS, NULL, NULL, NULL);
+
+ if(lines_count > 0)
+ {
+ // aggregate lines data into our own structures
+ ashift_lines = (dt_iop_ashift_line_t *)malloc((size_t)lines_count * sizeof(dt_iop_ashift_line_t));
+ if(ashift_lines == NULL) goto error;
+
+ for(int n = 0; n < lines_count; n++)
+ {
+ float x1 = lsd_lines[n * 7 + 0];
+ float y1 = lsd_lines[n * 7 + 1];
+ float x2 = lsd_lines[n * 7 + 2];
+ float y2 = lsd_lines[n * 7 + 3];
+
+ // check for lines running along image borders and skip them.
+ // these would likely be false-positives which could result
+ // from any kind of processing artifacts
+ if((fabs(x1 - x2) < 1 && fmax(x1, x2) < 2) ||
+ (fabs(x1 - x2) < 1 && fmin(x1, x2) > width - 3) ||
+ (fabs(y1 - y2) < 1 && fmax(y1, y2) < 2) ||
+ (fabs(y1 - y2) < 1 && fmin(y1, y2) > height - 3))
+ continue;
+
+ // line position in absolute coordinates
+ float px1 = x_off + x1;
+ float py1 = y_off + y1;
+ float px2 = x_off + x2;
+ float py2 = y_off + y2;
+
+ // scale back to input buffer
+ px1 /= scale;
+ py1 /= scale;
+ px2 /= scale;
+ py2 /= scale;
+
+ // store as homogeneous coordinates
+ ashift_lines[lct].p1[0] = px1;
+ ashift_lines[lct].p1[1] = py1;
+ ashift_lines[lct].p1[2] = 1.0f;
+ ashift_lines[lct].p2[0] = px2;
+ ashift_lines[lct].p2[1] = py2;
+ ashift_lines[lct].p2[2] = 1.0f;
+
+ // calculate homogeneous coordinates of connecting line (defined by the two points)
+ vec3prodn(ashift_lines[lct].L, ashift_lines[lct].p1, ashift_lines[lct].p2);
+
+ // normalaze line coordinates so that x^2 + y^2 = 1
+ // (this will always succeed as L is a real line connecting two real points)
+ vec3lnorm(ashift_lines[lct].L, ashift_lines[lct].L);
+
+ // length and width of rectangle (see LSD)
+ ashift_lines[lct].length = sqrt((px2 - px1) * (px2 - px1) + (py2 - py1) * (py2 - py1));
+ ashift_lines[lct].width = lsd_lines[n * 7 + 4] / scale;
+
+ // ... and weight (= angle precision * length * width)
+ const float weight = lsd_lines[n * 7 + 5] * ashift_lines[lct].length * ashift_lines[lct].width;
+ ashift_lines[lct].weight = weight;
+
+
+ const float angle = atan2(py2 - py1, px2 - px1) / M_PI * 180.0f;
+ const int vertical = fabs(fabs(angle) - 90.0f) < MAX_TANGENTIAL_DEVIATION ? 1 : 0;
+ const int horizontal = fabs(fabs(fabs(angle) - 90.0f) - 90.0f) < MAX_TANGENTIAL_DEVIATION ? 1 : 0;
+
+ const int relevant = ashift_lines[lct].length > MIN_LINE_LENGTH ? 1 : 0;
+
+ // register type of line
+ dt_iop_ashift_linetype_t type = ASHIFT_LINE_IRRELEVANT;
+ if(vertical && relevant)
+ {
+ type = ASHIFT_LINE_VERTICAL_SELECTED;
+ vertical_count++;
+ vertical_weight += weight;
+ }
+ else if(horizontal && relevant)
+ {
+ type = ASHIFT_LINE_HORIZONTAL_SELECTED;
+ horizontal_count++;
+ horizontal_weight += weight;
+ }
+ ashift_lines[lct].type = type;
+
+ // the next valid line
+ lct++;
+ }
+ }
+#ifdef ASHIFT_DEBUG
+ printf("%d lines (vertical %d, horizontal %d, not relevant %d)\n", lines_count, vertical_count,
+ horizontal_count, lct - vertical_count - horizontal_count);
+ float xmin = FLT_MAX, xmax = FLT_MIN, ymin = FLT_MAX, ymax = FLT_MIN;
+ for(int n = 0; n < lct; n++)
+ {
+ xmin = fmin(xmin, fmin(ashift_lines[n].p1[0], ashift_lines[n].p2[0]));
+ xmax = fmax(xmax, fmax(ashift_lines[n].p1[0], ashift_lines[n].p2[0]));
+ ymin = fmin(ymin, fmin(ashift_lines[n].p1[1], ashift_lines[n].p2[1]));
+ ymax = fmax(ymax, fmax(ashift_lines[n].p1[1], ashift_lines[n].p2[1]));
+ printf("x1 %.0f, y1 %.0f, x2 %.0f, y2 %.0f, length %.0f, width %f, X %f, Y %f, Z %f, type %d, scalars %f %f\n",
+ ashift_lines[n].p1[0], ashift_lines[n].p1[1], ashift_lines[n].p2[0], ashift_lines[n].p2[1],
+ ashift_lines[n].length, ashift_lines[n].width,
+ ashift_lines[n].L[0], ashift_lines[n].L[1], ashift_lines[n].L[2], ashift_lines[n].type,
+ vec3scalar(ashift_lines[n].p1, ashift_lines[n].L),
+ vec3scalar(ashift_lines[n].p2, ashift_lines[n].L));
+ }
+ printf("xmin %.0f, xmax %.0f, ymin %.0f, ymax %.0f\n", xmin, xmax, ymin, ymax);
+#endif
+
+ // store results in provided locations
+ *lcount = lct;
+ *vcount = vertical_count;
+ *vweight = vertical_weight;
+ *hcount = horizontal_count;
+ *hweight = horizontal_weight;
+ *alines = ashift_lines;
+
+ // free intermediate buffers
+ free(lsd_lines);
+ free(greyscale);
+ return lct > 0 ? TRUE : FALSE;
+
+error:
+ free(lsd_lines);
+ free(greyscale);
+ return FALSE;
+}
+
+// get image from buffer, analyze for structure and save results
+static int get_structure(dt_iop_module_t *module, dt_iop_ashift_enhance_t enhance)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data;
+
+ float *buffer = NULL;
+ int width = 0;
+ int height = 0;
+ int x_off = 0;
+ int y_off = 0;
+ float scale = 0.0f;
+
+ { //dt_pthread_mutex_lock(&g->lock);
+ MyMutex::MyLock lock(g->lock);
+
+ // read buffer data if they are available
+ if(g->buf != NULL)
+ {
+ width = g->buf_width;
+ height = g->buf_height;
+ x_off = g->buf_x_off;
+ y_off = g->buf_y_off;
+ scale = g->buf_scale;
+
+ // create a temporary buffer to hold image data
+ buffer = (float *)malloc((size_t)width * height * 4 * sizeof(float));
+ if(buffer != NULL)
+ memcpy(buffer, g->buf, (size_t)width * height * 4 * sizeof(float));
+ }
+ } /* dt_pthread_mutex_unlock(&g->lock); */
+
+ if(buffer == NULL) goto error;
+
+ // get rid of old structural data
+ g->lines_count = 0;
+ g->vertical_count = 0;
+ g->horizontal_count = 0;
+ free(g->lines);
+ g->lines = NULL;
+
+ dt_iop_ashift_line_t *lines;
+ int lines_count;
+ int vertical_count;
+ int horizontal_count;
+ float vertical_weight;
+ float horizontal_weight;
+
+ // get new structural data
+ if(!line_detect(buffer, width, height, x_off, y_off, scale, &lines, &lines_count,
+ &vertical_count, &horizontal_count, &vertical_weight, &horizontal_weight,
+ enhance, module->is_raw))//dt_image_is_raw(&module->dev->image_storage)))
+ goto error;
+
+ // save new structural data
+ g->lines_in_width = width;
+ g->lines_in_height = height;
+ g->lines_x_off = x_off;
+ g->lines_y_off = y_off;
+ g->lines_count = lines_count;
+ g->vertical_count = vertical_count;
+ g->horizontal_count = horizontal_count;
+ g->vertical_weight = vertical_weight;
+ g->horizontal_weight = horizontal_weight;
+ g->lines_version++;
+ g->lines_suppressed = 0;
+ g->lines = lines;
+
+ free(buffer);
+ return TRUE;
+
+error:
+ free(buffer);
+ return FALSE;
+}
+
+
+// swap two integer values
+static inline void swap(int *a, int *b)
+{
+ int tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+// do complete permutations
+static int quickperm(int *a, int *p, const int N, int *i)
+{
+ if(*i >= N) return FALSE;
+
+ p[*i]--;
+ int j = (*i % 2 == 1) ? p[*i] : 0;
+ swap(&a[j], &a[*i]);
+ *i = 1;
+ while(p[*i] == 0)
+ {
+ p[*i] = *i;
+ (*i)++;
+ }
+ return TRUE;
+}
+
+// Fisher-Yates shuffle
+static void shuffle(int *a, const int N)
+{
+ for(int i = 0; i < N; i++)
+ {
+ int j = i + rand() % (N - i);
+ swap(&a[j], &a[i]);
+ }
+}
+
+// factorial function
+static int fact(const int n)
+{
+ return (n == 1 ? 1 : n * fact(n - 1));
+}
+
+// We use a pseudo-RANSAC algorithm to elminiate ouliers from our set of lines. The
+// original RANSAC works on linear optimization problems. Our model is nonlinear. We
+// take advantage of the fact that lines interesting for our model are vantage lines
+// that meet in one vantage point for each subset of lines (vertical/horizontal).
+// Stragegy: we construct a model by (random) sampling within the subset of lines and
+// calculate the vantage point. Then we check the "distance" of all other lines to the
+// vantage point. The model that gives highest number of lines combined with the highest
+// total weight and lowest overall "distance" wins.
+// Disadvantage: compared to the original RANSAC we don't get any model parameters that
+// we could use for the following NMS fit.
+// Self-tuning: we optimize "epsilon", the hurdle rate to reject a line as an outlier,
+// by a number of dry runs first. The target average percentage value of lines to eliminate as
+// outliers (without judging on the quality of the model) is given by RANSAC_ELIMINATION_RATIO,
+// note: the actual percentage of outliers removed in the final run will be lower because we
+// will finally look for the best quality model with the optimized epsilon and that quality value also
+// encloses the number of good lines
+static void ransac(const dt_iop_ashift_line_t *lines, int *index_set, int *inout_set,
+ const int set_count, const float total_weight, const int xmin, const int xmax,
+ const int ymin, const int ymax)
+{
+ if(set_count < 3) return;
+
+ const size_t set_size = set_count * sizeof(int);
+ int *best_set = (int *)malloc(set_size);
+ memcpy(best_set, index_set, set_size);
+ int *best_inout = (int *)calloc(1, set_size);
+
+ float best_quality = 0.0f;
+
+ // hurdle value epsilon for rejecting a line as an outlier will be self-tuning
+ // in a number of dry runs
+ float epsilon = pow(10.0f, -RANSAC_EPSILON);
+ float epsilon_step = RANSAC_EPSILON_STEP;
+ // some accounting variables for self-tuning
+ int lines_eliminated = 0;
+ int valid_runs = 0;
+
+ // number of runs to optimize epsilon
+ const int optiruns = RANSAC_OPTIMIZATION_STEPS * RANSAC_OPTIMIZATION_DRY_RUNS;
+ // go for complete permutations on small set sizes, else for random sample consensus
+ const int riter = (set_count > RANSAC_HURDLE) ? RANSAC_RUNS : fact(set_count);
+
+ // some data needed for quickperm
+ int *perm = (int *)malloc((set_count + 1) * sizeof(int));
+ for(int n = 0; n < set_count + 1; n++) perm[n] = n;
+ int piter = 1;
+
+ // inout holds good/bad qualification for each line
+ int *inout = (int *)malloc(set_size);
+
+ for(int r = 0; r < optiruns + riter; r++)
+ {
+ // get random or systematic variation of index set
+ if(set_count > RANSAC_HURDLE || r < optiruns)
+ shuffle(index_set, set_count);
+ else
+ (void)quickperm(index_set, perm, set_count, &piter);
+
+ // summed quality evaluation of this run
+ float quality = 0.0f;
+
+ // we build a model ouf of the first two lines
+ const float *L1 = lines[index_set[0]].L;
+ const float *L2 = lines[index_set[1]].L;
+
+ // get intersection point (ideally a vantage point)
+ float V[3];
+ vec3prodn(V, L1, L2);
+
+ // catch special cases:
+ // a) L1 and L2 are identical -> V is NULL -> no valid vantage point
+ // b) vantage point lies inside image frame (no chance to correct for this case)
+ if(vec3isnull(V) ||
+ (fabs(V[2]) > 0.0f &&
+ V[0]/V[2] >= xmin &&
+ V[1]/V[2] >= ymin &&
+ V[0]/V[2] <= xmax &&
+ V[1]/V[2] <= ymax))
+ {
+ // no valid model
+ quality = 0.0f;
+ }
+ else
+ {
+ // valid model
+
+ // normalize V so that x^2 + y^2 + z^2 = 1
+ vec3norm(V, V);
+
+ // the two lines constituting the model are part of the set
+ inout[0] = 1;
+ inout[1] = 1;
+
+ // go through all remaining lines, check if they are within the model, and
+ // mark that fact in inout[].
+ // summarize a quality parameter for all lines within the model
+ for(int n = 2; n < set_count; n++)
+ {
+ // L is normalized so that x^2 + y^2 = 1
+ const float *L3 = lines[index_set[n]].L;
+
+ // we take the absolute value of the dot product of V and L as a measure
+ // of the "distance" between point and line. Note that this is not the real euclidian
+ // distance but - with the given normalization - just a pragmatically selected number
+ // that goes to zero if V lies on L and increases the more V and L are apart
+ const float d = fabs(vec3scalar(V, L3));
+
+ // depending on d we either include or exclude the point from the set
+ inout[n] = (d < epsilon) ? 1 : 0;
+
+ float q;
+
+ if(inout[n] == 1)
+ {
+ // a quality parameter that depends 1/3 on the number of lines within the model,
+ // 1/3 on their weight, and 1/3 on their weighted distance d to the vantage point
+ q = 0.33f / (float)set_count
+ + 0.33f * lines[index_set[n]].weight / total_weight
+ + 0.33f * (1.0f - d / epsilon) * (float)set_count * lines[index_set[n]].weight / total_weight;
+ }
+ else
+ {
+ q = 0.0f;
+ lines_eliminated++;
+ }
+
+ quality += q;
+ }
+ valid_runs++;
+ }
+
+ if(r < optiruns)
+ {
+ // on last run of each self-tuning step
+ if((r % RANSAC_OPTIMIZATION_DRY_RUNS) == (RANSAC_OPTIMIZATION_DRY_RUNS - 1) && (valid_runs > 0))
+ {
+#ifdef ASHIFT_DEBUG
+ printf("ransac self-tuning (run %d): epsilon %f", r, epsilon);
+#endif
+ // average ratio of lines that we eliminated with the given epsilon
+ float ratio = 100.0f * (float)lines_eliminated / ((float)set_count * valid_runs);
+ // adjust epsilon accordingly
+ if(ratio < RANSAC_ELIMINATION_RATIO)
+ epsilon = pow(10.0f, log10(epsilon) - epsilon_step);
+ else if(ratio > RANSAC_ELIMINATION_RATIO)
+ epsilon = pow(10.0f, log10(epsilon) + epsilon_step);
+#ifdef ASHIFT_DEBUG
+ printf(" (elimination ratio %f) -> %f\n", ratio, epsilon);
+#endif
+ // reduce step-size for next optimization round
+ epsilon_step /= 2.0f;
+ lines_eliminated = 0;
+ valid_runs = 0;
+ }
+ }
+ else
+ {
+ // in the "real" runs check against the best model found so far
+ if(quality > best_quality)
+ {
+ memcpy(best_set, index_set, set_size);
+ memcpy(best_inout, inout, set_size);
+ best_quality = quality;
+ }
+ }
+
+#ifdef ASHIFT_DEBUG
+ // report some statistics
+ int count = 0, lastcount = 0;
+ for(int n = 0; n < set_count; n++) count += best_inout[n];
+ for(int n = 0; n < set_count; n++) lastcount += inout[n];
+ printf("ransac run %d: best qual %.6f, eps %.6f, line count %d of %d (this run: qual %.5f, count %d (%2f%%))\n", r,
+ best_quality, epsilon, count, set_count, quality, lastcount, 100.0f * lastcount / (float)set_count);
+#endif
+ }
+
+ // store back best set
+ memcpy(index_set, best_set, set_size);
+ memcpy(inout_set, best_inout, set_size);
+
+ free(inout);
+ free(perm);
+ free(best_inout);
+ free(best_set);
+}
+
+
+// try to clean up structural data by eliminating outliers and thereby increasing
+// the chance of a convergent fitting
+static int remove_outliers(dt_iop_module_t *module)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data;
+
+ const int width = g->lines_in_width;
+ const int height = g->lines_in_height;
+ const int xmin = g->lines_x_off;
+ const int ymin = g->lines_y_off;
+ const int xmax = xmin + width;
+ const int ymax = ymin + height;
+
+ // holds the index set of lines we want to work on
+ int *lines_set = (int *)malloc(g->lines_count * sizeof(int));
+ // holds the result of ransac
+ int *inout_set = (int *)malloc(g->lines_count * sizeof(int));
+
+ // some accounting variables
+ int vnb = 0, vcount = 0;
+ int hnb = 0, hcount = 0;
+
+ // just to be on the safe side
+ if(g->lines == NULL) goto error;
+
+ // generate index list for the vertical lines
+ for(int n = 0; n < g->lines_count; n++)
+ {
+ // is this a selected vertical line?
+ if((g->lines[n].type & ASHIFT_LINE_MASK) != ASHIFT_LINE_VERTICAL_SELECTED)
+ continue;
+
+ lines_set[vnb] = n;
+ inout_set[vnb] = 0;
+ vnb++;
+ }
+
+ // it only makes sense to call ransac if we have more than two lines
+ if(vnb > 2)
+ ransac(g->lines, lines_set, inout_set, vnb, g->vertical_weight,
+ xmin, xmax, ymin, ymax);
+
+ // adjust line selected flag according to the ransac results
+ for(int n = 0; n < vnb; n++)
+ {
+ const int m = lines_set[n];
+ if(inout_set[n] == 1)
+ {
+ g->lines[m].type |= ASHIFT_LINE_SELECTED;
+ vcount++;
+ }
+ else
+ g->lines[m].type &= ~ASHIFT_LINE_SELECTED;
+ }
+ // update number of vertical lines
+ g->vertical_count = vcount;
+ g->lines_version++;
+
+ // now generate index list for the horizontal lines
+ for(int n = 0; n < g->lines_count; n++)
+ {
+ // is this a selected horizontal line?
+ if((g->lines[n].type & ASHIFT_LINE_MASK) != ASHIFT_LINE_HORIZONTAL_SELECTED)
+ continue;
+
+ lines_set[hnb] = n;
+ inout_set[hnb] = 0;
+ hnb++;
+ }
+
+ // it only makes sense to call ransac if we have more than two lines
+ if(hnb > 2)
+ ransac(g->lines, lines_set, inout_set, hnb, g->horizontal_weight,
+ xmin, xmax, ymin, ymax);
+
+ // adjust line selected flag according to the ransac results
+ for(int n = 0; n < hnb; n++)
+ {
+ const int m = lines_set[n];
+ if(inout_set[n] == 1)
+ {
+ g->lines[m].type |= ASHIFT_LINE_SELECTED;
+ hcount++;
+ }
+ else
+ g->lines[m].type &= ~ASHIFT_LINE_SELECTED;
+ }
+ // update number of horizontal lines
+ g->horizontal_count = hcount;
+ g->lines_version++;
+
+ free(inout_set);
+ free(lines_set);
+
+ return TRUE;
+
+error:
+ free(inout_set);
+ free(lines_set);
+ return FALSE;
+}
+
+// utility function to map a variable in [min; max] to [-INF; + INF]
+static inline double logit(double x, double min, double max)
+{
+ const double eps = 1.0e-6;
+ // make sure p does not touch the borders of its definition area,
+ // not critical for data accuracy as logit() is only used on initial fit parameters
+ double p = CLAMP((x - min) / (max - min), eps, 1.0 - eps);
+
+ return (2.0 * atanh(2.0 * p - 1.0));
+}
+
+// inverted function to logit()
+static inline double ilogit(double L, double min, double max)
+{
+ double p = 0.5 * (1.0 + tanh(0.5 * L));
+
+ return (p * (max - min) + min);
+}
+
+// helper function for simplex() return quality parameter for the given model
+// strategy:
+// * generate homography matrix out of fixed parameters and fitting parameters
+// * apply homography to all end points of affected lines
+// * generate new line out of transformed end points
+// * calculate scalar product s of line with perpendicular axis
+// * sum over weighted s^2 values
+static double model_fitness(double *params, void *data)
+{
+ dt_iop_ashift_fit_params_t *fit = (dt_iop_ashift_fit_params_t *)data;
+
+ // just for convenience: get shorter names
+ dt_iop_ashift_line_t *lines = fit->lines;
+ const int lines_count = fit->lines_count;
+ const int width = fit->width;
+ const int height = fit->height;
+ const float f_length_kb = fit->f_length_kb;
+ const float orthocorr = fit->orthocorr;
+ const float aspect = fit->aspect;
+
+ float rotation = fit->rotation;
+ float lensshift_v = fit->lensshift_v;
+ float lensshift_h = fit->lensshift_h;
+ float shear = fit->shear;
+ float camera_pitch = fit->camera_pitch;
+ float camera_yaw = fit->camera_yaw;
+ float rotation_range = fit->rotation_range;
+ /*
+ float lensshift_v_range = fit->lensshift_v_range;
+ float lensshift_h_range = fit->lensshift_h_range;
+ float shear_range = fit->shear_range;
+ */
+ float camera_pitch_range = fit->camera_pitch_range;
+ float camera_yaw_range = fit->camera_yaw_range;
+
+ int pcount = 0;
+
+ // fill in fit parameters from params[]. Attention: order matters!!!
+ if(isnan(rotation))
+ {
+ rotation = ilogit(params[pcount], -rotation_range, rotation_range);
+ pcount++;
+ }
+
+ /*
+ if(isnan(lensshift_v))
+ {
+ lensshift_v = ilogit(params[pcount], -lensshift_v_range, lensshift_v_range);
+ pcount++;
+ }
+
+ if(isnan(lensshift_h))
+ {
+ lensshift_h = ilogit(params[pcount], -lensshift_h_range, lensshift_h_range);
+ pcount++;
+ }
+
+ if(isnan(shear))
+ {
+ shear = ilogit(params[pcount], -shear_range, shear_range);
+ pcount++;
+ }
+ */
+
+ if(isnan(camera_pitch))
+ {
+ camera_pitch = ilogit(params[pcount], -camera_pitch_range, camera_pitch_range);
+ pcount++;
+ }
+
+ if(isnan(camera_yaw))
+ {
+ camera_yaw = ilogit(params[pcount], -camera_yaw_range, camera_yaw_range);
+ pcount++;
+ }
+
+ assert(pcount == fit->params_count);
+
+ // the possible reference axes
+ const float Av[3] = { 1.0f, 0.0f, 0.0f };
+ const float Ah[3] = { 0.0f, 1.0f, 0.0f };
+
+ // generate homograph out of the parameters
+ float homograph[3][3];
+ homography((float *)homograph, rotation, lensshift_v, lensshift_h, shear, camera_pitch, camera_yaw, f_length_kb,
+ orthocorr, aspect, width, height, ASHIFT_HOMOGRAPH_FORWARD);
+
+ // accounting variables
+ double sumsq_v = 0.0;
+ double sumsq_h = 0.0;
+ double weight_v = 0.0;
+ double weight_h = 0.0;
+ int count_v = 0;
+ int count_h = 0;
+ int count = 0;
+
+ // iterate over all lines
+ for(int n = 0; n < lines_count; n++)
+ {
+ // check if this is a line which we must skip
+ if((lines[n].type & fit->linemask) != fit->linetype)
+ continue;
+
+ // the direction of this line (vertical?)
+ const int isvertical = lines[n].type & ASHIFT_LINE_DIRVERT;
+
+ // select the perpendicular reference axis
+ const float *A = isvertical ? Ah : Av;
+
+ // apply homographic transformation to the end points
+ float P1[3], P2[3];
+ mat3mulv(P1, (float *)homograph, lines[n].p1);
+ mat3mulv(P2, (float *)homograph, lines[n].p2);
+
+ // get line connecting the two points
+ float L[3];
+ vec3prodn(L, P1, P2);
+
+ // normalize L so that x^2 + y^2 = 1; makes sure that
+ // y^2 = 1 / (1 + m^2) and x^2 = m^2 / (1 + m^2) with m defining the slope of the line
+ vec3lnorm(L, L);
+
+ // get scalar product of line L with orthogonal axis A -> gives 0 if line is perpendicular
+ float s = vec3scalar(L, A);
+
+ // sum up weighted s^2 for both directions individually
+ sumsq_v += isvertical ? (double)s * s * lines[n].weight : 0.0;
+ weight_v += isvertical ? lines[n].weight : 0.0;
+ count_v += isvertical ? 1 : 0;
+ sumsq_h += !isvertical ? (double)s * s * lines[n].weight : 0.0;
+ weight_h += !isvertical ? lines[n].weight : 0.0;
+ count_h += !isvertical ? 1 : 0;
+ count++;
+ }
+
+ const double v = weight_v > 0.0f && count > 0 ? sumsq_v / weight_v * (float)count_v / count : 0.0;
+ const double h = weight_h > 0.0f && count > 0 ? sumsq_h / weight_h * (float)count_h / count : 0.0;
+
+ double sum = sqrt(1.0 - (1.0 - v) * (1.0 - h)) * 1.0e6;
+ //double sum = sqrt(v + h) * 1.0e6;
+
+#ifdef ASHIFT_DEBUG
+ /*
+ printf("fitness with rotation %f, lensshift_v %f, lensshift_h %f, shear %f -> lines %d, quality %10f\n",
+ rotation, lensshift_v, lensshift_h, shear, count, sum);
+ */
+ printf("fitness with rotation %f, camera_pitch %f, camera_yaw %f -> lines %d, quality %10f\n",
+ rotation, camera_pitch, camera_yaw, count, sum);
+#endif
+
+ return sum;
+}
+
+// setup all data structures for fitting and call NM simplex
+static dt_iop_ashift_nmsresult_t nmsfit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data;
+
+ if(!g->lines) return NMS_NOT_ENOUGH_LINES;
+ if(dir == ASHIFT_FIT_NONE) return NMS_SUCCESS;
+
+ double params[4];
+ int pcount = 0;
+ int enough_lines = TRUE;
+
+ // initialize fit parameters
+ dt_iop_ashift_fit_params_t fit;
+ fit.lines = g->lines;
+ fit.lines_count = g->lines_count;
+ fit.width = g->lines_in_width;
+ fit.height = g->lines_in_height;
+ fit.f_length_kb = (p->mode == ASHIFT_MODE_GENERIC) ? (float)DEFAULT_F_LENGTH : p->f_length * p->crop_factor;
+ fit.orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr;
+ fit.aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect;
+ fit.rotation = p->rotation;
+ fit.lensshift_v = p->lensshift_v;
+ fit.lensshift_h = p->lensshift_h;
+ fit.shear = p->shear;
+ fit.camera_pitch = p->camera_pitch;
+ fit.camera_yaw = p->camera_yaw;
+ fit.rotation_range = g->rotation_range;
+ fit.lensshift_v_range = g->lensshift_v_range;
+ fit.lensshift_h_range = g->lensshift_h_range;
+ fit.shear_range = g->shear_range;
+ fit.camera_pitch_range = g->camera_pitch_range;
+ fit.camera_yaw_range = g->camera_yaw_range;
+ fit.linetype = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED;
+ fit.linemask = ASHIFT_LINE_MASK;
+ fit.params_count = 0;
+ fit.weight = 0.0f;
+
+ // if the image is flipped and if we do not want to fit both lens shift
+ // directions or none at all, then we need to change direction
+ dt_iop_ashift_fitaxis_t mdir = dir;
+ if((mdir & ASHIFT_FIT_LENS_BOTH) != ASHIFT_FIT_LENS_BOTH &&
+ (mdir & ASHIFT_FIT_LENS_BOTH) != 0)
+ {
+ // flip all directions
+ mdir ^= g->isflipped ? ASHIFT_FIT_FLIP : 0;
+ // special case that needs to be corrected
+ mdir |= (mdir & ASHIFT_FIT_LINES_BOTH) == 0 ? ASHIFT_FIT_LINES_BOTH : 0;
+ }
+
+
+ // prepare fit structure and starting parameters for simplex fit.
+ // note: the sequence of parameters in params[] needs to match the
+ // respective order in dt_iop_ashift_fit_params_t. Parameters which are
+ // to be fittet are marked with NAN in the fit structure. Non-NAN
+ // parameters are assumed to be constant.
+ if(mdir & ASHIFT_FIT_ROTATION)
+ {
+ // we fit rotation
+ fit.params_count++;
+ params[pcount] = logit(0, -fit.rotation_range, fit.rotation_range);
+ pcount++;
+ fit.rotation = NAN;
+ }
+
+ /*
+ if(mdir & ASHIFT_FIT_LENS_VERT)
+ {
+ // we fit vertical lens shift
+ fit.params_count++;
+ params[pcount] = logit(fit.lensshift_v, -fit.lensshift_v_range, fit.lensshift_v_range);
+ pcount++;
+ fit.lensshift_v = NAN;
+ }
+
+ if(mdir & ASHIFT_FIT_LENS_HOR)
+ {
+ // we fit horizontal lens shift
+ fit.params_count++;
+ params[pcount] = logit(fit.lensshift_h, -fit.lensshift_h_range, fit.lensshift_h_range);
+ pcount++;
+ fit.lensshift_h = NAN;
+ }
+
+ if(mdir & ASHIFT_FIT_SHEAR)
+ {
+ // we fit the shear parameter
+ fit.params_count++;
+ params[pcount] = logit(fit.shear, -fit.shear_range, fit.shear_range);
+ pcount++;
+ fit.shear = NAN;
+ }
+ */
+
+ if(mdir & ASHIFT_FIT_LENS_VERT)
+ {
+ // we fit pitch
+ fit.params_count++;
+ params[pcount] = logit(0, -fit.camera_pitch_range, fit.camera_pitch_range);
+ pcount++;
+ fit.camera_pitch = NAN;
+ }
+
+ if(mdir & ASHIFT_FIT_LENS_HOR)
+ {
+ // we fit yaw
+ fit.params_count++;
+ params[pcount] = logit(0, -fit.camera_yaw_range, fit.camera_yaw_range);
+ pcount++;
+ fit.camera_yaw = NAN;
+ }
+
+ if(mdir & ASHIFT_FIT_LINES_VERT)
+ {
+ // we use vertical lines for fitting
+ fit.linetype |= ASHIFT_LINE_DIRVERT;
+ fit.weight += g->vertical_weight;
+ enough_lines = enough_lines && (g->vertical_count >= MINIMUM_FITLINES);
+ }
+
+ if(mdir & ASHIFT_FIT_LINES_HOR)
+ {
+ // we use horizontal lines for fitting
+ fit.linetype |= 0;
+ fit.weight += g->horizontal_weight;
+ enough_lines = enough_lines && (g->horizontal_count >= MINIMUM_FITLINES);
+ }
+
+ // this needs to come after ASHIFT_FIT_LINES_VERT and ASHIFT_FIT_LINES_HOR
+ if((mdir & ASHIFT_FIT_LINES_BOTH) == ASHIFT_FIT_LINES_BOTH)
+ {
+ // if we use fitting in both directions we need to
+ // adjust fit.linetype and fit.linemask to match all selected lines
+ fit.linetype = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED;
+ fit.linemask = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED;
+ }
+
+ // error case: we do not run simplex if there are not enough lines
+ if(!enough_lines)
+ {
+#ifdef ASHIFT_DEBUG
+ printf("optimization not possible: insufficient number of lines\n");
+#endif
+ return NMS_NOT_ENOUGH_LINES;
+ }
+
+ // start the simplex fit
+ int iter = simplex(model_fitness, params, fit.params_count, NMS_EPSILON, NMS_SCALE, NMS_ITERATIONS, NULL, (void*)&fit);
+
+ // error case: the fit did not converge
+ if(iter >= NMS_ITERATIONS)
+ {
+#ifdef ASHIFT_DEBUG
+ printf("optimization not successful: maximum number of iterations reached (%d)\n", iter);
+#endif
+ return NMS_DID_NOT_CONVERGE;
+ }
+
+ // fit was successful: now consolidate the results (order matters!!!)
+ pcount = 0;
+ fit.rotation = isnan(fit.rotation) ? ilogit(params[pcount++], -fit.rotation_range, fit.rotation_range) : fit.rotation;
+ /*
+ fit.lensshift_v = isnan(fit.lensshift_v) ? ilogit(params[pcount++], -fit.lensshift_v_range, fit.lensshift_v_range) : fit.lensshift_v;
+ fit.lensshift_h = isnan(fit.lensshift_h) ? ilogit(params[pcount++], -fit.lensshift_h_range, fit.lensshift_h_range) : fit.lensshift_h;
+ fit.shear = isnan(fit.shear) ? ilogit(params[pcount++], -fit.shear_range, fit.shear_range) : fit.shear;
+ */
+ fit.camera_pitch = isnan(fit.camera_pitch) ? ilogit(params[pcount++], -fit.camera_pitch_range, fit.camera_pitch_range) : fit.camera_pitch;
+ fit.camera_yaw = isnan(fit.camera_yaw) ? ilogit(params[pcount++], -fit.camera_yaw_range, fit.camera_yaw_range) : fit.camera_yaw;
+#ifdef ASHIFT_DEBUG
+ /*
+ printf("params after optimization (%d iterations): rotation %f, lensshift_v %f, lensshift_h %f, shear %f\n",
+ iter, fit.rotation, fit.lensshift_v, fit.lensshift_h, fit.shear);
+ */
+ printf("params after optimization (%d iterations): rotation %f, camera_pitch %f, camera_yaw %f\n",
+ iter, fit.rotation, fit.camera_pitch, fit.camera_yaw);
+#endif
+
+ /*
+ // sanity check: in case of extreme values the image gets distorted so strongly that it spans an insanely huge area. we check that
+ // case and assume values that increase the image area by more than a factor of 4 as being insane.
+ float homograph[3][3];
+ homography((float *)homograph, fit.rotation, fit.lensshift_v, fit.lensshift_h, fit.shear, fit.f_length_kb,
+ fit.orthocorr, fit.aspect, fit.width, fit.height, ASHIFT_HOMOGRAPH_FORWARD);
+
+ // visit all four corners and find maximum span
+ float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
+ for(int y = 0; y < fit.height; y += fit.height - 1)
+ for(int x = 0; x < fit.width; x += fit.width - 1)
+ {
+ float pi[3], po[3];
+ pi[0] = x;
+ pi[1] = y;
+ pi[2] = 1.0f;
+ mat3mulv(po, (float *)homograph, pi);
+ po[0] /= po[2];
+ po[1] /= po[2];
+ xm = fmin(xm, po[0]);
+ ym = fmin(ym, po[1]);
+ xM = fmax(xM, po[0]);
+ yM = fmax(yM, po[1]);
+ }
+
+ if((xM - xm) * (yM - ym) > 4.0f * fit.width * fit.height)
+ {
+#ifdef ASHIFT_DEBUG
+ printf("optimization not successful: degenerate case with area growth factor (%f) exceeding limits\n",
+ (xM - xm) * (yM - ym) / (fit.width * fit.height));
+#endif
+ return NMS_INSANE;
+ }
+ */
+
+ // now write the results into structure p
+ p->rotation = fit.rotation;
+ /*
+ p->lensshift_v = fit.lensshift_v;
+ p->lensshift_h = fit.lensshift_h;
+ p->shear = fit.shear;
+ */
+ p->camera_pitch = fit.camera_pitch;
+ p->camera_yaw = fit.camera_yaw;
+ return NMS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+#ifdef ASHIFT_DEBUG
+// only used in development phase. call model_fitness() with current parameters and
+// print some useful information
+static void model_probe(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data;
+
+ if(!g->lines) return;
+ if(dir == ASHIFT_FIT_NONE) return;
+
+ double params[4];
+ int enough_lines = TRUE;
+
+ // initialize fit parameters
+ dt_iop_ashift_fit_params_t fit;
+ fit.lines = g->lines;
+ fit.lines_count = g->lines_count;
+ fit.width = g->lines_in_width;
+ fit.height = g->lines_in_height;
+ fit.f_length_kb = (p->mode == ASHIFT_MODE_GENERIC) ? DEFAULT_F_LENGTH : p->f_length * p->crop_factor;
+ fit.orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr;
+ fit.aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect;
+ fit.rotation = p->rotation;
+ fit.lensshift_v = p->lensshift_v;
+ fit.lensshift_h = p->lensshift_h;
+ fit.shear = p->shear;
+ fit.linetype = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED;
+ fit.linemask = ASHIFT_LINE_MASK;
+ fit.params_count = 0;
+ fit.weight = 0.0f;
+
+ // if the image is flipped and if we do not want to fit both lens shift
+ // directions or none at all, then we need to change direction
+ dt_iop_ashift_fitaxis_t mdir = dir;
+ if((mdir & ASHIFT_FIT_LENS_BOTH) != ASHIFT_FIT_LENS_BOTH &&
+ (mdir & ASHIFT_FIT_LENS_BOTH) != 0)
+ {
+ // flip all directions
+ mdir ^= g->isflipped ? ASHIFT_FIT_FLIP : 0;
+ // special case that needs to be corrected
+ mdir |= (mdir & ASHIFT_FIT_LINES_BOTH) == 0 ? ASHIFT_FIT_LINES_BOTH : 0;
+ }
+
+ if(mdir & ASHIFT_FIT_LINES_VERT)
+ {
+ // we use vertical lines for fitting
+ fit.linetype |= ASHIFT_LINE_DIRVERT;
+ fit.weight += g->vertical_weight;
+ enough_lines = enough_lines && (g->vertical_count >= MINIMUM_FITLINES);
+ }
+
+ if(mdir & ASHIFT_FIT_LINES_HOR)
+ {
+ // we use horizontal lines for fitting
+ fit.linetype |= 0;
+ fit.weight += g->horizontal_weight;
+ enough_lines = enough_lines && (g->horizontal_count >= MINIMUM_FITLINES);
+ }
+
+ // this needs to come after ASHIFT_FIT_LINES_VERT and ASHIFT_FIT_LINES_HOR
+ if((mdir & ASHIFT_FIT_LINES_BOTH) == ASHIFT_FIT_LINES_BOTH)
+ {
+ // if we use fitting in both directions we need to
+ // adjust fit.linetype and fit.linemask to match all selected lines
+ fit.linetype = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED;
+ fit.linemask = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED;
+ }
+
+ double quality = model_fitness(params, (void *)&fit);
+
+ printf("model fitness: %.8f (rotation %f, lensshift_v %f, lensshift_h %f, shear %f)\n",
+ quality, p->rotation, p->lensshift_v, p->lensshift_h, p->shear);
+}
+#endif
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT (no crop support yet)
+//-----------------------------------------------------------------------------
+#if 0
+// function to keep crop fitting parameters within constraints
+static void crop_constraint(double *params, int pcount)
+{
+ if(pcount > 0) params[0] = fabs(params[0]);
+ if(pcount > 1) params[1] = fabs(params[1]);
+ if(pcount > 2) params[2] = fabs(params[2]);
+
+ if(pcount > 0 && params[0] > 1.0) params[0] = 1.0 - params[0];
+ if(pcount > 1 && params[1] > 1.0) params[1] = 1.0 - params[1];
+ if(pcount > 2 && params[2] > 0.5*M_PI) params[2] = 0.5*M_PI - params[2];
+}
+
+// helper function for getting the best fitting crop area;
+// returns the negative area of the largest rectangle that fits within the
+// defined image with a given rectangle's center and its aspect angle;
+// the trick: the rectangle center coordinates are given in the input
+// image coordinates so we know for sure that it also lies within the image after
+// conversion to the output coordinates
+static double crop_fitness(double *params, void *data)
+{
+ dt_iop_ashift_cropfit_params_t *cropfit = (dt_iop_ashift_cropfit_params_t *)data;
+
+ const float wd = cropfit->width;
+ const float ht = cropfit->height;
+
+ // get variable and constant parameters, respectively
+ const float x = isnan(cropfit->x) ? params[0] : cropfit->x;
+ const float y = isnan(cropfit->y) ? params[1] : cropfit->y;
+ const float alpha = isnan(cropfit->alpha) ? params[2] : cropfit->alpha;
+
+ // the center of the rectangle in input image coordinates
+ const float Pc[3] = { x * wd, y * ht, 1.0f };
+
+ // convert to the output image coordinates and normalize
+ float P[3];
+ mat3mulv(P, (float *)cropfit->homograph, Pc);
+ P[0] /= P[2];
+ P[1] /= P[2];
+ P[2] = 1.0f;
+
+ // two auxiliary points (some arbitrary distance away from P) to construct the diagonals
+ const float Pa[2][3] = { { P[0] + 10.0f * cos(alpha), P[1] + 10.0f * sin(alpha), 1.0f },
+ { P[0] + 10.0f * cos(alpha), P[1] - 10.0f * sin(alpha), 1.0f } };
+
+ // the two diagonals: D = P x Pa
+ float D[2][3];
+ vec3prodn(D[0], P, Pa[0]);
+ vec3prodn(D[1], P, Pa[1]);
+
+ // find all intersection points of all four edges with both diagonals (I = E x D);
+ // the shortest distance d2min of the intersection point I to the crop area center P determines
+ // the size of the crop area that still fits into the image (for the given center and aspect angle)
+ float d2min = FLT_MAX;
+ for(int k = 0; k < 4; k++)
+ for(int l = 0; l < 2; l++)
+ {
+ // the intersection point
+ float I[3];
+ vec3prodn(I, cropfit->edges[k], D[l]);
+
+ // special case: I is all null -> E and D are identical -> P lies on E -> d2min = 0
+ if(vec3isnull(I))
+ {
+ d2min = 0.0f;
+ break;
+ }
+
+ // special case: I[2] is 0.0f -> E and D are parallel and intersect at infinity -> no relevant point
+ if(I[2] == 0.0f)
+ continue;
+
+ // the default case -> normalize I
+ I[0] /= I[2];
+ I[1] /= I[2];
+
+ // calculate distance from I to P
+ const float d2 = SQR(P[0] - I[0]) + SQR(P[1] - I[1]);
+
+ // the minimum distance over all intersection points
+ d2min = MIN(d2min, d2);
+ }
+
+ // calculate the area of the rectangle
+ const float A = 2.0f * d2min * sin(2.0f * alpha);
+
+#ifdef ASHIFT_DEBUG
+ printf("crop fitness with x %f, y %f, angle %f -> distance %f, area %f\n",
+ x, y, alpha, d2min, A);
+#endif
+ // and return -A to allow Nelder-Mead simplex to search for the minimum
+ return -A;
+}
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+// strategy: for a given center of the crop area and a specific aspect angle
+// we calculate the largest crop area that still lies within the output image;
+// now we allow a Nelder-Mead simplex to search for the center coordinates
+// (and optionally the aspect angle) that delivers the largest overall crop area.
+static void do_crop(dt_iop_module_t *module, dt_iop_ashift_params_t *p)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data;
+
+ // skip if fitting is still running
+ if(g->fitting) return;
+
+ // reset fit margins if auto-cropping is off
+ if(p->cropmode == ASHIFT_CROP_OFF)
+ {
+ p->cl = 0.0f;
+ p->cr = 1.0f;
+ p->ct = 0.0f;
+ p->cb = 1.0f;
+ return;
+ }
+
+ g->fitting = 1;
+
+ double params[3];
+ int pcount;
+
+ // get parameters for the homograph
+ const float f_length_kb = (p->mode == ASHIFT_MODE_GENERIC) ? DEFAULT_F_LENGTH : p->f_length * p->crop_factor;
+ const float orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr;
+ const float aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect;
+ const float rotation = p->rotation;
+ const float lensshift_v = p->lensshift_v;
+ const float lensshift_h = p->lensshift_h;
+ const float shear = p->shear;
+
+ // prepare structure of constant parameters
+ dt_iop_ashift_cropfit_params_t cropfit;
+ cropfit.width = g->buf_width;
+ cropfit.height = g->buf_height;
+ homography((float *)cropfit.homograph, rotation, lensshift_v, lensshift_h, shear, f_length_kb,
+ orthocorr, aspect, cropfit.width, cropfit.height, ASHIFT_HOMOGRAPH_FORWARD);
+
+ const float wd = cropfit.width;
+ const float ht = cropfit.height;
+
+ // the four vertices of the image in input image coordinates
+ const float Vc[4][3] = { { 0.0f, 0.0f, 1.0f },
+ { 0.0f, ht, 1.0f },
+ { wd, ht, 1.0f },
+ { wd, 0.0f, 1.0f } };
+
+ // convert the vertices to output image coordinates
+ float V[4][3];
+ for(int n = 0; n < 4; n++)
+ mat3mulv(V[n], (float *)cropfit.homograph, Vc[n]);
+
+ // get width and height of output image for later use
+ float xmin = FLT_MAX, ymin = FLT_MAX, xmax = FLT_MIN, ymax = FLT_MIN;
+ for(int n = 0; n < 4; n++)
+ {
+ // normalize V
+ V[n][0] /= V[n][2];
+ V[n][1] /= V[n][2];
+ V[n][2] = 1.0f;
+ xmin = MIN(xmin, V[n][0]);
+ xmax = MAX(xmax, V[n][0]);
+ ymin = MIN(ymin, V[n][1]);
+ ymax = MAX(ymax, V[n][1]);
+ }
+ const float owd = xmax - xmin;
+ const float oht = ymax - ymin;
+
+ // calculate the lines defining the four edges of the image area: E = V[n] x V[n+1]
+ for(int n = 0; n < 4; n++)
+ vec3prodn(cropfit.edges[n], V[n], V[(n + 1) % 4]);
+
+ // initial fit parameters: crop area is centered and aspect angle is that of the original image
+ // number of parameters: fit only crop center coordinates with a fixed aspect ratio, or fit all three variables
+ if(p->cropmode == ASHIFT_CROP_LARGEST)
+ {
+ params[0] = 0.5;
+ params[1] = 0.5;
+ params[2] = atan2((float)cropfit.height, (float)cropfit.width);
+ cropfit.x = NAN;
+ cropfit.y = NAN;
+ cropfit.alpha = NAN;
+ pcount = 3;
+ }
+ else //(p->cropmode == ASHIFT_CROP_ASPECT)
+ {
+ params[0] = 0.5;
+ params[1] = 0.5;
+ cropfit.x = NAN;
+ cropfit.y = NAN;
+ cropfit.alpha = atan2((float)cropfit.height, (float)cropfit.width);
+ pcount = 2;
+ }
+
+ // start the simplex fit
+ const int iter = simplex(crop_fitness, params, pcount, NMS_CROP_EPSILON, NMS_CROP_SCALE, NMS_CROP_ITERATIONS,
+ crop_constraint, (void*)&cropfit);
+
+ float A; // RT
+ float d; // RT
+ float Pc[3] = { 0.f, 0.f, 1.f }; // RT
+
+ // in case the fit did not converge -> failed
+ if(iter >= NMS_CROP_ITERATIONS) goto failed;
+
+ // the fit did converge -> get clipping margins out of params:
+ cropfit.x = isnan(cropfit.x) ? params[0] : cropfit.x;
+ cropfit.y = isnan(cropfit.y) ? params[1] : cropfit.y;
+ cropfit.alpha = isnan(cropfit.alpha) ? params[2] : cropfit.alpha;
+
+ // the area of the best fitting rectangle
+ /*RT const float*/ A = fabs(crop_fitness(params, (void*)&cropfit));
+
+ // unlikely to happen but we need to catch this case
+ if(A == 0.0f) goto failed;
+
+ // we need the half diagonal of that rectangle (this is in output image dimensions);
+ // no need to check for division by zero here as this case implies A == 0.0f, caught above
+ /*RT const float*/ d = sqrt(A / (2.0f * sin(2.0f * cropfit.alpha)));
+
+ // the rectangle's center in input image (homogeneous) coordinates
+ // RT const float Pc[3] = { cropfit.x * wd, cropfit.y * ht, 1.0f };
+ Pc[0] = cropfit.x * wd; // RT
+ Pc[1] = cropfit.y * ht; // RT
+
+ // convert rectangle center to output image coordinates and normalize
+ float P[3];
+ mat3mulv(P, (float *)cropfit.homograph, Pc);
+ P[0] /= P[2];
+ P[1] /= P[2];
+
+ // calculate clipping margins relative to output image dimensions
+ p->cl = CLAMP((P[0] - d * cos(cropfit.alpha)) / owd, 0.0f, 1.0f);
+ p->cr = CLAMP((P[0] + d * cos(cropfit.alpha)) / owd, 0.0f, 1.0f);
+ p->ct = CLAMP((P[1] - d * sin(cropfit.alpha)) / oht, 0.0f, 1.0f);
+ p->cb = CLAMP((P[1] + d * sin(cropfit.alpha)) / oht, 0.0f, 1.0f);
+
+ // final sanity check
+ if(p->cr - p->cl <= 0.0f || p->cb - p->ct <= 0.0f) goto failed;
+
+ g->fitting = 0;
+
+#ifdef ASHIFT_DEBUG
+ printf("margins after crop fitting: iter %d, x %f, y %f, angle %f, crop area (%f %f %f %f), width %f, height %f\n",
+ iter, cropfit.x, cropfit.y, cropfit.alpha, p->cl, p->cr, p->ct, p->cb, wd, ht);
+#endif
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+ dt_control_queue_redraw_center();
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+ return;
+
+failed:
+ // in case of failure: reset clipping margins, set "automatic cropping" parameter
+ // to "off" state, and display warning message
+ p->cl = 0.0f;
+ p->cr = 1.0f;
+ p->ct = 0.0f;
+ p->cb = 1.0f;
+ p->cropmode = ASHIFT_CROP_OFF;
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+ dt_bauhaus_combobox_set(g->cropmode, p->cropmode);
+#endif // if 0
+//-----------------------------------------------------------------------------
+ g->fitting = 0;
+ dt_control_log(_("automatic cropping failed"));
+ return;
+}
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT (no crop support yet)
+//-----------------------------------------------------------------------------
+#if 0
+// manually adjust crop area by shifting its center
+static void crop_adjust(dt_iop_module_t *module, dt_iop_ashift_params_t *p, const float newx, const float newy)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data;
+
+ // skip if fitting is still running
+ if(g->fitting) return;
+
+ // get parameters for the homograph
+ const float f_length_kb = (p->mode == ASHIFT_MODE_GENERIC) ? DEFAULT_F_LENGTH : p->f_length * p->crop_factor;
+ const float orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr;
+ const float aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect;
+ const float rotation = p->rotation;
+ const float lensshift_v = p->lensshift_v;
+ const float lensshift_h = p->lensshift_h;
+ const float shear = p->shear;
+
+ const float wd = g->buf_width;
+ const float ht = g->buf_height;
+
+ const float alpha = atan2(ht, wd);
+
+ float homograph[3][3];
+ homography((float *)homograph, rotation, lensshift_v, lensshift_h, shear, f_length_kb,
+ orthocorr, aspect, wd, ht, ASHIFT_HOMOGRAPH_FORWARD);
+
+ // the four vertices of the image in input image coordinates
+ const float Vc[4][3] = { { 0.0f, 0.0f, 1.0f },
+ { 0.0f, ht, 1.0f },
+ { wd, ht, 1.0f },
+ { wd, 0.0f, 1.0f } };
+
+ // convert the vertices to output image coordinates
+ float V[4][3];
+ for(int n = 0; n < 4; n++)
+ mat3mulv(V[n], (float *)homograph, Vc[n]);
+
+ // get width and height of output image
+ float xmin = FLT_MAX, ymin = FLT_MAX, xmax = FLT_MIN, ymax = FLT_MIN;
+ for(int n = 0; n < 4; n++)
+ {
+ // normalize V
+ V[n][0] /= V[n][2];
+ V[n][1] /= V[n][2];
+ V[n][2] = 1.0f;
+ xmin = MIN(xmin, V[n][0]);
+ xmax = MAX(xmax, V[n][0]);
+ ymin = MIN(ymin, V[n][1]);
+ ymax = MAX(ymax, V[n][1]);
+ }
+ const float owd = xmax - xmin;
+ const float oht = ymax - ymin;
+
+ // calculate the lines defining the four edges of the image area: E = V[n] x V[n+1]
+ float E[4][3];
+ for(int n = 0; n < 4; n++)
+ vec3prodn(E[n], V[n], V[(n + 1) % 4]);
+
+ // the center of the rectangle in output image coordinates
+ const float P[3] = { newx * owd, newy * oht, 1.0f };
+
+ // two auxiliary points (some arbitrary distance away from P) to construct the diagonals
+ const float Pa[2][3] = { { P[0] + 10.0f * cos(alpha), P[1] + 10.0f * sin(alpha), 1.0f },
+ { P[0] + 10.0f * cos(alpha), P[1] - 10.0f * sin(alpha), 1.0f } };
+
+ // the two diagonals: D = P x Pa
+ float D[2][3];
+ vec3prodn(D[0], P, Pa[0]);
+ vec3prodn(D[1], P, Pa[1]);
+
+ // find all intersection points of all four edges with both diagonals (I = E x D);
+ // the shortest distance d2min of the intersection point I to the crop area center P determines
+ // the size of the crop area that still fits into the image (for the given center and aspect angle)
+ float d2min = FLT_MAX;
+ for(int k = 0; k < 4; k++)
+ for(int l = 0; l < 2; l++)
+ {
+ // the intersection point
+ float I[3];
+ vec3prodn(I, E[k], D[l]);
+
+ // special case: I is all null -> E and D are identical -> P lies on E -> d2min = 0
+ if(vec3isnull(I))
+ {
+ d2min = 0.0f;
+ break;
+ }
+
+ // special case: I[2] is 0.0f -> E and D are parallel and intersect at infinity -> no relevant point
+ if(I[2] == 0.0f)
+ continue;
+
+ // the default case -> normalize I
+ I[0] /= I[2];
+ I[1] /= I[2];
+
+ // calculate distance from I to P
+ const float d2 = SQR(P[0] - I[0]) + SQR(P[1] - I[1]);
+
+ // the minimum distance over all intersection points
+ d2min = MIN(d2min, d2);
+ }
+
+ const float d = sqrt(d2min);
+
+ // do not allow crop area to drop below 1% of input image area
+ const float A = 2.0f * d * d * sin(2.0f * alpha);
+ if(A < 0.01f * wd * ht) return;
+
+ // calculate clipping margins relative to output image dimensions
+ p->cl = CLAMP((P[0] - d * cos(alpha)) / owd, 0.0f, 1.0f);
+ p->cr = CLAMP((P[0] + d * cos(alpha)) / owd, 0.0f, 1.0f);
+ p->ct = CLAMP((P[1] - d * sin(alpha)) / oht, 0.0f, 1.0f);
+ p->cb = CLAMP((P[1] + d * sin(alpha)) / oht, 0.0f, 1.0f);
+
+#ifdef ASHIFT_DEBUG
+ printf("margins after crop adjustment: x %f, y %f, angle %f, crop area (%f %f %f %f), width %f, height %f\n",
+ 0.5f * (p->cl + p->cr), 0.5f * (p->ct + p->cb), alpha, p->cl, p->cr, p->ct, p->cb, wd, ht);
+#endif
+ dt_control_queue_redraw_center();
+ return;
+}
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+// helper function to start analysis for structural data and report about errors
+static int do_get_structure(dt_iop_module_t *module, dt_iop_ashift_params_t *p,
+ dt_iop_ashift_enhance_t enhance)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data;
+
+ if(g->fitting) return FALSE;
+
+ g->fitting = 1;
+
+ float *b = NULL;
+ {
+ MyMutex::MyLock lock(g->lock);
+ b = g->buf;
+ }
+ /* dt_pthread_mutex_lock(&g->lock); */
+ /* float *b = g->buf; */
+ /* dt_pthread_mutex_unlock(&g->lock); */
+
+ if(b == NULL)
+ {
+ dt_control_log(_("data pending - please repeat"));
+ goto error;
+ }
+
+ if(!get_structure(module, enhance))
+ {
+ dt_control_log(_("could not detect structural data in image"));
+#ifdef ASHIFT_DEBUG
+ // find out more
+ printf("do_get_structure: buf %p, buf_hash %lu, buf_width %d, buf_height %d, lines %p, lines_count %d\n",
+ g->buf, g->buf_hash, g->buf_width, g->buf_height, g->lines, g->lines_count);
+#endif
+ goto error;
+ }
+
+ if(!remove_outliers(module))
+ {
+ dt_control_log(_("could not run outlier removal"));
+#ifdef ASHIFT_DEBUG
+ // find out more
+ printf("remove_outliers: buf %p, buf_hash %lu, buf_width %d, buf_height %d, lines %p, lines_count %d\n",
+ g->buf, g->buf_hash, g->buf_width, g->buf_height, g->lines, g->lines_count);
+#endif
+ goto error;
+ }
+
+ g->fitting = 0;
+ return TRUE;
+
+error:
+ g->fitting = 0;
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+// helper function to clean structural data
+static int do_clean_structure(dt_iop_module_t *module, dt_iop_ashift_params_t *p)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data;
+
+ if(g->fitting) return FALSE;
+
+ g->fitting = 1;
+ g->lines_count = 0;
+ g->vertical_count = 0;
+ g->horizontal_count = 0;
+ free(g->lines);
+ g->lines = NULL;
+ g->lines_version++;
+ g->lines_suppressed = 0;
+ g->fitting = 0;
+ return TRUE;
+}
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+// helper function to start parameter fit and report about errors
+static int do_fit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data;
+ dt_iop_ashift_nmsresult_t res;
+
+ if(g->fitting) return FALSE;
+
+ // if no structure available get it
+ if(g->lines == NULL)
+ if(!do_get_structure(module, p, ASHIFT_ENHANCE_NONE)) goto error;
+
+ g->fitting = 1;
+
+ res = nmsfit(module, p, dir);
+
+ switch(res)
+ {
+ case NMS_NOT_ENOUGH_LINES:
+ dt_control_log(_("not enough structure for automatic correction"));
+ goto error;
+ break;
+ case NMS_DID_NOT_CONVERGE:
+ case NMS_INSANE:
+ dt_control_log(_("automatic correction failed, please correct manually"));
+ goto error;
+ break;
+ case NMS_SUCCESS:
+ default:
+ break;
+ }
+
+ g->fitting = 0;
+
+ /*
+ // finally apply cropping
+ do_crop(module, p);
+ */
+
+ return TRUE;
+
+error:
+ g->fitting = 0;
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// RT: BEGIN COMMENT
+#if 0
+//-----------------------------------------------------------------------------
+void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
+ void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
+{
+ dt_iop_ashift_data_t *data = (dt_iop_ashift_data_t *)piece->data;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ const int ch = piece->colors;
+ const int ch_width = ch * roi_in->width;
+
+ // only for preview pipe: collect input buffer data and do some other evaluations
+ if(self->dev->gui_attached && g && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW)
+ {
+ // we want to find out if the final output image is flipped in relation to this iop
+ // so we can adjust the gui labels accordingly
+
+ const int width = roi_in->width;
+ const int height = roi_in->height;
+ const int x_off = roi_in->x;
+ const int y_off = roi_in->y;
+ const float scale = roi_in->scale;
+
+ // origin of image and opposite corner as reference points
+ float points[4] = { 0.0f, 0.0f, (float)piece->buf_in.width, (float)piece->buf_in.height };
+ float ivec[2] = { points[2] - points[0], points[3] - points[1] };
+ float ivecl = sqrt(ivec[0] * ivec[0] + ivec[1] * ivec[1]);
+
+ // where do they go?
+ dt_dev_distort_backtransform_plus(self->dev, self->dev->preview_pipe, self->priority + 1, 9999999, points,
+ 2);
+
+ float ovec[2] = { points[2] - points[0], points[3] - points[1] };
+ float ovecl = sqrt(ovec[0] * ovec[0] + ovec[1] * ovec[1]);
+
+ // angle between input vector and output vector
+ float alpha = acos(CLAMP((ivec[0] * ovec[0] + ivec[1] * ovec[1]) / (ivecl * ovecl), -1.0f, 1.0f));
+
+ // we are interested if |alpha| is in the range of 90° +/- 45° -> we assume the image is flipped
+ int isflipped = fabs(fmod(alpha + M_PI, M_PI) - M_PI / 2.0f) < M_PI / 4.0f ? 1 : 0;
+
+ // did modules prior to this one in pixelpipe have changed? -> check via hash value
+ uint64_t hash = dt_dev_hash_plus(self->dev, self->dev->preview_pipe, 0, self->priority - 1);
+
+ dt_pthread_mutex_lock(&g->lock);
+ g->isflipped = isflipped;
+
+ // save a copy of preview input buffer for parameter fitting
+ if(g->buf == NULL || (size_t)g->buf_width * g->buf_height < (size_t)width * height)
+ {
+ // if needed to allocate buffer
+ free(g->buf); // a no-op if g->buf is NULL
+ // only get new buffer if no old buffer available or old buffer does not fit in terms of size
+ g->buf = malloc((size_t)width * height * 4 * sizeof(float));
+ }
+
+ if(g->buf /* && hash != g->buf_hash */)
+ {
+ // copy data
+ memcpy(g->buf, ivoid, (size_t)width * height * ch * sizeof(float));
+
+ g->buf_width = width;
+ g->buf_height = height;
+ g->buf_x_off = x_off;
+ g->buf_y_off = y_off;
+ g->buf_scale = scale;
+ g->buf_hash = hash;
+ }
+
+ dt_pthread_mutex_unlock(&g->lock);
+ }
+
+ // if module is set to neutral parameters we just copy input->output and are done
+ if(isneutral(data))
+ {
+ memcpy(ovoid, ivoid, (size_t)roi_out->width * roi_out->height * ch * sizeof(float));
+ return;
+ }
+
+ const struct dt_interpolation *interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF);
+
+ float ihomograph[3][3];
+ homography((float *)ihomograph, data->rotation, data->lensshift_v, data->lensshift_h, data->shear, data->f_length_kb,
+ data->orthocorr, data->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_INVERTED);
+
+ // clipping offset
+ const float fullwidth = (float)piece->buf_out.width / (data->cr - data->cl);
+ const float fullheight = (float)piece->buf_out.height / (data->cb - data->ct);
+ const float cx = roi_out->scale * fullwidth * data->cl;
+ const float cy = roi_out->scale * fullheight * data->ct;
+
+
+#ifdef _OPENMP
+//#pragma omp parallel for schedule(static) shared(ihomograph, interpolation)
+#endif
+ // go over all pixels of output image
+ for(int j = 0; j < roi_out->height; j++)
+ {
+ float *out = ((float *)ovoid) + (size_t)ch * j * roi_out->width;
+ for(int i = 0; i < roi_out->width; i++, out += ch)
+ {
+ float pin[3], pout[3];
+
+ /* if (j == roi_out->height - 1) { */
+ /* printf("HERE\n"); */
+ /* } */
+
+ // convert output pixel coordinates to original image coordinates
+ pout[0] = roi_out->x + i + cx;
+ pout[1] = roi_out->y + j + cy;
+ pout[0] /= roi_out->scale;
+ pout[1] /= roi_out->scale;
+ pout[2] = 1.0f;
+
+ // apply homograph
+ mat3mulv(pin, (float *)ihomograph, pout);
+
+ // convert to input pixel coordinates
+ pin[0] /= pin[2];
+ pin[1] /= pin[2];
+ pin[0] *= roi_in->scale;
+ pin[1] *= roi_in->scale;
+
+ /* if (pin[0] < 0 || pin[1] < 0) { */
+ /* printf("NEGATIVE: %f %f -> %f %f\n", pout[0], pout[1], pin[0], pin[1]); */
+ /* fflush(stdout); */
+ /* } */
+
+
+ pin[0] -= roi_in->x;
+ pin[1] -= roi_in->y;
+
+ // get output values by interpolation from input image
+ dt_interpolation_compute_pixel4c(interpolation, (float *)ivoid, out, pin[0], pin[1], roi_in->width,
+ roi_in->height, ch_width);
+ }
+ }
+}
+
+#ifdef HAVE_OPENCL
+int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
+ const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
+{
+ dt_iop_ashift_data_t *d = (dt_iop_ashift_data_t *)piece->data;
+ dt_iop_ashift_global_data_t *gd = (dt_iop_ashift_global_data_t *)self->data;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ const int devid = piece->pipe->devid;
+ const int iwidth = roi_in->width;
+ const int iheight = roi_in->height;
+ const int width = roi_out->width;
+ const int height = roi_out->height;
+
+ cl_int err = -999;
+ cl_mem dev_homo = NULL;
+
+ // only for preview pipe: collect input buffer data and do some other evaluations
+ if(self->dev->gui_attached && g && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW)
+ {
+ // we want to find out if the final output image is flipped in relation to this iop
+ // so we can adjust the gui labels accordingly
+
+ const int x_off = roi_in->x;
+ const int y_off = roi_in->y;
+ const float scale = roi_in->scale;
+
+ // origin of image and opposite corner as reference points
+ float points[4] = { 0.0f, 0.0f, (float)piece->buf_in.width, (float)piece->buf_in.height };
+ float ivec[2] = { points[2] - points[0], points[3] - points[1] };
+ float ivecl = sqrt(ivec[0] * ivec[0] + ivec[1] * ivec[1]);
+
+ // where do they go?
+ dt_dev_distort_backtransform_plus(self->dev, self->dev->preview_pipe, self->priority + 1, 9999999, points,
+ 2);
+
+ float ovec[2] = { points[2] - points[0], points[3] - points[1] };
+ float ovecl = sqrt(ovec[0] * ovec[0] + ovec[1] * ovec[1]);
+
+ // angle between input vector and output vector
+ float alpha = acos(CLAMP((ivec[0] * ovec[0] + ivec[1] * ovec[1]) / (ivecl * ovecl), -1.0f, 1.0f));
+
+ // we are interested if |alpha| is in the range of 90° +/- 45° -> we assume the image is flipped
+ int isflipped = fabs(fmod(alpha + M_PI, M_PI) - M_PI / 2.0f) < M_PI / 4.0f ? 1 : 0;
+
+ // do modules coming before this one in pixelpipe have changed? -> check via hash value
+ uint64_t hash = dt_dev_hash_plus(self->dev, self->dev->preview_pipe, 0, self->priority - 1);
+
+ dt_pthread_mutex_lock(&g->lock);
+ g->isflipped = isflipped;
+
+ // save a copy of preview input buffer for parameter fitting
+ if(g->buf == NULL || (size_t)g->buf_width * g->buf_height < (size_t)iwidth * iheight)
+ {
+ // if needed allocate buffer
+ free(g->buf); // a no-op if g->buf is NULL
+ // only get new buffer if no old buffer or old buffer does not fit in terms of size
+ g->buf = malloc((size_t)iwidth * iheight * 4 * sizeof(float));
+ }
+
+ if(g->buf /* && hash != g->buf_hash */)
+ {
+ // copy data
+ err = dt_opencl_copy_device_to_host(devid, g->buf, dev_in, iwidth, iheight, 4 * sizeof(float));
+
+ g->buf_width = iwidth;
+ g->buf_height = iheight;
+ g->buf_x_off = x_off;
+ g->buf_y_off = y_off;
+ g->buf_scale = scale;
+ g->buf_hash = hash;
+ }
+ dt_pthread_mutex_unlock(&g->lock);
+ if(err != CL_SUCCESS) goto error;
+ }
+
+ // if module is set to neutral parameters we just copy input->output and are done
+ if(isneutral(d))
+ {
+ size_t origin[] = { 0, 0, 0 };
+ size_t region[] = { width, height, 1 };
+ err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, region);
+ if(err != CL_SUCCESS) goto error;
+ return TRUE;
+ }
+
+ float ihomograph[3][3];
+ homography((float *)ihomograph, d->rotation, d->lensshift_v, d->lensshift_h, d->shear, d->f_length_kb, d->camera_pitch, d->camera_yaw,
+ d->orthocorr, d->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_INVERTED);
+
+ // clipping offset
+ const float fullwidth = (float)piece->buf_out.width / (d->cr - d->cl);
+ const float fullheight = (float)piece->buf_out.height / (d->cb - d->ct);
+ const float cx = roi_out->scale * fullwidth * d->cl;
+ const float cy = roi_out->scale * fullheight * d->ct;
+
+ dev_homo = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 9, ihomograph);
+ if(dev_homo == NULL) goto error;
+
+ const int iroi[2] = { roi_in->x, roi_in->y };
+ const int oroi[2] = { roi_out->x, roi_out->y };
+ const float in_scale = roi_in->scale;
+ const float out_scale = roi_out->scale;
+ const float clip[2] = { cx, cy };
+
+ size_t sizes[] = { ROUNDUPWD(width), ROUNDUPHT(height), 1 };
+
+ const struct dt_interpolation *interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF);
+
+ int ldkernel = -1;
+
+ switch(interpolation->id)
+ {
+ case DT_INTERPOLATION_BILINEAR:
+ ldkernel = gd->kernel_ashift_bilinear;
+ break;
+ case DT_INTERPOLATION_BICUBIC:
+ ldkernel = gd->kernel_ashift_bicubic;
+ break;
+ case DT_INTERPOLATION_LANCZOS2:
+ ldkernel = gd->kernel_ashift_lanczos2;
+ break;
+ case DT_INTERPOLATION_LANCZOS3:
+ ldkernel = gd->kernel_ashift_lanczos3;
+ break;
+ default:
+ goto error;
+ }
+
+ dt_opencl_set_kernel_arg(devid, ldkernel, 0, sizeof(cl_mem), (void *)&dev_in);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 1, sizeof(cl_mem), (void *)&dev_out);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 2, sizeof(int), (void *)&width);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 3, sizeof(int), (void *)&height);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 4, sizeof(int), (void *)&iwidth);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 5, sizeof(int), (void *)&iheight);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 6, 2 * sizeof(int), (void *)iroi);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 7, 2 * sizeof(int), (void *)oroi);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 8, sizeof(float), (void *)&in_scale);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 9, sizeof(float), (void *)&out_scale);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 10, 2 * sizeof(float), (void *)clip);
+ dt_opencl_set_kernel_arg(devid, ldkernel, 11, sizeof(cl_mem), (void *)&dev_homo);
+ err = dt_opencl_enqueue_kernel_2d(devid, ldkernel, sizes);
+ if(err != CL_SUCCESS) goto error;
+
+ dt_opencl_release_mem_object(dev_homo);
+ return TRUE;
+
+error:
+ dt_opencl_release_mem_object(dev_homo);
+ dt_print(DT_DEBUG_OPENCL, "[opencl_ashift] couldn't enqueue kernel! %d\n", err);
+ return FALSE;
+}
+#endif
+
+// gather information about "near"-ness in g->points_idx
+static void get_near(const float *points, dt_iop_ashift_points_idx_t *points_idx, const int lines_count,
+ float pzx, float pzy, float delta)
+{
+ const float delta2 = delta * delta;
+
+ for(int n = 0; n < lines_count; n++)
+ {
+ points_idx[n].near = 0;
+
+ // skip irrelevant lines
+ if(points_idx[n].type == ASHIFT_LINE_IRRELEVANT)
+ continue;
+
+ // first check if the mouse pointer is outside the bounding box of the line -> skip this line
+ if(pzx < points_idx[n].bbx - delta &&
+ pzx > points_idx[n].bbX + delta &&
+ pzy < points_idx[n].bby - delta &&
+ pzy > points_idx[n].bbY + delta)
+ continue;
+
+ // pointer is inside bounding box
+ size_t offset = points_idx[n].offset;
+ const int length = points_idx[n].length;
+
+ // sanity check (this should not happen)
+ if(length < 2) continue;
+
+ // check line point by point
+ for(int l = 0; l < length; l++, offset++)
+ {
+ float dx = pzx - points[offset * 2];
+ float dy = pzy - points[offset * 2 + 1];
+
+ if(dx * dx + dy * dy < delta2)
+ {
+ points_idx[n].near = 1;
+ break;
+ }
+ }
+ }
+}
+
+// mark lines which are inside a rectangular area in isbounding mode
+static void get_bounded_inside(const float *points, dt_iop_ashift_points_idx_t *points_idx,
+ const int points_lines_count, float pzx, float pzy, float pzx2, float pzy2,
+ dt_iop_ashift_bounding_t mode)
+{
+ // get bounding box coordinates
+ float ax = pzx;
+ float ay = pzy;
+ float bx = pzx2;
+ float by = pzy2;
+ if(pzx > pzx2)
+ {
+ ax = pzx2;
+ bx = pzx;
+ }
+ if(pzy > pzy2)
+ {
+ ay = pzy2;
+ by = pzy;
+ }
+
+ // we either look for the selected or the deselected lines
+ dt_iop_ashift_linetype_t mask = ASHIFT_LINE_SELECTED;
+ dt_iop_ashift_linetype_t state = (mode == ASHIFT_BOUNDING_DESELECT) ? ASHIFT_LINE_SELECTED : 0;
+
+ for(int n = 0; n < points_lines_count; n++)
+ {
+ // mark line as "not near" and "not bounded"
+ points_idx[n].near = 0;
+ points_idx[n].bounded = 0;
+
+ // skip irrelevant lines
+ if(points_idx[n].type == ASHIFT_LINE_IRRELEVANT)
+ continue;
+
+ // is the line inside the box ?
+ if(points_idx[n].bbx >= ax && points_idx[n].bbx <= bx && points_idx[n].bbX >= ax
+ && points_idx[n].bbX <= bx && points_idx[n].bby >= ay && points_idx[n].bby <= by
+ && points_idx[n].bbY >= ay && points_idx[n].bbY <= by)
+ {
+ points_idx[n].bounded = 1;
+ // only mark "near"-ness of those lines we are interested in
+ points_idx[n].near = ((points_idx[n].type & mask) != state) ? 0 : 1;
+ }
+ }
+}
+
+// generate hash value for lines taking into account only the end point coordinates
+static uint64_t get_lines_hash(const dt_iop_ashift_line_t *lines, const int lines_count)
+{
+ uint64_t hash = 5381;
+ for(int n = 0; n < lines_count; n++)
+ {
+ float v[4] = { lines[n].p1[0], lines[n].p1[1], lines[n].p2[0], lines[n].p2[1] };
+
+ for(int i = 0; i < 4; i++)
+ hash = ((hash << 5) + hash) ^ ((uint32_t *)v)[i];
+ }
+ return hash;
+}
+
+// update color information in points_idx if lines have changed in terms of type (but not in terms
+// of number or position)
+static int update_colors(struct dt_iop_module_t *self, dt_iop_ashift_points_idx_t *points_idx,
+ int points_lines_count)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ // is the display flipped relative to the original image?
+ const int isflipped = g->isflipped;
+
+ // go through all lines
+ for(int n = 0; n < points_lines_count; n++)
+ {
+ const dt_iop_ashift_linetype_t type = points_idx[n].type;
+
+ // set line color according to line type/orientation
+ // note: if the screen display is flipped versus the original image we need
+ // to respect that fact in the color selection
+ if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_VERTICAL_SELECTED)
+ points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_BLUE : ASHIFT_LINECOLOR_GREEN;
+ else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_VERTICAL_NOT_SELECTED)
+ points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_YELLOW : ASHIFT_LINECOLOR_RED;
+ else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_HORIZONTAL_SELECTED)
+ points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_GREEN : ASHIFT_LINECOLOR_BLUE;
+ else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_HORIZONTAL_NOT_SELECTED)
+ points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_RED : ASHIFT_LINECOLOR_YELLOW;
+ else
+ points_idx[n].color = ASHIFT_LINECOLOR_GREY;
+ }
+
+ return TRUE;
+}
+
+// get all the points to display lines in the gui
+static int get_points(struct dt_iop_module_t *self, const dt_iop_ashift_line_t *lines, const int lines_count,
+ const int lines_version, float **points, dt_iop_ashift_points_idx_t **points_idx,
+ int *points_lines_count)
+{
+ dt_develop_t *dev = self->dev;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ dt_iop_ashift_points_idx_t *my_points_idx = NULL;
+ float *my_points = NULL;
+
+ // is the display flipped relative to the original image?
+ const int isflipped = g->isflipped;
+
+ // allocate new index array
+ my_points_idx = (dt_iop_ashift_points_idx_t *)malloc(lines_count * sizeof(dt_iop_ashift_points_idx_t));
+ if(my_points_idx == NULL) goto error;
+
+ // account for total number of points
+ size_t total_points = 0;
+
+ // first step: basic initialization of my_points_idx and counting of total_points
+ for(int n = 0; n < lines_count; n++)
+ {
+ const int length = lines[n].length;
+
+ total_points += length;
+
+ my_points_idx[n].length = length;
+ my_points_idx[n].near = 0;
+ my_points_idx[n].bounded = 0;
+
+ const dt_iop_ashift_linetype_t type = lines[n].type;
+ my_points_idx[n].type = type;
+
+ // set line color according to line type/orientation
+ // note: if the screen display is flipped versus the original image we need
+ // to respect that fact in the color selection
+ if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_VERTICAL_SELECTED)
+ my_points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_BLUE : ASHIFT_LINECOLOR_GREEN;
+ else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_VERTICAL_NOT_SELECTED)
+ my_points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_YELLOW : ASHIFT_LINECOLOR_RED;
+ else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_HORIZONTAL_SELECTED)
+ my_points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_GREEN : ASHIFT_LINECOLOR_BLUE;
+ else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_HORIZONTAL_NOT_SELECTED)
+ my_points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_RED : ASHIFT_LINECOLOR_YELLOW;
+ else
+ my_points_idx[n].color = ASHIFT_LINECOLOR_GREY;
+ }
+
+ // now allocate new points buffer
+ my_points = (float *)malloc((size_t)2 * total_points * sizeof(float));
+ if(my_points == NULL) goto error;
+
+ // second step: generate points for each line
+ for(int n = 0, offset = 0; n < lines_count; n++)
+ {
+ my_points_idx[n].offset = offset;
+
+ float x = lines[n].p1[0];
+ float y = lines[n].p1[1];
+ const int length = lines[n].length;
+
+ const float dx = (lines[n].p2[0] - x) / (float)(length - 1);
+ const float dy = (lines[n].p2[1] - y) / (float)(length - 1);
+
+ for(int l = 0; l < length && offset < total_points; l++, offset++)
+ {
+ my_points[2 * offset] = x;
+ my_points[2 * offset + 1] = y;
+
+ x += dx;
+ y += dy;
+ }
+ }
+
+ // third step: transform all points
+ if(!dt_dev_distort_transform_plus(dev, dev->preview_pipe, self->priority, 9999999, my_points, total_points))
+ goto error;
+
+ // fourth step: get bounding box in final coordinates (used later for checking "near"-ness to mouse pointer)
+ for(int n = 0; n < lines_count; n++)
+ {
+ float xmin = FLT_MAX, xmax = FLT_MIN, ymin = FLT_MAX, ymax = FLT_MIN;
+
+ size_t offset = my_points_idx[n].offset;
+ int length = my_points_idx[n].length;
+
+ for(int l = 0; l < length; l++)
+ {
+ xmin = fmin(xmin, my_points[2 * offset]);
+ xmax = fmax(xmax, my_points[2 * offset]);
+ ymin = fmin(ymin, my_points[2 * offset + 1]);
+ ymax = fmax(ymax, my_points[2 * offset + 1]);
+ }
+
+ my_points_idx[n].bbx = xmin;
+ my_points_idx[n].bbX = xmax;
+ my_points_idx[n].bby = ymin;
+ my_points_idx[n].bbY = ymax;
+ }
+
+ // check if lines_version has changed in-between -> too bad: we can forget about all we did :(
+ if(g->lines_version > lines_version)
+ goto error;
+
+ *points = my_points;
+ *points_idx = my_points_idx;
+ *points_lines_count = lines_count;
+
+ return TRUE;
+
+error:
+ if(my_points_idx != NULL) free(my_points_idx);
+ if(my_points != NULL) free(my_points);
+ return FALSE;
+}
+
+// does this gui have focus?
+static int gui_has_focus(struct dt_iop_module_t *self)
+{
+ return self->dev->gui_module == self;
+}
+
+void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height,
+ int32_t pointerx, int32_t pointery)
+{
+ dt_develop_t *dev = self->dev;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+
+ // the usual rescaling stuff
+ const float wd = dev->preview_pipe->backbuf_width;
+ const float ht = dev->preview_pipe->backbuf_height;
+ if(wd < 1.0 || ht < 1.0) return;
+ const float zoom_y = dt_control_get_dev_zoom_y();
+ const float zoom_x = dt_control_get_dev_zoom_x();
+ const dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
+ const int closeup = dt_control_get_dev_closeup();
+ const float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, 1<buf has been processed
+ if(g->buf && (p->cropmode != ASHIFT_CROP_OFF) && self->enabled)
+ {
+ // roi data of the preview pipe input buffer
+ const float iwd = g->buf_width;
+ const float iht = g->buf_height;
+ const float ixo = g->buf_x_off;
+ const float iyo = g->buf_y_off;
+
+ // the four corners of the input buffer of this module
+ const float V[4][2] = { { ixo, iyo },
+ { ixo, iyo + iht },
+ { ixo + iwd, iyo + iht },
+ { ixo + iwd, iyo } };
+
+ // convert coordinates of corners to coordinates of this module's output
+ if(!dt_dev_distort_transform_plus(self->dev, self->dev->preview_pipe, self->priority, self->priority + 1,
+ (float *)V, 4))
+ return;
+
+ // get x/y-offset as well as width and height of output buffer
+ float xmin = FLT_MAX, ymin = FLT_MAX, xmax = FLT_MIN, ymax = FLT_MIN;
+ for(int n = 0; n < 4; n++)
+ {
+ xmin = MIN(xmin, V[n][0]);
+ xmax = MAX(xmax, V[n][0]);
+ ymin = MIN(ymin, V[n][1]);
+ ymax = MAX(ymax, V[n][1]);
+ }
+ const float owd = xmax - xmin;
+ const float oht = ymax - ymin;
+
+ // the four clipping corners
+ const float C[4][2] = { { xmin + p->cl * owd, ymin + p->ct * oht },
+ { xmin + p->cl * owd, ymin + p->cb * oht },
+ { xmin + p->cr * owd, ymin + p->cb * oht },
+ { xmin + p->cr * owd, ymin + p->ct * oht } };
+
+ // convert clipping corners to final output image
+ if(!dt_dev_distort_transform_plus(self->dev, self->dev->preview_pipe, self->priority + 1, 9999999,
+ (float *)C, 4))
+ return;
+
+ cairo_save(cr);
+
+ double dashes = DT_PIXEL_APPLY_DPI(5.0) / zoom_scale;
+ cairo_set_dash(cr, &dashes, 0, 0);
+
+ cairo_rectangle(cr, 0, 0, width, height);
+ cairo_clip(cr);
+
+ // mask parts of image outside of clipping area in dark grey
+ cairo_set_source_rgba(cr, .2, .2, .2, .8);
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+ cairo_rectangle(cr, 0, 0, width, height);
+ cairo_translate(cr, width / 2.0, height / 2.0);
+ cairo_scale(cr, zoom_scale, zoom_scale);
+ cairo_translate(cr, -.5f * wd - zoom_x * wd, -.5f * ht - zoom_y * ht);
+ cairo_move_to(cr, C[0][0], C[0][1]);
+ cairo_line_to(cr, C[1][0], C[1][1]);
+ cairo_line_to(cr, C[2][0], C[2][1]);
+ cairo_line_to(cr, C[3][0], C[3][1]);
+ cairo_close_path(cr);
+ cairo_fill(cr);
+
+ // draw white outline around clipping area
+ cairo_set_source_rgb(cr, .7, .7, .7);
+ cairo_move_to(cr, C[0][0], C[0][1]);
+ cairo_line_to(cr, C[1][0], C[1][1]);
+ cairo_line_to(cr, C[2][0], C[2][1]);
+ cairo_line_to(cr, C[3][0], C[3][1]);
+ cairo_close_path(cr);
+ cairo_stroke(cr);
+
+ // if adjusting crop, draw indicator
+ if (g->adjust_crop && p->cropmode == ASHIFT_CROP_ASPECT)
+ {
+ const double xpos = (C[1][0] + C[2][0]) / 2.0f;
+ const double ypos = (C[0][1] + C[1][1]) / 2.0f;
+ const double size_circle = (C[2][0] - C[1][0]) / 30.0f;
+ const double size_line = (C[2][0] - C[1][0]) / 5.0f;
+ const double size_arrow = (C[2][0] - C[1][0]) / 25.0f;
+
+ cairo_set_line_width(cr, 2.0 / zoom_scale);
+ cairo_set_source_rgb(cr, .7, .7, .7);
+ cairo_arc (cr, xpos, ypos, size_circle, 0, 2.0 * M_PI);
+ cairo_stroke(cr);
+ cairo_fill(cr);
+
+ cairo_set_line_width(cr, 2.0 / zoom_scale);
+ cairo_set_source_rgb(cr, .7, .7, .7);
+
+ // horizontal line
+ cairo_move_to(cr, xpos - size_line, ypos);
+ cairo_line_to(cr, xpos + size_line, ypos);
+
+ cairo_move_to(cr, xpos - size_line, ypos);
+ cairo_rel_line_to(cr, size_arrow, size_arrow);
+ cairo_move_to(cr, xpos - size_line, ypos);
+ cairo_rel_line_to(cr, size_arrow, -size_arrow);
+
+ cairo_move_to(cr, xpos + size_line, ypos);
+ cairo_rel_line_to(cr, -size_arrow, size_arrow);
+ cairo_move_to(cr, xpos + size_line, ypos);
+ cairo_rel_line_to(cr, -size_arrow, -size_arrow);
+
+ // vertical line
+ cairo_move_to(cr, xpos, ypos - size_line);
+ cairo_line_to(cr, xpos, ypos + size_line);
+
+ cairo_move_to(cr, xpos, ypos - size_line);
+ cairo_rel_line_to(cr, -size_arrow, size_arrow);
+ cairo_move_to(cr, xpos, ypos - size_line);
+ cairo_rel_line_to(cr, size_arrow, size_arrow);
+
+ cairo_move_to(cr, xpos, ypos + size_line);
+ cairo_rel_line_to(cr, -size_arrow, -size_arrow);
+ cairo_move_to(cr, xpos, ypos + size_line);
+ cairo_rel_line_to(cr, size_arrow, -size_arrow);
+
+ cairo_stroke(cr);
+ }
+
+ cairo_restore(cr);
+ }
+
+ // show guide lines on request
+ if(g->show_guides)
+ {
+ dt_guides_t *guide = (dt_guides_t *)g_list_nth_data(darktable.guides, 0);
+ double dashes = DT_PIXEL_APPLY_DPI(5.0);
+ cairo_save(cr);
+ cairo_rectangle(cr, 0, 0, width, height);
+ cairo_clip(cr);
+ cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
+ cairo_set_source_rgb(cr, .8, .8, .8);
+ cairo_set_dash(cr, &dashes, 1, 0);
+ guide->draw(cr, 0, 0, width, height, 1.0, guide->user_data);
+ cairo_stroke_preserve(cr);
+ cairo_set_dash(cr, &dashes, 0, 0);
+ cairo_set_source_rgba(cr, 0.3, .3, .3, .8);
+ cairo_stroke(cr);
+ cairo_restore(cr);
+ }
+
+ // structural data are currently being collected or fit procedure is running? -> skip
+ if(g->fitting) return;
+
+ // no structural data or visibility switched off? -> stop here
+ if(g->lines == NULL || g->lines_suppressed || !gui_has_focus(self)) return;
+
+ // get hash value that changes if distortions from here to the end of the pixelpipe changed
+ uint64_t hash = dt_dev_hash_distort(dev);
+ // get hash value that changes if coordinates of lines have changed
+ uint64_t lines_hash = get_lines_hash(g->lines, g->lines_count);
+
+ // points data are missing or outdated, or distortion has changed?
+ if(g->points == NULL || g->points_idx == NULL || hash != g->grid_hash ||
+ (g->lines_version > g->points_version && g->lines_hash != lines_hash))
+ {
+ // we need to reprocess points
+ free(g->points);
+ g->points = NULL;
+ free(g->points_idx);
+ g->points_idx = NULL;
+ g->points_lines_count = 0;
+
+ if(!get_points(self, g->lines, g->lines_count, g->lines_version, &g->points, &g->points_idx,
+ &g->points_lines_count))
+ return;
+
+ g->points_version = g->lines_version;
+ g->grid_hash = hash;
+ g->lines_hash = lines_hash;
+ }
+ else if(g->lines_hash == lines_hash)
+ {
+ // update line type information in points_idx
+ for(int n = 0; n < g->points_lines_count; n++)
+ g->points_idx[n].type = g->lines[n].type;
+
+ // coordinates of lines are unchanged -> we only need to update colors
+ if(!update_colors(self, g->points_idx, g->points_lines_count))
+ return;
+
+ g->points_version = g->lines_version;
+ }
+
+ // a final check
+ if(g->points == NULL || g->points_idx == NULL) return;
+
+ cairo_save(cr);
+ cairo_rectangle(cr, 0, 0, width, height);
+ cairo_clip(cr);
+ cairo_translate(cr, width / 2.0, height / 2.0);
+ cairo_scale(cr, zoom_scale, zoom_scale);
+ cairo_translate(cr, -.5f * wd - zoom_x * wd, -.5f * ht - zoom_y * ht);
+
+ // this must match the sequence of enum dt_iop_ashift_linecolor_t!
+ const float line_colors[5][4] =
+ { { 0.3f, 0.3f, 0.3f, 0.8f }, // grey (misc. lines)
+ { 0.0f, 1.0f, 0.0f, 0.8f }, // green (selected vertical lines)
+ { 0.8f, 0.0f, 0.0f, 0.8f }, // red (de-selected vertical lines)
+ { 0.0f, 0.0f, 1.0f, 0.8f }, // blue (selected horizontal lines)
+ { 0.8f, 0.8f, 0.0f, 0.8f } }; // yellow (de-selected horizontal lines)
+
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+
+ // now draw all lines
+ for(int n = 0; n < g->points_lines_count; n++)
+ {
+ // is the near flag set? -> draw line a bit thicker
+ if(g->points_idx[n].near)
+ cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(3.0) / zoom_scale);
+ else
+ cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.5) / zoom_scale);
+
+ // the color of this line
+ const float *color = line_colors[g->points_idx[n].color];
+ cairo_set_source_rgba(cr, color[0], color[1], color[2], color[3]);
+
+ size_t offset = g->points_idx[n].offset;
+ const int length = g->points_idx[n].length;
+
+ // sanity check (this should not happen)
+ if(length < 2) continue;
+
+ // set starting point of multi-segment line
+ cairo_move_to(cr, g->points[offset * 2], g->points[offset * 2 + 1]);
+
+ offset++;
+ // draw individual line segments
+ for(int l = 1; l < length; l++, offset++)
+ {
+ cairo_line_to(cr, g->points[offset * 2], g->points[offset * 2 + 1]);
+ }
+
+ // finally stroke the line
+ cairo_stroke(cr);
+ }
+
+ // and we draw the selection box if any
+ if(g->isbounding != ASHIFT_BOUNDING_OFF)
+ {
+ float pzx, pzy;
+ dt_dev_get_pointer_zoom_pos(dev, pointerx, pointery, &pzx, &pzy);
+ pzx += 0.5f;
+ pzy += 0.5f;
+
+ double dashed[] = { 4.0, 4.0 };
+ dashed[0] /= zoom_scale;
+ dashed[1] /= zoom_scale;
+ int len = sizeof(dashed) / sizeof(dashed[0]);
+
+ cairo_rectangle(cr, g->lastx * wd, g->lasty * ht, (pzx - g->lastx) * wd, (pzy - g->lasty) * ht);
+
+ cairo_set_source_rgba(cr, .3, .3, .3, .8);
+ cairo_set_line_width(cr, 1.0 / zoom_scale);
+ cairo_set_dash(cr, dashed, len, 0);
+ cairo_stroke_preserve(cr);
+ cairo_set_source_rgba(cr, .8, .8, .8, .8);
+ cairo_set_dash(cr, dashed, len, 4);
+ cairo_stroke(cr);
+ }
+
+ // indicate which area is used for "near"-ness detection when selecting/deselecting lines
+ if(g->near_delta > 0)
+ {
+ float pzx, pzy;
+ dt_dev_get_pointer_zoom_pos(dev, pointerx, pointery, &pzx, &pzy);
+ pzx += 0.5f;
+ pzy += 0.5f;
+
+ double dashed[] = { 4.0, 4.0 };
+ dashed[0] /= zoom_scale;
+ dashed[1] /= zoom_scale;
+ int len = sizeof(dashed) / sizeof(dashed[0]);
+
+ cairo_arc(cr, pzx * wd, pzy * ht, g->near_delta, 0, 2.0 * M_PI);
+
+ cairo_set_source_rgba(cr, .3, .3, .3, .8);
+ cairo_set_line_width(cr, 1.0 / zoom_scale);
+ cairo_set_dash(cr, dashed, len, 0);
+ cairo_stroke_preserve(cr);
+ cairo_set_source_rgba(cr, .8, .8, .8, .8);
+ cairo_set_dash(cr, dashed, len, 4);
+ cairo_stroke(cr);
+ }
+
+ cairo_restore(cr);
+}
+
+update the number of selected vertical and horizontal lines
+static void update_lines_count(const dt_iop_ashift_line_t *lines, const int lines_count,
+ int *vertical_count, int *horizontal_count)
+{
+ int vlines = 0;
+ int hlines = 0;
+
+ for(int n = 0; n < lines_count; n++)
+ {
+ if((lines[n].type & ASHIFT_LINE_MASK) == ASHIFT_LINE_VERTICAL_SELECTED)
+ vlines++;
+ else if((lines[n].type & ASHIFT_LINE_MASK) == ASHIFT_LINE_HORIZONTAL_SELECTED)
+ hlines++;
+ }
+
+ *vertical_count = vlines;
+ *horizontal_count = hlines;
+}
+
+int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ int handled = 0;
+
+ const float wd = self->dev->preview_pipe->backbuf_width;
+ const float ht = self->dev->preview_pipe->backbuf_height;
+ if(wd < 1.0 || ht < 1.0) return 1;
+
+ float pzx, pzy;
+ dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy);
+ pzx += 0.5f;
+ pzy += 0.5f;
+
+ if (g->adjust_crop)
+ {
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ const float newx = g->crop_cx + pzx - g->lastx;
+ const float newy = g->crop_cy + pzy - g->lasty;
+ crop_adjust(self, p, newx, newy);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+ return TRUE;
+ }
+
+ // if in rectangle selecting mode adjust "near"-ness of lines according to
+ // the rectangular selection
+ if(g->isbounding != ASHIFT_BOUNDING_OFF)
+ {
+ if(wd >= 1.0 && ht >= 1.0)
+ {
+ // mark lines inside the rectangle
+ get_bounded_inside(g->points, g->points_idx, g->points_lines_count, pzx * wd, pzy * ht, g->lastx * wd,
+ g->lasty * ht, g->isbounding);
+ }
+
+ dt_control_queue_redraw_center();
+ return FALSE;
+ }
+
+ // gather information about "near"-ness in g->points_idx
+ get_near(g->points, g->points_idx, g->points_lines_count, pzx * wd, pzy * ht, g->near_delta);
+
+ // if we are in sweeping mode iterate over lines as we move the pointer and change "selected" state.
+ if(g->isdeselecting || g->isselecting)
+ {
+ for(int n = 0; g->selecting_lines_version == g->lines_version && n < g->points_lines_count; n++)
+ {
+ if(g->points_idx[n].near == 0)
+ continue;
+
+ if(g->isdeselecting)
+ g->lines[n].type &= ~ASHIFT_LINE_SELECTED;
+ else if(g->isselecting)
+ g->lines[n].type |= ASHIFT_LINE_SELECTED;
+
+ handled = 1;
+ }
+ }
+
+ if(handled)
+ {
+ update_lines_count(g->lines, g->lines_count, &g->vertical_count, &g->horizontal_count);
+ g->lines_version++;
+ g->selecting_lines_version++;
+ }
+
+ dt_control_queue_redraw_center();
+
+ // if not in sweeping mode we need to pass the event
+ return (g->isdeselecting || g->isselecting);
+}
+
+int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type,
+ uint32_t state)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ int handled = 0;
+
+ float pzx, pzy;
+ dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy);
+ pzx += 0.5f;
+ pzy += 0.5f;
+
+ const float wd = self->dev->preview_pipe->backbuf_width;
+ const float ht = self->dev->preview_pipe->backbuf_height;
+ if(wd < 1.0 || ht < 1.0) return 1;
+
+
+ // if visibility of lines is switched off or no lines available -> potentially adjust crop area
+ if(g->lines_suppressed || g->lines == NULL)
+ {
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ if (p->cropmode == ASHIFT_CROP_ASPECT)
+ {
+ dt_control_change_cursor(GDK_HAND1);
+ g->adjust_crop = TRUE;
+ g->lastx = pzx;
+ g->lasty = pzy;
+ g->crop_cx = 0.5f * (p->cl + p->cr);
+ g->crop_cy = 0.5f * (p->ct + p->cb);
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+
+ // remember lines version at this stage so we can continuously monitor if the
+ // lines have changed in-between
+ g->selecting_lines_version = g->lines_version;
+
+ // if shift button is pressed go into bounding mode (selecting or deselecting
+ // in a rectangle area)
+ if((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
+ {
+ g->lastx = pzx;
+ g->lasty = pzy;
+
+ g->isbounding = (which == 3) ? ASHIFT_BOUNDING_DESELECT : ASHIFT_BOUNDING_SELECT;
+ dt_control_change_cursor(GDK_CROSS);
+
+ return TRUE;
+ }
+
+ dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
+ const int closeup = dt_control_get_dev_closeup();
+ const float min_scale = dt_dev_get_zoom_scale(self->dev, DT_ZOOM_FIT, 1<dev, zoom, 1<points_lines_count > 0);
+
+ g->near_delta = dt_conf_get_float("plugins/darkroom/ashift/near_delta");
+
+ // gather information about "near"-ness in g->points_idx
+ get_near(g->points, g->points_idx, g->points_lines_count, pzx * wd, pzy * ht, g->near_delta);
+
+ // iterate over all lines close to the pointer and change "selected" state.
+ // left-click selects and right-click deselects the line
+ for(int n = 0; g->selecting_lines_version == g->lines_version && n < g->points_lines_count; n++)
+ {
+ if(g->points_idx[n].near == 0)
+ continue;
+
+ if(which == 3)
+ g->lines[n].type &= ~ASHIFT_LINE_SELECTED;
+ else
+ g->lines[n].type |= ASHIFT_LINE_SELECTED;
+
+ handled = 1;
+ }
+
+ // we switch into sweeping mode either if we anyhow take control
+ // or if cursor was close to a line when button was pressed. in other
+ // cases we hand over the event (for image panning)
+ if((take_control || handled) && which == 3)
+ {
+ dt_control_change_cursor(GDK_PIRATE);
+ g->isdeselecting = 1;
+ }
+ else if(take_control || handled)
+ {
+ dt_control_change_cursor(GDK_PLUS);
+ g->isselecting = 1;
+ }
+
+ if(handled)
+ {
+ update_lines_count(g->lines, g->lines_count, &g->vertical_count, &g->horizontal_count);
+ g->lines_version++;
+ g->selecting_lines_version++;
+ }
+
+ return (take_control || handled);
+}
+
+int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ // stop adjust crop
+ g->adjust_crop = FALSE;
+ dt_control_change_cursor(GDK_LEFT_PTR);
+
+ // finalize the isbounding mode
+ // if user has released the shift button in-between -> do nothing
+ if(g->isbounding != ASHIFT_BOUNDING_OFF && (state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
+ {
+ int handled = 0;
+
+ // we compute the rectangle selection
+ float pzx, pzy;
+ dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy);
+
+ pzx += 0.5f;
+ pzy += 0.5f;
+
+ const float wd = self->dev->preview_pipe->backbuf_width;
+ const float ht = self->dev->preview_pipe->backbuf_height;
+
+ if(wd >= 1.0 && ht >= 1.0)
+ {
+ // mark lines inside the rectangle
+ get_bounded_inside(g->points, g->points_idx, g->points_lines_count, pzx * wd, pzy * ht, g->lastx * wd,
+ g->lasty * ht, g->isbounding);
+
+ // select or deselect lines within the rectangle according to isbounding state
+ for(int n = 0; g->selecting_lines_version == g->lines_version && n < g->points_lines_count; n++)
+ {
+ if(g->points_idx[n].bounded == 0) continue;
+
+ if(g->isbounding == ASHIFT_BOUNDING_DESELECT)
+ g->lines[n].type &= ~ASHIFT_LINE_SELECTED;
+ else
+ g->lines[n].type |= ASHIFT_LINE_SELECTED;
+
+ handled = 1;
+ }
+
+ if(handled)
+ {
+ update_lines_count(g->lines, g->lines_count, &g->vertical_count, &g->horizontal_count);
+ g->lines_version++;
+ g->selecting_lines_version++;
+ }
+
+ dt_control_queue_redraw_center();
+ }
+ }
+
+ // end of sweeping/isbounding mode
+ dt_control_change_cursor(GDK_LEFT_PTR);
+ g->isselecting = g->isdeselecting = 0;
+ g->isbounding = ASHIFT_BOUNDING_OFF;
+ g->near_delta = 0;
+ g->lastx = g->lasty = -1.0f;
+ g->crop_cx = g->crop_cy = -1.0f;
+
+ return 0;
+}
+
+int scrolled(struct dt_iop_module_t *self, double x, double y, int up, uint32_t state)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ // do nothing if visibility of lines is switched off or no lines available
+ if(g->lines_suppressed || g->lines == NULL)
+ return FALSE;
+
+ if(g->near_delta > 0 && (g->isdeselecting || g->isselecting))
+ {
+ int handled = 0;
+
+ float pzx, pzy;
+ dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy);
+ pzx += 0.5f;
+ pzy += 0.5f;
+
+ const float wd = self->dev->preview_pipe->backbuf_width;
+ const float ht = self->dev->preview_pipe->backbuf_height;
+
+ float near_delta = dt_conf_get_float("plugins/darkroom/ashift/near_delta");
+ const float amount = up ? 0.8f : 1.25f;
+ near_delta = MAX(4.0f, MIN(near_delta * amount, 100.0f));
+ dt_conf_set_float("plugins/darkroom/ashift/near_delta", near_delta);
+ g->near_delta = near_delta;
+
+ // gather information about "near"-ness in g->points_idx
+ get_near(g->points, g->points_idx, g->points_lines_count, pzx * wd, pzy * ht, g->near_delta);
+
+ // iterate over all lines close to the pointer and change "selected" state.
+ for(int n = 0; g->selecting_lines_version == g->lines_version && n < g->points_lines_count; n++)
+ {
+ if(g->points_idx[n].near == 0)
+ continue;
+
+ if(g->isdeselecting)
+ g->lines[n].type &= ~ASHIFT_LINE_SELECTED;
+ else if(g->isselecting)
+ g->lines[n].type |= ASHIFT_LINE_SELECTED;
+
+ handled = 1;
+ }
+
+ if(handled)
+ {
+ update_lines_count(g->lines, g->lines_count, &g->vertical_count, &g->horizontal_count);
+ g->lines_version++;
+ g->selecting_lines_version++;
+ }
+
+ dt_control_queue_redraw_center();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void rotation_callback(GtkWidget *slider, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ p->rotation = dt_bauhaus_slider_get(slider);
+#ifdef ASHIFT_DEBUG
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ model_probe(self, p, g->lastfit);
+#endif
+ do_crop(self, p);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+}
+
+static void lensshift_v_callback(GtkWidget *slider, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ p->lensshift_v = dt_bauhaus_slider_get(slider);
+#ifdef ASHIFT_DEBUG
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ model_probe(self, p, g->lastfit);
+#endif
+ do_crop(self, p);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+}
+
+static void lensshift_h_callback(GtkWidget *slider, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ p->lensshift_h = dt_bauhaus_slider_get(slider);
+#ifdef ASHIFT_DEBUG
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ model_probe(self, p, g->lastfit);
+#endif
+ do_crop(self, p);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+}
+
+static void shear_callback(GtkWidget *slider, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ p->shear = dt_bauhaus_slider_get(slider);
+#ifdef ASHIFT_DEBUG
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ model_probe(self, p, g->lastfit);
+#endif
+ do_crop(self, p);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+}
+
+static void guide_lines_callback(GtkWidget *widget, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ g->show_guides = dt_bauhaus_combobox_get(widget);
+ dt_iop_request_focus(self);
+ dt_dev_reprocess_all(self->dev);
+}
+
+static void cropmode_callback(GtkWidget *widget, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ p->cropmode = dt_bauhaus_combobox_get(widget);
+ if(g->lines != NULL && !g->lines_suppressed)
+ {
+ g->lines_suppressed = 1;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->eye), g->lines_suppressed);
+ }
+ do_crop(self, p);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+}
+
+static void mode_callback(GtkWidget *widget, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ p->mode = dt_bauhaus_combobox_get(widget);
+
+ switch(p->mode)
+ {
+ case ASHIFT_MODE_GENERIC:
+ gtk_widget_hide(g->f_length);
+ gtk_widget_hide(g->crop_factor);
+ gtk_widget_hide(g->orthocorr);
+ gtk_widget_hide(g->aspect);
+ break;
+ case ASHIFT_MODE_SPECIFIC:
+ default:
+ gtk_widget_show(g->f_length);
+ gtk_widget_show(g->crop_factor);
+ gtk_widget_show(g->orthocorr);
+ gtk_widget_show(g->aspect);
+ break;
+ }
+
+ do_crop(self, p);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+}
+
+static void f_length_callback(GtkWidget *slider, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ p->f_length = dt_bauhaus_slider_get(slider);
+ do_crop(self, p);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+}
+
+static void crop_factor_callback(GtkWidget *slider, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ p->crop_factor = dt_bauhaus_slider_get(slider);
+ do_crop(self, p);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+}
+
+static void orthocorr_callback(GtkWidget *slider, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ p->orthocorr = dt_bauhaus_slider_get(slider);
+ do_crop(self, p);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+}
+
+static void aspect_callback(GtkWidget *slider, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(self->dt->gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ p->aspect = dt_bauhaus_slider_get(slider);
+ do_crop(self, p);
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+}
+
+static int fit_v_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(darktable.gui->reset) return FALSE;
+
+ if(event->button == 1)
+ {
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ const int control = (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK;
+ const int shift = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK;
+
+ dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE;
+
+ if(control)
+ g->lastfit = fitaxis = ASHIFT_FIT_ROTATION_VERTICAL_LINES;
+ else if(shift)
+ g->lastfit = fitaxis = ASHIFT_FIT_VERTICALLY_NO_ROTATION;
+ else
+ g->lastfit = fitaxis = ASHIFT_FIT_VERTICALLY;
+
+ dt_iop_request_focus(self);
+ dt_dev_reprocess_all(self->dev);
+
+ if(self->enabled)
+ {
+ // module is enable -> we process directly
+ if(do_fit(self, p, fitaxis))
+ {
+ darktable.gui->reset = 1;
+ dt_bauhaus_slider_set_soft(g->rotation, p->rotation);
+ dt_bauhaus_slider_set_soft(g->lensshift_v, p->lensshift_v);
+ dt_bauhaus_slider_set_soft(g->lensshift_h, p->lensshift_h);
+ dt_bauhaus_slider_set_soft(g->shear, p->shear);
+ darktable.gui->reset = 0;
+ }
+ }
+ else
+ {
+ // module is not enabled -> invoke it and queue the job to be processed once
+ // the preview image is ready
+ g->jobcode = ASHIFT_JOBCODE_FIT;
+ g->jobparams = g->lastfit = fitaxis;
+ p->toggle ^= 1;
+ }
+
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int fit_h_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(darktable.gui->reset) return FALSE;
+
+ if(event->button == 1)
+ {
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ const int control = (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK;
+ const int shift = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK;
+
+ dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE;
+
+ if(control)
+ g->lastfit = fitaxis = ASHIFT_FIT_ROTATION_HORIZONTAL_LINES;
+ else if(shift)
+ g->lastfit = fitaxis = ASHIFT_FIT_HORIZONTALLY_NO_ROTATION;
+ else
+ g->lastfit = fitaxis = ASHIFT_FIT_HORIZONTALLY;
+
+ dt_iop_request_focus(self);
+ dt_dev_reprocess_all(self->dev);
+
+ if(self->enabled)
+ {
+ // module is enable -> we process directly
+ if(do_fit(self, p, fitaxis))
+ {
+ darktable.gui->reset = 1;
+ dt_bauhaus_slider_set_soft(g->rotation, p->rotation);
+ dt_bauhaus_slider_set_soft(g->lensshift_v, p->lensshift_v);
+ dt_bauhaus_slider_set_soft(g->lensshift_h, p->lensshift_h);
+ dt_bauhaus_slider_set_soft(g->shear, p->shear);
+ darktable.gui->reset = 0;
+ }
+ }
+ else
+ {
+ // module is not enabled -> invoke it and queue the job to be processed once
+ // the preview image is ready
+ g->jobcode = ASHIFT_JOBCODE_FIT;
+ g->jobparams = g->lastfit = fitaxis;
+ p->toggle ^= 1;
+ }
+
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int fit_both_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(darktable.gui->reset) return FALSE;
+
+ if(event->button == 1)
+ {
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ const int control = (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK;
+ const int shift = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK;
+
+ dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE;
+
+ if(control && shift)
+ fitaxis = ASHIFT_FIT_BOTH;
+ else if(control)
+ fitaxis = ASHIFT_FIT_ROTATION_BOTH_LINES;
+ else if(shift)
+ fitaxis = ASHIFT_FIT_BOTH_NO_ROTATION;
+ else
+ fitaxis = ASHIFT_FIT_BOTH_SHEAR;
+
+ dt_iop_request_focus(self);
+ dt_dev_reprocess_all(self->dev);
+
+ if(self->enabled)
+ {
+ // module is enable -> we process directly
+ if(do_fit(self, p, fitaxis))
+ {
+ darktable.gui->reset = 1;
+ dt_bauhaus_slider_set_soft(g->rotation, p->rotation);
+ dt_bauhaus_slider_set_soft(g->lensshift_v, p->lensshift_v);
+ dt_bauhaus_slider_set_soft(g->lensshift_h, p->lensshift_h);
+ dt_bauhaus_slider_set_soft(g->shear, p->shear);
+ darktable.gui->reset = 0;
+ }
+ }
+ else
+ {
+ // module is not enabled -> invoke it and queue the job to be processed once
+ // the preview image is ready
+ g->jobcode = ASHIFT_JOBCODE_FIT;
+ g->jobparams = g->lastfit = fitaxis;
+ p->toggle ^= 1;
+ }
+
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int structure_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(darktable.gui->reset) return FALSE;
+
+ if(event->button == 1)
+ {
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ const int control = (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK;
+ const int shift = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK;
+
+ dt_iop_ashift_enhance_t enhance;
+
+ if(control && shift)
+ enhance = ASHIFT_ENHANCE_EDGES | ASHIFT_ENHANCE_DETAIL;
+ else if(shift)
+ enhance = ASHIFT_ENHANCE_DETAIL;
+ else if(control)
+ enhance = ASHIFT_ENHANCE_EDGES;
+ else
+ enhance = ASHIFT_ENHANCE_NONE;
+
+ dt_iop_request_focus(self);
+ dt_dev_reprocess_all(self->dev);
+
+ if(self->enabled)
+ {
+ // module is enabled -> process directly
+ (void)do_get_structure(self, p, enhance);
+ }
+ else
+ {
+ // module is not enabled -> invoke it and queue the job to be processed once
+ // the preview image is ready
+ g->jobcode = ASHIFT_JOBCODE_GET_STRUCTURE;
+ g->jobparams = enhance;
+ p->toggle ^= 1;
+ }
+
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void clean_button_clicked(GtkButton *button, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ if(darktable.gui->reset) return;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ (void)do_clean_structure(self, p);
+ dt_iop_request_focus(self);
+ dt_control_queue_redraw_center();
+}
+
+static void eye_button_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ if(darktable.gui->reset) return;
+ if(g->lines == NULL)
+ {
+ g->lines_suppressed = 0;
+ gtk_toggle_button_set_active(togglebutton, 0);
+ }
+ else
+ {
+ g->lines_suppressed = gtk_toggle_button_get_active(togglebutton);
+ }
+ dt_iop_request_focus(self);
+ dt_control_queue_redraw_center();
+}
+
+// routine that is called after preview image has been processed. we use it
+// to perform structure collection or fitting in case those have been triggered while
+// the module had not yet been enabled
+static void process_after_preview_callback(gpointer instance, gpointer user_data)
+{
+ dt_iop_module_t *self = (dt_iop_module_t *)user_data;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+
+ dt_iop_ashift_jobcode_t jobcode = g->jobcode;
+ int jobparams = g->jobparams;
+
+ // purge
+ g->jobcode = ASHIFT_JOBCODE_NONE;
+ g->jobparams = 0;
+
+ if(darktable.gui->reset) return;
+
+ switch(jobcode)
+ {
+ case ASHIFT_JOBCODE_GET_STRUCTURE:
+ (void)do_get_structure(self, p, (dt_iop_ashift_enhance_t)jobparams);
+ break;
+
+ case ASHIFT_JOBCODE_FIT:
+ if(do_fit(self, p, (dt_iop_ashift_fitaxis_t)jobparams))
+ {
+ darktable.gui->reset = 1;
+ dt_bauhaus_slider_set_soft(g->rotation, p->rotation);
+ dt_bauhaus_slider_set_soft(g->lensshift_v, p->lensshift_v);
+ dt_bauhaus_slider_set_soft(g->lensshift_h, p->lensshift_h);
+ dt_bauhaus_slider_set_soft(g->shear, p->shear);
+ darktable.gui->reset = 0;
+ }
+ dt_dev_add_history_item(darktable.develop, self, TRUE);
+ break;
+
+ case ASHIFT_JOBCODE_NONE:
+ default:
+ break;
+ }
+
+ dt_control_queue_redraw_center();
+}
+
+void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe,
+ dt_dev_pixelpipe_iop_t *piece)
+{
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)p1;
+ dt_iop_ashift_data_t *d = (dt_iop_ashift_data_t *)piece->data;
+
+ d->rotation = p->rotation;
+ d->lensshift_v = p->lensshift_v;
+ d->lensshift_h = p->lensshift_h;
+ d->shear = p->shear;
+ d->f_length_kb = (p->mode == ASHIFT_MODE_GENERIC) ? DEFAULT_F_LENGTH : p->f_length * p->crop_factor;
+ d->orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr;
+ d->aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect;
+
+ if(gui_has_focus(self))
+ {
+ // if gui has focus we want to see the full uncropped image
+ d->cl = 0.0f;
+ d->cr = 1.0f;
+ d->ct = 0.0f;
+ d->cb = 1.0f;
+ }
+ else
+ {
+ d->cl = p->cl;
+ d->cr = p->cr;
+ d->ct = p->ct;
+ d->cb = p->cb;
+ }
+}
+
+void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
+{
+ dt_iop_ashift_data_t *d = (dt_iop_ashift_data_t *)calloc(1, sizeof(dt_iop_ashift_data_t));
+ piece->data = (void *)d;
+ self->commit_params(self, self->default_params, pipe, piece);
+}
+
+void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
+{
+ free(piece->data);
+ piece->data = NULL;
+}
+
+void gui_update(struct dt_iop_module_t *self)
+{
+ dt_iop_module_t *module = (dt_iop_module_t *)self;
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)module->params;
+ dt_bauhaus_slider_set_soft(g->rotation, p->rotation);
+ dt_bauhaus_slider_set_soft(g->lensshift_v, p->lensshift_v);
+ dt_bauhaus_slider_set_soft(g->lensshift_h, p->lensshift_h);
+ dt_bauhaus_slider_set_soft(g->shear, p->shear);
+ dt_bauhaus_slider_set_soft(g->f_length, p->f_length);
+ dt_bauhaus_slider_set_soft(g->crop_factor, p->crop_factor);
+ dt_bauhaus_slider_set(g->orthocorr, p->orthocorr);
+ dt_bauhaus_slider_set(g->aspect, p->aspect);
+ dt_bauhaus_combobox_set(g->mode, p->mode);
+ dt_bauhaus_combobox_set(g->guide_lines, g->show_guides);
+ dt_bauhaus_combobox_set(g->cropmode, p->cropmode);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->eye), 0);
+
+ switch(p->mode)
+ {
+ case ASHIFT_MODE_GENERIC:
+ gtk_widget_hide(g->f_length);
+ gtk_widget_hide(g->crop_factor);
+ gtk_widget_hide(g->orthocorr);
+ gtk_widget_hide(g->aspect);
+ break;
+ case ASHIFT_MODE_SPECIFIC:
+ default:
+ gtk_widget_show(g->f_length);
+ gtk_widget_show(g->crop_factor);
+ gtk_widget_show(g->orthocorr);
+ gtk_widget_show(g->aspect);
+ break;
+ }
+}
+
+void init(dt_iop_module_t *module)
+{
+ module->params = calloc(1, sizeof(dt_iop_ashift_params_t));
+ module->default_params = calloc(1, sizeof(dt_iop_ashift_params_t));
+ module->default_enabled = 0;
+ module->priority = 214; // module order created by iop_dependencies.py, do not edit!
+ module->params_size = sizeof(dt_iop_ashift_params_t);
+ module->gui_data = NULL;
+ dt_iop_ashift_params_t tmp = (dt_iop_ashift_params_t){ 0.0f, 0.0f, 0.0f, 0.0f, DEFAULT_F_LENGTH, 1.0f, 100.0f, 1.0f, ASHIFT_MODE_GENERIC, 0,
+ ASHIFT_CROP_OFF, 0.0f, 1.0f, 0.0f, 1.0f };
+ memcpy(module->params, &tmp, sizeof(dt_iop_ashift_params_t));
+ memcpy(module->default_params, &tmp, sizeof(dt_iop_ashift_params_t));
+}
+
+void reload_defaults(dt_iop_module_t *module)
+{
+ // our module is disabled by default
+ module->default_enabled = 0;
+
+ int isflipped = 0;
+ float f_length = DEFAULT_F_LENGTH;
+ float crop_factor = 1.0f;
+
+ // try to get information on orientation, focal length and crop factor from image data
+ if(module->dev)
+ {
+ const dt_image_t *img = &module->dev->image_storage;
+ // orientation only needed as a-priori information to correctly label some sliders
+ // before pixelpipe has been set up. later we will get a definite result by
+ // assessing the pixelpipe
+ isflipped = (img->orientation == ORIENTATION_ROTATE_CCW_90_DEG
+ || img->orientation == ORIENTATION_ROTATE_CW_90_DEG)
+ ? 1
+ : 0;
+
+ // focal length should be available in exif data if lens is electronically coupled to the camera
+ f_length = isfinite(img->exif_focal_length) && img->exif_focal_length > 0.0f ? img->exif_focal_length : f_length;
+ // crop factor of the camera is often not available and user will need to set it manually in the gui
+ crop_factor = isfinite(img->exif_crop) && img->exif_crop > 0.0f ? img->exif_crop : crop_factor;
+ }
+
+ // init defaults:
+ dt_iop_ashift_params_t tmp = (dt_iop_ashift_params_t){ 0.0f, 0.0f, 0.0f, 0.0f, f_length, crop_factor, 100.0f, 1.0f, ASHIFT_MODE_GENERIC, 0,
+ ASHIFT_CROP_OFF, 0.0f, 1.0f, 0.0f, 1.0f };
+ memcpy(module->params, &tmp, sizeof(dt_iop_ashift_params_t));
+ memcpy(module->default_params, &tmp, sizeof(dt_iop_ashift_params_t));
+
+ // reset gui elements
+ if(module->gui_data)
+ {
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data;
+
+ char string_v[256];
+ char string_h[256];
+
+ snprintf(string_v, sizeof(string_v), _("lens shift (%s)"), isflipped ? _("horizontal") : _("vertical"));
+ snprintf(string_h, sizeof(string_h), _("lens shift (%s)"), isflipped ? _("vertical") : _("horizontal"));
+
+ dt_bauhaus_widget_set_label(g->lensshift_v, NULL, string_v);
+ dt_bauhaus_widget_set_label(g->lensshift_h, NULL, string_h);
+
+ dt_bauhaus_slider_set_default(g->f_length, tmp.f_length);
+ dt_bauhaus_slider_set_default(g->crop_factor, tmp.crop_factor);
+
+ dt_pthread_mutex_lock(&g->lock);
+ free(g->buf);
+ g->buf = NULL;
+ g->buf_width = 0;
+ g->buf_height = 0;
+ g->buf_x_off = 0;
+ g->buf_y_off = 0;
+ g->buf_scale = 1.0f;
+ g->buf_hash = 0;
+ g->isflipped = -1;
+ g->lastfit = ASHIFT_FIT_NONE;
+ dt_pthread_mutex_unlock(&g->lock);
+
+ g->fitting = 0;
+ free(g->lines);
+ g->lines = NULL;
+ g->lines_count =0;
+ g->horizontal_count = 0;
+ g->vertical_count = 0;
+ g->grid_hash = 0;
+ g->lines_hash = 0;
+ g->rotation_range = ROTATION_RANGE_SOFT;
+ g->lensshift_v_range = LENSSHIFT_RANGE_SOFT;
+ g->lensshift_h_range = LENSSHIFT_RANGE_SOFT;
+ g->shear_range = SHEAR_RANGE_SOFT;
+ g->lines_suppressed = 0;
+ g->lines_version = 0;
+ g->show_guides = 0;
+ g->isselecting = 0;
+ g->isdeselecting = 0;
+ g->isbounding = ASHIFT_BOUNDING_OFF;
+ g->near_delta = 0;
+ g->selecting_lines_version = 0;
+
+ free(g->points);
+ g->points = NULL;
+ free(g->points_idx);
+ g->points_idx = NULL;
+ g->points_lines_count = 0;
+ g->points_version = 0;
+
+ g->jobcode = ASHIFT_JOBCODE_NONE;
+ g->jobparams = 0;
+ g->adjust_crop = FALSE;
+ g->lastx = g->lasty = -1.0f;
+ g->crop_cx = g->crop_cy = 1.0f;
+ }
+}
+
+
+void init_global(dt_iop_module_so_t *module)
+{
+ dt_iop_ashift_global_data_t *gd
+ = (dt_iop_ashift_global_data_t *)malloc(sizeof(dt_iop_ashift_global_data_t));
+ module->data = gd;
+
+ const int program = 2; // basic.cl, from programs.conf
+ gd->kernel_ashift_bilinear = dt_opencl_create_kernel(program, "ashift_bilinear");
+ gd->kernel_ashift_bicubic = dt_opencl_create_kernel(program, "ashift_bicubic");
+ gd->kernel_ashift_lanczos2 = dt_opencl_create_kernel(program, "ashift_lanczos2");
+ gd->kernel_ashift_lanczos3 = dt_opencl_create_kernel(program, "ashift_lanczos3");
+}
+
+void cleanup(dt_iop_module_t *module)
+{
+ free(module->params);
+ module->params = NULL;
+}
+
+void cleanup_global(dt_iop_module_so_t *module)
+{
+ dt_iop_ashift_global_data_t *gd = (dt_iop_ashift_global_data_t *)module->data;
+ dt_opencl_free_kernel(gd->kernel_ashift_bilinear);
+ dt_opencl_free_kernel(gd->kernel_ashift_bicubic);
+ dt_opencl_free_kernel(gd->kernel_ashift_lanczos2);
+ dt_opencl_free_kernel(gd->kernel_ashift_lanczos3);
+ free(module->data);
+ module->data = NULL;
+}
+
+// adjust labels of lens shift parameters according to flip status of image
+static gboolean draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self)
+{
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ if(darktable.gui->reset) return FALSE;
+
+ dt_pthread_mutex_lock(&g->lock);
+ const int isflipped = g->isflipped;
+ dt_pthread_mutex_unlock(&g->lock);
+
+ if(isflipped == -1) return FALSE;
+
+ char string_v[256];
+ char string_h[256];
+
+ snprintf(string_v, sizeof(string_v), _("lens shift (%s)"), isflipped ? _("horizontal") : _("vertical"));
+ snprintf(string_h, sizeof(string_h), _("lens shift (%s)"), isflipped ? _("vertical") : _("horizontal"));
+
+ darktable.gui->reset = 1;
+ dt_bauhaus_widget_set_label(g->lensshift_v, NULL, string_v);
+ dt_bauhaus_widget_set_label(g->lensshift_h, NULL, string_h);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->eye), g->lines_suppressed);
+ darktable.gui->reset = 0;
+
+ return FALSE;
+}
+
+void gui_focus(struct dt_iop_module_t *self, gboolean in)
+{
+ if(self->enabled)
+ dt_dev_reprocess_all(self->dev);
+}
+
+static float log10_callback(GtkWidget *self, float inval, dt_bauhaus_callback_t dir)
+{
+ float outval;
+ switch(dir)
+ {
+ case DT_BAUHAUS_SET:
+ outval = log10(fmax(inval, 1e-15f));
+ break;
+ case DT_BAUHAUS_GET:
+ outval = exp(M_LN10 * inval);
+ break;
+ default:
+ outval = inval;
+ }
+ return outval;
+}
+
+static float log2_callback(GtkWidget *self, float inval, dt_bauhaus_callback_t dir)
+{
+ float outval;
+ switch(dir)
+ {
+ case DT_BAUHAUS_SET:
+ outval = log(fmax(inval, 1e-15f)) / M_LN2;
+ break;
+ case DT_BAUHAUS_GET:
+ outval = exp(M_LN2 * inval);
+ break;
+ default:
+ outval = inval;
+ }
+ return outval;
+}
+
+void gui_init(struct dt_iop_module_t *self)
+{
+ self->gui_data = malloc(sizeof(dt_iop_ashift_gui_data_t));
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params;
+
+ dt_pthread_mutex_init(&g->lock, NULL);
+ dt_pthread_mutex_lock(&g->lock);
+ g->buf = NULL;
+ g->buf_width = 0;
+ g->buf_height = 0;
+ g->buf_x_off = 0;
+ g->buf_y_off = 0;
+ g->buf_scale = 1.0f;
+ g->buf_hash = 0;
+ g->isflipped = -1;
+ g->lastfit = ASHIFT_FIT_NONE;
+ dt_pthread_mutex_unlock(&g->lock);
+
+ g->fitting = 0;
+ g->lines = NULL;
+ g->lines_count = 0;
+ g->vertical_count = 0;
+ g->horizontal_count = 0;
+ g->lines_version = 0;
+ g->lines_suppressed = 0;
+ g->points = NULL;
+ g->points_idx = NULL;
+ g->points_lines_count = 0;
+ g->points_version = 0;
+ g->grid_hash = 0;
+ g->lines_hash = 0;
+ g->rotation_range = ROTATION_RANGE_SOFT;
+ g->lensshift_v_range = LENSSHIFT_RANGE_SOFT;
+ g->lensshift_h_range = LENSSHIFT_RANGE_SOFT;
+ g->shear_range = SHEAR_RANGE_SOFT;
+ g->show_guides = 0;
+ g->isselecting = 0;
+ g->isdeselecting = 0;
+ g->isbounding = ASHIFT_BOUNDING_OFF;
+ g->near_delta = 0;
+ g->selecting_lines_version = 0;
+
+ g->jobcode = ASHIFT_JOBCODE_NONE;
+ g->jobparams = 0;
+ g->adjust_crop = FALSE;
+ g->lastx = g->lasty = -1.0f;
+ g->crop_cx = g->crop_cy = 1.0f;
+
+ self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE);
+ dt_gui_add_help_link(self->widget, dt_get_help_url(self->op));
+
+ g->rotation = dt_bauhaus_slider_new_with_range(self, -ROTATION_RANGE, ROTATION_RANGE, 0.01*ROTATION_RANGE, p->rotation, 2);
+ dt_bauhaus_widget_set_label(g->rotation, NULL, _("rotation"));
+ dt_bauhaus_slider_set_format(g->rotation, "%.2f°");
+ dt_bauhaus_slider_enable_soft_boundaries(g->rotation, -ROTATION_RANGE_SOFT, ROTATION_RANGE_SOFT);
+ gtk_box_pack_start(GTK_BOX(self->widget), g->rotation, TRUE, TRUE, 0);
+
+ g->lensshift_v = dt_bauhaus_slider_new_with_range(self, -LENSSHIFT_RANGE, LENSSHIFT_RANGE, 0.01*LENSSHIFT_RANGE, p->lensshift_v, 3);
+ dt_bauhaus_widget_set_label(g->lensshift_v, NULL, _("lens shift (vertical)"));
+ dt_bauhaus_slider_enable_soft_boundaries(g->lensshift_v, -LENSSHIFT_RANGE_SOFT, LENSSHIFT_RANGE_SOFT);
+ gtk_box_pack_start(GTK_BOX(self->widget), g->lensshift_v, TRUE, TRUE, 0);
+
+ g->lensshift_h = dt_bauhaus_slider_new_with_range(self, -LENSSHIFT_RANGE, LENSSHIFT_RANGE, 0.01*LENSSHIFT_RANGE, p->lensshift_h, 3);
+ dt_bauhaus_widget_set_label(g->lensshift_h, NULL, _("lens shift (horizontal)"));
+ dt_bauhaus_slider_enable_soft_boundaries(g->lensshift_h, -LENSSHIFT_RANGE_SOFT, LENSSHIFT_RANGE_SOFT);
+ gtk_box_pack_start(GTK_BOX(self->widget), g->lensshift_h, TRUE, TRUE, 0);
+
+ g->shear = dt_bauhaus_slider_new_with_range(self, -SHEAR_RANGE, SHEAR_RANGE, 0.01*SHEAR_RANGE, p->shear, 3);
+ dt_bauhaus_widget_set_label(g->shear, NULL, _("shear"));
+ dt_bauhaus_slider_enable_soft_boundaries(g->shear, -SHEAR_RANGE_SOFT, SHEAR_RANGE_SOFT);
+ gtk_box_pack_start(GTK_BOX(self->widget), g->shear, TRUE, TRUE, 0);
+
+ g->guide_lines = dt_bauhaus_combobox_new(self);
+ dt_bauhaus_widget_set_label(g->guide_lines, NULL, _("guides"));
+ dt_bauhaus_combobox_add(g->guide_lines, _("off"));
+ dt_bauhaus_combobox_add(g->guide_lines, _("on"));
+ gtk_box_pack_start(GTK_BOX(self->widget), g->guide_lines, TRUE, TRUE, 0);
+
+ g->cropmode = dt_bauhaus_combobox_new(self);
+ dt_bauhaus_widget_set_label(g->cropmode, NULL, _("automatic cropping"));
+ dt_bauhaus_combobox_add(g->cropmode, _("off"));
+ dt_bauhaus_combobox_add(g->cropmode, _("largest area"));
+ dt_bauhaus_combobox_add(g->cropmode, _("original format"));
+ gtk_box_pack_start(GTK_BOX(self->widget), g->cropmode, TRUE, TRUE, 0);
+
+ g->mode = dt_bauhaus_combobox_new(self);
+ dt_bauhaus_widget_set_label(g->mode, NULL, _("lens model"));
+ dt_bauhaus_combobox_add(g->mode, _("generic"));
+ dt_bauhaus_combobox_add(g->mode, _("specific"));
+ gtk_box_pack_start(GTK_BOX(self->widget), g->mode, TRUE, TRUE, 0);
+
+ g->f_length = dt_bauhaus_slider_new_with_range(self, 1.0f, 3.0f, 0.01f, 1.0f, 2);
+ dt_bauhaus_widget_set_label(g->f_length, NULL, _("focal length"));
+ dt_bauhaus_slider_set_callback(g->f_length, log10_callback);
+ dt_bauhaus_slider_set_format(g->f_length, "%.0fmm");
+ dt_bauhaus_slider_set_default(g->f_length, DEFAULT_F_LENGTH);
+ dt_bauhaus_slider_set(g->f_length, DEFAULT_F_LENGTH);
+ dt_bauhaus_slider_enable_soft_boundaries(g->f_length, 1.0f, 2000.0f);
+ gtk_box_pack_start(GTK_BOX(self->widget), g->f_length, TRUE, TRUE, 0);
+
+ g->crop_factor = dt_bauhaus_slider_new_with_range(self, 1.0f, 2.0f, 0.01f, p->crop_factor, 2);
+ dt_bauhaus_widget_set_label(g->crop_factor, NULL, _("crop factor"));
+ dt_bauhaus_slider_enable_soft_boundaries(g->crop_factor, 0.5f, 10.0f);
+ gtk_box_pack_start(GTK_BOX(self->widget), g->crop_factor, TRUE, TRUE, 0);
+
+ g->orthocorr = dt_bauhaus_slider_new_with_range(self, 0.0f, 100.0f, 1.0f, p->orthocorr, 2);
+ dt_bauhaus_widget_set_label(g->orthocorr, NULL, _("lens dependence"));
+ dt_bauhaus_slider_set_format(g->orthocorr, "%.0f%%");
+#if 0
+ // this parameter could serve to finetune between generic model (0%) and specific model (100%).
+ // however, users can more easily get the same effect with the aspect adjust parameter so we keep
+ // this one hidden.
+ gtk_box_pack_start(GTK_BOX(self->widget), g->orthocorr, TRUE, TRUE, 0);
+#endif
+
+ g->aspect = dt_bauhaus_slider_new_with_range(self, -1.0f, 1.0f, 0.01f, 0.0f, 2);
+ dt_bauhaus_widget_set_label(g->aspect, NULL, _("aspect adjust"));
+ dt_bauhaus_slider_set_callback(g->aspect, log2_callback);
+ dt_bauhaus_slider_set_default(g->aspect, 1.0f);
+ dt_bauhaus_slider_set(g->aspect, 1.0f);
+ gtk_box_pack_start(GTK_BOX(self->widget), g->aspect, TRUE, TRUE, 0);
+
+ GtkWidget *grid = gtk_grid_new();
+ gtk_grid_set_row_spacing(GTK_GRID(grid), 2 * DT_BAUHAUS_SPACE);
+ gtk_grid_set_column_spacing(GTK_GRID(grid), DT_PIXEL_APPLY_DPI(10));
+
+ GtkWidget *label1 = gtk_label_new(_("automatic fit"));
+ gtk_widget_set_halign(label1, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(grid), label1, 0, 0, 1, 1);
+
+ g->fit_v = dtgtk_button_new(dtgtk_cairo_paint_perspective, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | 1, NULL);
+ gtk_widget_set_hexpand(GTK_WIDGET(g->fit_v), TRUE);
+ gtk_widget_set_size_request(g->fit_v, -1, DT_PIXEL_APPLY_DPI(24));
+ gtk_grid_attach_next_to(GTK_GRID(grid), g->fit_v, label1, GTK_POS_RIGHT, 1, 1);
+
+ g->fit_h = dtgtk_button_new(dtgtk_cairo_paint_perspective, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | 2, NULL);
+ gtk_widget_set_hexpand(GTK_WIDGET(g->fit_h), TRUE);
+ gtk_widget_set_size_request(g->fit_h, -1, DT_PIXEL_APPLY_DPI(24));
+ gtk_grid_attach_next_to(GTK_GRID(grid), g->fit_h, g->fit_v, GTK_POS_RIGHT, 1, 1);
+
+ g->fit_both = dtgtk_button_new(dtgtk_cairo_paint_perspective, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | 3, NULL);
+ gtk_widget_set_hexpand(GTK_WIDGET(g->fit_both), TRUE);
+ gtk_widget_set_size_request(g->fit_both, -1, DT_PIXEL_APPLY_DPI(24));
+ gtk_grid_attach_next_to(GTK_GRID(grid), g->fit_both, g->fit_h, GTK_POS_RIGHT, 1, 1);
+
+ GtkWidget *label2 = gtk_label_new(_("get structure"));
+ gtk_widget_set_halign(label2, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(grid), label2, 0, 1, 1, 1);
+
+ g->structure = dtgtk_button_new(dtgtk_cairo_paint_structure, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL);
+ gtk_widget_set_hexpand(GTK_WIDGET(g->structure), TRUE);
+ gtk_grid_attach_next_to(GTK_GRID(grid), g->structure, label2, GTK_POS_RIGHT, 1, 1);
+
+ g->clean = dtgtk_button_new(dtgtk_cairo_paint_cancel, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL);
+ gtk_widget_set_hexpand(GTK_WIDGET(g->clean), TRUE);
+ gtk_grid_attach_next_to(GTK_GRID(grid), g->clean, g->structure, GTK_POS_RIGHT, 1, 1);
+
+ g->eye = dtgtk_togglebutton_new(dtgtk_cairo_paint_eye_toggle, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL);
+ gtk_widget_set_hexpand(GTK_WIDGET(g->eye), TRUE);
+ gtk_grid_attach_next_to(GTK_GRID(grid), g->eye, g->clean, GTK_POS_RIGHT, 1, 1);
+
+ gtk_box_pack_start(GTK_BOX(self->widget), grid, TRUE, TRUE, 0);
+
+ gtk_widget_show_all(g->f_length);
+ gtk_widget_set_no_show_all(g->f_length, TRUE);
+ gtk_widget_show_all(g->crop_factor);
+ gtk_widget_set_no_show_all(g->crop_factor, TRUE);
+ gtk_widget_show_all(g->orthocorr);
+ gtk_widget_set_no_show_all(g->orthocorr, TRUE);
+ gtk_widget_show_all(g->aspect);
+ gtk_widget_set_no_show_all(g->aspect, TRUE);
+
+
+ switch(p->mode)
+ {
+ case ASHIFT_MODE_GENERIC:
+ gtk_widget_hide(g->f_length);
+ gtk_widget_hide(g->crop_factor);
+ gtk_widget_hide(g->orthocorr);
+ gtk_widget_hide(g->aspect);
+ break;
+ case ASHIFT_MODE_SPECIFIC:
+ default:
+ gtk_widget_show(g->f_length);
+ gtk_widget_show(g->crop_factor);
+ gtk_widget_show(g->orthocorr);
+ gtk_widget_show(g->aspect);
+ break;
+ }
+
+ gtk_widget_set_tooltip_text(g->rotation, _("rotate image"));
+ gtk_widget_set_tooltip_text(g->lensshift_v, _("apply lens shift correction in one direction"));
+ gtk_widget_set_tooltip_text(g->lensshift_h, _("apply lens shift correction in one direction"));
+ gtk_widget_set_tooltip_text(g->shear, _("shear the image along one diagonal"));
+ gtk_widget_set_tooltip_text(g->guide_lines, _("display guide lines overlay"));
+ gtk_widget_set_tooltip_text(g->cropmode, _("automatically crop to avoid black edges"));
+ gtk_widget_set_tooltip_text(g->mode, _("lens model of the perspective correction: "
+ "generic or according to the focal length"));
+ gtk_widget_set_tooltip_text(g->f_length, _("focal length of the lens, "
+ "default value set from exif data if available"));
+ gtk_widget_set_tooltip_text(g->crop_factor, _("crop factor of the camera sensor, "
+ "default value set from exif data if available, "
+ "manual setting is often required"));
+ gtk_widget_set_tooltip_text(g->orthocorr, _("the level of lens dependent correction, set to maximum for full lens dependency, "
+ "set to zero for the generic case"));
+ gtk_widget_set_tooltip_text(g->aspect, _("adjust aspect ratio of image by horizontal and vertical scaling"));
+ gtk_widget_set_tooltip_text(g->fit_v, _("automatically correct for vertical perspective distortion\n"
+ "ctrl-click to only fit rotation\n"
+ "shift-click to only fit lens shift"));
+ gtk_widget_set_tooltip_text(g->fit_h, _("automatically correct for horizontal perspective distortion\n"
+ "ctrl-click to only fit rotation\n"
+ "shift-click to only fit lens shift"));
+ gtk_widget_set_tooltip_text(g->fit_both, _("automatically correct for vertical and "
+ "horizontal perspective distortions; fitting rotation,"
+ "lens shift in both directions, and shear\n"
+ "ctrl-click to only fit rotation\n"
+ "shift-click to only fit lens shift\n"
+ "ctrl-shift-click to only fit rotation and lens shift"));
+ gtk_widget_set_tooltip_text(g->structure, _("analyse line structure in image\n"
+ "ctrl-click for an additional edge enhancement\n"
+ "shift-click for an additional detail enhancement\n"
+ "ctrl-shift-click for a combination of both methods"));
+ gtk_widget_set_tooltip_text(g->clean, _("remove line structure information"));
+ gtk_widget_set_tooltip_text(g->eye, _("toggle visibility of structure lines"));
+
+ g_signal_connect(G_OBJECT(g->rotation), "value-changed", G_CALLBACK(rotation_callback), self);
+ g_signal_connect(G_OBJECT(g->lensshift_v), "value-changed", G_CALLBACK(lensshift_v_callback), self);
+ g_signal_connect(G_OBJECT(g->lensshift_h), "value-changed", G_CALLBACK(lensshift_h_callback), self);
+ g_signal_connect(G_OBJECT(g->shear), "value-changed", G_CALLBACK(shear_callback), self);
+ g_signal_connect(G_OBJECT(g->guide_lines), "value-changed", G_CALLBACK(guide_lines_callback), self);
+ g_signal_connect(G_OBJECT(g->cropmode), "value-changed", G_CALLBACK(cropmode_callback), self);
+ g_signal_connect(G_OBJECT(g->mode), "value-changed", G_CALLBACK(mode_callback), self);
+ g_signal_connect(G_OBJECT(g->f_length), "value-changed", G_CALLBACK(f_length_callback), self);
+ g_signal_connect(G_OBJECT(g->crop_factor), "value-changed", G_CALLBACK(crop_factor_callback), self);
+ g_signal_connect(G_OBJECT(g->orthocorr), "value-changed", G_CALLBACK(orthocorr_callback), self);
+ g_signal_connect(G_OBJECT(g->aspect), "value-changed", G_CALLBACK(aspect_callback), self);
+ g_signal_connect(G_OBJECT(g->fit_v), "button-press-event", G_CALLBACK(fit_v_button_clicked), (gpointer)self);
+ g_signal_connect(G_OBJECT(g->fit_h), "button-press-event", G_CALLBACK(fit_h_button_clicked), (gpointer)self);
+ g_signal_connect(G_OBJECT(g->fit_both), "button-press-event", G_CALLBACK(fit_both_button_clicked), (gpointer)self);
+ g_signal_connect(G_OBJECT(g->structure), "button-press-event", G_CALLBACK(structure_button_clicked), (gpointer)self);
+ g_signal_connect(G_OBJECT(g->clean), "clicked", G_CALLBACK(clean_button_clicked), (gpointer)self);
+ g_signal_connect(G_OBJECT(g->eye), "toggled", G_CALLBACK(eye_button_toggled), (gpointer)self);
+ g_signal_connect(G_OBJECT(self->widget), "draw", G_CALLBACK(draw), self);
+
+ /* add signal handler for preview pipe finish to redraw the overlay */
+ dt_control_signal_connect(darktable.signals, DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED,
+ G_CALLBACK(process_after_preview_callback), self);
+
+}
+
+void gui_cleanup(struct dt_iop_module_t *self)
+{
+ dt_control_signal_disconnect(darktable.signals, G_CALLBACK(process_after_preview_callback), self);
+
+ dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data;
+ dt_pthread_mutex_destroy(&g->lock);
+ free(g->lines);
+ free(g->buf);
+ free(g->points);
+ free(g->points_idx);
+ free(self->gui_data);
+ self->gui_data = NULL;
+}
+#endif // if 0
+//-----------------------------------------------------------------------------
+
+// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
+// vim: shiftwidth=2 expandtab tabstop=2 cindent
+// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
diff --git a/rtengine/ashift_lsd.c b/rtengine/ashift_lsd.c
new file mode 100644
index 000000000..a884da437
--- /dev/null
+++ b/rtengine/ashift_lsd.c
@@ -0,0 +1,2335 @@
+/*
+ This file is part of darktable,
+ copyright (c) 2016 Ulrich Pegelow.
+
+ darktable 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.
+
+ darktable 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 darktable. If not, see .
+*/
+
+
+/* For line detection we are using the LSD code as published below.
+ * Changes versus the original code:
+ * do not include "lsd.h" (not needed)
+ * make all interface functions static
+ * comment out unsused interface functions
+ * catch (unlikely) division by zero near line 2035
+ * rename rad1 and rad2 to radius1 and radius2 in reduce_region_radius()
+ * to avoid naming conflict in windows build
+ *
+ */
+
+/* TODO: LSD employs intensive sanity checks of input data and allocation
+ * results. In case of errors it will terminate the program. On the one side
+ * this is not optimal for a module within darktable - it should better
+ * report errors upstream where they should be handled properly. On the other
+ * hand the kind of error which could be triggered in LSD would make it very unlikely
+ * that the darktable process could survive anyhow.
+ */
+
+// clang-format off
+
+/*==================================================================================
+ * begin LSD code version 1.6. downloaded on January 30, 2016
+ *==================================================================================*/
+
+/*----------------------------------------------------------------------------
+
+ LSD - Line Segment Detector on digital images
+
+ This code is part of the following publication and was subject
+ to peer review:
+
+ "LSD: a Line Segment Detector" by Rafael Grompone von Gioi,
+ Jeremie Jakubowicz, Jean-Michel Morel, and Gregory Randall,
+ Image Processing On Line, 2012. DOI:10.5201/ipol.2012.gjmr-lsd
+ http://dx.doi.org/10.5201/ipol.2012.gjmr-lsd
+
+ Copyright (c) 2007-2011 rafael grompone von gioi
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+ ----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** @file lsd.c
+ LSD module code
+ @author rafael grompone von gioi
+ */
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** @mainpage LSD code documentation
+
+ This is an implementation of the Line Segment Detector described
+ in the paper:
+
+ "LSD: A Fast Line Segment Detector with a False Detection Control"
+ by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel,
+ and Gregory Randall, IEEE Transactions on Pattern Analysis and
+ Machine Intelligence, vol. 32, no. 4, pp. 722-732, April, 2010.
+
+ and in more details in the CMLA Technical Report:
+
+ "LSD: A Line Segment Detector, Technical Report",
+ by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel,
+ Gregory Randall, CMLA, ENS Cachan, 2010.
+
+ The version implemented here includes some further improvements
+ described in the following publication, of which this code is part:
+
+ "LSD: a Line Segment Detector" by Rafael Grompone von Gioi,
+ Jeremie Jakubowicz, Jean-Michel Morel, and Gregory Randall,
+ Image Processing On Line, 2012. DOI:10.5201/ipol.2012.gjmr-lsd
+ http://dx.doi.org/10.5201/ipol.2012.gjmr-lsd
+
+ The module's main function is lsd().
+
+ The source code is contained in two files: lsd.h and lsd.c.
+
+ HISTORY:
+ - version 1.6 - nov 2011:
+ - changes in the interface,
+ - max_grad parameter removed,
+ - the factor 11 was added to the number of test
+ to consider the different precision values
+ tested,
+ - a minor bug corrected in the gradient sorting
+ code,
+ - the algorithm now also returns p and log_nfa
+ for each detection,
+ - a minor bug was corrected in the image scaling,
+ - the angle comparison in "isaligned" changed
+ from < to <=,
+ - "eps" variable renamed "log_eps",
+ - "lsd_scale_region" interface was added,
+ - minor changes to comments.
+ - version 1.5 - dec 2010: Changes in 'refine', -W option added,
+ and more comments added.
+ - version 1.4 - jul 2010: lsd_scale interface added and doxygen doc.
+ - version 1.3 - feb 2010: Multiple bug correction and improved code.
+ - version 1.2 - dec 2009: First full Ansi C Language version.
+ - version 1.1 - sep 2009: Systematic subsampling to scale 0.8 and
+ correction to partially handle "angle problem".
+ - version 1.0 - jan 2009: First complete Megawave2 and Ansi C Language
+ version.
+
+ @author rafael grompone von gioi
+ */
+/*----------------------------------------------------------------------------*/
+
+#include
+#include
+#include
+#include
+#include
+//#include "lsd.h"
+
+/** ln(10) */
+#ifndef M_LN10
+#define M_LN10 2.30258509299404568402
+#endif /* !M_LN10 */
+
+/** PI */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif /* !M_PI */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !FALSE */
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !TRUE */
+
+/** Label for pixels with undefined gradient. */
+#define NOTDEF -1024.0
+
+/** 3/2 pi */
+#define M_3_2_PI 4.71238898038
+
+/** 2 pi */
+#define M_2__PI 6.28318530718
+
+/** Label for pixels not used in yet. */
+#define NOTUSED 0
+
+/** Label for pixels already used in detection. */
+#define USED 1
+
+/*----------------------------------------------------------------------------*/
+/** Chained list of coordinates.
+ */
+struct coorlist
+{
+ int x,y;
+ struct coorlist * next;
+};
+
+/*----------------------------------------------------------------------------*/
+/** A point (or pixel).
+ */
+struct point {int x,y;};
+
+
+/*----------------------------------------------------------------------------*/
+/*------------------------- Miscellaneous functions --------------------------*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** Fatal error, print a message to standard-error output and exit.
+ */
+static void error(const char * msg)
+{
+ fprintf(stderr,"LSD Error: %s\n",msg);
+ exit(EXIT_FAILURE);
+}
+
+/*----------------------------------------------------------------------------*/
+/** Doubles relative error factor
+ */
+#define RELATIVE_ERROR_FACTOR 100.0
+
+/*----------------------------------------------------------------------------*/
+/** Compare doubles by relative error.
+
+ The resulting rounding error after floating point computations
+ depend on the specific operations done. The same number computed by
+ different algorithms could present different rounding errors. For a
+ useful comparison, an estimation of the relative rounding error
+ should be considered and compared to a factor times EPS. The factor
+ should be related to the cumulated rounding error in the chain of
+ computation. Here, as a simplification, a fixed factor is used.
+ */
+static int double_equal(double a, double b)
+{
+ double abs_diff,aa,bb,abs_max;
+
+ /* trivial case */
+ if( a == b ) return TRUE;
+
+ abs_diff = fabs(a-b);
+ aa = fabs(a);
+ bb = fabs(b);
+ abs_max = aa > bb ? aa : bb;
+
+ /* DBL_MIN is the smallest normalized number, thus, the smallest
+ number whose relative error is bounded by DBL_EPSILON. For
+ smaller numbers, the same quantization steps as for DBL_MIN
+ are used. Then, for smaller numbers, a meaningful "relative"
+ error should be computed by dividing the difference by DBL_MIN. */
+ if( abs_max < DBL_MIN ) abs_max = DBL_MIN;
+
+ /* equal if relative error <= factor x eps */
+ return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON);
+}
+
+/*----------------------------------------------------------------------------*/
+/** Computes Euclidean distance between point (x1,y1) and point (x2,y2).
+ */
+static double dist(double x1, double y1, double x2, double y2)
+{
+ return sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) );
+}
+
+
+/*----------------------------------------------------------------------------*/
+/*----------------------- 'list of n-tuple' data type ------------------------*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** 'list of n-tuple' data type
+
+ The i-th component of the j-th n-tuple of an n-tuple list 'ntl'
+ is accessed with:
+
+ ntl->values[ i + j * ntl->dim ]
+
+ The dimension of the n-tuple (n) is:
+
+ ntl->dim
+
+ The number of n-tuples in the list is:
+
+ ntl->size
+
+ The maximum number of n-tuples that can be stored in the
+ list with the allocated memory at a given time is given by:
+
+ ntl->max_size
+ */
+typedef struct ntuple_list_s
+{
+ unsigned int size;
+ unsigned int max_size;
+ unsigned int dim;
+ double * values;
+} * ntuple_list;
+
+/*----------------------------------------------------------------------------*/
+/** Free memory used in n-tuple 'in'.
+ */
+static void free_ntuple_list(ntuple_list in)
+{
+ if( in == NULL || in->values == NULL )
+ error("free_ntuple_list: invalid n-tuple input.");
+ free( (void *) in->values );
+ free( (void *) in );
+}
+
+/*----------------------------------------------------------------------------*/
+/** Create an n-tuple list and allocate memory for one element.
+ @param dim the dimension (n) of the n-tuple.
+ */
+static ntuple_list new_ntuple_list(unsigned int dim)
+{
+ ntuple_list n_tuple;
+
+ /* check parameters */
+ if( dim == 0 ) error("new_ntuple_list: 'dim' must be positive.");
+
+ /* get memory for list structure */
+ n_tuple = (ntuple_list) malloc( sizeof(struct ntuple_list_s) );
+ if( n_tuple == NULL ) error("not enough memory.");
+
+ /* initialize list */
+ n_tuple->size = 0;
+ n_tuple->max_size = 1;
+ n_tuple->dim = dim;
+
+ /* get memory for tuples */
+ n_tuple->values = (double *) malloc( sizeof(double) * dim*n_tuple->max_size );
+ if( n_tuple->values == NULL ) error("not enough memory.");
+
+ return n_tuple;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Enlarge the allocated memory of an n-tuple list.
+ */
+static void enlarge_ntuple_list(ntuple_list n_tuple)
+{
+ /* check parameters */
+ if( n_tuple == NULL || n_tuple->values == NULL || n_tuple->max_size == 0 )
+ error("enlarge_ntuple_list: invalid n-tuple.");
+
+ /* duplicate number of tuples */
+ n_tuple->max_size *= 2;
+
+ /* realloc memory */
+ n_tuple->values = (double *) realloc( (void *) n_tuple->values,
+ sizeof(double) * n_tuple->dim * n_tuple->max_size );
+ if( n_tuple->values == NULL ) error("not enough memory.");
+}
+
+/*----------------------------------------------------------------------------*/
+/** Add a 7-tuple to an n-tuple list.
+ */
+static void add_7tuple( ntuple_list out, double v1, double v2, double v3,
+ double v4, double v5, double v6, double v7 )
+{
+ /* check parameters */
+ if( out == NULL ) error("add_7tuple: invalid n-tuple input.");
+ if( out->dim != 7 ) error("add_7tuple: the n-tuple must be a 7-tuple.");
+
+ /* if needed, alloc more tuples to 'out' */
+ if( out->size == out->max_size ) enlarge_ntuple_list(out);
+ if( out->values == NULL ) error("add_7tuple: invalid n-tuple input.");
+
+ /* add new 7-tuple */
+ out->values[ out->size * out->dim + 0 ] = v1;
+ out->values[ out->size * out->dim + 1 ] = v2;
+ out->values[ out->size * out->dim + 2 ] = v3;
+ out->values[ out->size * out->dim + 3 ] = v4;
+ out->values[ out->size * out->dim + 4 ] = v5;
+ out->values[ out->size * out->dim + 5 ] = v6;
+ out->values[ out->size * out->dim + 6 ] = v7;
+
+ /* update number of tuples counter */
+ out->size++;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/*----------------------------- Image Data Types -----------------------------*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** char image data type
+
+ The pixel value at (x,y) is accessed by:
+
+ image->data[ x + y * image->xsize ]
+
+ with x and y integer.
+ */
+typedef struct image_char_s
+{
+ unsigned char * data;
+ unsigned int xsize,ysize;
+} * image_char;
+
+/*----------------------------------------------------------------------------*/
+/** Free memory used in image_char 'i'.
+ */
+static void free_image_char(image_char i)
+{
+ if( i == NULL || i->data == NULL )
+ error("free_image_char: invalid input image.");
+ free( (void *) i->data );
+ free( (void *) i );
+}
+
+/*----------------------------------------------------------------------------*/
+/** Create a new image_char of size 'xsize' times 'ysize'.
+ */
+static image_char new_image_char(unsigned int xsize, unsigned int ysize)
+{
+ image_char image;
+
+ /* check parameters */
+ if( xsize == 0 || ysize == 0 ) error("new_image_char: invalid image size.");
+
+ /* get memory */
+ image = (image_char) malloc( sizeof(struct image_char_s) );
+ if( image == NULL ) error("not enough memory.");
+ image->data = (unsigned char *) calloc( (size_t) (xsize*ysize),
+ sizeof(unsigned char) );
+ if( image->data == NULL ) error("not enough memory.");
+
+ /* set image size */
+ image->xsize = xsize;
+ image->ysize = ysize;
+
+ return image;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Create a new image_char of size 'xsize' times 'ysize',
+ initialized to the value 'fill_value'.
+ */
+static image_char new_image_char_ini( unsigned int xsize, unsigned int ysize,
+ unsigned char fill_value )
+{
+ image_char image = new_image_char(xsize,ysize); /* create image */
+ unsigned int N = xsize*ysize;
+ unsigned int i;
+
+ /* check parameters */
+ if( image == NULL || image->data == NULL )
+ error("new_image_char_ini: invalid image.");
+
+ /* initialize */
+ for(i=0; idata[i] = fill_value;
+
+ return image;
+}
+
+/*----------------------------------------------------------------------------*/
+/** int image data type
+
+ The pixel value at (x,y) is accessed by:
+
+ image->data[ x + y * image->xsize ]
+
+ with x and y integer.
+ */
+typedef struct image_int_s
+{
+ int * data;
+ unsigned int xsize,ysize;
+} * image_int;
+
+/*----------------------------------------------------------------------------*/
+/** Create a new image_int of size 'xsize' times 'ysize'.
+ */
+static image_int new_image_int(unsigned int xsize, unsigned int ysize)
+{
+ image_int image;
+
+ /* check parameters */
+ if( xsize == 0 || ysize == 0 ) error("new_image_int: invalid image size.");
+
+ /* get memory */
+ image = (image_int) malloc( sizeof(struct image_int_s) );
+ if( image == NULL ) error("not enough memory.");
+ image->data = (int *) calloc( (size_t) (xsize*ysize), sizeof(int) );
+ if( image->data == NULL ) error("not enough memory.");
+
+ /* set image size */
+ image->xsize = xsize;
+ image->ysize = ysize;
+
+ return image;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Create a new image_int of size 'xsize' times 'ysize',
+ initialized to the value 'fill_value'.
+ */
+static image_int new_image_int_ini( unsigned int xsize, unsigned int ysize,
+ int fill_value )
+{
+ image_int image = new_image_int(xsize,ysize); /* create image */
+ unsigned int N = xsize*ysize;
+ unsigned int i;
+
+ /* initialize */
+ for(i=0; idata[i] = fill_value;
+
+ return image;
+}
+
+/*----------------------------------------------------------------------------*/
+/** double image data type
+
+ The pixel value at (x,y) is accessed by:
+
+ image->data[ x + y * image->xsize ]
+
+ with x and y integer.
+ */
+typedef struct image_double_s
+{
+ double * data;
+ unsigned int xsize,ysize;
+} * image_double;
+
+/*----------------------------------------------------------------------------*/
+/** Free memory used in image_double 'i'.
+ */
+static void free_image_double(image_double i)
+{
+ if( i == NULL || i->data == NULL )
+ error("free_image_double: invalid input image.");
+ free( (void *) i->data );
+ free( (void *) i );
+}
+
+/*----------------------------------------------------------------------------*/
+/** Create a new image_double of size 'xsize' times 'ysize'.
+ */
+static image_double new_image_double(unsigned int xsize, unsigned int ysize)
+{
+ image_double image;
+
+ /* check parameters */
+ if( xsize == 0 || ysize == 0 ) error("new_image_double: invalid image size.");
+
+ /* get memory */
+ image = (image_double) malloc( sizeof(struct image_double_s) );
+ if( image == NULL ) error("not enough memory.");
+ image->data = (double *) calloc( (size_t) (xsize*ysize), sizeof(double) );
+ if( image->data == NULL ) error("not enough memory.");
+
+ /* set image size */
+ image->xsize = xsize;
+ image->ysize = ysize;
+
+ return image;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Create a new image_double of size 'xsize' times 'ysize'
+ with the data pointed by 'data'.
+ */
+static image_double new_image_double_ptr( unsigned int xsize,
+ unsigned int ysize, double * data )
+{
+ image_double image;
+
+ /* check parameters */
+ if( xsize == 0 || ysize == 0 )
+ error("new_image_double_ptr: invalid image size.");
+ if( data == NULL ) error("new_image_double_ptr: NULL data pointer.");
+
+ /* get memory */
+ image = (image_double) malloc( sizeof(struct image_double_s) );
+ if( image == NULL ) error("not enough memory.");
+
+ /* set image */
+ image->xsize = xsize;
+ image->ysize = ysize;
+ image->data = data;
+
+ return image;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/*----------------------------- Gaussian filter ------------------------------*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** Compute a Gaussian kernel of length 'kernel->dim',
+ standard deviation 'sigma', and centered at value 'mean'.
+
+ For example, if mean=0.5, the Gaussian will be centered
+ in the middle point between values 'kernel->values[0]'
+ and 'kernel->values[1]'.
+ */
+static void gaussian_kernel(ntuple_list kernel, double sigma, double mean)
+{
+ double sum = 0.0;
+ double val;
+ unsigned int i;
+
+ /* check parameters */
+ if( kernel == NULL || kernel->values == NULL )
+ error("gaussian_kernel: invalid n-tuple 'kernel'.");
+ if( sigma <= 0.0 ) error("gaussian_kernel: 'sigma' must be positive.");
+
+ /* compute Gaussian kernel */
+ if( kernel->max_size < 1 ) enlarge_ntuple_list(kernel);
+ kernel->size = 1;
+ for(i=0;idim;i++)
+ {
+ val = ( (double) i - mean ) / sigma;
+ kernel->values[i] = exp( -0.5 * val * val );
+ sum += kernel->values[i];
+ }
+
+ /* normalization */
+ if( sum >= 0.0 ) for(i=0;idim;i++) kernel->values[i] /= sum;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Scale the input image 'in' by a factor 'scale' by Gaussian sub-sampling.
+
+ For example, scale=0.8 will give a result at 80% of the original size.
+
+ The image is convolved with a Gaussian kernel
+ @f[
+ G(x,y) = \frac{1}{2\pi\sigma^2} e^{-\frac{x^2+y^2}{2\sigma^2}}
+ @f]
+ before the sub-sampling to prevent aliasing.
+
+ The standard deviation sigma given by:
+ - sigma = sigma_scale / scale, if scale < 1.0
+ - sigma = sigma_scale, if scale >= 1.0
+
+ To be able to sub-sample at non-integer steps, some interpolation
+ is needed. In this implementation, the interpolation is done by
+ the Gaussian kernel, so both operations (filtering and sampling)
+ are done at the same time. The Gaussian kernel is computed
+ centered on the coordinates of the required sample. In this way,
+ when applied, it gives directly the result of convolving the image
+ with the kernel and interpolated to that particular position.
+
+ A fast algorithm is done using the separability of the Gaussian
+ kernel. Applying the 2D Gaussian kernel is equivalent to applying
+ first a horizontal 1D Gaussian kernel and then a vertical 1D
+ Gaussian kernel (or the other way round). The reason is that
+ @f[
+ G(x,y) = G(x) * G(y)
+ @f]
+ where
+ @f[
+ G(x) = \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{x^2}{2\sigma^2}}.
+ @f]
+ The algorithm first applies a combined Gaussian kernel and sampling
+ in the x axis, and then the combined Gaussian kernel and sampling
+ in the y axis.
+ */
+static image_double gaussian_sampler( image_double in, double scale,
+ double sigma_scale )
+{
+ image_double aux,out;
+ ntuple_list kernel;
+ unsigned int N,M,h,n,x,y,i;
+ int xc,yc,j,double_x_size,double_y_size;
+ double sigma,xx,yy,sum,prec;
+
+ /* check parameters */
+ if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 )
+ error("gaussian_sampler: invalid image.");
+ if( scale <= 0.0 ) error("gaussian_sampler: 'scale' must be positive.");
+ if( sigma_scale <= 0.0 )
+ error("gaussian_sampler: 'sigma_scale' must be positive.");
+
+ /* compute new image size and get memory for images */
+ if( in->xsize * scale > (double) UINT_MAX ||
+ in->ysize * scale > (double) UINT_MAX )
+ error("gaussian_sampler: the output image size exceeds the handled size.");
+ N = (unsigned int) ceil( in->xsize * scale );
+ M = (unsigned int) ceil( in->ysize * scale );
+ aux = new_image_double(N,in->ysize);
+ out = new_image_double(N,M);
+
+ /* sigma, kernel size and memory for the kernel */
+ sigma = scale < 1.0 ? sigma_scale / scale : sigma_scale;
+ /*
+ The size of the kernel is selected to guarantee that the
+ the first discarded term is at least 10^prec times smaller
+ than the central value. For that, h should be larger than x, with
+ e^(-x^2/2sigma^2) = 1/10^prec.
+ Then,
+ x = sigma * sqrt( 2 * prec * ln(10) ).
+ */
+ prec = 3.0;
+ h = (unsigned int) ceil( sigma * sqrt( 2.0 * prec * log(10.0) ) );
+ n = 1+2*h; /* kernel size */
+ kernel = new_ntuple_list(n);
+
+ /* auxiliary double image size variables */
+ double_x_size = (int) (2 * in->xsize);
+ double_y_size = (int) (2 * in->ysize);
+
+ /* First subsampling: x axis */
+ for(x=0;xxsize;x++)
+ {
+ /*
+ x is the coordinate in the new image.
+ xx is the corresponding x-value in the original size image.
+ xc is the integer value, the pixel coordinate of xx.
+ */
+ xx = (double) x / scale;
+ /* coordinate (0.0,0.0) is in the center of pixel (0,0),
+ so the pixel with xc=0 get the values of xx from -0.5 to 0.5 */
+ xc = (int) floor( xx + 0.5 );
+ gaussian_kernel( kernel, sigma, (double) h + xx - (double) xc );
+ /* the kernel must be computed for each x because the fine
+ offset xx-xc is different in each case */
+
+ for(y=0;yysize;y++)
+ {
+ sum = 0.0;
+ for(i=0;idim;i++)
+ {
+ j = xc - h + i;
+
+ /* symmetry boundary condition */
+ while( j < 0 ) j += double_x_size;
+ while( j >= double_x_size ) j -= double_x_size;
+ if( j >= (int) in->xsize ) j = double_x_size-1-j;
+
+ sum += in->data[ j + y * in->xsize ] * kernel->values[i];
+ }
+ aux->data[ x + y * aux->xsize ] = sum;
+ }
+ }
+
+ /* Second subsampling: y axis */
+ for(y=0;yysize;y++)
+ {
+ /*
+ y is the coordinate in the new image.
+ yy is the corresponding x-value in the original size image.
+ yc is the integer value, the pixel coordinate of xx.
+ */
+ yy = (double) y / scale;
+ /* coordinate (0.0,0.0) is in the center of pixel (0,0),
+ so the pixel with yc=0 get the values of yy from -0.5 to 0.5 */
+ yc = (int) floor( yy + 0.5 );
+ gaussian_kernel( kernel, sigma, (double) h + yy - (double) yc );
+ /* the kernel must be computed for each y because the fine
+ offset yy-yc is different in each case */
+
+ for(x=0;xxsize;x++)
+ {
+ sum = 0.0;
+ for(i=0;idim;i++)
+ {
+ j = yc - h + i;
+
+ /* symmetry boundary condition */
+ while( j < 0 ) j += double_y_size;
+ while( j >= double_y_size ) j -= double_y_size;
+ if( j >= (int) in->ysize ) j = double_y_size-1-j;
+
+ sum += aux->data[ x + j * aux->xsize ] * kernel->values[i];
+ }
+ out->data[ x + y * out->xsize ] = sum;
+ }
+ }
+
+ /* free memory */
+ free_ntuple_list(kernel);
+ free_image_double(aux);
+
+ return out;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/*--------------------------------- Gradient ---------------------------------*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** Computes the direction of the level line of 'in' at each point.
+
+ The result is:
+ - an image_double with the angle at each pixel, or NOTDEF if not defined.
+ - the image_double 'modgrad' (a pointer is passed as argument)
+ with the gradient magnitude at each point.
+ - a list of pixels 'list_p' roughly ordered by decreasing
+ gradient magnitude. (The order is made by classifying points
+ into bins by gradient magnitude. The parameters 'n_bins' and
+ 'max_grad' specify the number of bins and the gradient modulus
+ at the highest bin. The pixels in the list would be in
+ decreasing gradient magnitude, up to a precision of the size of
+ the bins.)
+ - a pointer 'mem_p' to the memory used by 'list_p' to be able to
+ free the memory when it is not used anymore.
+ */
+static image_double ll_angle( image_double in, double threshold,
+ struct coorlist ** list_p, void ** mem_p,
+ image_double * modgrad, unsigned int n_bins )
+{
+ image_double g;
+ unsigned int n,p,x,y,adr,i;
+ double com1,com2,gx,gy,norm,norm2;
+ /* the rest of the variables are used for pseudo-ordering
+ the gradient magnitude values */
+ int list_count = 0;
+ struct coorlist * list;
+ struct coorlist ** range_l_s; /* array of pointers to start of bin list */
+ struct coorlist ** range_l_e; /* array of pointers to end of bin list */
+ struct coorlist * start;
+ struct coorlist * end;
+ double max_grad = 0.0;
+
+ /* check parameters */
+ if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 )
+ error("ll_angle: invalid image.");
+ if( threshold < 0.0 ) error("ll_angle: 'threshold' must be positive.");
+ if( list_p == NULL ) error("ll_angle: NULL pointer 'list_p'.");
+ if( mem_p == NULL ) error("ll_angle: NULL pointer 'mem_p'.");
+ if( modgrad == NULL ) error("ll_angle: NULL pointer 'modgrad'.");
+ if( n_bins == 0 ) error("ll_angle: 'n_bins' must be positive.");
+
+ /* image size shortcuts */
+ n = in->ysize;
+ p = in->xsize;
+
+ /* allocate output image */
+ g = new_image_double(in->xsize,in->ysize);
+
+ /* get memory for the image of gradient modulus */
+ *modgrad = new_image_double(in->xsize,in->ysize);
+
+ /* get memory for "ordered" list of pixels */
+ list = (struct coorlist *) calloc( (size_t) (n*p), sizeof(struct coorlist) );
+ *mem_p = (void *) list;
+ range_l_s = (struct coorlist **) calloc( (size_t) n_bins,
+ sizeof(struct coorlist *) );
+ range_l_e = (struct coorlist **) calloc( (size_t) n_bins,
+ sizeof(struct coorlist *) );
+ if( list == NULL || range_l_s == NULL || range_l_e == NULL )
+ error("not enough memory.");
+ for(i=0;idata[(n-1)*p+x] = NOTDEF;
+ for(y=0;ydata[p*y+p-1] = NOTDEF;
+
+ /* compute gradient on the remaining pixels */
+ for(x=0;xdata[adr+p+1] - in->data[adr];
+ com2 = in->data[adr+1] - in->data[adr+p];
+
+ gx = com1+com2; /* gradient x component */
+ gy = com1-com2; /* gradient y component */
+ norm2 = gx*gx+gy*gy;
+ norm = sqrt( norm2 / 4.0 ); /* gradient norm */
+
+ (*modgrad)->data[adr] = norm; /* store gradient norm */
+
+ if( norm <= threshold ) /* norm too small, gradient no defined */
+ g->data[adr] = NOTDEF; /* gradient angle not defined */
+ else
+ {
+ /* gradient angle computation */
+ g->data[adr] = atan2(gx,-gy);
+
+ /* look for the maximum of the gradient */
+ if( norm > max_grad ) max_grad = norm;
+ }
+ }
+
+ /* compute histogram of gradient values */
+ for(x=0;xdata[y*p+x];
+
+ /* store the point in the right bin according to its norm */
+ i = (unsigned int) (norm * (double) n_bins / max_grad);
+ if( i >= n_bins ) i = n_bins-1;
+ if( range_l_e[i] == NULL )
+ range_l_s[i] = range_l_e[i] = list+list_count++;
+ else
+ {
+ range_l_e[i]->next = list+list_count;
+ range_l_e[i] = list+list_count++;
+ }
+ range_l_e[i]->x = (int) x;
+ range_l_e[i]->y = (int) y;
+ range_l_e[i]->next = NULL;
+ }
+
+ /* Make the list of pixels (almost) ordered by norm value.
+ It starts by the larger bin, so the list starts by the
+ pixels with the highest gradient value. Pixels would be ordered
+ by norm value, up to a precision given by max_grad/n_bins.
+ */
+ for(i=n_bins-1; i>0 && range_l_s[i]==NULL; i--);
+ start = range_l_s[i];
+ end = range_l_e[i];
+ if( start != NULL )
+ while(i>0)
+ {
+ --i;
+ if( range_l_s[i] != NULL )
+ {
+ end->next = range_l_s[i];
+ end = range_l_e[i];
+ }
+ }
+ *list_p = start;
+
+ /* free memory */
+ free( (void *) range_l_s );
+ free( (void *) range_l_e );
+
+ return g;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Is point (x,y) aligned to angle theta, up to precision 'prec'?
+ */
+static int isaligned( int x, int y, image_double angles, double theta,
+ double prec )
+{
+ double a;
+
+ /* check parameters */
+ if( angles == NULL || angles->data == NULL )
+ error("isaligned: invalid image 'angles'.");
+ if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
+ error("isaligned: (x,y) out of the image.");
+ if( prec < 0.0 ) error("isaligned: 'prec' must be positive.");
+
+ /* angle at pixel (x,y) */
+ a = angles->data[ x + y * angles->xsize ];
+
+ /* pixels whose level-line angle is not defined
+ are considered as NON-aligned */
+ if( a == NOTDEF ) return FALSE; /* there is no need to call the function
+ 'double_equal' here because there is
+ no risk of problems related to the
+ comparison doubles, we are only
+ interested in the exact NOTDEF value */
+
+ /* it is assumed that 'theta' and 'a' are in the range [-pi,pi] */
+ theta -= a;
+ if( theta < 0.0 ) theta = -theta;
+ if( theta > M_3_2_PI )
+ {
+ theta -= M_2__PI;
+ if( theta < 0.0 ) theta = -theta;
+ }
+
+ return theta <= prec;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Absolute value angle difference.
+ */
+static double angle_diff(double a, double b)
+{
+ a -= b;
+ while( a <= -M_PI ) a += M_2__PI;
+ while( a > M_PI ) a -= M_2__PI;
+ if( a < 0.0 ) a = -a;
+ return a;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Signed angle difference.
+ */
+static double angle_diff_signed(double a, double b)
+{
+ a -= b;
+ while( a <= -M_PI ) a += M_2__PI;
+ while( a > M_PI ) a -= M_2__PI;
+ return a;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/*----------------------------- NFA computation ------------------------------*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** Computes the natural logarithm of the absolute value of
+ the gamma function of x using the Lanczos approximation.
+ See http://www.rskey.org/gamma.htm
+
+ The formula used is
+ @f[
+ \Gamma(x) = \frac{ \sum_{n=0}^{N} q_n x^n }{ \Pi_{n=0}^{N} (x+n) }
+ (x+5.5)^{x+0.5} e^{-(x+5.5)}
+ @f]
+ so
+ @f[
+ \log\Gamma(x) = \log\left( \sum_{n=0}^{N} q_n x^n \right)
+ + (x+0.5) \log(x+5.5) - (x+5.5) - \sum_{n=0}^{N} \log(x+n)
+ @f]
+ and
+ q0 = 75122.6331530,
+ q1 = 80916.6278952,
+ q2 = 36308.2951477,
+ q3 = 8687.24529705,
+ q4 = 1168.92649479,
+ q5 = 83.8676043424,
+ q6 = 2.50662827511.
+ */
+static double log_gamma_lanczos(double x)
+{
+ static double q[7] = { 75122.6331530, 80916.6278952, 36308.2951477,
+ 8687.24529705, 1168.92649479, 83.8676043424,
+ 2.50662827511 };
+ double a = (x+0.5) * log(x+5.5) - (x+5.5);
+ double b = 0.0;
+ int n;
+
+ for(n=0;n<7;n++)
+ {
+ a -= log( x + (double) n );
+ b += q[n] * pow( x, (double) n );
+ }
+ return a + log(b);
+}
+
+/*----------------------------------------------------------------------------*/
+/** Computes the natural logarithm of the absolute value of
+ the gamma function of x using Windschitl method.
+ See http://www.rskey.org/gamma.htm
+
+ The formula used is
+ @f[
+ \Gamma(x) = \sqrt{\frac{2\pi}{x}} \left( \frac{x}{e}
+ \sqrt{ x\sinh(1/x) + \frac{1}{810x^6} } \right)^x
+ @f]
+ so
+ @f[
+ \log\Gamma(x) = 0.5\log(2\pi) + (x-0.5)\log(x) - x
+ + 0.5x\log\left( x\sinh(1/x) + \frac{1}{810x^6} \right).
+ @f]
+ This formula is a good approximation when x > 15.
+ */
+static double log_gamma_windschitl(double x)
+{
+ return 0.918938533204673 + (x-0.5)*log(x) - x
+ + 0.5*x*log( x*sinh(1/x) + 1/(810.0*pow(x,6.0)) );
+}
+
+/*----------------------------------------------------------------------------*/
+/** Computes the natural logarithm of the absolute value of
+ the gamma function of x. When x>15 use log_gamma_windschitl(),
+ otherwise use log_gamma_lanczos().
+ */
+#define log_gamma(x) ((x)>15.0?log_gamma_windschitl(x):log_gamma_lanczos(x))
+
+/*----------------------------------------------------------------------------*/
+/** Size of the table to store already computed inverse values.
+ */
+#define TABSIZE 100000
+
+// clang-format on
+
+static double *inv = NULL; /* table to keep computed inverse values */
+
+__attribute__((constructor)) static void invConstructor()
+{
+ if(inv) return;
+ inv = (double *)malloc(sizeof(double) * TABSIZE);
+}
+
+__attribute__((destructor)) static void invDestructor()
+{
+ free(inv);
+ inv = NULL;
+}
+
+// clang-format off
+
+/*----------------------------------------------------------------------------*/
+/** Computes -log10(NFA).
+
+ NFA stands for Number of False Alarms:
+ @f[
+ \mathrm{NFA} = NT \cdot B(n,k,p)
+ @f]
+
+ - NT - number of tests
+ - B(n,k,p) - tail of binomial distribution with parameters n,k and p:
+ @f[
+ B(n,k,p) = \sum_{j=k}^n
+ \left(\begin{array}{c}n\\j\end{array}\right)
+ p^{j} (1-p)^{n-j}
+ @f]
+
+ The value -log10(NFA) is equivalent but more intuitive than NFA:
+ - -1 corresponds to 10 mean false alarms
+ - 0 corresponds to 1 mean false alarm
+ - 1 corresponds to 0.1 mean false alarms
+ - 2 corresponds to 0.01 mean false alarms
+ - ...
+
+ Used this way, the bigger the value, better the detection,
+ and a logarithmic scale is used.
+
+ @param n,k,p binomial parameters.
+ @param logNT logarithm of Number of Tests
+
+ The computation is based in the gamma function by the following
+ relation:
+ @f[
+ \left(\begin{array}{c}n\\k\end{array}\right)
+ = \frac{ \Gamma(n+1) }{ \Gamma(k+1) \cdot \Gamma(n-k+1) }.
+ @f]
+ We use efficient algorithms to compute the logarithm of
+ the gamma function.
+
+ To make the computation faster, not all the sum is computed, part
+ of the terms are neglected based on a bound to the error obtained
+ (an error of 10% in the result is accepted).
+ */
+static double nfa(int n, int k, double p, double logNT)
+{
+ double tolerance = 0.1; /* an error of 10% in the result is accepted */
+ double log1term,term,bin_term,mult_term,bin_tail,err,p_term;
+ int i;
+
+ /* check parameters */
+ if( n<0 || k<0 || k>n || p<=0.0 || p>=1.0 )
+ error("nfa: wrong n, k or p values.");
+
+ /* trivial cases */
+ if( n==0 || k==0 ) return -logNT;
+ if( n==k ) return -logNT - (double) n * log10(p);
+
+ /* probability term */
+ p_term = p / (1.0-p);
+
+ /* compute the first term of the series */
+ /*
+ binomial_tail(n,k,p) = sum_{i=k}^n bincoef(n,i) * p^i * (1-p)^{n-i}
+ where bincoef(n,i) are the binomial coefficients.
+ But
+ bincoef(n,k) = gamma(n+1) / ( gamma(k+1) * gamma(n-k+1) ).
+ We use this to compute the first term. Actually the log of it.
+ */
+ log1term = log_gamma( (double) n + 1.0 ) - log_gamma( (double) k + 1.0 )
+ - log_gamma( (double) (n-k) + 1.0 )
+ + (double) k * log(p) + (double) (n-k) * log(1.0-p);
+ term = exp(log1term);
+
+ /* in some cases no more computations are needed */
+ if( double_equal(term,0.0) ) /* the first term is almost zero */
+ {
+ if( (double) k > (double) n * p ) /* at begin or end of the tail? */
+ return -log1term / M_LN10 - logNT; /* end: use just the first term */
+ else
+ return -logNT; /* begin: the tail is roughly 1 */
+ }
+
+ /* compute more terms if needed */
+ bin_tail = term;
+ for(i=k+1;i<=n;i++)
+ {
+ /*
+ As
+ term_i = bincoef(n,i) * p^i * (1-p)^(n-i)
+ and
+ bincoef(n,i)/bincoef(n,i-1) = n-1+1 / i,
+ then,
+ term_i / term_i-1 = (n-i+1)/i * p/(1-p)
+ and
+ term_i = term_i-1 * (n-i+1)/i * p/(1-p).
+ 1/i is stored in a table as they are computed,
+ because divisions are expensive.
+ p/(1-p) is computed only once and stored in 'p_term'.
+ */
+ bin_term = (double) (n-i+1) * ( ii.
+ Then, the error on the binomial tail when truncated at
+ the i term can be bounded by a geometric series of form
+ term_i * sum mult_term_i^j. */
+ err = term * ( ( 1.0 - pow( mult_term, (double) (n-i+1) ) ) /
+ (1.0-mult_term) - 1.0 );
+
+ /* One wants an error at most of tolerance*final_result, or:
+ tolerance * abs(-log10(bin_tail)-logNT).
+ Now, the error that can be accepted on bin_tail is
+ given by tolerance*final_result divided by the derivative
+ of -log10(x) when x=bin_tail. that is:
+ tolerance * abs(-log10(bin_tail)-logNT) / (1/bin_tail)
+ Finally, we truncate the tail if the error is less than:
+ tolerance * abs(-log10(bin_tail)-logNT) * bin_tail */
+ if( err < tolerance * fabs(-log10(bin_tail)-logNT) * bin_tail ) break;
+ }
+ }
+ return -log10(bin_tail) - logNT;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/*--------------------------- Rectangle structure ----------------------------*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** Rectangle structure: line segment with width.
+ */
+struct rect
+{
+ double x1,y1,x2,y2; /* first and second point of the line segment */
+ double width; /* rectangle width */
+ double x,y; /* center of the rectangle */
+ double theta; /* angle */
+ double dx,dy; /* (dx,dy) is vector oriented as the line segment */
+ double prec; /* tolerance angle */
+ double p; /* probability of a point with angle within 'prec' */
+};
+
+/*----------------------------------------------------------------------------*/
+/** Copy one rectangle structure to another.
+ */
+static void rect_copy(struct rect * in, struct rect * out)
+{
+ /* check parameters */
+ if( in == NULL || out == NULL ) error("rect_copy: invalid 'in' or 'out'.");
+
+ /* copy values */
+ out->x1 = in->x1;
+ out->y1 = in->y1;
+ out->x2 = in->x2;
+ out->y2 = in->y2;
+ out->width = in->width;
+ out->x = in->x;
+ out->y = in->y;
+ out->theta = in->theta;
+ out->dx = in->dx;
+ out->dy = in->dy;
+ out->prec = in->prec;
+ out->p = in->p;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Rectangle points iterator.
+
+ The integer coordinates of pixels inside a rectangle are
+ iteratively explored. This structure keep track of the process and
+ functions ri_ini(), ri_inc(), ri_end(), and ri_del() are used in
+ the process. An example of how to use the iterator is as follows:
+ \code
+
+ struct rect * rec = XXX; // some rectangle
+ rect_iter * i;
+ for( i=ri_ini(rec); !ri_end(i); ri_inc(i) )
+ {
+ // your code, using 'i->x' and 'i->y' as coordinates
+ }
+ ri_del(i); // delete iterator
+
+ \endcode
+ The pixels are explored 'column' by 'column', where we call
+ 'column' a set of pixels with the same x value that are inside the
+ rectangle. The following is an schematic representation of a
+ rectangle, the 'column' being explored is marked by colons, and
+ the current pixel being explored is 'x,y'.
+ \verbatim
+
+ vx[1],vy[1]
+ * *
+ * *
+ * *
+ * ye
+ * : *
+ vx[0],vy[0] : *
+ * : *
+ * x,y *
+ * : *
+ * : vx[2],vy[2]
+ * : *
+ y ys *
+ ^ * *
+ | * *
+ | * *
+ +---> x vx[3],vy[3]
+
+ \endverbatim
+ The first 'column' to be explored is the one with the smaller x
+ value. Each 'column' is explored starting from the pixel of the
+ 'column' (inside the rectangle) with the smallest y value.
+
+ The four corners of the rectangle are stored in order that rotates
+ around the corners at the arrays 'vx[]' and 'vy[]'. The first
+ point is always the one with smaller x value.
+
+ 'x' and 'y' are the coordinates of the pixel being explored. 'ys'
+ and 'ye' are the start and end values of the current column being
+ explored. So, 'ys' < 'ye'.
+ */
+typedef struct
+{
+ double vx[4]; /* rectangle's corner X coordinates in circular order */
+ double vy[4]; /* rectangle's corner Y coordinates in circular order */
+ double ys,ye; /* start and end Y values of current 'column' */
+ int x,y; /* coordinates of currently explored pixel */
+} rect_iter;
+
+/*----------------------------------------------------------------------------*/
+/** Interpolate y value corresponding to 'x' value given, in
+ the line 'x1,y1' to 'x2,y2'; if 'x1=x2' return the smaller
+ of 'y1' and 'y2'.
+
+ The following restrictions are required:
+ - x1 <= x2
+ - x1 <= x
+ - x <= x2
+ */
+static double inter_low(double x, double x1, double y1, double x2, double y2)
+{
+ /* check parameters */
+ if( x1 > x2 || x < x1 || x > x2 )
+ error("inter_low: unsuitable input, 'x1>x2' or 'xx2'.");
+
+ /* interpolation */
+ if( double_equal(x1,x2) && y1y2 ) return y2;
+ return y1 + (x-x1) * (y2-y1) / (x2-x1);
+}
+
+/*----------------------------------------------------------------------------*/
+/** Interpolate y value corresponding to 'x' value given, in
+ the line 'x1,y1' to 'x2,y2'; if 'x1=x2' return the larger
+ of 'y1' and 'y2'.
+
+ The following restrictions are required:
+ - x1 <= x2
+ - x1 <= x
+ - x <= x2
+ */
+static double inter_hi(double x, double x1, double y1, double x2, double y2)
+{
+ /* check parameters */
+ if( x1 > x2 || x < x1 || x > x2 )
+ error("inter_hi: unsuitable input, 'x1>x2' or 'xx2'.");
+
+ /* interpolation */
+ if( double_equal(x1,x2) && y1y2 ) return y1;
+ return y1 + (x-x1) * (y2-y1) / (x2-x1);
+}
+
+/*----------------------------------------------------------------------------*/
+/** Free memory used by a rectangle iterator.
+ */
+static void ri_del(rect_iter * iter)
+{
+ if( iter == NULL ) error("ri_del: NULL iterator.");
+ free( (void *) iter );
+}
+
+/*----------------------------------------------------------------------------*/
+/** Check if the iterator finished the full iteration.
+
+ See details in \ref rect_iter
+ */
+static int ri_end(rect_iter * i)
+{
+ /* check input */
+ if( i == NULL ) error("ri_end: NULL iterator.");
+
+ /* if the current x value is larger than the largest
+ x value in the rectangle (vx[2]), we know the full
+ exploration of the rectangle is finished. */
+ return (double)(i->x) > i->vx[2];
+}
+
+/*----------------------------------------------------------------------------*/
+/** Increment a rectangle iterator.
+
+ See details in \ref rect_iter
+ */
+static void ri_inc(rect_iter * i)
+{
+ /* check input */
+ if( i == NULL ) error("ri_inc: NULL iterator.");
+
+ /* if not at end of exploration,
+ increase y value for next pixel in the 'column' */
+ if( !ri_end(i) ) i->y++;
+
+ /* if the end of the current 'column' is reached,
+ and it is not the end of exploration,
+ advance to the next 'column' */
+ while( (double) (i->y) > i->ye && !ri_end(i) )
+ {
+ /* increase x, next 'column' */
+ i->x++;
+
+ /* if end of exploration, return */
+ if( ri_end(i) ) return;
+
+ /* update lower y limit (start) for the new 'column'.
+
+ We need to interpolate the y value that corresponds to the
+ lower side of the rectangle. The first thing is to decide if
+ the corresponding side is
+
+ vx[0],vy[0] to vx[3],vy[3] or
+ vx[3],vy[3] to vx[2],vy[2]
+
+ Then, the side is interpolated for the x value of the
+ 'column'. But, if the side is vertical (as it could happen if
+ the rectangle is vertical and we are dealing with the first
+ or last 'columns') then we pick the lower value of the side
+ by using 'inter_low'.
+ */
+ if( (double) i->x < i->vx[3] )
+ i->ys = inter_low((double)i->x,i->vx[0],i->vy[0],i->vx[3],i->vy[3]);
+ else
+ i->ys = inter_low((double)i->x,i->vx[3],i->vy[3],i->vx[2],i->vy[2]);
+
+ /* update upper y limit (end) for the new 'column'.
+
+ We need to interpolate the y value that corresponds to the
+ upper side of the rectangle. The first thing is to decide if
+ the corresponding side is
+
+ vx[0],vy[0] to vx[1],vy[1] or
+ vx[1],vy[1] to vx[2],vy[2]
+
+ Then, the side is interpolated for the x value of the
+ 'column'. But, if the side is vertical (as it could happen if
+ the rectangle is vertical and we are dealing with the first
+ or last 'columns') then we pick the lower value of the side
+ by using 'inter_low'.
+ */
+ if( (double)i->x < i->vx[1] )
+ i->ye = inter_hi((double)i->x,i->vx[0],i->vy[0],i->vx[1],i->vy[1]);
+ else
+ i->ye = inter_hi((double)i->x,i->vx[1],i->vy[1],i->vx[2],i->vy[2]);
+
+ /* new y */
+ i->y = (int) ceil(i->ys);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/** Create and initialize a rectangle iterator.
+
+ See details in \ref rect_iter
+ */
+static rect_iter * ri_ini(struct rect * r)
+{
+ double vx[4],vy[4];
+ int n,offset;
+ rect_iter * i;
+
+ /* check parameters */
+ if( r == NULL ) error("ri_ini: invalid rectangle.");
+
+ /* get memory */
+ i = (rect_iter *) malloc(sizeof(rect_iter));
+ if( i == NULL ) error("ri_ini: Not enough memory.");
+
+ /* build list of rectangle corners ordered
+ in a circular way around the rectangle */
+ vx[0] = r->x1 - r->dy * r->width / 2.0;
+ vy[0] = r->y1 + r->dx * r->width / 2.0;
+ vx[1] = r->x2 - r->dy * r->width / 2.0;
+ vy[1] = r->y2 + r->dx * r->width / 2.0;
+ vx[2] = r->x2 + r->dy * r->width / 2.0;
+ vy[2] = r->y2 - r->dx * r->width / 2.0;
+ vx[3] = r->x1 + r->dy * r->width / 2.0;
+ vy[3] = r->y1 - r->dx * r->width / 2.0;
+
+ /* compute rotation of index of corners needed so that the first
+ point has the smaller x.
+
+ if one side is vertical, thus two corners have the same smaller x
+ value, the one with the largest y value is selected as the first.
+ */
+ if( r->x1 < r->x2 && r->y1 <= r->y2 ) offset = 0;
+ else if( r->x1 >= r->x2 && r->y1 < r->y2 ) offset = 1;
+ else if( r->x1 > r->x2 && r->y1 >= r->y2 ) offset = 2;
+ else offset = 3;
+
+ /* apply rotation of index. */
+ for(n=0; n<4; n++)
+ {
+ i->vx[n] = vx[(offset+n)%4];
+ i->vy[n] = vy[(offset+n)%4];
+ }
+
+ /* Set an initial condition.
+
+ The values are set to values that will cause 'ri_inc' (that will
+ be called immediately) to initialize correctly the first 'column'
+ and compute the limits 'ys' and 'ye'.
+
+ 'y' is set to the integer value of vy[0], the starting corner.
+
+ 'ys' and 'ye' are set to very small values, so 'ri_inc' will
+ notice that it needs to start a new 'column'.
+
+ The smallest integer coordinate inside of the rectangle is
+ 'ceil(vx[0])'. The current 'x' value is set to that value minus
+ one, so 'ri_inc' (that will increase x by one) will advance to
+ the first 'column'.
+ */
+ i->x = (int) ceil(i->vx[0]) - 1;
+ i->y = (int) ceil(i->vy[0]);
+ i->ys = i->ye = -DBL_MAX;
+
+ /* advance to the first pixel */
+ ri_inc(i);
+
+ return i;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Compute a rectangle's NFA value.
+ */
+static double rect_nfa(struct rect * rec, image_double angles, double logNT)
+{
+ rect_iter * i;
+ int pts = 0;
+ int alg = 0;
+
+ /* check parameters */
+ if( rec == NULL ) error("rect_nfa: invalid rectangle.");
+ if( angles == NULL ) error("rect_nfa: invalid 'angles'.");
+
+ /* compute the total number of pixels and of aligned points in 'rec' */
+ for(i=ri_ini(rec); !ri_end(i); ri_inc(i)) /* rectangle iterator */
+ if( i->x >= 0 && i->y >= 0 &&
+ i->x < (int) angles->xsize && i->y < (int) angles->ysize )
+ {
+ ++pts; /* total number of pixels counter */
+ if( isaligned(i->x, i->y, angles, rec->theta, rec->prec) )
+ ++alg; /* aligned points counter */
+ }
+ ri_del(i); /* delete iterator */
+
+ return nfa(pts,alg,rec->p,logNT); /* compute NFA value */
+}
+
+
+/*----------------------------------------------------------------------------*/
+/*---------------------------------- Regions ---------------------------------*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** Compute region's angle as the principal inertia axis of the region.
+
+ The following is the region inertia matrix A:
+ @f[
+
+ A = \left(\begin{array}{cc}
+ Ixx & Ixy \\
+ Ixy & Iyy \\
+ \end{array}\right)
+
+ @f]
+ where
+
+ Ixx = sum_i G(i).(y_i - cx)^2
+
+ Iyy = sum_i G(i).(x_i - cy)^2
+
+ Ixy = - sum_i G(i).(x_i - cx).(y_i - cy)
+
+ and
+ - G(i) is the gradient norm at pixel i, used as pixel's weight.
+ - x_i and y_i are the coordinates of pixel i.
+ - cx and cy are the coordinates of the center of th region.
+
+ lambda1 and lambda2 are the eigenvalues of matrix A,
+ with lambda1 >= lambda2. They are found by solving the
+ characteristic polynomial:
+
+ det( lambda I - A) = 0
+
+ that gives:
+
+ lambda1 = ( Ixx + Iyy + sqrt( (Ixx-Iyy)^2 + 4.0*Ixy*Ixy) ) / 2
+
+ lambda2 = ( Ixx + Iyy - sqrt( (Ixx-Iyy)^2 + 4.0*Ixy*Ixy) ) / 2
+
+ To get the line segment direction we want to get the angle the
+ eigenvector associated to the smallest eigenvalue. We have
+ to solve for a,b in:
+
+ a.Ixx + b.Ixy = a.lambda2
+
+ a.Ixy + b.Iyy = b.lambda2
+
+ We want the angle theta = atan(b/a). It can be computed with
+ any of the two equations:
+
+ theta = atan( (lambda2-Ixx) / Ixy )
+
+ or
+
+ theta = atan( Ixy / (lambda2-Iyy) )
+
+ When |Ixx| > |Iyy| we use the first, otherwise the second (just to
+ get better numeric precision).
+ */
+static double get_theta( struct point * reg, int reg_size, double x, double y,
+ image_double modgrad, double reg_angle, double prec )
+{
+ double lambda,theta,weight;
+ double Ixx = 0.0;
+ double Iyy = 0.0;
+ double Ixy = 0.0;
+ int i;
+
+ /* check parameters */
+ if( reg == NULL ) error("get_theta: invalid region.");
+ if( reg_size <= 1 ) error("get_theta: region size <= 1.");
+ if( modgrad == NULL || modgrad->data == NULL )
+ error("get_theta: invalid 'modgrad'.");
+ if( prec < 0.0 ) error("get_theta: 'prec' must be positive.");
+
+ /* compute inertia matrix */
+ for(i=0; idata[ reg[i].x + reg[i].y * modgrad->xsize ];
+ Ixx += ( (double) reg[i].y - y ) * ( (double) reg[i].y - y ) * weight;
+ Iyy += ( (double) reg[i].x - x ) * ( (double) reg[i].x - x ) * weight;
+ Ixy -= ( (double) reg[i].x - x ) * ( (double) reg[i].y - y ) * weight;
+ }
+ if( double_equal(Ixx,0.0) && double_equal(Iyy,0.0) && double_equal(Ixy,0.0) )
+ error("get_theta: null inertia matrix.");
+
+ /* compute smallest eigenvalue */
+ lambda = 0.5 * ( Ixx + Iyy - sqrt( (Ixx-Iyy)*(Ixx-Iyy) + 4.0*Ixy*Ixy ) );
+
+ /* compute angle */
+ theta = fabs(Ixx)>fabs(Iyy) ? atan2(lambda-Ixx,Ixy) : atan2(Ixy,lambda-Iyy);
+
+ /* The previous procedure doesn't cares about orientation,
+ so it could be wrong by 180 degrees. Here is corrected if necessary. */
+ if( angle_diff(theta,reg_angle) > prec ) theta += M_PI;
+
+ return theta;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Computes a rectangle that covers a region of points.
+ */
+static void region2rect( struct point * reg, int reg_size,
+ image_double modgrad, double reg_angle,
+ double prec, double p, struct rect * rec )
+{
+ double x,y,dx,dy,l,w,theta,weight,sum,l_min,l_max,w_min,w_max;
+ int i;
+
+ /* check parameters */
+ if( reg == NULL ) error("region2rect: invalid region.");
+ if( reg_size <= 1 ) error("region2rect: region size <= 1.");
+ if( modgrad == NULL || modgrad->data == NULL )
+ error("region2rect: invalid image 'modgrad'.");
+ if( rec == NULL ) error("region2rect: invalid 'rec'.");
+
+ /* center of the region:
+
+ It is computed as the weighted sum of the coordinates
+ of all the pixels in the region. The norm of the gradient
+ is used as the weight of a pixel. The sum is as follows:
+ cx = \sum_i G(i).x_i
+ cy = \sum_i G(i).y_i
+ where G(i) is the norm of the gradient of pixel i
+ and x_i,y_i are its coordinates.
+ */
+ x = y = sum = 0.0;
+ for(i=0; idata[ reg[i].x + reg[i].y * modgrad->xsize ];
+ x += (double) reg[i].x * weight;
+ y += (double) reg[i].y * weight;
+ sum += weight;
+ }
+ if( sum <= 0.0 ) error("region2rect: weights sum equal to zero.");
+ x /= sum;
+ y /= sum;
+
+ /* theta */
+ theta = get_theta(reg,reg_size,x,y,modgrad,reg_angle,prec);
+
+ /* length and width:
+
+ 'l' and 'w' are computed as the distance from the center of the
+ region to pixel i, projected along the rectangle axis (dx,dy) and
+ to the orthogonal axis (-dy,dx), respectively.
+
+ The length of the rectangle goes from l_min to l_max, where l_min
+ and l_max are the minimum and maximum values of l in the region.
+ Analogously, the width is selected from w_min to w_max, where
+ w_min and w_max are the minimum and maximum of w for the pixels
+ in the region.
+ */
+ dx = cos(theta);
+ dy = sin(theta);
+ l_min = l_max = w_min = w_max = 0.0;
+ for(i=0; i l_max ) l_max = l;
+ if( l < l_min ) l_min = l;
+ if( w > w_max ) w_max = w;
+ if( w < w_min ) w_min = w;
+ }
+
+ /* store values */
+ rec->x1 = x + l_min * dx;
+ rec->y1 = y + l_min * dy;
+ rec->x2 = x + l_max * dx;
+ rec->y2 = y + l_max * dy;
+ rec->width = w_max - w_min;
+ rec->x = x;
+ rec->y = y;
+ rec->theta = theta;
+ rec->dx = dx;
+ rec->dy = dy;
+ rec->prec = prec;
+ rec->p = p;
+
+ /* we impose a minimal width of one pixel
+
+ A sharp horizontal or vertical step would produce a perfectly
+ horizontal or vertical region. The width computed would be
+ zero. But that corresponds to a one pixels width transition in
+ the image.
+ */
+ if( rec->width < 1.0 ) rec->width = 1.0;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Build a region of pixels that share the same angle, up to a
+ tolerance 'prec', starting at point (x,y).
+ */
+static void region_grow( int x, int y, image_double angles, struct point * reg,
+ int * reg_size, double * reg_angle, image_char used,
+ double prec )
+{
+ double sumdx,sumdy;
+ int xx,yy,i;
+
+ /* check parameters */
+ if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize )
+ error("region_grow: (x,y) out of the image.");
+ if( angles == NULL || angles->data == NULL )
+ error("region_grow: invalid image 'angles'.");
+ if( reg == NULL ) error("region_grow: invalid 'reg'.");
+ if( reg_size == NULL ) error("region_grow: invalid pointer 'reg_size'.");
+ if( reg_angle == NULL ) error("region_grow: invalid pointer 'reg_angle'.");
+ if( used == NULL || used->data == NULL )
+ error("region_grow: invalid image 'used'.");
+
+ /* first point of the region */
+ *reg_size = 1;
+ reg[0].x = x;
+ reg[0].y = y;
+ *reg_angle = angles->data[x+y*angles->xsize]; /* region's angle */
+ sumdx = cos(*reg_angle);
+ sumdy = sin(*reg_angle);
+ used->data[x+y*used->xsize] = USED;
+
+ /* try neighbors as new region points */
+ for(i=0; i<*reg_size; i++)
+ for(xx=reg[i].x-1; xx<=reg[i].x+1; xx++)
+ for(yy=reg[i].y-1; yy<=reg[i].y+1; yy++)
+ if( xx>=0 && yy>=0 && xx<(int)used->xsize && yy<(int)used->ysize &&
+ used->data[xx+yy*used->xsize] != USED &&
+ isaligned(xx,yy,angles,*reg_angle,prec) )
+ {
+ /* add point */
+ used->data[xx+yy*used->xsize] = USED;
+ reg[*reg_size].x = xx;
+ reg[*reg_size].y = yy;
+ ++(*reg_size);
+
+ /* update region's angle */
+ sumdx += cos( angles->data[xx+yy*angles->xsize] );
+ sumdy += sin( angles->data[xx+yy*angles->xsize] );
+ *reg_angle = atan2(sumdy,sumdx);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/** Try some rectangles variations to improve NFA value. Only if the
+ rectangle is not meaningful (i.e., log_nfa <= log_eps).
+ */
+static double rect_improve( struct rect * rec, image_double angles,
+ double logNT, double log_eps )
+{
+ struct rect r;
+ double log_nfa,log_nfa_new;
+ double delta = 0.5;
+ double delta_2 = delta / 2.0;
+ int n;
+
+ log_nfa = rect_nfa(rec,angles,logNT);
+
+ if( log_nfa > log_eps ) return log_nfa;
+
+ /* try finer precisions */
+ rect_copy(rec,&r);
+ for(n=0; n<5; n++)
+ {
+ r.p /= 2.0;
+ r.prec = r.p * M_PI;
+ log_nfa_new = rect_nfa(&r,angles,logNT);
+ if( log_nfa_new > log_nfa )
+ {
+ log_nfa = log_nfa_new;
+ rect_copy(&r,rec);
+ }
+ }
+
+ if( log_nfa > log_eps ) return log_nfa;
+
+ /* try to reduce width */
+ rect_copy(rec,&r);
+ for(n=0; n<5; n++)
+ {
+ if( (r.width - delta) >= 0.5 )
+ {
+ r.width -= delta;
+ log_nfa_new = rect_nfa(&r,angles,logNT);
+ if( log_nfa_new > log_nfa )
+ {
+ rect_copy(&r,rec);
+ log_nfa = log_nfa_new;
+ }
+ }
+ }
+
+ if( log_nfa > log_eps ) return log_nfa;
+
+ /* try to reduce one side of the rectangle */
+ rect_copy(rec,&r);
+ for(n=0; n<5; n++)
+ {
+ if( (r.width - delta) >= 0.5 )
+ {
+ r.x1 += -r.dy * delta_2;
+ r.y1 += r.dx * delta_2;
+ r.x2 += -r.dy * delta_2;
+ r.y2 += r.dx * delta_2;
+ r.width -= delta;
+ log_nfa_new = rect_nfa(&r,angles,logNT);
+ if( log_nfa_new > log_nfa )
+ {
+ rect_copy(&r,rec);
+ log_nfa = log_nfa_new;
+ }
+ }
+ }
+
+ if( log_nfa > log_eps ) return log_nfa;
+
+ /* try to reduce the other side of the rectangle */
+ rect_copy(rec,&r);
+ for(n=0; n<5; n++)
+ {
+ if( (r.width - delta) >= 0.5 )
+ {
+ r.x1 -= -r.dy * delta_2;
+ r.y1 -= r.dx * delta_2;
+ r.x2 -= -r.dy * delta_2;
+ r.y2 -= r.dx * delta_2;
+ r.width -= delta;
+ log_nfa_new = rect_nfa(&r,angles,logNT);
+ if( log_nfa_new > log_nfa )
+ {
+ rect_copy(&r,rec);
+ log_nfa = log_nfa_new;
+ }
+ }
+ }
+
+ if( log_nfa > log_eps ) return log_nfa;
+
+ /* try even finer precisions */
+ rect_copy(rec,&r);
+ for(n=0; n<5; n++)
+ {
+ r.p /= 2.0;
+ r.prec = r.p * M_PI;
+ log_nfa_new = rect_nfa(&r,angles,logNT);
+ if( log_nfa_new > log_nfa )
+ {
+ log_nfa = log_nfa_new;
+ rect_copy(&r,rec);
+ }
+ }
+
+ return log_nfa;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Reduce the region size, by elimination the points far from the
+ starting point, until that leads to rectangle with the right
+ density of region points or to discard the region if too small.
+ */
+static int reduce_region_radius( struct point * reg, int * reg_size,
+ image_double modgrad, double reg_angle,
+ double prec, double p, struct rect * rec,
+ image_char used, image_double angles,
+ double density_th )
+{
+ double density,radius1,radius2,rad,xc,yc;
+ int i;
+
+ /* check parameters */
+ if( reg == NULL ) error("reduce_region_radius: invalid pointer 'reg'.");
+ if( reg_size == NULL )
+ error("reduce_region_radius: invalid pointer 'reg_size'.");
+ if( prec < 0.0 ) error("reduce_region_radius: 'prec' must be positive.");
+ if( rec == NULL ) error("reduce_region_radius: invalid pointer 'rec'.");
+ if( used == NULL || used->data == NULL )
+ error("reduce_region_radius: invalid image 'used'.");
+ if( angles == NULL || angles->data == NULL )
+ error("reduce_region_radius: invalid image 'angles'.");
+
+ /* compute region points density */
+ density = (double) *reg_size /
+ ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
+
+ /* if the density criterion is satisfied there is nothing to do */
+ if( density >= density_th ) return TRUE;
+
+ /* compute region's radius */
+ xc = (double) reg[0].x;
+ yc = (double) reg[0].y;
+ radius1 = dist( xc, yc, rec->x1, rec->y1 );
+ radius2 = dist( xc, yc, rec->x2, rec->y2 );
+ rad = radius1 > radius2 ? radius1 : radius2;
+
+ /* while the density criterion is not satisfied, remove farther pixels */
+ while( density < density_th )
+ {
+ rad *= 0.75; /* reduce region's radius to 75% of its value */
+
+ /* remove points from the region and update 'used' map */
+ for(i=0; i<*reg_size; i++)
+ if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) > rad )
+ {
+ /* point not kept, mark it as NOTUSED */
+ used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED;
+ /* remove point from the region */
+ reg[i].x = reg[*reg_size-1].x; /* if i==*reg_size-1 copy itself */
+ reg[i].y = reg[*reg_size-1].y;
+ --(*reg_size);
+ --i; /* to avoid skipping one point */
+ }
+
+ /* reject if the region is too small.
+ 2 is the minimal region size for 'region2rect' to work. */
+ if( *reg_size < 2 ) return FALSE;
+
+ /* re-compute rectangle */
+ region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec);
+
+ /* re-compute region points density */
+ density = (double) *reg_size /
+ ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
+ }
+
+ /* if this point is reached, the density criterion is satisfied */
+ return TRUE;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Refine a rectangle.
+
+ For that, an estimation of the angle tolerance is performed by the
+ standard deviation of the angle at points near the region's
+ starting point. Then, a new region is grown starting from the same
+ point, but using the estimated angle tolerance. If this fails to
+ produce a rectangle with the right density of region points,
+ 'reduce_region_radius' is called to try to satisfy this condition.
+ */
+static int refine( struct point * reg, int * reg_size, image_double modgrad,
+ double reg_angle, double prec, double p, struct rect * rec,
+ image_char used, image_double angles, double density_th )
+{
+ double angle,ang_d,mean_angle,tau,density,xc,yc,ang_c,sum,s_sum;
+ int i,n;
+
+ /* check parameters */
+ if( reg == NULL ) error("refine: invalid pointer 'reg'.");
+ if( reg_size == NULL ) error("refine: invalid pointer 'reg_size'.");
+ if( prec < 0.0 ) error("refine: 'prec' must be positive.");
+ if( rec == NULL ) error("refine: invalid pointer 'rec'.");
+ if( used == NULL || used->data == NULL )
+ error("refine: invalid image 'used'.");
+ if( angles == NULL || angles->data == NULL )
+ error("refine: invalid image 'angles'.");
+
+ /* compute region points density */
+ density = (double) *reg_size /
+ ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
+
+ /* if the density criterion is satisfied there is nothing to do */
+ if( density >= density_th ) return TRUE;
+
+ /*------ First try: reduce angle tolerance ------*/
+
+ /* compute the new mean angle and tolerance */
+ xc = (double) reg[0].x;
+ yc = (double) reg[0].y;
+ ang_c = angles->data[ reg[0].x + reg[0].y * angles->xsize ];
+ sum = s_sum = 0.0;
+ n = 0;
+ for(i=0; i<*reg_size; i++)
+ {
+ used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED;
+ if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) < rec->width )
+ {
+ angle = angles->data[ reg[i].x + reg[i].y * angles->xsize ];
+ ang_d = angle_diff_signed(angle,ang_c);
+ sum += ang_d;
+ s_sum += ang_d * ang_d;
+ ++n;
+ }
+ }
+
+ /* should not happen */
+ if(n == 0) return FALSE;
+
+ mean_angle = sum / (double) n;
+ tau = 2.0 * sqrt( (s_sum - 2.0 * mean_angle * sum) / (double) n
+ + mean_angle*mean_angle ); /* 2 * standard deviation */
+
+ /* find a new region from the same starting point and new angle tolerance */
+ region_grow(reg[0].x,reg[0].y,angles,reg,reg_size,®_angle,used,tau);
+
+ /* if the region is too small, reject */
+ if( *reg_size < 2 ) return FALSE;
+
+ /* re-compute rectangle */
+ region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec);
+
+ /* re-compute region points density */
+ density = (double) *reg_size /
+ ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width );
+
+ /*------ Second try: reduce region radius ------*/
+ if( density < density_th )
+ return reduce_region_radius( reg, reg_size, modgrad, reg_angle, prec, p,
+ rec, used, angles, density_th );
+
+ /* if this point is reached, the density criterion is satisfied */
+ return TRUE;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/*-------------------------- Line Segment Detector ---------------------------*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/** LSD full interface.
+ */
+static
+double * LineSegmentDetection( int * n_out,
+ double * img, int X, int Y,
+ double scale, double sigma_scale, double quant,
+ double ang_th, double log_eps, double density_th,
+ int n_bins,
+ int ** reg_img, int * reg_x, int * reg_y )
+{
+ image_double image;
+ ntuple_list out = new_ntuple_list(7);
+ double * return_value;
+ image_double scaled_image,angles,modgrad;
+ image_char used;
+ image_int region = NULL;
+ struct coorlist * list_p;
+ void * mem_p;
+ struct rect rec;
+ struct point * reg;
+ int reg_size,min_reg_size,i;
+ unsigned int xsize,ysize;
+ double rho,reg_angle,prec,p,log_nfa,logNT;
+ int ls_count = 0; /* line segments are numbered 1,2,3,... */
+
+
+ /* check parameters */
+ if( img == NULL || X <= 0 || Y <= 0 ) error("invalid image input.");
+ if( scale <= 0.0 ) error("'scale' value must be positive.");
+ if( sigma_scale <= 0.0 ) error("'sigma_scale' value must be positive.");
+ if( quant < 0.0 ) error("'quant' value must be positive.");
+ if( ang_th <= 0.0 || ang_th >= 180.0 )
+ error("'ang_th' value must be in the range (0,180).");
+ if( density_th < 0.0 || density_th > 1.0 )
+ error("'density_th' value must be in the range [0,1].");
+ if( n_bins <= 0 ) error("'n_bins' value must be positive.");
+
+
+ /* angle tolerance */
+ prec = M_PI * ang_th / 180.0;
+ p = ang_th / 180.0;
+ rho = quant / sin(prec); /* gradient magnitude threshold */
+
+
+ /* load and scale image (if necessary) and compute angle at each pixel */
+ image = new_image_double_ptr( (unsigned int) X, (unsigned int) Y, img );
+ if( scale != 1.0 )
+ {
+ scaled_image = gaussian_sampler( image, scale, sigma_scale );
+ angles = ll_angle( scaled_image, rho, &list_p, &mem_p,
+ &modgrad, (unsigned int) n_bins );
+ free_image_double(scaled_image);
+ }
+ else
+ angles = ll_angle( image, rho, &list_p, &mem_p, &modgrad,
+ (unsigned int) n_bins );
+ xsize = angles->xsize;
+ ysize = angles->ysize;
+
+ /* Number of Tests - NT
+
+ The theoretical number of tests is Np.(XY)^(5/2)
+ where X and Y are number of columns and rows of the image.
+ Np corresponds to the number of angle precisions considered.
+ As the procedure 'rect_improve' tests 5 times to halve the
+ angle precision, and 5 more times after improving other factors,
+ 11 different precision values are potentially tested. Thus,
+ the number of tests is
+ 11 * (X*Y)^(5/2)
+ whose logarithm value is
+ log10(11) + 5/2 * (log10(X) + log10(Y)).
+ */
+ logNT = 5.0 * ( log10( (double) xsize ) + log10( (double) ysize ) ) / 2.0
+ + log10(11.0);
+ min_reg_size = (int) (-logNT/log10(p)); /* minimal number of points in region
+ that can give a meaningful event */
+
+
+ /* initialize some structures */
+ if( reg_img != NULL && reg_x != NULL && reg_y != NULL ) /* save region data */
+ region = new_image_int_ini(angles->xsize,angles->ysize,0);
+ used = new_image_char_ini(xsize,ysize,NOTUSED);
+ reg = (struct point *) calloc( (size_t) (xsize*ysize), sizeof(struct point) );
+ if( reg == NULL ) error("not enough memory!");
+
+
+ /* search for line segments */
+ for(; list_p != NULL; list_p = list_p->next )
+ if( used->data[ list_p->x + list_p->y * used->xsize ] == NOTUSED &&
+ angles->data[ list_p->x + list_p->y * angles->xsize ] != NOTDEF )
+ /* there is no risk of double comparison problems here
+ because we are only interested in the exact NOTDEF value */
+ {
+ /* find the region of connected point and ~equal angle */
+ region_grow( list_p->x, list_p->y, angles, reg, ®_size,
+ ®_angle, used, prec );
+
+ /* reject small regions */
+ if( reg_size < min_reg_size ) continue;
+
+ /* construct rectangular approximation for the region */
+ region2rect(reg,reg_size,modgrad,reg_angle,prec,p,&rec);
+
+ /* Check if the rectangle exceeds the minimal density of
+ region points. If not, try to improve the region.
+ The rectangle will be rejected if the final one does
+ not fulfill the minimal density condition.
+ This is an addition to the original LSD algorithm published in
+ "LSD: A Fast Line Segment Detector with a False Detection Control"
+ by R. Grompone von Gioi, J. Jakubowicz, J.M. Morel, and G. Randall.
+ The original algorithm is obtained with density_th = 0.0.
+ */
+ if( !refine( reg, ®_size, modgrad, reg_angle,
+ prec, p, &rec, used, angles, density_th ) ) continue;
+
+ /* compute NFA value */
+ log_nfa = rect_improve(&rec,angles,logNT,log_eps);
+ if( log_nfa <= log_eps ) continue;
+
+ /* A New Line Segment was found! */
+ ++ls_count; /* increase line segment counter */
+
+ /*
+ The gradient was computed with a 2x2 mask, its value corresponds to
+ points with an offset of (0.5,0.5), that should be added to output.
+ The coordinates origin is at the center of pixel (0,0).
+ */
+ rec.x1 += 0.5; rec.y1 += 0.5;
+ rec.x2 += 0.5; rec.y2 += 0.5;
+
+ /* scale the result values if a subsampling was performed */
+ if( scale != 1.0 )
+ {
+ rec.x1 /= scale; rec.y1 /= scale;
+ rec.x2 /= scale; rec.y2 /= scale;
+ rec.width /= scale;
+ }
+
+ /* add line segment found to output */
+ add_7tuple( out, rec.x1, rec.y1, rec.x2, rec.y2,
+ rec.width, rec.p, log_nfa );
+
+ /* add region number to 'region' image if needed */
+ if( region != NULL )
+ for(i=0; idata[ reg[i].x + reg[i].y * region->xsize ] = ls_count;
+ }
+
+
+ /* free memory */
+ free( (void *) image ); /* only the double_image structure should be freed,
+ the data pointer was provided to this functions
+ and should not be destroyed. */
+ free_image_double(angles);
+ free_image_double(modgrad);
+ free_image_char(used);
+ free( (void *) reg );
+ free( (void *) mem_p );
+
+ /* return the result */
+ if( reg_img != NULL && reg_x != NULL && reg_y != NULL )
+ {
+ if( region == NULL ) error("'region' should be a valid image.");
+ *reg_img = region->data;
+ if( region->xsize > (unsigned int) INT_MAX ||
+ region->ysize > (unsigned int) INT_MAX )
+ error("region image to big to fit in INT sizes.");
+ *reg_x = (int) (region->xsize);
+ *reg_y = (int) (region->ysize);
+
+ /* free the 'region' structure.
+ we cannot use the function 'free_image_int' because we need to keep
+ the memory with the image data to be returned by this function. */
+ free( (void *) region );
+ }
+ if( out->size > (unsigned int) INT_MAX )
+ error("too many detections to fit in an INT.");
+ *n_out = (int) (out->size);
+
+ return_value = out->values;
+ free( (void *) out ); /* only the 'ntuple_list' structure must be freed,
+ but the 'values' pointer must be keep to return
+ as a result. */
+
+ return return_value;
+}
+#if 0
+/*----------------------------------------------------------------------------*/
+/** LSD Simple Interface with Scale and Region output.
+ */
+static
+double * lsd_scale_region( int * n_out,
+ double * img, int X, int Y, double scale,
+ int ** reg_img, int * reg_x, int * reg_y )
+{
+ /* LSD parameters */
+ double sigma_scale = 0.6; /* Sigma for Gaussian filter is computed as
+ sigma = sigma_scale/scale. */
+ double quant = 2.0; /* Bound to the quantization error on the
+ gradient norm. */
+ double ang_th = 22.5; /* Gradient angle tolerance in degrees. */
+ double log_eps = 0.0; /* Detection threshold: -log10(NFA) > log_eps */
+ double density_th = 0.7; /* Minimal density of region points in rectangle. */
+ int n_bins = 1024; /* Number of bins in pseudo-ordering of gradient
+ modulus. */
+
+ return LineSegmentDetection( n_out, img, X, Y, scale, sigma_scale, quant,
+ ang_th, log_eps, density_th, n_bins,
+ reg_img, reg_x, reg_y );
+}
+
+/*----------------------------------------------------------------------------*/
+/** LSD Simple Interface with Scale.
+ */
+static
+double * lsd_scale(int * n_out, double * img, int X, int Y, double scale)
+{
+ return lsd_scale_region(n_out,img,X,Y,scale,NULL,NULL,NULL);
+}
+
+/*----------------------------------------------------------------------------*/
+/** LSD Simple Interface.
+ */
+static
+double * lsd(int * n_out, double * img, int X, int Y)
+{
+ /* LSD parameters */
+ double scale = 0.8; /* Scale the image by Gaussian filter to 'scale'. */
+
+ return lsd_scale(n_out,img,X,Y,scale);
+}
+/*----------------------------------------------------------------------------*/
+#endif
+/*==================================================================================
+ * end of LSD code
+ *==================================================================================*/
+
+// clang-format on
+
+#undef NOTDEF
+#undef NOTUSED
+#undef USED
+#undef RELATIVE_ERROR_FACTOR
+#undef TABSIZE
+
+// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
+// vim: shiftwidth=2 expandtab tabstop=2 cindent
+// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
diff --git a/rtengine/ashift_nmsimplex.c b/rtengine/ashift_nmsimplex.c
new file mode 100644
index 000000000..512bac878
--- /dev/null
+++ b/rtengine/ashift_nmsimplex.c
@@ -0,0 +1,425 @@
+/*
+ This file is part of darktable,
+ copyright (c) 2016 Ulrich Pegelow.
+
+ darktable 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.
+
+ darktable 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 darktable. If not, see .
+*/
+
+/* For parameter optimization we are using the Nelder-Mead simplex method
+ * implemented by Michael F. Hutt.
+ * Changes versus the original code:
+ * do not include "nmsimplex.h" (not needed)
+ * renamed configuration variables to NMS_*
+ * add additional argument to objfun for arbitrary parameters
+ * simplex() returns number of used iterations instead of min value
+ * maximum number of iterations as function parameter
+ * make interface function simplex() static
+ * initialize i and j to avoid compiler warnings
+ * comment out printing of status inormation
+ * reformat according to darktable's clang standards
+ */
+
+/*==================================================================================
+ * begin nmsimplex code downloaded from http://www.mikehutt.com/neldermead.html
+ * on February 6, 2016
+ *==================================================================================*/
+/*
+ * Program: nmsimplex.c
+ * Author : Michael F. Hutt
+ * http://www.mikehutt.com
+ * 11/3/97
+ *
+ * An implementation of the Nelder-Mead simplex method.
+ *
+ * Copyright (c) 1997-2011
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ * Jan. 6, 1999
+ * Modified to conform to the algorithm presented
+ * in Margaret H. Wright's paper on Direct Search Methods.
+ *
+ * Jul. 23, 2007
+ * Fixed memory leak.
+ *
+ * Mar. 1, 2011
+ * Added constraints.
+ */
+
+//#include "nmsimplex.h"
+
+static int simplex(double (*objfunc)(double[], void *params), double start[], int n, double EPSILON, double scale,
+ int maxiter, void (*constrain)(double[], int n), void *params)
+{
+
+ int vs; /* vertex with smallest value */
+ int vh; /* vertex with next smallest value */
+ int vg; /* vertex with largest value */
+
+ int i = 0, j = 0, m, row;
+ int k; /* track the number of function evaluations */
+ int itr; /* track the number of iterations */
+
+ double **v; /* holds vertices of simplex */
+ double pn, qn; /* values used to create initial simplex */
+ double *f; /* value of function at each vertex */
+ double fr; /* value of function at reflection point */
+ double fe; /* value of function at expansion point */
+ double fc; /* value of function at contraction point */
+ double *vr; /* reflection - coordinates */
+ double *ve; /* expansion - coordinates */
+ double *vc; /* contraction - coordinates */
+ double *vm; /* centroid - coordinates */
+ //double min;
+
+ double fsum, favg, s, cent;
+
+ /* dynamically allocate arrays */
+
+ /* allocate the rows of the arrays */
+ v = (double **)malloc((n + 1) * sizeof(double *));
+ f = (double *)malloc((n + 1) * sizeof(double));
+ vr = (double *)malloc(n * sizeof(double));
+ ve = (double *)malloc(n * sizeof(double));
+ vc = (double *)malloc(n * sizeof(double));
+ vm = (double *)malloc(n * sizeof(double));
+
+ /* allocate the columns of the arrays */
+ for(i = 0; i <= n; i++)
+ {
+ v[i] = (double *)malloc(n * sizeof(double));
+ }
+
+ /* create the initial simplex */
+ /* assume one of the vertices is 0,0 */
+
+ pn = scale * (sqrt(n + 1) - 1 + n) / (n * sqrt(2));
+ qn = scale * (sqrt(n + 1) - 1) / (n * sqrt(2));
+
+ for(i = 0; i < n; i++)
+ {
+ v[0][i] = start[i];
+ }
+
+ for(i = 1; i <= n; i++)
+ {
+ for(j = 0; j < n; j++)
+ {
+ if(i - 1 == j)
+ {
+ v[i][j] = pn + start[j];
+ }
+ else
+ {
+ v[i][j] = qn + start[j];
+ }
+ }
+ }
+
+ if(constrain != NULL)
+ {
+ constrain(v[j], n);
+ }
+ /* find the initial function values */
+ for(j = 0; j <= n; j++)
+ {
+ f[j] = objfunc(v[j], params);
+ }
+
+ k = n + 1;
+#if 0
+ /* print out the initial values */
+ printf("Initial Values\n");
+ for(j = 0; j <= n; j++)
+ {
+ for(i = 0; i < n; i++)
+ {
+ printf("%f %f\n", v[j][i], f[j]);
+ }
+ }
+#endif
+
+ /* begin the main loop of the minimization */
+ for(itr = 1; itr <= maxiter; itr++)
+ {
+ /* find the index of the largest value */
+ vg = 0;
+ for(j = 0; j <= n; j++)
+ {
+ if(f[j] > f[vg])
+ {
+ vg = j;
+ }
+ }
+
+ /* find the index of the smallest value */
+ vs = 0;
+ for(j = 0; j <= n; j++)
+ {
+ if(f[j] < f[vs])
+ {
+ vs = j;
+ }
+ }
+
+ /* find the index of the second largest value */
+ vh = vs;
+ for(j = 0; j <= n; j++)
+ {
+ if(f[j] > f[vh] && f[j] < f[vg])
+ {
+ vh = j;
+ }
+ }
+
+ /* calculate the centroid */
+ for(j = 0; j <= n - 1; j++)
+ {
+ cent = 0.0;
+ for(m = 0; m <= n; m++)
+ {
+ if(m != vg)
+ {
+ cent += v[m][j];
+ }
+ }
+ vm[j] = cent / n;
+ }
+
+ /* reflect vg to new vertex vr */
+ for(j = 0; j <= n - 1; j++)
+ {
+ /*vr[j] = (1+NMS_ALPHA)*vm[j] - NMS_ALPHA*v[vg][j];*/
+ vr[j] = vm[j] + NMS_ALPHA * (vm[j] - v[vg][j]);
+ }
+ if(constrain != NULL)
+ {
+ constrain(vr, n);
+ }
+ fr = objfunc(vr, params);
+ k++;
+
+ if(fr < f[vh] && fr >= f[vs])
+ {
+ for(j = 0; j <= n - 1; j++)
+ {
+ v[vg][j] = vr[j];
+ }
+ f[vg] = fr;
+ }
+
+ /* investigate a step further in this direction */
+ if(fr < f[vs])
+ {
+ for(j = 0; j <= n - 1; j++)
+ {
+ /*ve[j] = NMS_GAMMA*vr[j] + (1-NMS_GAMMA)*vm[j];*/
+ ve[j] = vm[j] + NMS_GAMMA * (vr[j] - vm[j]);
+ }
+ if(constrain != NULL)
+ {
+ constrain(ve, n);
+ }
+ fe = objfunc(ve, params);
+ k++;
+
+ /* by making fe < fr as opposed to fe < f[vs],
+ Rosenbrocks function takes 63 iterations as opposed
+ to 64 when using double variables. */
+
+ if(fe < fr)
+ {
+ for(j = 0; j <= n - 1; j++)
+ {
+ v[vg][j] = ve[j];
+ }
+ f[vg] = fe;
+ }
+ else
+ {
+ for(j = 0; j <= n - 1; j++)
+ {
+ v[vg][j] = vr[j];
+ }
+ f[vg] = fr;
+ }
+ }
+
+ /* check to see if a contraction is necessary */
+ if(fr >= f[vh])
+ {
+ if(fr < f[vg] && fr >= f[vh])
+ {
+ /* perform outside contraction */
+ for(j = 0; j <= n - 1; j++)
+ {
+ /*vc[j] = NMS_BETA*v[vg][j] + (1-NMS_BETA)*vm[j];*/
+ vc[j] = vm[j] + NMS_BETA * (vr[j] - vm[j]);
+ }
+ if(constrain != NULL)
+ {
+ constrain(vc, n);
+ }
+ fc = objfunc(vc, params);
+ k++;
+ }
+ else
+ {
+ /* perform inside contraction */
+ for(j = 0; j <= n - 1; j++)
+ {
+ /*vc[j] = NMS_BETA*v[vg][j] + (1-NMS_BETA)*vm[j];*/
+ vc[j] = vm[j] - NMS_BETA * (vm[j] - v[vg][j]);
+ }
+ if(constrain != NULL)
+ {
+ constrain(vc, n);
+ }
+ fc = objfunc(vc, params);
+ k++;
+ }
+
+
+ if(fc < f[vg])
+ {
+ for(j = 0; j <= n - 1; j++)
+ {
+ v[vg][j] = vc[j];
+ }
+ f[vg] = fc;
+ }
+ /* at this point the contraction is not successful,
+ we must halve the distance from vs to all the
+ vertices of the simplex and then continue.
+ 10/31/97 - modified to account for ALL vertices.
+ */
+ else
+ {
+ for(row = 0; row <= n; row++)
+ {
+ if(row != vs)
+ {
+ for(j = 0; j <= n - 1; j++)
+ {
+ v[row][j] = v[vs][j] + (v[row][j] - v[vs][j]) / 2.0;
+ }
+ }
+ }
+ if(constrain != NULL)
+ {
+ constrain(v[vg], n);
+ }
+ f[vg] = objfunc(v[vg], params);
+ k++;
+ if(constrain != NULL)
+ {
+ constrain(v[vh], n);
+ }
+ f[vh] = objfunc(v[vh], params);
+ k++;
+ }
+ }
+#if 0
+ /* print out the value at each iteration */
+ printf("Iteration %d\n", itr);
+ for(j = 0; j <= n; j++)
+ {
+ for(i = 0; i < n; i++)
+ {
+ printf("%f %f\n", v[j][i], f[j]);
+ }
+ }
+#endif
+ /* test for convergence */
+ fsum = 0.0;
+ for(j = 0; j <= n; j++)
+ {
+ fsum += f[j];
+ }
+ favg = fsum / (n + 1);
+ s = 0.0;
+ for(j = 0; j <= n; j++)
+ {
+ s += pow((f[j] - favg), 2.0) / (n);
+ }
+ s = sqrt(s);
+ if(s < EPSILON) break;
+ }
+ /* end main loop of the minimization */
+
+ /* find the index of the smallest value */
+ vs = 0;
+ for(j = 0; j <= n; j++)
+ {
+ if(f[j] < f[vs])
+ {
+ vs = j;
+ }
+ }
+#if 0
+ printf("The minimum was found at\n");
+ for(j = 0; j < n; j++)
+ {
+ printf("%e\n", v[vs][j]);
+ start[j] = v[vs][j];
+ }
+ double min = objfunc(v[vs], params);
+ k++;
+ printf("The minimum value is %f\n", min);
+ printf("%d Function Evaluations\n", k);
+ printf("%d Iterations through program\n", itr);
+#else
+ for(j = 0; j < n; j++)
+ {
+ start[j] = v[vs][j];
+ }
+#endif
+ free(f);
+ free(vr);
+ free(ve);
+ free(vc);
+ free(vm);
+ for(i = 0; i <= n; i++)
+ {
+ free(v[i]);
+ }
+ free(v);
+ return itr;
+}
+
+/*==================================================================================
+ * end of nmsimplex code
+ *==================================================================================*/
+
+// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
+// vim: shiftwidth=2 expandtab tabstop=2 cindent
+// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
diff --git a/rtengine/camconst.json b/rtengine/camconst.json
index 5fc7d4062..6e686c2f1 100644
--- a/rtengine/camconst.json
+++ b/rtengine/camconst.json
@@ -430,6 +430,7 @@ Camera constants:
{ // Quality B, some intermediate ISO samples missing, LENR samples missing so White Levels not properly indicated, some aperture scaling missing
"make_model": "Canon EOS 5D Mark IV",
+ "global_green_equilibration" : true,
"dcraw_matrix": [ 6446,-366,-864,-4436,12204,2513,-952,2496,6348 ], // DNG_V9.7 D65
"raw_crop": [ 136, 42, 6740, 4500 ], // full size 6880x4544, official crop 148,54,6867,4533
"masked_areas": [ 54, 4, 4534, 132 ],
@@ -1295,12 +1296,6 @@ Camera constants:
"ranges": { "white": 16100 }
},
- { // Quality C
- "make_model": [ "FUJIFILM X100V", "FUJIFILM X-T4" ],
- "dcraw_matrix": [ 13426,-6334,-1177,-4244,12136,2371,-580,1303,5980 ], // DNG_v12.2 D65
- "raw_crop": [ 0, 5, 6252, 4140 ]
- },
-
{ // Quality C
"make_model": "Fujifilm X10",
"ranges": { "white": 3788 }
@@ -1384,7 +1379,7 @@ Camera constants:
},
{ // Quality C, only raw crop
- "make_model": [ "FUJIFILM X-T3", "FUJIFILM X-T30", "FUJIFILM X-PRO3" ],
+ "make_model": [ "FUJIFILM X-T3", "FUJIFILM X-T30", "FUJIFILM X-PRO3", "FUJIFILM X100V", "FUJIFILM X-T4" ],
"dcraw_matrix": [ 13426,-6334,-1177,-4244,12136,2371,-580,1303,5980 ], // DNG_v11, standard_v2 d65
"raw_crop": [ 0, 5, 6252, 4176]
},
diff --git a/rtengine/ciecam02.cc b/rtengine/ciecam02.cc
index 208ed4366..c591bcb2c 100644
--- a/rtengine/ciecam02.cc
+++ b/rtengine/ciecam02.cc
@@ -22,11 +22,6 @@
#include
#include "sleef.h"
-#ifdef _DEBUG
-#include "settings.h"
-#include
-#endif
-
#undef CLIPD
#define CLIPD(a) ((a)>0.f?((a)<1.f?(a):1.f):0.f)
#define MAXR(a,b) ((a) > (b) ? (a) : (b))
@@ -420,13 +415,6 @@ void Ciecam02::initcam1float (float yb, float pilotd, float f, float la, float x
aw = achromatic_response_to_whitefloat ( xw, yw, zw, d, fl, nbb);
wh = ( 4.0f / c ) * ( aw + 4.0f ) * pow_F ( fl, 0.25f );
pfl = pow_F ( fl, 0.25f );
-#ifdef _DEBUG
-
- if (settings->verbose) {
- printf ("Source float d=%f aw=%f fl=%f wh=%f c=%f awc=%f\n", d, aw, fl, wh, c, (4.f / c) * (aw + 4.f));
- }
-
-#endif
}
void Ciecam02::initcam2float (float yb, float pilotd, float f, float la, float xw, float yw, float zw, float &n, float &d, float &nbb, float &ncb,
@@ -445,13 +433,6 @@ void Ciecam02::initcam2float (float yb, float pilotd, float f, float la, float x
nbb = ncb = 0.725f * pow_F ( 1.0f / n, 0.2f );
cz = 1.48f + sqrt ( n );
aw = achromatic_response_to_whitefloat ( xw, yw, zw, d, fl, nbb);
-#ifdef _DEBUG
-
- if (settings->verbose) {
- printf ("Viewing float d=%f aw=%f fl=%f n=%f\n", d, aw, fl, n);
- }
-
-#endif
}
void Ciecam02::xyz2jchqms_ciecam02float ( float &J, float &C, float &h, float &Q, float &M, float &s, float aw, float fl, float wh,
diff --git a/rtengine/color.cc b/rtengine/color.cc
index 89be69e9d..11668451e 100644
--- a/rtengine/color.cc
+++ b/rtengine/color.cc
@@ -25,10 +25,6 @@
#include "opthelper.h"
#include "iccstore.h"
-#ifdef _DEBUG
-#include "mytime.h"
-#endif
-
using namespace std;
namespace rtengine
@@ -45,6 +41,9 @@ LUTf Color::igammatab_srgb;
LUTf Color::igammatab_srgb1;
LUTf Color::gammatab_srgb;
LUTf Color::gammatab_srgb1;
+LUTf Color::gammatab_srgb327;
+LUTf Color::gammatab_bt709;
+LUTf Color::igammatab_bt709;
LUTf Color::denoiseGammaTab;
LUTf Color::denoiseIGammaTab;
@@ -103,20 +102,6 @@ LUTf Color::_10GY30, Color::_10GY40, Color::_10GY50, Color::_10GY60, Color::_10G
LUTf Color::_75GY30, Color::_75GY40, Color::_75GY50, Color::_75GY60, Color::_75GY70, Color::_75GY80;
LUTf Color::_5GY30, Color::_5GY40, Color::_5GY50, Color::_5GY60, Color::_5GY70, Color::_5GY80;
-#ifdef _DEBUG
-MunsellDebugInfo::MunsellDebugInfo()
-{
- reinitValues();
-}
-void MunsellDebugInfo::reinitValues()
-{
- maxdhue[0] = maxdhue[1] = maxdhue[2] = maxdhue[3] = 0.0f;
- maxdhuelum[0] = maxdhuelum[1] = maxdhuelum[2] = maxdhuelum[3] = 0.0f;
- depass = depassLum = 0;
-}
-#endif
-
-
void Color::init ()
{
@@ -130,9 +115,12 @@ void Color::init ()
gammatabThumb(maxindex, 0);
igammatab_srgb(maxindex, 0);
+ igammatab_bt709(maxindex, 0);
igammatab_srgb1(maxindex, 0);
gammatab_srgb(maxindex, 0);
+ gammatab_bt709(maxindex, 0);
gammatab_srgb1(maxindex, 0);
+ gammatab_srgb327(32768, 0);
denoiseGammaTab(maxindex, 0);
denoiseIGammaTab(maxindex, 0);
@@ -197,6 +185,18 @@ void Color::init ()
}
#ifdef _OPENMP
#pragma omp section
+#endif
+ {
+ for (int i = 0; i < 32768; i++)
+ {
+ gammatab_srgb327[i] = gamma2(i / 32767.0);
+ }
+
+ gammatab_srgb327 *= 32767.f;
+ // gamma2curve.share(gammatab_srgb, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); // shares the buffer with gammatab_srgb but has different clip flags
+ }
+#ifdef _OPENMP
+ #pragma omp section
#endif
{
for (int i = 0; i < maxindex; i++)
@@ -206,6 +206,7 @@ void Color::init ()
igammatab_srgb *= 65535.f;
}
+
#ifdef _OPENMP
#pragma omp section
#endif
@@ -282,6 +283,22 @@ void Color::init ()
break;
}
+#ifdef _OPENMP
+ #pragma omp section
+#endif
+
+ for (int i = 0; i < maxindex; i++) {
+ gammatab_bt709[i] = 65535.0 * gamma709(i / 65535.0);
+ }
+
+#ifdef _OPENMP
+ #pragma omp section
+#endif
+
+ for (int i = 0; i < maxindex; i++) {
+ igammatab_bt709[i] = 65535.0 * igamma709(i / 65535.0);
+ }
+
#ifdef _OPENMP
#pragma omp section
#endif
@@ -1421,15 +1438,7 @@ void Color::interpolateRGBColor (const float balance, const float r1, const floa
Color::Lab2Lch(a_1, b_1, c1, h1);
Lr = L1 / 327.68f; //for gamutlch
//gamut control on r1 g1 b1
-#ifndef NDEBUG
- bool neg = false;
- bool more_rgb = false;
-
- //gamut control : Lab values are in gamut
- Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f, neg, more_rgb);
-#else
Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f);
-#endif
L1 = Lr * 327.68f;
@@ -1440,14 +1449,7 @@ void Color::interpolateRGBColor (const float balance, const float r1, const floa
Lr = L2 / 327.68f; //for gamutlch
//gamut control on r2 g2 b2
-#ifndef NDEBUG
- neg = false;
- more_rgb = false;
- //gamut control : Lab values are in gamut
- Color::gamutLchonly(h2, Lr, c2, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f, neg, more_rgb);
-#else
Color::gamutLchonly(h2, Lr, c2, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f);
-#endif
L2 = Lr * 327.68f;
// interpolating Lch values
@@ -1477,15 +1479,8 @@ void Color::interpolateRGBColor (const float balance, const float r1, const floa
// here I have put gamut control with gamutlchonly on final process
Lr = L1 / 327.68f; //for gamutlch
-#ifndef NDEBUG
- neg = false;
- more_rgb = false;
- //gamut control : Lab values are in gamut
- Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f, neg, more_rgb);
-#else
//gamut control : Lab values are in gamut
Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f);
-#endif
//convert CH ==> ab
L1 = Lr * 327.68f;
@@ -2264,11 +2259,7 @@ void Color::transitred (const float HH, float const Chprov1, const float dred, c
* float correctlum : correction Hue for luminance (brigtness, contrast,...)
* MunsellDebugInfo* munsDbgInfo: (Debug target only) object to collect information.
*/
-#ifdef _DEBUG
-void Color::AllMunsellLch(bool lumaMuns, float Lprov1, float Loldd, float HH, float Chprov1, float CC, float &correctionHuechroma, float &correctlum, MunsellDebugInfo* munsDbgInfo)
-#else
void Color::AllMunsellLch(bool lumaMuns, float Lprov1, float Loldd, float HH, float Chprov1, float CC, float &correctionHuechroma, float &correctlum)
-#endif
{
bool contin1, contin2;
@@ -2292,22 +2283,6 @@ void Color::AllMunsellLch(bool lumaMuns, float Lprov1, float Loldd, float HH, fl
contin1 = contin2 = false;
correctL = false;
MunsellLch (Lprov1, HH, Chprov1, CC, correctionHue, zo, correctionHueLum, correctL); //munsell chroma correction
-#ifdef _DEBUG
- float absCorrectionHue = fabs(correctionHue);
-
- if(correctionHue != 0.0) {
- int idx = zo - 1;
- #pragma omp critical (maxdhue)
- {
- munsDbgInfo->maxdhue[idx] = MAX(munsDbgInfo->maxdhue[idx], absCorrectionHue);
- }
- }
-
- if(absCorrectionHue > 0.45)
- #pragma omp atomic
- munsDbgInfo->depass++; //verify if no bug in calculation
-
-#endif
correctionHuechroma = correctionHue; //preserve
if(lumaMuns) {
@@ -2339,46 +2314,11 @@ void Color::AllMunsellLch(bool lumaMuns, float Lprov1, float Loldd, float HH, fl
if(contin1 && contin2) {
correctlum = correctlumprov2 - correctlumprov;
}
-
-#ifdef _DEBUG
- float absCorrectLum = fabs(correctlum);
-
- if(correctlum != 0.0) {
- int idx = zo - 1;
- #pragma omp critical (maxdhuelum)
- {
- munsDbgInfo->maxdhuelum[idx] = MAX(munsDbgInfo->maxdhuelum[idx], absCorrectLum);
- }
- }
-
- if(absCorrectLum > 0.35)
- #pragma omp atomic
- munsDbgInfo->depassLum++; //verify if no bug in calculation
-
-#endif
}
}
}
}
-
}
-
-#ifdef _DEBUG
-
- if (correctlum < -0.35f) {
- correctlum = -0.35f;
- } else if(correctlum > 0.35f) {
- correctlum = 0.35f;
- }
-
- if (correctionHuechroma < -0.45f) {
- correctionHuechroma = -0.45f;
- } else if(correctionHuechroma > 0.45f) {
- correctionHuechroma = 0.45f;
- }
-
-#endif
-
}
/*
@@ -2437,17 +2377,10 @@ void Color::AllMunsellLch(float Lprov1, float HH, float Chprov1, float CC, float
* float coef : a float number between [0.95 ; 1.0[... the nearest it is from 1.0, the more precise it will be... and the longer too as more iteration will be necessary)
* bool neg and moreRGB : only in DEBUG mode to calculate iterations for negatives values and > 65535
*/
-#ifdef _DEBUG
-void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef, bool &neg, bool &more_rgb)
-#else
void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef)
-#endif
{
const float ClipLevel = 65535.0f;
bool inGamut;
-#ifdef _DEBUG
- neg = false, more_rgb = false;
-#endif
float2 sincosval = xsincosf(HH);
do {
@@ -2471,10 +2404,6 @@ void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, flo
// gamut control before saturation to put Lab values in future gamut, but not RGB
if (R < 0.0f || G < 0.0f || B < 0.0f) {
-#ifdef _DEBUG
- neg = true;
-#endif
-
if (Lprov1 < 0.1f) {
Lprov1 = 0.1f;
}
@@ -2520,10 +2449,6 @@ void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, flo
} else if (!isHLEnabled && rtengine::max(R, G, B) > ClipLevel && rtengine::min(R, G, B) <= ClipLevel) {
// if "highlight reconstruction" is enabled or the point is completely white (clipped, no color), don't control Gamut
-#ifdef _DEBUG
- more_rgb = true;
-#endif
-
if (Lprov1 > 99.999f) {
Lprov1 = 99.98f;
}
@@ -2558,17 +2483,10 @@ void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, flo
* float coef : a float number between [0.95 ; 1.0[... the nearest it is from 1.0, the more precise it will be... and the longer too as more iteration will be necessary)
* bool neg and moreRGB : only in DEBUG mode to calculate iterations for negatives values and > 65535
*/
-#ifdef _DEBUG
-void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef, bool &neg, bool &more_rgb)
-#else
void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef)
-#endif
{
constexpr float ClipLevel = 65535.0f;
bool inGamut;
-#ifdef _DEBUG
- neg = false, more_rgb = false;
-#endif
float ChprovSave = Chprov1;
do {
@@ -2590,9 +2508,6 @@ void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chpr
// gamut control before saturation to put Lab values in future gamut, but not RGB
if (R < 0.0f || G < 0.0f || B < 0.0f) {
-#ifdef _DEBUG
- neg = true;
-#endif
if (isnan(HH)) {
float atemp = ChprovSave * sincosval.y * 327.68;
@@ -2645,10 +2560,6 @@ void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chpr
} else if (!isHLEnabled && rtengine::max(R, G, B) > ClipLevel && rtengine::min(R, G, B) <= ClipLevel) {
// if "highlight reconstruction" is enabled or the point is completely white (clipped, no color), don't control Gamut
-#ifdef _DEBUG
- more_rgb = true;
-#endif
-
if (Lprov1 > 99.999f) {
Lprov1 = 99.98f;
}
@@ -2770,17 +2681,10 @@ void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chpr
}
-#ifdef _DEBUG
-void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const float wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef, bool &neg, bool &more_rgb)
-#else
void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const float wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef)
-#endif
{
const float ClipLevel = 65535.0f;
bool inGamut;
-#ifdef _DEBUG
- neg = false, more_rgb = false;
-#endif
do {
inGamut = true;
@@ -2804,10 +2708,6 @@ void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const
// gamut control before saturation to put Lab values in future gamut, but not RGB
if (R < 0.0f || G < 0.0f || B < 0.0f) {
-#ifdef _DEBUG
- neg = true;
-#endif
-
if (Lprov1 < 0.01f) {
Lprov1 = 0.01f;
}
@@ -2822,10 +2722,6 @@ void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const
} else if (!isHLEnabled && rtengine::max(R, G, B) > ClipLevel && rtengine::min(R, G, B) <= ClipLevel) {
// if "highlight reconstruction" is enabled or the point is completely white (clipped, no color), don't control Gamut
-#ifdef _DEBUG
- more_rgb = true;
-#endif
-
if (Lprov1 > 99.999f) {
Lprov1 = 99.98f;
}
@@ -2865,17 +2761,6 @@ void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const
*/
void Color::LabGamutMunsell(float *labL, float *laba, float *labb, const int N, bool corMunsell, bool lumaMuns, bool isHLEnabled, bool gamut, const double wip[3][3])
{
-#ifdef _DEBUG
- MyTime t1e, t2e;
- t1e.set();
- int negat = 0, moreRGB = 0;
- MunsellDebugInfo* MunsDebugInfo = nullptr;
-
- if (corMunsell) {
- MunsDebugInfo = new MunsellDebugInfo();
- }
-
-#endif
float correctlum = 0.f;
float correctionHuechroma = 0.f;
#ifdef __SSE2__
@@ -2914,9 +2799,6 @@ void Color::LabGamutMunsell(float *labL, float *laba, float *labb, const int N,
float2 sincosval;
if(gamut) {
-#ifdef _DEBUG
- bool neg, more_rgb;
-#endif
// According to mathematical laws we can get the sin and cos of HH by simple operations
float R, G, B;
@@ -2929,23 +2811,7 @@ void Color::LabGamutMunsell(float *labL, float *laba, float *labb, const int N,
}
//gamut control : Lab values are in gamut
-#ifdef _DEBUG
- gamutLchonly(HH, sincosval, Lprov1, Chprov1, R, G, B, wip, isHLEnabled, 0.15f, 0.96f, neg, more_rgb);
-#else
gamutLchonly(HH, sincosval, Lprov1, Chprov1, R, G, B, wip, isHLEnabled, 0.15f, 0.96f);
-#endif
-
-#ifdef _DEBUG
-
- if(neg) {
- negat++;
- }
-
- if(more_rgb) {
- moreRGB++;
- }
-
-#endif
}
labL[j] = Lprov1 * 327.68f;
@@ -2953,12 +2819,7 @@ void Color::LabGamutMunsell(float *labL, float *laba, float *labb, const int N,
correctlum = 0.f;
if(corMunsell)
-#ifdef _DEBUG
- AllMunsellLch(lumaMuns, Lprov1, Loldd, HH, Chprov1, Coldd, correctionHuechroma, correctlum, MunsDebugInfo);
-
-#else
AllMunsellLch(lumaMuns, Lprov1, Loldd, HH, Chprov1, Coldd, correctionHuechroma, correctlum);
-#endif
if(correctlum == 0.f && correctionHuechroma == 0.f) {
if(!gamut) {
@@ -2979,28 +2840,6 @@ void Color::LabGamutMunsell(float *labL, float *laba, float *labb, const int N,
laba[j] = Chprov1 * sincosval.y * 327.68f;
labb[j] = Chprov1 * sincosval.x * 327.68f;
}
-
-#ifdef _DEBUG
- t2e.set();
-
- if (settings->verbose) {
- printf("Color::LabGamutMunsell (correction performed in %d usec):\n", t2e.etime(t1e));
- printf(" Gamut : G1negat=%iiter G165535=%iiter \n", negat, moreRGB);
-
- if (MunsDebugInfo) {
- printf(" Munsell chrominance: MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad depass=%u\n", MunsDebugInfo->maxdhue[0], MunsDebugInfo->maxdhue[1], MunsDebugInfo->maxdhue[2], MunsDebugInfo->maxdhue[3], MunsDebugInfo->depass);
- printf(" Munsell luminance : MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad depass=%u\n", MunsDebugInfo->maxdhuelum[0] , MunsDebugInfo->maxdhuelum[1], MunsDebugInfo->maxdhuelum[2], MunsDebugInfo->maxdhuelum[3], MunsDebugInfo->depassLum);
- } else {
- printf(" Munsell correction wasn't requested\n");
- }
- }
-
- if (MunsDebugInfo) {
- delete MunsDebugInfo;
- }
-
-#endif
-
}
/*
@@ -3105,11 +2944,6 @@ void Color::SkinSat (float lum, float hue, float chrom, float &satreduc)
*/
void Color::initMunsell ()
{
-#ifdef _DEBUG
- MyTime t1e, t2e;
- t1e.set();
-#endif
-
const int maxInd = 140;
const int maxInd2 = 90;
const int maxInd3 = 50;
@@ -5710,14 +5544,6 @@ void Color::initMunsell ()
//printf("5GY %1.2f %1.2f %1.2f\n",_5GY80[44],_5GY80[84],_5GY80[125] );
-#ifdef _DEBUG
- t2e.set();
-
- if (settings->verbose) {
- printf("Lutf Munsell %d usec\n", t2e.etime(t1e));
- }
-
-#endif
}
}
diff --git a/rtengine/color.h b/rtengine/color.h
index 045e062ad..55ef52e36 100644
--- a/rtengine/color.h
+++ b/rtengine/color.h
@@ -36,23 +36,6 @@ namespace rtengine
typedef std::array GammaValues;
-#ifdef _DEBUG
-
-class MunsellDebugInfo
-{
-public:
- float maxdhuelum[4];
- float maxdhue[4];
- unsigned int depass;
- unsigned int depassLum;
-
- MunsellDebugInfo();
- void reinitValues();
-};
-
-#endif
-
-
class Color
{
@@ -162,12 +145,15 @@ public:
static LUTf igammatab_srgb;
static LUTf igammatab_srgb1;
static LUTf gammatab_srgb;
+ static LUTf gammatab_srgb327;
static LUTf gammatab_srgb1;
+ static LUTf gammatab_bt709;
static LUTf denoiseGammaTab;
static LUTf denoiseIGammaTab;
static LUTf igammatab_24_17;
+ static LUTf igammatab_bt709;
static LUTf gammatab_24_17a;
static LUTf gammatab_13_2;
static LUTf igammatab_13_2;
@@ -1150,23 +1136,25 @@ public:
}
- /*
+/*
* @brief Get the gamma value for Gamma=2.2 Slope=4.5
* @param x red, green or blue channel's value [0 ; 1]
* @return the gamma modified's value [0 ; 1]
*
+*/
static inline double gamma709 (double x) {
return x <= 0.0176 ? x*4.5 : 1.0954*exp(log(x)/2.2)-0.0954;
}
-
+/*
* @brief Get the inverse gamma value for Gamma=2.2 Slope=4.5
* @param x red, green or blue channel's value [0 ; 1]
* @return the inverse gamma modified's value [0 ; 1]
*
+*/
static inline double igamma709 (double x) {
return x <= 0.0795 ? x/4.5 : exp(log((x+0.0954)/1.0954)*2.2);
}
- */
+
@@ -1390,11 +1378,7 @@ public:
* @param munsDbgInfo (Debug target only) object to collect information
*/
-#ifdef _DEBUG
- static void AllMunsellLch (bool lumaMuns, float Lprov1, float Loldd, float HH, float Chprov1, float CC, float &correctionHueChroma, float &correctlum, MunsellDebugInfo* munsDbgInfo);
-#else
static void AllMunsellLch (bool lumaMuns, float Lprov1, float Loldd, float HH, float Chprov1, float CC, float &correctionHueChroma, float &correctlum);
-#endif
static void AllMunsellLch (float Lprov1, float HH, float Chprov1, float CC, float &correctionHueChroma);
@@ -1419,15 +1403,9 @@ public:
* @param neg (Debug target only) to calculate iterations for negatives values
* @param moreRGB (Debug target only) to calculate iterations for values >65535
*/
-#ifdef _DEBUG
- static void gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef, bool &neg, bool &more_rgb);
- static void gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef, bool &neg, bool &more_rgb);
- static void gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const float wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef, bool &neg, bool &more_rgb);
-#else
static void gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef);
static void gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef);
static void gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const float wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef);
-#endif
static void gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chprov1, float &saturation, const float wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef);
@@ -1494,6 +1472,57 @@ public:
static void skinredfloat ( float J, float h, float sres, float Sp, float dred, float protect_red, int sk, float rstprotection, float ko, float &s);
// static void scaleredcdbl ( float skinprot, float param, float limit, float HH, float deltaHH, float &scale,float &scaleext);
+ static inline void pregamutlab(float lum, float hue, float &chr) //big approximation to limit gamut (Prophoto) before good gamut procedure for locallab chroma, to avoid crash
+ {
+ if (lum >= 95.0f) {
+ if (hue > 1.5f && hue < 2.f) {
+ chr = 120.f;
+ } else if (hue > 0.7f && hue <= 1.5f) {
+ chr = 60.f;
+ } else {
+ chr = 40.f;
+ }
+ } else if (lum > 75.f) {
+ if (hue > 1.f && hue < 3.14f) {
+ chr = 130.f;
+ } else if (hue > -0.4f && hue <= 1.f) {
+ chr = 80.f;
+ } else if (hue > -3.15f && hue < -2.f) {
+ chr = 80.f;
+ } else {
+ chr = 60.f;
+ }
+
+ } else if (lum > 35.f) {
+ chr = 100.f;
+ } else if (lum > 20.f) {
+ if (hue < -1.f && hue > -2.f) {
+ chr = 120.f;
+ } else {
+ chr = 80.f;
+ }
+ } else if (lum > 7.f) {
+ if (hue < -1.f && hue > -1.8f) {
+ chr = 120.f;
+ } else {
+ chr = 60.f;
+ }
+
+ } else {
+ if (hue < -1.f && hue > -1.6f) {
+ chr = 80.f;
+ } else {
+ chr = 40.f;
+ }
+
+ }
+
+ // if(lum < 4.f) {
+ // chr = 0.1f;
+ // }
+ }
+
+
static inline void SkinSatCbdl (float lum, float hue, float chrom, float skinprot, float &scale, bool neg, float b_l, float t_l, float t_r)
{
diff --git a/rtengine/colortemp.cc b/rtengine/colortemp.cc
index 2780fcc84..417476876 100644
--- a/rtengine/colortemp.cc
+++ b/rtengine/colortemp.cc
@@ -2898,15 +2898,15 @@ void ColorTemp::icieCAT02float(float Xw, float Yw, float Zw, float &iCAM02BB00,
float D = adap / 2.;
//white destination Wd : RT use always D50
- cam_dest[0] = INVCAT02[0][0] * whiteD50p[0] + INVCAT02[0][1] * whiteD50p[1] + INVCAT02[0][2] * whiteD50p[2]; //Cone reponse RoD
+ cam_dest[0] = INVCAT02[0][0] * whiteD50p[0] + INVCAT02[0][1] * whiteD50p[1] + INVCAT02[0][2] * whiteD50p[2]; //Cone response RoD
cam_dest[1] = INVCAT02[1][0] * whiteD50p[0] + INVCAT02[1][1] * whiteD50p[1] + INVCAT02[1][2] * whiteD50p[2]; //GaD
cam_dest[2] = INVCAT02[2][0] * whiteD50p[0] + INVCAT02[2][1] * whiteD50p[1] + INVCAT02[2][2] * whiteD50p[2]; //BeD
//origin white Ws : A, D65, custom, etc.
- cam_orig[0] = INVCAT02[0][0] * Xw + INVCAT02[0][1] * Yw + INVCAT02[0][2] * Zw; //Cone reponse RoS
+ cam_orig[0] = INVCAT02[0][0] * Xw + INVCAT02[0][1] * Yw + INVCAT02[0][2] * Zw; //Cone response RoS
cam_orig[1] = INVCAT02[1][0] * Xw + INVCAT02[1][1] * Yw + INVCAT02[1][2] * Zw;
cam_orig[2] = INVCAT02[2][0] * Xw + INVCAT02[2][1] * Yw + INVCAT02[2][2] * Zw;
-// cam_orig[0] = CAT02[0][0] * Xw + CAT02[0][1] * Yw + CAT02[0][2] * Zw; //Cone reponse RoS
+// cam_orig[0] = CAT02[0][0] * Xw + CAT02[0][1] * Yw + CAT02[0][2] * Zw; //Cone response RoS
// cam_orig[1] = CAT02[1][0] * Xw + CAT02[1][1] * Yw + CAT02[1][2] * Zw;
// cam_orig[2] = CAT02[2][0] * Xw + CAT02[2][1] * Yw + CAT02[2][2] * Zw;
@@ -3624,7 +3624,7 @@ void ColorTemp::tempxy(bool separated, int repref, float **Tx, float **Ty, float
double XX;
double ZZ;
} WbTxyz;
- //probbaly can be "passed" with rawimagesource.cc but I don't know how to do.
+ //probably can be "passed" with rawimagesource.cc but I don't know how to do this.
constexpr WbTxyz Txyz[118] = {//temperature Xwb Zwb 118 values - same table as in Rawimagesource.cc x wb and y wb are calculated after
{2001., 1.273842, 0.145295},
{2101., 1.244008, 0.167533},
diff --git a/rtengine/cplx_wavelet_dec.h b/rtengine/cplx_wavelet_dec.h
index c127a7adf..592bd2f37 100644
--- a/rtengine/cplx_wavelet_dec.h
+++ b/rtengine/cplx_wavelet_dec.h
@@ -33,37 +33,38 @@ class wavelet_decomposition :
public NonCopyable
{
public:
-
- typedef float internal_type;
- float *coeff0;
- bool memoryAllocationFailed;
-
-private:
-
- static const int maxlevels = 10;//should be greater than any conceivable order of decimation
-
- int lvltot, subsamp;
- int m_w, m_h;//dimensions
-
- int wavfilt_len, wavfilt_offset;
- float *wavfilt_anal;
- float *wavfilt_synth;
-
-
- wavelet_level * wavelet_decomp[maxlevels];
-
-public:
+ using internal_type = float;
template
wavelet_decomposition(E * src, int width, int height, int maxlvl, int subsampling, int skipcrop = 1, int numThreads = 1, int Daub4Len = 6);
~wavelet_decomposition();
- internal_type ** level_coeffs(int level) const
+ bool memory_allocation_failed() const
+ {
+ return memoryAllocationFailed;
+ }
+
+ const internal_type* const* level_coeffs(int level) const
{
return wavelet_decomp[level]->subbands();
}
+ internal_type* const* level_coeffs(int level)
+ {
+ return wavelet_decomp[level]->subbands();
+ }
+
+ const internal_type* get_coeff0() const
+ {
+ return coeff0;
+ }
+
+ internal_type* get_coeff0()
+ {
+ return coeff0;
+ }
+
int level_W(int level) const
{
return wavelet_decomp[level]->width();
@@ -88,13 +89,47 @@ public:
{
return subsamp;
}
+
template
void reconstruct(E * dst, const float blend = 1.f);
+
+private:
+ static const int maxlevels = 10; // should be greater than any conceivable order of decimation
+
+ int lvltot;
+ int subsamp;
+ // Dimensions
+ int m_w;
+ int m_h;
+
+ int wavfilt_len;
+ int wavfilt_offset;
+ internal_type* wavfilt_anal;
+ internal_type* wavfilt_synth;
+
+ internal_type* coeff0;
+ bool memoryAllocationFailed;
+
+ wavelet_level* wavelet_decomp[maxlevels];
};
template
-wavelet_decomposition::wavelet_decomposition(E * src, int width, int height, int maxlvl, int subsampling, int skipcrop, int numThreads, int Daub4Len)
- : coeff0(nullptr), memoryAllocationFailed(false), lvltot(0), subsamp(subsampling), m_w(width), m_h(height)
+wavelet_decomposition::wavelet_decomposition(
+ E * src,
+ int width,
+ int height,
+ int maxlvl,
+ int subsampling,
+ int skipcrop,
+ int numThreads,
+ int Daub4Len
+) :
+ lvltot(0),
+ subsamp(subsampling),
+ m_w(width),
+ m_h(height),
+ coeff0(nullptr),
+ memoryAllocationFailed(false)
{
//initialize wavelet filters
@@ -135,6 +170,14 @@ wavelet_decomposition::wavelet_decomposition(E * src, int width, int height, int
//n=0 lopass, n=1 hipass
}
}
+/* } else if(wavfilt_len == 22) {
+ for (int n = 0; n < 2; n++) {
+ for (int i = 0; i < wavfilt_len; i++) {
+ wavfilt_anal[wavfilt_len * (n) + i] = Daub4_anal22[n][i];
+ wavfilt_synth[wavfilt_len * (n) + i] = Daub4_anal22[n][wavfilt_len - 1 - i];
+ //n=0 lopass, n=1 hipass
+ }
+ } */
} else if(wavfilt_len == 4) {
for (int n = 0; n < 2; n++) {
for (int i = 0; i < wavfilt_len; i++) {
@@ -144,7 +187,7 @@ wavelet_decomposition::wavelet_decomposition(E * src, int width, int height, int
}
}
}
-
+//printf("OK cplx\n");
// after coefficient rotation, data structure is:
// wavelet_decomp[scale][channel={lo,hi1,hi2,hi3}][pixel_array]
diff --git a/rtengine/cplx_wavelet_filter_coeffs.h b/rtengine/cplx_wavelet_filter_coeffs.h
index 5d191e50c..3386be8d0 100644
--- a/rtengine/cplx_wavelet_filter_coeffs.h
+++ b/rtengine/cplx_wavelet_filter_coeffs.h
@@ -48,11 +48,14 @@ const float Daub4_anal16[2][16] ALIGNED16 = {//Daub 14
{0.f, 0.f, 0.055049715f, 0.28039564f, 0.515574245f, 0.33218624f, -0.10175691f, -0.158417505f, 0.05042335f, 0.057001725f, -0.026891225f, -0.01171997f, 0.008874895f, 0.0003037575f, -0.0012739524f, 0.0002501134f},
{ -0.0002501134f, -0.0012739524f, -0.0003037575f, 0.008874895f, 0.01171997f , -0.026891225f, -0.057001725f, 0.05042335f, 0.158417505f, -0.10175691f, -0.33218624f, 0.515574245f, -0.28039564f, 0.055049715f, 0.f, 0.f}
};
-/*
-const double Daub4_anal22[2][22] ALIGNED16 = {//Daub 20
- {0., 0., 0.01885858, 0.13306109, 0.37278754, 0.48681406, 0.19881887, -0.1766681, -0.13855494, 0.09006372, 0.0658015, -0.05048328, -0.02082962, 0.0234849, 0.0025502185, -0.0075895, 0.0009866625, 0.0014088433, -0.00048497392, -0.0000823545, 0.00006617718, -0.000009379205},
- {0.000009379205, -0.00006617718, 0.0000823545, 0.00048497392, -0.0014088433, -0.0009866627, 0.0075895, -0.0025502185, -0.0234849, 0.02082962, 0.05048328, -0.0658015, -0.09006372, 0.13855494, 0.1766681, -0.19881887, -0.48681406, -0.37278754, -0.13306109, -0.01885858, 0., 0.}
+
+const float Daub4_anal22[2][22] ALIGNED16 = {//Daub 20
+ {0.f, 0.f, 0.01885858f, 0.13306109f, 0.37278535f, 0.48681406f, 0.19881887f, -0.1766681f, -0.13855494f, 0.09006372f, 0.0658015f, -0.05048328f, -0.02082962f,
+ 0.0234849f, 0.002550218f, -0.0075895f, 0.0009866627f, 0.001408843f, -0.000484973f, -0.0000823545f, 0.0000661271f, -0.00000939f},
+ {0.00000939f, -0.0000661271f, 0.0000823545f, 0.000484973f, -0.001408843f, -0.0009866627f, 0.0075895f, -0.002550218f, -0.0234849f,
+ 0.02082962f, 0.05048328f, -0.0658015f, -0.09006372f, 0.13855494f, 0.1766681f, -0.19881887f, -0.48681406f, -0.37278535f, -0.13306109f, -0.01885858f, 0.f, 0.f}
};
-*/
+
+// if necessary ?? we can add D20 !!
}
diff --git a/rtengine/curves.cc b/rtengine/curves.cc
index ebb23e754..7f74467be 100644
--- a/rtengine/curves.cc
+++ b/rtengine/curves.cc
@@ -39,7 +39,6 @@ using namespace std;
namespace rtengine
{
-
bool sanitizeCurve(std::vector& curve)
{
// A curve is valid under one of the following conditions:
@@ -47,19 +46,19 @@ bool sanitizeCurve(std::vector& curve)
// 2) Number of curve entries is > 3 and odd
// 3) curve[0] == DCT_Parametric and curve size is >= 8 and curve[1] .. curve[3] are ordered ascending and are distinct
if (curve.empty()) {
- curve.push_back (DCT_Linear);
+ curve.push_back(DCT_Linear);
return true;
- } else if(curve.size() == 1 && curve[0] != DCT_Linear) {
+ } else if (curve.size() == 1 && curve[0] != DCT_Linear) {
curve[0] = DCT_Linear;
return true;
- } else if((curve.size() % 2 == 0 || curve.size() < 5) && curve[0] != DCT_Parametric) {
+ } else if ((curve.size() % 2 == 0 || curve.size() < 5) && curve[0] != DCT_Parametric) {
curve.clear();
- curve.push_back (DCT_Linear);
+ curve.push_back(DCT_Linear);
return true;
- } else if(curve[0] == DCT_Parametric) {
+ } else if (curve[0] == DCT_Parametric) {
if (curve.size() < 8) {
curve.clear();
- curve.push_back (DCT_Linear);
+ curve.push_back(DCT_Linear);
return true;
} else {
// curve[1] to curve[3] must be ordered ascending and distinct
@@ -73,12 +72,13 @@ bool sanitizeCurve(std::vector& curve)
}
}
}
+
return false;
}
-Curve::Curve () : N(0), ppn(0), x(nullptr), y(nullptr), mc(0.0), mfc(0.0), msc(0.0), mhc(0.0), hashSize(1000 /* has to be initialized to the maximum value */), ypp(nullptr), x1(0.0), y1(0.0), x2(0.0), y2(0.0), x3(0.0), y3(0.0), firstPointIncluded(false), increment(0.0), nbr_points(0) {}
+Curve::Curve() : N(0), ppn(0), x(nullptr), y(nullptr), mc(0.0), mfc(0.0), msc(0.0), mhc(0.0), hashSize(1000 /* has to be initialized to the maximum value */), ypp(nullptr), x1(0.0), y1(0.0), x2(0.0), y2(0.0), x3(0.0), y3(0.0), firstPointIncluded(false), increment(0.0), nbr_points(0) {}
-void Curve::AddPolygons ()
+void Curve::AddPolygons()
{
if (firstPointIncluded) {
poly_x.push_back(x1);
@@ -93,8 +93,8 @@ void Curve::AddPolygons ()
double tr2t = tr * 2 * t;
// adding a point to the polyline
- poly_x.push_back( tr2 * x1 + tr2t * x2 + t2 * x3);
- poly_y.push_back( tr2 * y1 + tr2t * y2 + t2 * y3);
+ poly_x.push_back(tr2 * x1 + tr2t * x2 + t2 * x3);
+ poly_y.push_back(tr2 * y1 + tr2t * y2 + t2 * y3);
}
// adding the last point of the sub-curve
@@ -102,11 +102,11 @@ void Curve::AddPolygons ()
poly_y.push_back(y3);
}
-void Curve::fillDyByDx ()
+void Curve::fillDyByDx()
{
dyByDx.resize(poly_x.size() - 1);
- for(unsigned int i = 0; i < poly_x.size() - 1; i++) {
+ for (unsigned int i = 0; i < poly_x.size() - 1; i++) {
double dx = poly_x[i + 1] - poly_x[i];
double dy = poly_y[i + 1] - poly_y[i];
dyByDx[i] = dy / dx;
@@ -123,7 +123,7 @@ void Curve::fillHash()
double milestone = 0.;
for (unsigned short i = 0; i < (hashSize + 1);) {
- while(poly_x[polyIter] <= milestone) {
+ while (poly_x[polyIter] <= milestone) {
++polyIter;
}
@@ -136,7 +136,7 @@ void Curve::fillHash()
polyIter = 0;
for (unsigned int i = 0; i < hashSize + 1u;) {
- while(poly_x[polyIter] < (milestone + increment)) {
+ while (poly_x[polyIter] < (milestone + increment)) {
++polyIter;
}
@@ -173,7 +173,7 @@ void Curve::fillHash()
* This method return the number of control points of a curve. Not suitable for parametric curves.
* @return number of control points of the curve. 0 will be sent back for Parametric curves
*/
-int Curve::getSize () const
+int Curve::getSize() const
{
return N;
}
@@ -204,11 +204,11 @@ void fillCurveArray(DiagonalCurve* diagCurve, LUTf &outCurve, int skip, bool nee
{
if (needed) {
- for (int i = 0; i <= 0xffff; i += i < 0xffff - skip ? skip : 1 ) {
+ for (int i = 0; i <= 0xffff; i += i < 0xffff - skip ? skip : 1) {
// change to [0,1] range
float val = (float)i / 65535.f;
// apply custom/parametric/NURBS curve, if any
- val = diagCurve->getVal (val);
+ val = diagCurve->getVal(val);
// store result in a temporary array
outCurve[i] = val;
}
@@ -218,8 +218,8 @@ void fillCurveArray(DiagonalCurve* diagCurve, LUTf &outCurve, int skip, bool nee
float skipmul = 1.f / (float) skip;
for (int i = 0; i <= 0x10000 - skip; i += skip) {
- for(int j = 1; j < skip; j++) {
- outCurve[i + j] = ( outCurve[i] * (skip - j) + outCurve[i + skip] * j ) * skipmul;
+ for (int j = 1; j < skip; j++) {
+ outCurve[i + j] = (outCurve[i] * (skip - j) + outCurve[i + skip] * j) * skipmul;
}
}
}
@@ -230,7 +230,7 @@ void fillCurveArray(DiagonalCurve* diagCurve, LUTf &outCurve, int skip, bool nee
}
}
-void CurveFactory::curveLightBrightColor (const std::vector& curvePoints1, const std::vector& curvePoints2, const std::vector& curvePoints3,
+void CurveFactory::curveLightBrightColor(const std::vector& curvePoints1, const std::vector& curvePoints2, const std::vector& curvePoints3,
const LUTu & histogram, LUTu & outBeforeCCurveHistogram,//for Luminance
const LUTu & histogramC, LUTu & outBeforeCCurveHistogramC,//for chroma
ColorAppearance & customColCurve1, ColorAppearance & customColCurve2, ColorAppearance & customColCurve3, int skip)
@@ -289,9 +289,9 @@ void CurveFactory::curveLightBrightColor (const std::vector& curvePoints
}
}
-void CurveFactory::curveBW ( const std::vector& curvePointsbw, const std::vector& curvePointsbw2,
- const LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw,//for Luminance
- ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip)
+void CurveFactory::curveBW(const std::vector& curvePointsbw, const std::vector& curvePointsbw2,
+ const LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw,//for Luminance
+ ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip)
{
const float gamma_ = Color::sRGBGammaCurve;
@@ -319,7 +319,7 @@ void CurveFactory::curveBW ( const std::vector& curvePointsbw, const std
if (!curvePointsbw.empty() && curvePointsbw[0] > DCT_Linear && curvePointsbw[0] < DCT_Unchanged) {
DiagonalCurve tcurve(curvePointsbw, CURVES_MIN_POLY_POINTS / skip);
- if (outBeforeCCurveHistogrambw ) {
+ if (outBeforeCCurveHistogrambw) {
histNeeded = true;
}
@@ -336,13 +336,13 @@ void CurveFactory::curveBW ( const std::vector& curvePointsbw, const std
}
// add curve Lab : C=f(L)
-void CurveFactory::curveCL ( bool & clcutili, const std::vector& clcurvePoints, LUTf & clCurve, int skip)
+void CurveFactory::curveCL(bool & clcutili, const std::vector& clcurvePoints, LUTf & clCurve, int skip)
{
clcutili = false;
std::unique_ptr dCurve;
if (!clcurvePoints.empty() && clcurvePoints[0] != 0) {
- dCurve = std::unique_ptr(new DiagonalCurve(clcurvePoints, CURVES_MIN_POLY_POINTS / skip));
+ dCurve = std::unique_ptr (new DiagonalCurve(clcurvePoints, CURVES_MIN_POLY_POINTS / skip));
if (dCurve && !dCurve->isIdentity()) {
clcutili = true;
@@ -352,7 +352,7 @@ void CurveFactory::curveCL ( bool & clcutili, const std::vector& clcurve
fillCurveArray(dCurve.get(), clCurve, skip, clcutili);
}
-void CurveFactory::mapcurve ( bool & mapcontlutili, const std::vector& mapcurvePoints, LUTf & mapcurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram)
+void CurveFactory::mapcurve(bool & mapcontlutili, const std::vector& mapcurvePoints, LUTf & mapcurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram)
{
bool needed = false;
std::unique_ptr dCurve;
@@ -360,7 +360,7 @@ void CurveFactory::mapcurve ( bool & mapcontlutili, const std::vector& m
bool histNeeded = false;
if (!mapcurvePoints.empty() && mapcurvePoints[0] != 0) {
- dCurve = std::unique_ptr(new DiagonalCurve(mapcurvePoints, CURVES_MIN_POLY_POINTS / skip));
+ dCurve = std::unique_ptr (new DiagonalCurve(mapcurvePoints, CURVES_MIN_POLY_POINTS / skip));
if (outBeforeCurveHistogram) {
histNeeded = true;
@@ -379,7 +379,7 @@ void CurveFactory::mapcurve ( bool & mapcontlutili, const std::vector& m
fillCurveArray(dCurve.get(), mapcurve, skip, needed);
}
-void CurveFactory::curveDehaContL ( bool & dehacontlutili, const std::vector& dehaclcurvePoints, LUTf & dehaclCurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram)
+void CurveFactory::curveDehaContL(bool & dehacontlutili, const std::vector& dehaclcurvePoints, LUTf & dehaclCurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram)
{
bool needed = false;
std::unique_ptr dCurve;
@@ -387,7 +387,7 @@ void CurveFactory::curveDehaContL ( bool & dehacontlutili, const std::vector(new DiagonalCurve(dehaclcurvePoints, CURVES_MIN_POLY_POINTS / skip));
+ dCurve = std::unique_ptr (new DiagonalCurve(dehaclcurvePoints, CURVES_MIN_POLY_POINTS / skip));
if (outBeforeCurveHistogram) {
histNeeded = true;
@@ -407,13 +407,13 @@ void CurveFactory::curveDehaContL ( bool & dehacontlutili, const std::vector& wavclcurvePoints, LUTf & wavclCurve, /*LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,*/int skip)
+void CurveFactory::curveWavContL(bool & wavcontlutili, const std::vector& wavclcurvePoints, LUTf & wavclCurve, /*LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,*/int skip)
{
bool needed = false;
std::unique_ptr dCurve;
if (!wavclcurvePoints.empty() && wavclcurvePoints[0] != 0) {
- dCurve = std::unique_ptr(new DiagonalCurve(wavclcurvePoints, CURVES_MIN_POLY_POINTS / skip));
+ dCurve = std::unique_ptr (new DiagonalCurve(wavclcurvePoints, CURVES_MIN_POLY_POINTS / skip));
if (dCurve && !dCurve->isIdentity()) {
needed = true;
@@ -425,13 +425,13 @@ void CurveFactory::curveWavContL ( bool & wavcontlutili, const std::vector& curvePoints, LUTf & ToningCurve, int skip)
+void CurveFactory::curveToning(const std::vector& curvePoints, LUTf & ToningCurve, int skip)
{
bool needed = false;
std::unique_ptr dCurve;
if (!curvePoints.empty() && curvePoints[0] != 0) {
- dCurve = std::unique_ptr(new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip));
+ dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip));
if (dCurve && !dCurve->isIdentity()) {
needed = true;
@@ -441,12 +441,294 @@ void CurveFactory::curveToning ( const std::vector& curvePoints, LUTf &
fillCurveArray(dCurve.get(), ToningCurve, skip, needed);
}
+void CurveFactory::curveLocal(bool & locallutili, const std::vector& curvePoints, LUTf & LocalLCurve, int skip)
+{
+ bool needed = false;
+ std::unique_ptr dCurve;
+
+ if (!curvePoints.empty() && curvePoints[0] != 0) {
+ dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip));
+
+ if (dCurve && !dCurve->isIdentity()) {
+ needed = true;
+ locallutili = true;
+ }
+ }
+
+ fillCurveArray(dCurve.get(), LocalLCurve, skip, needed);
+ //LocalLCurve.dump("wav");
+
+}
+
+void CurveFactory::curveCCLocal(bool & localcutili, const std::vector& curvePoints, LUTf & LocalCCurve, int skip)
+{
+ bool needed = false;
+ std::unique_ptr dCurve;
+
+ if (!curvePoints.empty() && curvePoints[0] != 0) {
+ dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip));
+
+ if (dCurve && !dCurve->isIdentity()) {
+ needed = true;
+ localcutili = true;
+ }
+ }
+
+ fillCurveArray(dCurve.get(), LocalCCurve, skip, needed);
+ //LocalLCurve.dump("wav");
+
+}
+
+void CurveFactory::curveskLocal(bool & localskutili, const std::vector& curvePoints, LUTf & LocalskCurve, int skip)
+{
+ bool needed = false;
+ std::unique_ptr dCurve;
+
+// if (localskutili && !curvePoints.empty() && curvePoints[0] != 0) {
+ if (!curvePoints.empty() && curvePoints[0] != 0) {
+ dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip));
+
+ if (dCurve && !dCurve->isIdentity()) {
+ needed = true;
+ localskutili = true;
+ }
+ }
+
+ fillCurveArray(dCurve.get(), LocalskCurve, skip, needed);
+
+}
+
+void CurveFactory::curveexLocal(bool & localexutili, const std::vector& curvePoints, LUTf & LocalexCurve, int skip)
+{
+ bool needed = false;
+ std::unique_ptr dCurve;
+
+// if (localexutili && !curvePoints.empty() && curvePoints[0] != 0) {
+ if (!curvePoints.empty() && curvePoints[0] != 0) {
+ dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip));
+
+ if (dCurve && !dCurve->isIdentity()) {
+ needed = true;
+ localexutili = true;
+ }
+ }
+
+ fillCurveArray(dCurve.get(), LocalexCurve, skip, needed);
+
+}
+
+void CurveFactory::curvemaskLocal(bool & localmaskutili, const std::vector& curvePoints, LUTf & LocalmaskCurve, int skip)
+{
+ bool needed = false;
+ std::unique_ptr dCurve;
+
+// if (localexutili && !curvePoints.empty() && curvePoints[0] != 0) {
+ if (!curvePoints.empty() && curvePoints[0] != 0) {
+ dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip));
+
+ if (dCurve && !dCurve->isIdentity()) {
+ needed = true;
+ localmaskutili = true;
+ }
+ }
+
+ fillCurveArray(dCurve.get(), LocalmaskCurve, skip, needed);
+
+}
+
+
+
+void CurveFactory::localLCurve(double br, double contr, /*const std::vector& curvePoints,*/
+ LUTu & histogram, LUTf & outCurve,
+ int skip, bool & utili)
+{
+
+ // curve without contrast
+ LUTf dcurve(65536, 0);
+
+ // clear array that stores histogram valid before applying the custom curve
+
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ // tone curve base. a: slope (from exp.comp.), b: black, def_mul: max. x value (can be>1), hr,sr: highlight,shadow recovery
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ // check if brightness curve is needed
+ if (br > 0.00001 || br < -0.00001) {
+ utili = true;
+
+ std::vector brightcurvePoints;
+ brightcurvePoints.resize(9);
+ brightcurvePoints.at(0) = double (DCT_NURBS);
+
+ brightcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
+ brightcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range
+
+ if (br > 0) {
+ brightcurvePoints.at(3) = 0.1; // toe point
+ brightcurvePoints.at(4) = 0.1 + br / 150.0; //value at toe point
+
+ brightcurvePoints.at(5) = 0.7; // shoulder point
+ brightcurvePoints.at(6) = min(1.0, 0.7 + br / 300.0); //value at shoulder point
+ } else {
+ brightcurvePoints.at(3) = 0.1 - br / 150.0; // toe point
+ brightcurvePoints.at(4) = 0.1; // value at toe point
+
+ brightcurvePoints.at(5) = min(1.0, 0.7 - br / 300.0); // shoulder point
+ brightcurvePoints.at(6) = 0.7; // value at shoulder point
+ }
+
+ brightcurvePoints.at(7) = 1.; // white point
+ brightcurvePoints.at(8) = 1.; // value at white point
+
+ DiagonalCurve* brightcurve = new DiagonalCurve(brightcurvePoints, CURVES_MIN_POLY_POINTS / skip);
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ // Applying brightness curve
+ for (int i = 0; i < 32768; i++) { // L values range up to 32767, higher values are for highlight overflow
+
+ // change to [0,1] range
+ float val = (float)i / 32767.0;
+
+ // apply brightness curve
+ val = brightcurve->getVal(val);
+
+ // store result in a temporary array
+ dcurve[i] = val;
+ }
+
+ delete brightcurve;
+ } else {
+ for (int i = 0; i < 32768; i++) { // L values range up to 32767, higher values are for highlight overflow
+ // set the identity curve in the temporary array
+ dcurve[i] = (float)i / 32767.0;
+ }
+ }
+
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ // check if contrast curve is needed
+ if (contr > 0.00001 || contr < -0.00001) {
+ utili = true;
+
+ DiagonalCurve* contrastcurve = NULL;
+
+ // compute mean luminance of the image with the curve applied
+ int sum = 0;
+ float avg = 0;
+
+ //float sqavg = 0;
+ for (int i = 0; i < 32768; i++) {
+ avg += dcurve[i] * histogram[i];
+ //sqavg += dcurve[i]*dcurve[i] * histogram[i];
+ sum += histogram[i];
+ }
+
+ if (sum) {
+ avg /= sum;
+ //sqavg /= sum;
+ //float stddev = sqrt(sqavg-avg*avg);
+ // printf("avg=%f\n",avg);
+
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ std::vector contrastcurvePoints;
+ contrastcurvePoints.resize(9);
+ contrastcurvePoints.at(0) = double (DCT_NURBS);
+
+ contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
+ contrastcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range
+
+ contrastcurvePoints.at(3) = avg - avg * (0.6 - contr / 250.0); // toe point
+ contrastcurvePoints.at(4) = avg - avg * (0.6 + contr / 250.0); // value at toe point
+
+ contrastcurvePoints.at(5) = avg + (1 - avg) * (0.6 - contr / 250.0); // shoulder point
+ contrastcurvePoints.at(6) = avg + (1 - avg) * (0.6 + contr / 250.0); // value at shoulder point
+
+ contrastcurvePoints.at(7) = 1.; // white point
+ contrastcurvePoints.at(8) = 1.; // value at white point
+
+ contrastcurve = new DiagonalCurve(contrastcurvePoints, CURVES_MIN_POLY_POINTS / skip);
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ } else {
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ // sum has an invalid value (next to 0, producing a division by zero, so we create a fake contrast curve, producing a white image
+ std::vector contrastcurvePoints;
+ contrastcurvePoints.resize(5);
+ contrastcurvePoints.at(0) = double (DCT_NURBS);
+
+ contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
+ contrastcurvePoints.at(2) = 1.; // black point. Value in [0 ; 1] range
+
+ contrastcurvePoints.at(3) = 1.; // white point
+ contrastcurvePoints.at(4) = 1.; // value at white point
+
+ contrastcurve = new DiagonalCurve(contrastcurvePoints, CURVES_MIN_POLY_POINTS / skip);
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ }
+
+ // apply contrast enhancement
+ for (int i = 0; i < 32768; i++) {
+ dcurve[i] = contrastcurve->getVal(dcurve[i]);
+ }
+
+ delete contrastcurve;
+ }
+
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ // create a curve if needed
+ /* DiagonalCurve* tcurve = NULL;
+ bool histNeeded = false;
+ if (!curvePoints.empty() && curvePoints[0]!=0) {
+ tcurve = new DiagonalCurve (curvePoints, CURVES_MIN_POLY_POINTS/skip);
+ }
+ if (tcurve && tcurve->isIdentity()) {
+ delete tcurve;
+ tcurve = NULL;
+ }
+
+ if (tcurve) {
+ utili=true;//if active
+
+ // L values go up to 32767, last stop is for highlight overflow
+ for (int i=0; i<32768; i++) {
+ float val;
+ // apply custom/parametric/NURBS curve, if any
+ val = tcurve->getVal (dcurve[i]);
+
+ outCurve[i] = (32767.0 * val);
+ }
+ }
+ else
+ */
+ {
+ // Skip the slow getval method if no curve is used (or an identity curve)
+ // L values go up to 32767, last stop is for highlight overflow
+ for (int i = 0; i < 32768; i++) {
+ outCurve[i] = 32767.0 * dcurve[i];
+ }
+ }
+
+ for (int i = 32768; i < 65536; i++) {
+ outCurve[i] = (float)i;
+ }
+
+ // if (tcurve)
+ // delete tcurve;
+
+}
+
+
+
+
+
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-void CurveFactory::complexsgnCurve (bool & autili, bool & butili, bool & ccutili, bool & cclutili,
- const std::vector& acurvePoints, const std::vector& bcurvePoints, const std::vector& cccurvePoints,
- const std::vector& lccurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve,
- int skip)
+void CurveFactory::complexsgnCurve(bool & autili, bool & butili, bool & ccutili, bool & cclutili,
+ const std::vector& acurvePoints, const std::vector& bcurvePoints, const std::vector& cccurvePoints,
+ const std::vector& lccurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve,
+ int skip)
{
autili = butili = ccutili = cclutili = false;
@@ -454,7 +736,7 @@ void CurveFactory::complexsgnCurve (bool & autili, bool & butili, bool & ccutil
// create a curve if needed
if (!acurvePoints.empty() && acurvePoints[0] != 0) {
- dCurve = std::unique_ptr(new DiagonalCurve(acurvePoints, CURVES_MIN_POLY_POINTS / skip));
+ dCurve = std::unique_ptr (new DiagonalCurve(acurvePoints, CURVES_MIN_POLY_POINTS / skip));
if (dCurve && !dCurve->isIdentity()) {
autili = true;
@@ -468,7 +750,7 @@ void CurveFactory::complexsgnCurve (bool & autili, bool & butili, bool & ccutil
//-----------------------------------------------------
if (!bcurvePoints.empty() && bcurvePoints[0] != 0) {
- dCurve = std::unique_ptr(new DiagonalCurve(bcurvePoints, CURVES_MIN_POLY_POINTS / skip));
+ dCurve = std::unique_ptr (new DiagonalCurve(bcurvePoints, CURVES_MIN_POLY_POINTS / skip));
if (dCurve && !dCurve->isIdentity()) {
butili = true;
@@ -482,7 +764,7 @@ void CurveFactory::complexsgnCurve (bool & autili, bool & butili, bool & ccutil
//-----------------------------------------------
if (!cccurvePoints.empty() && cccurvePoints[0] != 0) {
- dCurve = std::unique_ptr(new DiagonalCurve(cccurvePoints, CURVES_MIN_POLY_POINTS / skip));
+ dCurve = std::unique_ptr (new DiagonalCurve(cccurvePoints, CURVES_MIN_POLY_POINTS / skip));
if (dCurve && !dCurve->isIdentity()) {
ccutili = true;
@@ -496,7 +778,7 @@ void CurveFactory::complexsgnCurve (bool & autili, bool & butili, bool & ccutil
//----------------------------
if (!lccurvePoints.empty() && lccurvePoints[0] != 0) {
- dCurve = std::unique_ptr(new DiagonalCurve(lccurvePoints, CURVES_MIN_POLY_POINTS / skip));
+ dCurve = std::unique_ptr (new DiagonalCurve(lccurvePoints, CURVES_MIN_POLY_POINTS / skip));
if (dCurve && !dCurve->isIdentity()) {
cclutili = true;
@@ -522,13 +804,14 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
// the curve shapes are defined in sRGB gamma, but the output curves will operate on linear floating point data,
// hence we do both forward and inverse gamma conversions here.
const float gamma_ = Color::sRGBGammaCurve;
- const float start = expf(gamma_ * logf( -0.055 / ((1.0 / gamma_ - 1.0) * 1.055 )));
- const float slope = 1.055 * powf (start, 1.0 / gamma_ - 1) - 0.055 / start;
+ const float start = expf(gamma_ * logf(-0.055 / ((1.0 / gamma_ - 1.0) * 1.055)));
+ const float slope = 1.055 * powf(start, 1.0 / gamma_ - 1) - 0.055 / start;
const float mul = 1.055;
const float add = 0.055;
+
// a: slope of the curve, black: starting point at the x axis
- const float a = powf (2.0, ecomp);
+ const float a = powf(2.0, ecomp);
// clear array that stores histogram valid before applying the custom curve
outBeforeCCurveHistogram.clear();
@@ -548,14 +831,14 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
brightcurvePoints[1] = 0.; //black point. Value in [0 ; 1] range
brightcurvePoints[2] = 0.; //black point. Value in [0 ; 1] range
- if(br > 0) {
+ if (br > 0) {
brightcurvePoints[3] = 0.1; //toe point
brightcurvePoints[4] = 0.1 + br / 150.0; //value at toe point
brightcurvePoints[5] = 0.7; //shoulder point
- brightcurvePoints[6] = min(1.0, 0.7 + br / 300.0); //value at shoulder point
+ brightcurvePoints[6] = min(1.0, 0.7 + br / 300.0); //value at shoulder point
} else {
- brightcurvePoints[3] = max(0.0, 0.1 - br / 150.0); //toe point
+ brightcurvePoints[3] = max(0.0, 0.1 - br / 150.0); //toe point
brightcurvePoints[4] = 0.1; //value at toe point
brightcurvePoints[5] = 0.7 - br / 300.0; //shoulder point
@@ -565,12 +848,12 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
brightcurvePoints[7] = 1.; // white point
brightcurvePoints[8] = 1.; // value at white point
- brightcurve = std::unique_ptr(new DiagonalCurve(brightcurvePoints, CURVES_MIN_POLY_POINTS / skip));
+ brightcurve = std::unique_ptr (new DiagonalCurve(brightcurvePoints, CURVES_MIN_POLY_POINTS / skip));
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- hlCurve.setClip(LUT_CLIP_BELOW); // used LUT_CLIP_BELOW, because we want to have a baseline of 2^expcomp in this curve. If we don't clip the lut we get wrong values, see Issue 2621 #14 for details
+ hlCurve.setClip(LUT_CLIP_BELOW); // used LUT_CLIP_BELOW, because we want to have a baseline of 2^expcomp in this curve. If we don't clip the lut we get wrong values, see Issue 2621 #14 for details
float exp_scale = a;
float scale = 65536.0;
float comp = (max(0.0, ecomp) + 1.0) * hlcompr / 100.0;
@@ -586,11 +869,11 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
#ifdef __SSE2__
int i = shoulder + 1;
- if(i & 1) { // original formula, slower than optimized formulas below but only used once or none, so I let it as is for reference
+ if (i & 1) { // original formula, slower than optimized formulas below but only used once or none, so I let it as is for reference
// change to [0,1] range
float val = (float)i - shoulder;
float R = val * comp / (scalemshoulder);
- hlCurve[i] = xlog(1.0 + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision
+ hlCurve[i] = xlog(1.0 + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision
i++;
}
@@ -615,7 +898,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
for (int i = shoulder + 1; i < 0x10000; i++) {
// change to [0,1] range
- hlCurve[i] = xlog(1.0 + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision
+ hlCurve[i] = xlog(1.0 + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision
R += increment;
}
@@ -629,7 +912,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
//%%%%%%%%%%%%%%%%%%%%%%%%%%
// change to [0,1] range
- shCurve.setClip(LUT_CLIP_ABOVE); // used LUT_CLIP_ABOVE, because the curve converges to 1.0 at the upper end and we don't want to exceed this value.
+ shCurve.setClip(LUT_CLIP_ABOVE); // used LUT_CLIP_ABOVE, because the curve converges to 1.0 at the upper end and we don't want to exceed this value.
if (black == 0.0) {
shCurve.makeConstant(1.f);
} else {
@@ -709,7 +992,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// apply contrast enhancement
for (int i = 0; i <= 0xffff; i++) {
- dcurve[i] = contrastcurve.getVal (dcurve[i]);
+ dcurve[i] = contrastcurve.getVal(dcurve[i]);
}
}
@@ -726,7 +1009,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
customToneCurve2.Set(tcurve, gamma_);
}
- if (outBeforeCCurveHistogram ) {
+ if (outBeforeCCurveHistogram) {
histNeeded = true;
}
}
@@ -762,7 +1045,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
for (int i = 0; i <= 0xffff; i += 4) {
vfloat valv = LVFU(dcurve[i]);
- valv = igamma (valv, gamma_v, startv, slopev, mulv, addv);
+ valv = igamma(valv, gamma_v, startv, slopev, mulv, addv);
STVFU(outCurve[i], c65535v * valv);
}
@@ -770,7 +1053,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
for (int i = 0; i <= 0xffff; i++) {
float val = dcurve[i];
- val = igamma (val, gamma_, start, slope, mul, add);
+ val = igamma(val, gamma_, start, slope, mul, add);
outCurve[i] = (65535.f * val);
}
@@ -788,16 +1071,342 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou
}
+void CurveFactory::Curvelocalhl(double ecomp, double hlcompr, double hlcomprthresh, LUTf & hlCurve)
+{
+
+ // a: slope of the curve
+ const float a = powf(2.0f, ecomp);
+
+
+
+
+ hlCurve.setClip(LUT_CLIP_BELOW); // used LUT_CLIP_BELOW, because we want to have a baseline of 2^expcomp in this curve. If we don't clip the lut we get wrong values, see Issue 2621 #14 for details
+ float exp_scale = a;
+ float maxran = 65536.f;
+ float scale = maxran;
+ float comp = (max(0.0, ecomp) + 1.0) * hlcompr / 100.0;
+ float shoulder = ((scale / max(1.0f, exp_scale)) * (hlcomprthresh / 200.0)) + 0.1;
+
+ if (comp <= 0.0f) {
+ hlCurve.makeConstant(exp_scale);
+ } else {
+ hlCurve.makeConstant(exp_scale, shoulder + 1);
+
+ float scalemshoulder = scale - shoulder;
+
+#ifdef __SSE2__
+ int i = shoulder + 1;
+
+ if (i & 1) { // original formula, slower than optimized formulas below but only used once or none, so I let it as is for reference
+ // change to [0,1] range
+ float val = (float)i - shoulder;
+ float R = val * comp / (scalemshoulder);
+ hlCurve[i] = xlog(1.0f + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision
+ i++;
+ }
+
+ vdouble onev = _mm_set1_pd(1.0);
+ vdouble Rv = _mm_set_pd((i + 1 - shoulder) * (double)comp / scalemshoulder, (i - shoulder) * (double)comp / scalemshoulder);
+ vdouble incrementv = _mm_set1_pd(2.0 * comp / scalemshoulder);
+ vdouble exp_scalev = _mm_set1_pd(exp_scale);
+
+ // for (; i < 0x10000; i += 2) {
+ for (; i < maxran; i += 2) {
+ // change to [0,1] range
+ vdouble resultv = xlog(onev + Rv * exp_scalev) / Rv;
+ vfloat resultfv = _mm_cvtpd_ps(resultv);
+ _mm_store_ss(&hlCurve[i], resultfv);
+ resultfv = PERMUTEPS(resultfv, _MM_SHUFFLE(1, 1, 1, 1));
+ _mm_store_ss(&hlCurve[i + 1], resultfv);
+ Rv += incrementv;
+ }
+
+#else
+ float R = comp / scalemshoulder;
+ float increment = R;
+
+ // for (int i = shoulder + 1; i < 0x10000; i++) {
+ for (int i = shoulder + 1; i < maxran; i++) {
+ // change to [0,1] range
+ hlCurve[i] = xlog(1.0f + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision
+ R += increment;
+ }
+
+#endif
+
+ }
+}
+
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+void CurveFactory::complexCurvelocal(double ecomp, double black, double hlcompr, double hlcomprthresh,
+ double shcompr, double br, double cont, double lumare,
+ LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTf & lightCurveloc, float avg,
+ int skip)
+{
+
+ const float gamma_ = 2.22; //BT 709
+ const float start = expf(gamma_ * logf(-0.0954f / ((1.0f / gamma_ - 1.0f) * 1.0954f)));
+ const float slope = 1.0954f * powf(start, 1.0f / gamma_ - 1.f) - 0.0954f / start;
+ const float mul = 1.0954f;
+ const float add = 0.0954f;
+ float maxran = 65536.f; //65536
+
+// ecomp /= 100.;
+
+ // check if brightness curve is needed
+ if (br > 0.00001 || br < -0.00001) {
+ // utili = true;
+ if(br > 0) {
+ br /= 4.f;//to avoid artifacts in some cases
+ }
+ std::vector brightcurvePoints;
+ brightcurvePoints.resize(9);
+ brightcurvePoints.at(0) = double (DCT_NURBS);
+
+ brightcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
+ brightcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range
+
+ if (br > 0) {
+ brightcurvePoints.at(3) = 0.2; // toe point
+ brightcurvePoints.at(4) = 0.2 + br / 250.0; //value at toe point
+
+ brightcurvePoints.at(5) = 0.6; // shoulder point
+ brightcurvePoints.at(6) = min(1.0, 0.6 + br / 200.0); //value at shoulder point
+ } else {
+ brightcurvePoints.at(3) = 0.1 - br / 150.0; // toe point
+ brightcurvePoints.at(4) = 0.1; // value at toe point
+
+ brightcurvePoints.at(5) = min(1.0, 0.7 - br / 300.0); // shoulder point
+ brightcurvePoints.at(6) = 0.7; // value at shoulder point
+ }
+
+ brightcurvePoints.at(7) = 1.; // white point
+ brightcurvePoints.at(8) = 1.; // value at white point
+
+ DiagonalCurve brightcurve(brightcurvePoints, CURVES_MIN_POLY_POINTS / skip);
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ // Applying brightness curve
+ for (int i = 0; i < 32768; i++) { // L values range up to 32767, higher values are for highlight overflow
+
+ // change to [0,1] range
+ float val = (float)i / 32767.0;
+
+ // apply brightness curve
+ val = brightcurve.getVal(val);
+
+ // store result in a temporary array
+ lightCurveloc[i] = val;
+ }
+
+
+
+ } else {
+ lightCurveloc.makeIdentity(32767.f);
+ }
+
+ // check if contrast curve is needed
+ if (cont > 0.00001 || cont < -0.00001) {
+
+
+ int k = avg * 32768;
+ avg = lightCurveloc[k];
+ // printf("avg=%f lumaref=%f\n", avg, lumare/100.f);
+ std::vector contrastcurvePoints;
+ bool lumm = true;
+
+ if (lumm) {
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ contrastcurvePoints.resize(9);
+ contrastcurvePoints.at(0) = double (DCT_NURBS);
+
+ contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
+ contrastcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range
+
+ contrastcurvePoints.at(3) = avg - avg * (0.6 - cont / 250.0); // toe point
+ contrastcurvePoints.at(4) = avg - avg * (0.6 + cont / 250.0); // value at toe point
+
+ contrastcurvePoints.at(5) = avg + (1 - avg) * (0.6 - cont / 250.0); // shoulder point
+ contrastcurvePoints.at(6) = avg + (1 - avg) * (0.6 + cont / 250.0); // value at shoulder point
+
+ contrastcurvePoints.at(7) = 1.; // white point
+ contrastcurvePoints.at(8) = 1.; // value at white point
+
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ } else {
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ // sum has an invalid value (next to 0, producing a division by zero, so we create a fake contrast curve, producing a white image
+ contrastcurvePoints.resize(5);
+ contrastcurvePoints.at(0) = double (DCT_NURBS);
+
+ contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
+ contrastcurvePoints.at(2) = 1.; // black point. Value in [0 ; 1] range
+
+ contrastcurvePoints.at(3) = 1.; // white point
+ contrastcurvePoints.at(4) = 1.; // value at white point
+
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ }
+
+ DiagonalCurve contrastcurve(contrastcurvePoints, CURVES_MIN_POLY_POINTS / skip);
+
+ // apply contrast enhancement
+ for (int i = 0; i < 32768; i++) {
+ lightCurveloc[i] = contrastcurve.getVal(lightCurveloc[i]);
+ }
+
+ }
+
+ lightCurveloc *= 32767.f;
+
+ for (int i = 32768; i < 32770; i++) { // set last two elements of lut to 32768 and 32769 to allow linear interpolation
+ lightCurveloc[i] = (float)i;
+ }
+
+
+ // a: slope of the curve, black: starting point at the x axis
+ const float a = powf(2.0f, ecomp);
+
+
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ // tone curve base. a: slope (from exp.comp.), b: black, def_mul: max. x value (can be>1), hr,sr: highlight,shadow recovery
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+ hlCurve.setClip(LUT_CLIP_BELOW); // used LUT_CLIP_BELOW, because we want to have a baseline of 2^expcomp in this curve. If we don't clip the lut we get wrong values, see Issue 2621 #14 for details
+ float exp_scale = a;
+// float scale = 65536.0;
+ float scale = maxran;
+ float comp = (max(0.0, ecomp) + 1.0) * hlcompr / 100.0;
+ float shoulder = ((scale / max(1.0f, exp_scale)) * (hlcomprthresh / 200.0)) + 0.1;
+
+ if (comp <= 0.0f) {
+ hlCurve.makeConstant(exp_scale);
+ } else {
+ hlCurve.makeConstant(exp_scale, shoulder + 1);
+
+ float scalemshoulder = scale - shoulder;
+
+#ifdef __SSE2__
+ int i = shoulder + 1;
+
+ if (i & 1) { // original formula, slower than optimized formulas below but only used once or none, so I let it as is for reference
+ // change to [0,1] range
+ float val = (float)i - shoulder;
+ float R = val * comp / (scalemshoulder);
+ hlCurve[i] = xlog(1.0f + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision
+ i++;
+ }
+
+ vdouble onev = _mm_set1_pd(1.0);
+ vdouble Rv = _mm_set_pd((i + 1 - shoulder) * (double)comp / scalemshoulder, (i - shoulder) * (double)comp / scalemshoulder);
+ vdouble incrementv = _mm_set1_pd(2.0 * comp / scalemshoulder);
+ vdouble exp_scalev = _mm_set1_pd(exp_scale);
+
+ // for (; i < 0x10000; i += 2) {
+ for (; i < maxran; i += 2) {
+ // change to [0,1] range
+ vdouble resultv = xlog(onev + Rv * exp_scalev) / Rv;
+ vfloat resultfv = _mm_cvtpd_ps(resultv);
+ _mm_store_ss(&hlCurve[i], resultfv);
+ resultfv = PERMUTEPS(resultfv, _MM_SHUFFLE(1, 1, 1, 1));
+ _mm_store_ss(&hlCurve[i + 1], resultfv);
+ Rv += incrementv;
+ }
+
+#else
+ float R = comp / scalemshoulder;
+ float increment = R;
+
+ // for (int i = shoulder + 1; i < 0x10000; i++) {
+ for (int i = shoulder + 1; i < maxran; i++) {
+ // change to [0,1] range
+ hlCurve[i] = xlog(1.0f + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision
+ R += increment;
+ }
+
+#endif
+
+ }
+
+
+ // curve without contrast
+ LUTf dcurve(maxran);
+
+ //%%%%%%%%%%%%%%%%%%%%%%%%%%
+ // change to [0,1] range
+ shCurve.setClip(LUT_CLIP_ABOVE); // used LUT_CLIP_ABOVE, because the curve converges to 1.0 at the upper end and we don't want to exceed this value.
+ if (black == 0.0) {
+ shCurve.makeConstant(1.f);
+ } else {
+ const float val = 1.f / (maxran - 1.f);
+ shCurve[0] = simplebasecurve(val, black, 0.015 * shcompr) / val;
+ }
+
+
+
+ // gamma correction
+
+ float val = Color::gammatab_bt709[0] / maxran;
+ // store result in a temporary array
+ dcurve[0] = LIM01(val);
+
+ for (int i = 1; i < maxran; i++) {
+ float val = i / (maxran - 1.f);
+
+// float val2 = simplebasecurve(val, black, 0.015 * shcompr);
+// shCurve[i] = val2 / val;
+ if (black != 0.0) {
+ const float val = i / 65535.f;
+ shCurve[i] = simplebasecurve(val, black, 0.015 * shcompr) / val;
+ }
+
+ // gamma correction
+ val = Color::gammatab_bt709[i] / maxran;
+ // store result in a temporary array
+ dcurve[i] = val;
+ }
+
+
+#ifdef __SSE2__
+ vfloat gamma_v = F2V(gamma_);
+ vfloat startv = F2V(start);
+ vfloat slopev = F2V(slope);
+ vfloat mulv = F2V(mul);
+ vfloat addv = F2V(add);
+ // vfloat c65535v = F2V (65535.f);
+ vfloat c65535v = F2V(maxran - 1.f);
+
+ for (int i = 0; i <= (maxran - 1.f); i += 4) {
+ vfloat valv = LVFU(dcurve[i]);
+ valv = igamma(valv, gamma_v, startv, slopev, mulv, addv);
+ STVFU(outCurve[i], c65535v * valv);
+ }
+
+#else
+
+ for (int i = 0; i <= (maxran - 1.f); i++) {
+ float val = dcurve[i];
+ val = igamma(val, gamma_, start, slope, mul, add);
+ outCurve[i] = ((maxran - 1.) * val);
+ }
+
+#endif
+
+}
+
+
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-void CurveFactory::complexLCurve (double br, double contr, const std::vector& curvePoints,
- const LUTu & histogram, LUTf & outCurve,
- LUTu & outBeforeCCurveHistogram, int skip, bool & utili)
+void CurveFactory::complexLCurve(double br, double contr, const std::vector& curvePoints,
+ const LUTu & histogram, LUTf & outCurve,
+ LUTu & outBeforeCCurveHistogram, int skip, bool & utili)
{
utili = false;
+
// clear array that stores histogram valid before applying the custom curve
if (outBeforeCCurveHistogram) {
outBeforeCCurveHistogram.clear();
@@ -813,27 +1422,27 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vector brightcurvePoints;
brightcurvePoints.resize(9);
- brightcurvePoints.at(0) = double(DCT_NURBS);
+ brightcurvePoints.at(0) = double (DCT_NURBS);
- brightcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
- brightcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range
+ brightcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
+ brightcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range
if (br > 0) {
- brightcurvePoints.at(3) = 0.1; // toe point
- brightcurvePoints.at(4) = 0.1 + br / 150.0; //value at toe point
+ brightcurvePoints.at(3) = 0.1; // toe point
+ brightcurvePoints.at(4) = 0.1 + br / 150.0; //value at toe point
- brightcurvePoints.at(5) = 0.7; // shoulder point
- brightcurvePoints.at(6) = min(1.0, 0.7 + br / 300.0); //value at shoulder point
+ brightcurvePoints.at(5) = 0.7; // shoulder point
+ brightcurvePoints.at(6) = min(1.0, 0.7 + br / 300.0); //value at shoulder point
} else {
- brightcurvePoints.at(3) = 0.1 - br / 150.0; // toe point
- brightcurvePoints.at(4) = 0.1; // value at toe point
+ brightcurvePoints.at(3) = 0.1 - br / 150.0; // toe point
+ brightcurvePoints.at(4) = 0.1; // value at toe point
- brightcurvePoints.at(5) = min(1.0, 0.7 - br / 300.0); // shoulder point
- brightcurvePoints.at(6) = 0.7; // value at shoulder point
+ brightcurvePoints.at(5) = min(1.0, 0.7 - br / 300.0); // shoulder point
+ brightcurvePoints.at(6) = 0.7; // value at shoulder point
}
- brightcurvePoints.at(7) = 1.; // white point
- brightcurvePoints.at(8) = 1.; // value at white point
+ brightcurvePoints.at(7) = 1.; // white point
+ brightcurvePoints.at(8) = 1.; // value at white point
DiagonalCurve brightcurve(brightcurvePoints, CURVES_MIN_POLY_POINTS / skip);
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -845,7 +1454,7 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vector(val);
@@ -874,37 +1483,37 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vector contrastcurvePoints;
- if(sum) {
+ if (sum) {
avg /= sum;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
contrastcurvePoints.resize(9);
- contrastcurvePoints.at(0) = double(DCT_NURBS);
+ contrastcurvePoints.at(0) = double (DCT_NURBS);
- contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
- contrastcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range
+ contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
+ contrastcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range
- contrastcurvePoints.at(3) = avg - avg * (0.6 - contr / 250.0); // toe point
- contrastcurvePoints.at(4) = avg - avg * (0.6 + contr / 250.0); // value at toe point
+ contrastcurvePoints.at(3) = avg - avg * (0.6 - contr / 250.0); // toe point
+ contrastcurvePoints.at(4) = avg - avg * (0.6 + contr / 250.0); // value at toe point
- contrastcurvePoints.at(5) = avg + (1 - avg) * (0.6 - contr / 250.0); // shoulder point
- contrastcurvePoints.at(6) = avg + (1 - avg) * (0.6 + contr / 250.0); // value at shoulder point
+ contrastcurvePoints.at(5) = avg + (1 - avg) * (0.6 - contr / 250.0); // shoulder point
+ contrastcurvePoints.at(6) = avg + (1 - avg) * (0.6 + contr / 250.0); // value at shoulder point
- contrastcurvePoints.at(7) = 1.; // white point
- contrastcurvePoints.at(8) = 1.; // value at white point
+ contrastcurvePoints.at(7) = 1.; // white point
+ contrastcurvePoints.at(8) = 1.; // value at white point
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
} else {
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// sum has an invalid value (next to 0, producing a division by zero, so we create a fake contrast curve, producing a white image
contrastcurvePoints.resize(5);
- contrastcurvePoints.at(0) = double(DCT_NURBS);
+ contrastcurvePoints.at(0) = double (DCT_NURBS);
- contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
- contrastcurvePoints.at(2) = 1.; // black point. Value in [0 ; 1] range
+ contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range
+ contrastcurvePoints.at(2) = 1.; // black point. Value in [0 ; 1] range
- contrastcurvePoints.at(3) = 1.; // white point
- contrastcurvePoints.at(4) = 1.; // value at white point
+ contrastcurvePoints.at(3) = 1.; // white point
+ contrastcurvePoints.at(4) = 1.; // value at white point
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
}
@@ -913,7 +1522,7 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vector(new DiagonalCurve (curvePoints, CURVES_MIN_POLY_POINTS / skip));
+ tcurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip));
if (outBeforeCCurveHistogram) {
histNeeded = true;
@@ -950,7 +1559,7 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vectorgetVal (outCurve[i]);
+ val = tcurve->getVal(outCurve[i]);
outCurve[i] = (32767.f * val);
}
@@ -958,7 +1567,7 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vector& curvePoints, LUTf & outCurve, int skip)
+void CurveFactory::RGBCurve(const std::vector& curvePoints, LUTf & outCurve, int skip)
{
// create a curve if needed
std::unique_ptr tcurve;
if (!curvePoints.empty() && curvePoints[0] != 0) {
- tcurve = std::unique_ptr(new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip));
+ tcurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip));
}
if (tcurve && tcurve->isIdentity()) {
@@ -1008,6 +1617,1273 @@ void CurveFactory::RGBCurve (const std::vector& curvePoints, LUTf & outC
}
+LocretigainCurverab::LocretigainCurverab() : sum(0.f) {};
+
+void LocretigainCurverab::Reset()
+{
+ lutLocretigainCurverab.reset();
+ sum = 0.f;
+}
+
+void LocretigainCurverab::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocretigainCurverab(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocretigainCurverab[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocretigainCurverab[i] < 0.02f) {
+ lutLocretigainCurverab[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocretigainCurverab[i];
+ }
+
+ //lutLocCurve.dump("wav");
+}
+
+void LocretigainCurverab::Set(const std::vector &curvePoints)
+{
+
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ tcurve.setIdentityValue(0.);
+ Set(tcurve);
+ } else {
+ Reset();
+ }
+}
+LocHHmaskblCurve::LocHHmaskblCurve() : sum(0.f) {};
+
+void LocHHmaskblCurve::Reset()
+{
+ lutLocHHmaskblCurve.reset();
+ sum = 0.f;
+}
+
+
+void LocHHmaskblCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocHHmaskblCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocHHmaskblCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocHHmaskblCurve[i] < 0.02f) {
+ lutLocHHmaskblCurve[i] = 0.02f;
+ }
+
+ sum += lutLocHHmaskblCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+void LocHHmaskblCurve::Set(const std::vector &curvePoints, bool & lhmasblutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lhmasblutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocLLmaskblCurve::LocLLmaskblCurve() : sum(0.f) {};
+
+void LocLLmaskblCurve::Reset()
+{
+ lutLocLLmaskblCurve.reset();
+ sum = 0.f;
+}
+
+void LocLLmaskblCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocLLmaskblCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocLLmaskblCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocLLmaskblCurve[i] < 0.02f) {
+ lutLocLLmaskblCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocLLmaskblCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocLLmaskblCurve::Set(const std::vector &curvePoints, bool & llmasblutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ llmasblutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocCCmaskblCurve::LocCCmaskblCurve() : sum(0.f) {};
+
+void LocCCmaskblCurve::Reset()
+{
+ lutLocCCmaskblCurve.reset();
+ sum = 0.f;
+}
+
+void LocCCmaskblCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocCCmaskblCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocCCmaskblCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocCCmaskblCurve[i] < 0.02f) {
+ lutLocCCmaskblCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocCCmaskblCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocCCmaskblCurve::Set(const std::vector &curvePoints, bool & lcmasblutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lcmasblutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+LocHHmasktmCurve::LocHHmasktmCurve() : sum(0.f) {};
+
+void LocHHmasktmCurve::Reset()
+{
+ lutLocHHmasktmCurve.reset();
+ sum = 0.f;
+}
+
+
+void LocHHmasktmCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocHHmasktmCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocHHmasktmCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocHHmasktmCurve[i] < 0.02f) {
+ lutLocHHmasktmCurve[i] = 0.02f;
+ }
+
+ sum += lutLocHHmasktmCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+void LocHHmasktmCurve::Set(const std::vector &curvePoints, bool & lhmastmutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lhmastmutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocLLmasktmCurve::LocLLmasktmCurve() : sum(0.f) {};
+
+void LocLLmasktmCurve::Reset()
+{
+ lutLocLLmasktmCurve.reset();
+ sum = 0.f;
+}
+
+void LocLLmasktmCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocLLmasktmCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocLLmasktmCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocLLmasktmCurve[i] < 0.02f) {
+ lutLocLLmasktmCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocLLmasktmCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocLLmasktmCurve::Set(const std::vector &curvePoints, bool & llmastmutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ llmastmutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocCCmasktmCurve::LocCCmasktmCurve() : sum(0.f) {};
+
+void LocCCmasktmCurve::Reset()
+{
+ lutLocCCmasktmCurve.reset();
+ sum = 0.f;
+}
+
+void LocCCmasktmCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocCCmasktmCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocCCmasktmCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocCCmasktmCurve[i] < 0.02f) {
+ lutLocCCmasktmCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocCCmasktmCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocCCmasktmCurve::Set(const std::vector &curvePoints, bool & lcmastmutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lcmastmutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+
+LocHHmaskretiCurve::LocHHmaskretiCurve() : sum(0.f) {};
+
+void LocHHmaskretiCurve::Reset()
+{
+ lutLocHHmaskretiCurve.reset();
+ sum = 0.f;
+}
+
+
+void LocHHmaskretiCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocHHmaskretiCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocHHmaskretiCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocHHmaskretiCurve[i] < 0.02f) {
+ lutLocHHmaskretiCurve[i] = 0.02f;
+ }
+
+ sum += lutLocHHmaskretiCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+void LocHHmaskretiCurve::Set(const std::vector &curvePoints, bool & lhmasretiutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lhmasretiutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocLLmaskretiCurve::LocLLmaskretiCurve() : sum(0.f) {};
+
+void LocLLmaskretiCurve::Reset()
+{
+ lutLocLLmaskretiCurve.reset();
+ sum = 0.f;
+}
+
+void LocLLmaskretiCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocLLmaskretiCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocLLmaskretiCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocLLmaskretiCurve[i] < 0.02f) {
+ lutLocLLmaskretiCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocLLmaskretiCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocLLmaskretiCurve::Set(const std::vector &curvePoints, bool & llmasretiutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ llmasretiutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocCCmaskretiCurve::LocCCmaskretiCurve() : sum(0.f) {};
+
+void LocCCmaskretiCurve::Reset()
+{
+ lutLocCCmaskretiCurve.reset();
+ sum = 0.f;
+}
+
+void LocCCmaskretiCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocCCmaskretiCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocCCmaskretiCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocCCmaskretiCurve[i] < 0.02f) {
+ lutLocCCmaskretiCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocCCmaskretiCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocCCmaskretiCurve::Set(const std::vector &curvePoints, bool & lcmasretiutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lcmasretiutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+
+
+
+
+
+LocHHmaskcbCurve::LocHHmaskcbCurve() : sum(0.f) {};
+
+void LocHHmaskcbCurve::Reset()
+{
+ lutLocHHmaskcbCurve.reset();
+ sum = 0.f;
+}
+
+
+void LocHHmaskcbCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocHHmaskcbCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocHHmaskcbCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocHHmaskcbCurve[i] < 0.02f) {
+ lutLocHHmaskcbCurve[i] = 0.02f;
+ }
+
+ sum += lutLocHHmaskcbCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+void LocHHmaskcbCurve::Set(const std::vector &curvePoints, bool & lhmascbutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lhmascbutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocLLmaskcbCurve::LocLLmaskcbCurve() : sum(0.f) {};
+
+void LocLLmaskcbCurve::Reset()
+{
+ lutLocLLmaskcbCurve.reset();
+ sum = 0.f;
+}
+
+void LocLLmaskcbCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocLLmaskcbCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocLLmaskcbCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocLLmaskcbCurve[i] < 0.02f) {
+ lutLocLLmaskcbCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocLLmaskcbCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocLLmaskcbCurve::Set(const std::vector &curvePoints, bool & llmascbutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ llmascbutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocCCmaskcbCurve::LocCCmaskcbCurve() : sum(0.f) {};
+
+void LocCCmaskcbCurve::Reset()
+{
+ lutLocCCmaskcbCurve.reset();
+ sum = 0.f;
+}
+
+void LocCCmaskcbCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocCCmaskcbCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocCCmaskcbCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocCCmaskcbCurve[i] < 0.02f) {
+ lutLocCCmaskcbCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocCCmaskcbCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocCCmaskcbCurve::Set(const std::vector &curvePoints, bool & lcmascbutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lcmascbutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+
+LocHHmaskSHCurve::LocHHmaskSHCurve() : sum(0.f) {};
+
+void LocHHmaskSHCurve::Reset()
+{
+ lutLocHHmaskSHCurve.reset();
+ sum = 0.f;
+}
+
+
+void LocHHmaskSHCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocHHmaskSHCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocHHmaskSHCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocHHmaskSHCurve[i] < 0.02f) {
+ lutLocHHmaskSHCurve[i] = 0.02f;
+ }
+
+ sum += lutLocHHmaskSHCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocHHmaskSHCurve::Set(const std::vector &curvePoints, bool & lhmasSHutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lhmasSHutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+LocLLmaskSHCurve::LocLLmaskSHCurve() : sum(0.f) {};
+
+void LocLLmaskSHCurve::Reset()
+{
+ lutLocLLmaskSHCurve.reset();
+ sum = 0.f;
+}
+
+void LocLLmaskSHCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocLLmaskSHCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocLLmaskSHCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocLLmaskSHCurve[i] < 0.02f) {
+ lutLocLLmaskSHCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocLLmaskSHCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocLLmaskSHCurve::Set(const std::vector &curvePoints, bool & llmasSHutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ llmasSHutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+
+LocCCmaskSHCurve::LocCCmaskSHCurve() : sum(0.f) {};
+
+void LocCCmaskSHCurve::Reset()
+{
+ lutLocCCmaskSHCurve.reset();
+ sum = 0.f;
+}
+
+void LocCCmaskSHCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocCCmaskSHCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocCCmaskSHCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocCCmaskSHCurve[i] < 0.02f) {
+ lutLocCCmaskSHCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocCCmaskSHCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocCCmaskSHCurve::Set(const std::vector &curvePoints, bool & lcmasSHutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lcmasSHutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+
+
+LocHHmaskexpCurve::LocHHmaskexpCurve() : sum(0.f) {};
+
+void LocHHmaskexpCurve::Reset()
+{
+ lutLocHHmaskexpCurve.reset();
+ sum = 0.f;
+}
+
+
+void LocHHmaskexpCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocHHmaskexpCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocHHmaskexpCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocHHmaskexpCurve[i] < 0.02f) {
+ lutLocHHmaskexpCurve[i] = 0.02f;
+ }
+
+ sum += lutLocHHmaskexpCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocHHmaskexpCurve::Set(const std::vector &curvePoints, bool & lhmasexputili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lhmasexputili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+LocLLmaskexpCurve::LocLLmaskexpCurve() : sum(0.f) {};
+
+void LocLLmaskexpCurve::Reset()
+{
+ lutLocLLmaskexpCurve.reset();
+ sum = 0.f;
+}
+
+void LocLLmaskexpCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocLLmaskexpCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocLLmaskexpCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocLLmaskexpCurve[i] < 0.02f) {
+ lutLocLLmaskexpCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocLLmaskexpCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocLLmaskexpCurve::Set(const std::vector &curvePoints, bool & llmasexputili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ llmasexputili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+
+LocCCmaskexpCurve::LocCCmaskexpCurve() : sum(0.f) {};
+
+void LocCCmaskexpCurve::Reset()
+{
+ lutLocCCmaskexpCurve.reset();
+ sum = 0.f;
+}
+
+void LocCCmaskexpCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocCCmaskexpCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocCCmaskexpCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocCCmaskexpCurve[i] < 0.02f) {
+ lutLocCCmaskexpCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocCCmaskexpCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocCCmaskexpCurve::Set(const std::vector &curvePoints, bool & lcmasexputili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lcmasexputili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocHHmaskCurve::LocHHmaskCurve() : sum(0.f) {};
+
+void LocHHmaskCurve::Reset()
+{
+ lutLocHHmaskCurve.reset();
+ sum = 0.f;
+}
+
+
+void LocHHmaskCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocHHmaskCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocHHmaskCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocHHmaskCurve[i] < 0.02f) {
+ lutLocHHmaskCurve[i] = 0.02f;
+ }
+
+ sum += lutLocHHmaskCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocHHmaskCurve::Set(const std::vector &curvePoints, bool & lhmasutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lhmasutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+
+LocCCmaskCurve::LocCCmaskCurve() : sum(0.f) {};
+
+void LocCCmaskCurve::Reset()
+{
+ lutLocCCmaskCurve.reset();
+ sum = 0.f;
+}
+
+
+void LocCCmaskCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocCCmaskCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocCCmaskCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocCCmaskCurve[i] < 0.02f) {
+ lutLocCCmaskCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocCCmaskCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocCCmaskCurve::Set(const std::vector &curvePoints, bool & lcmasutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ lcmasutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocLLmaskCurve::LocLLmaskCurve() : sum(0.f) {};
+
+void LocLLmaskCurve::Reset()
+{
+ lutLocLLmaskCurve.reset();
+ sum = 0.f;
+}
+
+void LocLLmaskCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocLLmaskCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocLLmaskCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocLLmaskCurve[i] < 0.02f) {
+ lutLocLLmaskCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocLLmaskCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocLLmaskCurve::Set(const std::vector &curvePoints, bool & llmasutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ llmasutili = true;
+ Set(ttcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
+
+LocHHCurve::LocHHCurve() : sum(0.f) {};
+
+void LocHHCurve::Reset()
+{
+ lutLocHHCurve.reset();
+ sum = 0.f;
+}
+void LocHHCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocHHCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocHHCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocHHCurve[i] < 0.02f) {
+ lutLocHHCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocHHCurve[i];
+ }
+
+ //lutLocHHCurve.dump("wav");
+}
+
+
+
+void LocHHCurve::Set(const std::vector &curvePoints, bool &HHutili)
+{
+ // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ ttcurve.setIdentityValue(0.);
+ Set(ttcurve);
+ HHutili = true;
+ } else {
+ Reset();
+ }
+}
+
+
+LocLHCurve::LocLHCurve() : sum(0.f) {};
+
+void LocLHCurve::Reset()
+{
+ lutLocLHCurve.reset();
+ sum = 0.f;
+}
+
+void LocLHCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocLHCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocLHCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocLHCurve[i] < 0.02f) {
+ lutLocLHCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocLHCurve[i];
+ }
+
+ //lutLocCurve.dump("wav");
+}
+
+
+
+
+void LocLHCurve::Set(const std::vector &curvePoints, bool &LHutili)
+{
+
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+// if (LHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ tcurve.setIdentityValue(0.);
+ Set(tcurve);
+ LHutili = true;
+ } else {
+ Reset();
+ }
+}
+
+LocwavCurve::LocwavCurve() : sum(0.f) {};
+
+void LocwavCurve::Reset()
+{
+ lutLocwavCurve.reset();
+ sum = 0.f;
+}
+
+void LocwavCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocwavCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocwavCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocwavCurve[i] < 0.02f) {
+ lutLocwavCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocwavCurve[i];
+ }
+
+ //lutLocCurve.dump("wav");
+}
+void LocwavCurve::Set(const std::vector &curvePoints, bool & lcwavutili)
+{
+
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ tcurve.setIdentityValue(0.);
+ lcwavutili = true;
+ Set(tcurve);
+ } else {
+ Reset();
+ }
+}
+
+LocretitransCurve::LocretitransCurve() : sum(0.f) {};
+
+void LocretitransCurve::Reset()
+{
+ lutLocretitransCurve.reset();
+ sum = 0.f;
+}
+
+void LocretitransCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocretitransCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocretitransCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocretitransCurve[i] < 0.02f) {
+ lutLocretitransCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocretitransCurve[i];
+ }
+
+ //lutLocCurve.dump("wav");
+}
+void LocretitransCurve::Set(const std::vector &curvePoints)
+{
+
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ tcurve.setIdentityValue(0.);
+ Set(tcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+LocretigainCurve::LocretigainCurve() : sum(0.f) {};
+
+void LocretigainCurve::Reset()
+{
+ lutLocretigainCurve.reset();
+ sum = 0.f;
+}
+
+void LocretigainCurve::Set(const Curve &pCurve)
+{
+ if (pCurve.isIdentity()) {
+ Reset(); // raise this value if the quality suffers from this number of samples
+ return;
+ }
+
+ lutLocretigainCurve(501); // raise this value if the quality suffers from this number of samples
+ sum = 0.f;
+
+ for (int i = 0; i < 501; i++) {
+ lutLocretigainCurve[i] = pCurve.getVal(double (i) / 500.);
+
+ if (lutLocretigainCurve[i] < 0.02f) {
+ lutLocretigainCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
+ }
+
+ sum += lutLocretigainCurve[i];
+ }
+
+ //lutLocCurve.dump("wav");
+}
+void LocretigainCurve::Set(const std::vector &curvePoints)
+{
+
+ if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
+ FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2);
+ tcurve.setIdentityValue(0.);
+ Set(tcurve);
+ } else {
+ Reset();
+ }
+}
+
+
+
void ColorAppearance::Reset()
{
lutColCurve.reset();
@@ -1019,7 +2895,7 @@ void ColorAppearance::Set(const Curve &pCurve)
lutColCurve(65536);
for (int i = 0; i < 65536; i++) {
- lutColCurve[i] = pCurve.getVal(double(i) / 65535.) * 65535.;
+ lutColCurve[i] = pCurve.getVal(double (i) / 65535.) * 65535.;
}
}
@@ -1038,10 +2914,10 @@ void RetinextransmissionCurve::Set(const Curve &pCurve)
return;
}
- luttransmission(501); // raise this value if the quality suffers from this number of samples
+ luttransmission(501); // raise this value if the quality suffers from this number of samples
for (int i = 0; i < 501; i++) {
- luttransmission[i] = pCurve.getVal(double(i) / 500.);
+ luttransmission[i] = pCurve.getVal(double (i) / 500.);
}
}
@@ -1071,10 +2947,10 @@ void RetinexgaintransmissionCurve::Set(const Curve &pCurve)
return;
}
- lutgaintransmission(501); // raise this value if the quality suffers from this number of samples
+ lutgaintransmission(501); // raise this value if the quality suffers from this number of samples
for (int i = 0; i < 501; i++) {
- lutgaintransmission[i] = pCurve.getVal(double(i) / 500.);
+ lutgaintransmission[i] = pCurve.getVal(double (i) / 500.);
}
}
@@ -1101,9 +2977,9 @@ void ToneCurve::Set(const Curve &pCurve, float gamma)
if (gamma <= 0.0 || gamma == 1.) {
for (int i = 0; i < 65536; i++) {
- lutToneCurve[i] = (float)pCurve.getVal(float(i) / 65535.f) * 65535.f;
+ lutToneCurve[i] = (float)pCurve.getVal(float (i) / 65535.f) * 65535.f;
}
- } else if(gamma == (float)Color::sRGBGammaCurve) {
+ } else if (gamma == (float)Color::sRGBGammaCurve) {
// for sRGB gamma we can use luts, which is much faster
for (int i = 0; i < 65536; i++) {
float val = Color::gammatab_srgb[i] / 65535.f;
@@ -1113,17 +2989,17 @@ void ToneCurve::Set(const Curve &pCurve, float gamma)
}
} else {
- const float start = expf(gamma * logf( -0.055 / ((1.0 / gamma - 1.0) * 1.055 )));
- const float slope = 1.055 * powf (start, 1.0 / gamma - 1) - 0.055 / start;
+ const float start = expf(gamma * logf(-0.055 / ((1.0 / gamma - 1.0) * 1.055)));
+ const float slope = 1.055 * powf(start, 1.0 / gamma - 1) - 0.055 / start;
const float mul = 1.055;
const float add = 0.055;
// apply gamma, that is 'pCurve' is defined with the given gamma and here we convert it to a curve in linear space
for (int i = 0; i < 65536; i++) {
- float val = float(i) / 65535.f;
- val = CurveFactory::gamma (val, gamma, start, slope, mul, add);
+ float val = float (i) / 65535.f;
+ val = CurveFactory::gamma(val, gamma, start, slope, mul, add);
val = pCurve.getVal(val);
- val = CurveFactory::igamma (val, gamma, start, slope, mul, add);
+ val = CurveFactory::igamma(val, gamma, start, slope, mul, add);
lutToneCurve[i] = val * 65535.f;
}
}
@@ -1141,10 +3017,10 @@ void OpacityCurve::Set(const Curve *pCurve)
return;
}
- lutOpacityCurve(501); // raise this value if the quality suffers from this number of samples
+ lutOpacityCurve(501); // raise this value if the quality suffers from this number of samples
for (int i = 0; i < 501; i++) {
- lutOpacityCurve[i] = pCurve->getVal(double(i) / 500.);
+ lutOpacityCurve[i] = pCurve->getVal(double (i) / 500.);
}
//lutOpacityCurve.dump("opacity");
@@ -1155,7 +3031,7 @@ void OpacityCurve::Set(const std::vector &curvePoints, bool &opautili)
std::unique_ptr tcurve;
if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
- tcurve = std::unique_ptr(new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2));
+ tcurve = std::unique_ptr (new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2));
tcurve->setIdentityValue(0.);
}
@@ -1182,13 +3058,13 @@ void WavCurve::Set(const Curve &pCurve)
return;
}
- lutWavCurve(501); // raise this value if the quality suffers from this number of samples
+ lutWavCurve(501); // raise this value if the quality suffers from this number of samples
sum = 0.f;
for (int i = 0; i < 501; i++) {
- lutWavCurve[i] = pCurve.getVal(double(i) / 500.);
+ lutWavCurve[i] = pCurve.getVal(double (i) / 500.);
- if(lutWavCurve[i] < 0.02f) {
+ if (lutWavCurve[i] < 0.02f) {
lutWavCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
}
@@ -1259,10 +3135,10 @@ void WavOpacityCurveRG::Set(const Curve &pCurve)
return;
}
- lutOpacityCurveRG(501); // raise this value if the quality suffers from this number of samples
+ lutOpacityCurveRG(501); // raise this value if the quality suffers from this number of samples
for (int i = 0; i < 501; i++) {
- lutOpacityCurveRG[i] = pCurve.getVal(double(i) / 500.);
+ lutOpacityCurveRG[i] = pCurve.getVal(double (i) / 500.);
}
}
@@ -1329,10 +3205,10 @@ void WavOpacityCurveBY::Set(const Curve &pCurve)
return;
}
- lutOpacityCurveBY(501); // raise this value if the quality suffers from this number of samples
+ lutOpacityCurveBY(501); // raise this value if the quality suffers from this number of samples
for (int i = 0; i < 501; i++) {
- lutOpacityCurveBY[i] = pCurve.getVal(double(i) / 500.);
+ lutOpacityCurveBY[i] = pCurve.getVal(double (i) / 500.);
}
}
@@ -1361,10 +3237,10 @@ void WavOpacityCurveW::Set(const Curve &pCurve)
return;
}
- lutOpacityCurveW(501); // raise this value if the quality suffers from this number of samples
+ lutOpacityCurveW(501); // raise this value if the quality suffers from this number of samples
for (int i = 0; i < 501; i++) {
- lutOpacityCurveW[i] = pCurve.getVal(double(i) / 500.);
+ lutOpacityCurveW[i] = pCurve.getVal(double (i) / 500.);
}
}
@@ -1393,10 +3269,10 @@ void WavOpacityCurveWL::Set(const Curve &pCurve)
return;
}
- lutOpacityCurveWL(501); // raise this value if the quality suffers from this number of samples
+ lutOpacityCurveWL(501); // raise this value if the quality suffers from this number of samples
for (int i = 0; i < 501; i++) {
- lutOpacityCurveWL[i] = pCurve.getVal(double(i) / 500.);
+ lutOpacityCurveWL[i] = pCurve.getVal(double (i) / 500.);
}
}
@@ -1427,13 +3303,13 @@ void NoiseCurve::Set(const Curve &pCurve)
return;
}
- lutNoiseCurve(501); // raise this value if the quality suffers from this number of samples
+ lutNoiseCurve(501); // raise this value if the quality suffers from this number of samples
sum = 0.f;
for (int i = 0; i < 501; i++) {
- lutNoiseCurve[i] = pCurve.getVal(double(i) / 500.);
+ lutNoiseCurve[i] = pCurve.getVal(double (i) / 500.);
- if(lutNoiseCurve[i] < 0.01f) {
+ if (lutNoiseCurve[i] < 0.01f) {
lutNoiseCurve[i] = 0.01f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value
}
@@ -1507,7 +3383,7 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3],
//lr1=low;
for (int i = 0; i <= upperBound; ++i) {
- double x = double(i) / double(upperBound);
+ double x = double (i) / double (upperBound);
if (x > nextX) {
++ptNum;
@@ -1523,13 +3399,13 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3],
}
if (!ptNum) {
- Color::hsv2rgb(float(prevY), satur, lr1, r, g, b);
+ Color::hsv2rgb(float (prevY), satur, lr1, r, g, b);
Color::rgbxyz(r, g, b, xx, yy, zz, xyz_rgb);
lut1[i] = xx;
lut2[i] = yy;
lut3[i] = zz;
} else if (ptNum >= nPoints) {
- Color::hsv2rgb(float(nextY), satur, lr2, r, g, b);
+ Color::hsv2rgb(float (nextY), satur, lr2, r, g, b);
Color::rgbxyz(r, g, b, xx, yy, zz, xyz_rgb);
lut1[i] = xx;
lut2[i] = yy;
@@ -1539,8 +3415,8 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3],
if (dY > 0.000001 || dY < -0.000001) {
float r1, g1, b1, r2, g2, b2;
- Color::hsv2rgb(float(prevY), satur, lr1, r1, g1, b1);
- Color::hsv2rgb(float(nextY), satur, lr2, r2, g2, b2);
+ Color::hsv2rgb(float (prevY), satur, lr1, r1, g1, b1);
+ Color::hsv2rgb(float (nextY), satur, lr2, r2, g2, b2);
LUTf dum;
float X1, X2, Y1, Y2, Z1, Z2, L1, a_1, b_1, c1, h1;
Color::rgbxyz(r2, g2, b2, X2, Y2, Z2, xyz_rgb);
@@ -1548,34 +3424,27 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3],
//I use XYZ to mix color 1 and 2 rather than rgb (gamut) and rather than Lab artifacts
X1 = X1 + (X2 - X1) * currY / dY;
- if(X1 < 0.f) {
+ if (X1 < 0.f) {
X1 = 0.f; //negative value not good
}
Y1 = Y1 + (Y2 - Y1) * currY / dY;
- if(Y1 < 0.f) {
+ if (Y1 < 0.f) {
Y1 = 0.f;
}
Z1 = Z1 + (Z2 - Z1) * currY / dY;
- if(Z1 < 0.f) {
+ if (Z1 < 0.f) {
Z1 = 0.f;
}
- Color::XYZ2Lab(X1, Y1, Z1, L1, a_1, b_1);//prepare to gamut control
+ Color::XYZ2Lab(X1, Y1, Z1, L1, a_1, b_1); //prepare to gamut control
Color::Lab2Lch(a_1, b_1, c1, h1);
float Lr = L1 / 327.68f;
float RR, GG, BB;
-#ifndef NDEBUG
- bool neg = false;
- bool more_rgb = false;
- //gamut control : Lab values are in gamut
- Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f, neg, more_rgb);
-#else
Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f);
-#endif
L1 = Lr * 327.68f;
float La, Lb, X, Y, Z;
// converting back to rgb
@@ -1585,7 +3454,7 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3],
lut2[i] = Y;
lut3[i] = Z;
} else {
- Color::hsv2rgb(float(nextY), satur, lumin, r, g, b);
+ Color::hsv2rgb(float (nextY), satur, lumin, r, g, b);
Color::rgbxyz(r, g, b, xx, yy, zz, xyz_rgb);
lut1[i] = xx;
lut2[i] = yy;
@@ -1593,14 +3462,6 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3],
}
}
}
-
- /*
- #ifndef NDEBUG
- lutRed.dump("red");
- lutGreen.dump("green");
- lutBlue.dump("blue");
- #endif
- */
}
void ColorGradientCurve::SetXYZ(const std::vector &curvePoints, const double xyz_rgb[3][3], float satur, float lumin)
@@ -1608,7 +3469,7 @@ void ColorGradientCurve::SetXYZ(const std::vector &curvePoints, const do
std::unique_ptr tcurve;
if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
- tcurve = std::unique_ptr(new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2));
+ tcurve = std::unique_ptr (new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2));
}
if (tcurve) {
@@ -1644,7 +3505,7 @@ void ColorGradientCurve::SetRGB(const Curve *pCurve)
Color::eInterpolationDirection dir = Color::ID_DOWN;
for (int i = 0; i <= upperBound; ++i) {
- double x = double(i) / double(upperBound);
+ double x = double (i) / double (upperBound);
if (x > nextX) {
++ptNum;
@@ -1658,12 +3519,12 @@ void ColorGradientCurve::SetRGB(const Curve *pCurve)
}
if (!ptNum) {
- Color::hsv2rgb(float(prevY), 1.f, 1.f, r, g, b);
+ Color::hsv2rgb(float (prevY), 1.f, 1.f, r, g, b);
lut1[i] = r;
lut2[i] = g;
lut3[i] = b;
} else if (ptNum >= nPoints) {
- Color::hsv2rgb(float(nextY), 1.f, 1.f, r, g, b);
+ Color::hsv2rgb(float (nextY), 1.f, 1.f, r, g, b);
lut1[i] = r;
lut2[i] = g;
lut3[i] = b;
@@ -1677,29 +3538,21 @@ void ColorGradientCurve::SetRGB(const Curve *pCurve)
Color::hsv2rgb(h2, 1.f, 1.f, ro, go, bo);
#else
float r1, g1, b1, r2, g2, b2, ro, go, bo;
- Color::hsv2rgb(float(prevY), 1., 1., r1, g1, b1);
- Color::hsv2rgb(float(nextY), 1., 1., r2, g2, b2);
+ Color::hsv2rgb(float (prevY), 1., 1., r1, g1, b1);
+ Color::hsv2rgb(float (nextY), 1., 1., r2, g2, b2);
Color::interpolateRGBColor(currY / dY, r1, g1, b1, r2, g2, b2, Color::CHANNEL_LIGHTNESS | Color::CHANNEL_CHROMATICITY | Color::CHANNEL_HUE, xyz_rgb, rgb_xyz, ro, go, bo);
#endif
lut1[i] = ro;
lut2[i] = go;
lut3[i] = bo;
} else {
- Color::hsv2rgb(float(nextY), 1.f, 1.f, r, g, b);
+ Color::hsv2rgb(float (nextY), 1.f, 1.f, r, g, b);
lut1[i] = r;
lut2[i] = g;
lut3[i] = b;
}
}
}
-
- /*
- #ifndef NDEBUG
- lut1.dump("red");
- lut2.dump("green");
- lut3.dump("blue");
- #endif
- */
}
void ColorGradientCurve::SetRGB(const std::vector &curvePoints)
@@ -1707,7 +3560,7 @@ void ColorGradientCurve::SetRGB(const std::vector &curvePoints)
std::unique_ptr tcurve;
if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) {
- tcurve = std::unique_ptr(new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2));
+ tcurve = std::unique_ptr (new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2));
}
if (tcurve) {
@@ -1753,18 +3606,18 @@ void PerceptualToneCurve::cubic_spline(const float x[], const float y[], const i
A[i][len - 1] = 6 * (b[i + 1] - b[i]);
}
- for(i = 1; i < len - 2; i++) {
+ for (i = 1; i < len - 2; i++) {
float v = A[i + 1][i] / A[i][i];
- for(j = 1; j <= len - 1; j++) {
+ for (j = 1; j <= len - 1; j++) {
A[i + 1][j] -= v * A[i][j];
}
}
- for(i = len - 2; i > 0; i--) {
+ for (i = len - 2; i > 0; i--) {
float acc = 0;
- for(j = i; j <= len - 2; j++) {
+ for (j = i; j <= len - 2; j++) {
acc += A[i][j] * c[j];
}
@@ -1884,7 +3737,7 @@ float PerceptualToneCurve::calculateToneCurveContrastValue() const
// Note: the analysis is made on the gamma encoded curve, as the LUT is linear we make backwards gamma to
struct find_tc_slope_fun_arg arg = { this };
- float k = find_minimum_interval_halving(find_tc_slope_fun, &arg, 0.1, 5.0, 0.01, 20); // normally found in 8 iterations
+ float k = find_minimum_interval_halving(find_tc_slope_fun, &arg, 0.1, 5.0, 0.01, 20); // normally found in 8 iterations
//fprintf(stderr, "average slope: %f\n", k);
float maxslope = 0;
@@ -1941,7 +3794,7 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float
if (oog_r && oog_g && oog_b) {
continue;
}
-
+
float r = CLIP(rc[i]);
float g = CLIP(gc[i]);
float b = CLIP(bc[i]);
@@ -1964,17 +3817,35 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float
if (ar >= 65535.f && ag >= 65535.f && ab >= 65535.f) {
// clip fast path, will also avoid strange colours of clipped highlights
//rc[i] = gc[i] = bc[i] = 65535.f;
- if (!oog_r) rc[i] = 65535.f;
- if (!oog_g) gc[i] = 65535.f;
- if (!oog_b) bc[i] = 65535.f;
+ if (!oog_r) {
+ rc[i] = 65535.f;
+ }
+
+ if (!oog_g) {
+ gc[i] = 65535.f;
+ }
+
+ if (!oog_b) {
+ bc[i] = 65535.f;
+ }
+
continue;
}
if (ar <= 0.f && ag <= 0.f && ab <= 0.f) {
//rc[i] = gc[i] = bc[i] = 0;
- if (!oog_r) rc[i] = 0.f;
- if (!oog_g) gc[i] = 0.f;
- if (!oog_b) bc[i] = 0.f;
+ if (!oog_r) {
+ rc[i] = 0.f;
+ }
+
+ if (!oog_g) {
+ gc[i] = 0.f;
+ }
+
+ if (!oog_b) {
+ bc[i] = 0.f;
+ }
+
continue;
}
@@ -1997,11 +3868,11 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float
Color::Prophotoxyz(r, g, b, x, y, z);
float J, C, h;
- Ciecam02::xyz2jch_ciecam02float( J, C, h,
- aw, fl,
- x * 0.0015259022f, y * 0.0015259022f, z * 0.0015259022f,
- xw, yw, zw,
- c, nc, pow1, nbb, ncb, cz, d);
+ Ciecam02::xyz2jch_ciecam02float(J, C, h,
+ aw, fl,
+ x * 0.0015259022f, y * 0.0015259022f, z * 0.0015259022f,
+ xw, yw, zw,
+ c, nc, pow1, nbb, ncb, cz, d);
if (!isfinite(J) || !isfinite(C) || !isfinite(h)) {
@@ -2014,9 +3885,18 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float
g = newg;
b = newb;
}
- if (!oog_r) rc[i] = r;
- if (!oog_g) gc[i] = g;
- if (!oog_b) bc[i] = b;
+
+ if (!oog_r) {
+ rc[i] = r;
+ }
+
+ if (!oog_g) {
+ gc[i] = g;
+ }
+
+ if (!oog_b) {
+ bc[i] = b;
+ }
continue;
}
@@ -2108,10 +3988,10 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float
C *= cmul;
- Ciecam02::jch2xyz_ciecam02float( x, y, z,
- J, C, h,
- xw, yw, zw,
- c, nc, pow1, nbb, ncb, fl, cz, d, aw );
+ Ciecam02::jch2xyz_ciecam02float(x, y, z,
+ J, C, h,
+ xw, yw, zw,
+ c, nc, pow1, nbb, ncb, fl, cz, d, aw);
if (!isfinite(x) || !isfinite(y) || !isfinite(z)) {
// can happen for colours on the rim of being outside gamut, that worked without chroma scaling but not with. Then we return only the curve's result.
@@ -2124,9 +4004,17 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float
b = newb;
}
- if (!oog_r) rc[i] = r;
- if (!oog_g) gc[i] = g;
- if (!oog_b) bc[i] = b;
+ if (!oog_r) {
+ rc[i] = r;
+ }
+
+ if (!oog_g) {
+ gc[i] = g;
+ }
+
+ if (!oog_b) {
+ bc[i] = b;
+ }
continue;
}
@@ -2187,9 +4075,18 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float
g = newg;
b = newb;
}
- if (!oog_r) rc[i] = r;
- if (!oog_g) gc[i] = g;
- if (!oog_b) bc[i] = b;
+
+ if (!oog_r) {
+ rc[i] = r;
+ }
+
+ if (!oog_g) {
+ gc[i] = g;
+ }
+
+ if (!oog_b) {
+ bc[i] = b;
+ }
}
}
float PerceptualToneCurve::cf_range[2];
@@ -2212,7 +4109,7 @@ void PerceptualToneCurve::init()
Ciecam02::initcam1float(yb, 1.f, f, la, xw, yw, zw, n, d, nbb, ncb,
cz, aw, wh, pfl, fl, c);
- pow1 = pow_F( 1.64f - pow_F( 0.29f, n ), 0.73f );
+ pow1 = pow_F(1.64f - pow_F(0.29f, n), 0.73f);
{
// init contrast-value-to-chroma-scaling conversion curve
@@ -2280,7 +4177,7 @@ void PerceptualToneCurve::initApplyState(PerceptualToneCurveState & state, const
state.Working2Prophoto[i][j] += prophoto_xyz[i][k] * Work[k][j];
}
- Work = ICCStore::getInstance()->workingSpaceInverseMatrix (workingSpace);
+ Work = ICCStore::getInstance()->workingSpaceInverseMatrix(workingSpace);
memset(state.Prophoto2Working, 0, sizeof(state.Prophoto2Working));
for (int i = 0; i < 3; i++)
diff --git a/rtengine/curves.h b/rtengine/curves.h
index 0fb58cc9e..9eb3d8e56 100644
--- a/rtengine/curves.h
+++ b/rtengine/curves.h
@@ -74,7 +74,8 @@ inline void setUnlessOOG(vfloat &r, vfloat &g, vfloat &b, const vfloat rr, const
bool sanitizeCurve(std::vector& curve);
-namespace curves {
+namespace curves
+{
inline void setLutVal(const LUTf &lut, float &val)
{
@@ -124,33 +125,33 @@ class CurveFactory
protected:
// functions calculating the parameters of the contrast curve based on the desired slope at the center
- static double solve_upper (double m, double c, double deriv);
- static double solve_lower (double m, double c, double deriv);
- static double dupper (const double b, const double m, const double c);
- static double dlower (const double b, const double m, const double c);
+ static double solve_upper(double m, double c, double deriv);
+ static double solve_lower(double m, double c, double deriv);
+ static double dupper(const double b, const double m, const double c);
+ static double dlower(const double b, const double m, const double c);
// basic convex function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point
- static inline double basel (double x, double m1, double m2)
+ static inline double basel(double x, double m1, double m2)
{
if (x == 0.0) {
return 0.0;
}
- double k = sqrt ((m1 - 1.0) * (m1 - m2) * 0.5) / (1.0 - m2);
+ double k = sqrt((m1 - 1.0) * (m1 - m2) * 0.5) / (1.0 - m2);
double l = (m1 - m2) / (1.0 - m2) + k;
double lx = xlog(x);
return m2 * x + (1.0 - m2) * (2.0 - xexp(k * lx)) * xexp(l * lx);
}
// basic concave function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point
- static inline double baseu (double x, double m1, double m2)
+ static inline double baseu(double x, double m1, double m2)
{
return 1.0 - basel(1.0 - x, m1, m2);
}
// convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery
- static inline double cupper (double x, double m, double hr)
+ static inline double cupper(double x, double m, double hr)
{
if (hr > 1.0) {
- return baseu (x, m, 2.0 * (hr - 1.0) / m);
+ return baseu(x, m, 2.0 * (hr - 1.0) / m);
}
double x1 = (1.0 - hr) / m;
@@ -167,12 +168,12 @@ protected:
return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0);
}
// concave curve between (0,0) and (1,1) with slope m at (1,1). sr controls the shadow recovery
- static inline double clower (double x, double m, double sr)
+ static inline double clower(double x, double m, double sr)
{
return 1.0 - cupper(1.0 - x, m, sr);
}
// convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery
- static inline double cupper2 (double x, double m, double hr)
+ static inline double cupper2(double x, double m, double hr)
{
double x1 = (1.0 - hr) / m;
double x2 = x1 + hr;
@@ -187,7 +188,7 @@ protected:
return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0.3 * hr);
}
- static inline double clower2 (double x, double m, double sr)
+ static inline double clower2(double x, double m, double sr)
{
//curve for b<0; starts with positive slope and then rolls over toward straight line to x=y=1
double x1 = sr / 1.5 + 0.00001;
@@ -201,7 +202,7 @@ protected:
}
// tone curve base. a: slope (from exp.comp.), b: black point normalized by 65535,
// D: max. x value (can be>1), hr,sr: highlight,shadow recovery
- static inline double basecurve (double x, double a, double b, double D, double hr, double sr)
+ static inline double basecurve(double x, double a, double b, double D, double hr, double sr)
{
if (b < 0) {
double m = 0.5;//midpoint
@@ -219,7 +220,7 @@ protected:
double y = a * D > 1.0 ? 0.25 : (m - b / a) * slope;
if (x <= m) {
- return b == 0 ? x * slope : clower (x / m, slope * m / y, sr) * y;
+ return b == 0 ? x * slope : clower(x / m, slope * m / y, sr) * y;
} else if (a * D > 1.0) {
return y + (1.0 - y) * cupper2((x - m) / (D - m), slope * (D - m) / (1.0 - y), hr);
} else {
@@ -227,7 +228,7 @@ protected:
}
}
}
- static inline double simplebasecurve (double x, double b, double sr)
+ static inline double simplebasecurve(double x, double b, double sr)
{
// a = 1, D = 1, hr = 0 (unused for a = D = 1)
if (b == 0.0) {
@@ -248,7 +249,7 @@ protected:
double y = (m - b) * slope;
if (x <= m) {
- return clower (x / m, slope * m / y, sr) * y;
+ return clower(x / m, slope * m / y, sr) * y;
} else {
return y + (x - m) * slope;
}
@@ -281,58 +282,58 @@ public:
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- static inline double centercontrast (double x, double b, double m);
+ static inline double centercontrast(double x, double b, double m);
// standard srgb gamma and its inverse
- static inline double gamma2 (double x)
+ static inline double gamma2(double x)
{
return x <= 0.00304 ? x * 12.92310 : 1.055 * exp(log(x) / sRGBGammaCurve) - 0.055;
}
- static inline double igamma2 (double x)
+ static inline double igamma2(double x)
{
return x <= 0.03928 ? x / 12.92310 : exp(log((x + 0.055) / 1.055) * sRGBGammaCurve);
}
- static inline float gamma2 (float x)
+ static inline float gamma2(float x)
{
return x <= 0.00304f ? x * 12.92310f : 1.055f * expf(logf(x) / static_cast(sRGBGammaCurve)) - 0.055f;
}
- static inline float igamma2 (float x)
+ static inline float igamma2(float x)
{
return x <= 0.03928f ? x / 12.92310f : expf(logf((x + 0.055f) / 1.055f) * static_cast(sRGBGammaCurve));
}
// gamma function with adjustable parameters
- static inline double gamma (double x, double gamma, double start, double slope, double mul, double add)
+ static inline double gamma(double x, double gamma, double start, double slope, double mul, double add)
{
return (x <= start ? x*slope : exp(log(x) / gamma) * mul - add);
}
- static inline double igamma (double x, double gamma, double start, double slope, double mul, double add)
+ static inline double igamma(double x, double gamma, double start, double slope, double mul, double add)
{
- return (x <= start * slope ? x / slope : exp(log((x + add) / mul) * gamma) );
+ return (x <= start * slope ? x / slope : exp(log((x + add) / mul) * gamma));
}
- static inline float gamma (float x, float gamma, float start, float slope, float mul, float add)
+ static inline float gamma(float x, float gamma, float start, float slope, float mul, float add)
{
return (x <= start ? x*slope : xexpf(xlogf(x) / gamma) * mul - add);
}
- static inline float igamma (float x, float gamma, float start, float slope, float mul, float add)
+ static inline float igamma(float x, float gamma, float start, float slope, float mul, float add)
{
- return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma) );
+ return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma));
}
#ifdef __SSE2__
- static inline vfloat igamma (vfloat x, vfloat gamma, vfloat start, vfloat slope, vfloat mul, vfloat add)
+ static inline vfloat igamma(vfloat x, vfloat gamma, vfloat start, vfloat slope, vfloat mul, vfloat add)
{
#if !defined(__clang__)
- return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma) );
+ return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma));
#else
return vself(vmaskf_le(x, start * slope), x / slope, xexpf(xlogf((x + add) / mul) * gamma));
#endif
}
#endif
- static inline float hlcurve (const float exp_scale, const float comp, const float hlrange, float level)
+ static inline float hlcurve(const float exp_scale, const float comp, const float hlrange, float level)
{
if (comp > 0.f) {
float val = level + (hlrange - 65536.f);
- if(val == 0.0f) { // to avoid division by zero
+ if (val == 0.0f) { // to avoid division by zero
val = 0.000001f;
}
@@ -350,29 +351,49 @@ public:
}
}
+
public:
- static void complexCurve (double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double contr,
- const std::vector& curvePoints, const std::vector& curvePoints2,
- const LUTu & histogram, LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, ToneCurve & outToneCurve, ToneCurve & outToneCurve2,
+ static void complexCurve(double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double contr,
+ const std::vector& curvePoints, const std::vector& curvePoints2,
+ const LUTu & histogram, LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, ToneCurve & outToneCurve, ToneCurve & outToneCurve2,
+ int skip = 1);
- int skip = 1);
- static void curveBW (const std::vector& curvePointsbw, const std::vector& curvePointsbw2, const LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw,
- ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip);
-
- static void curveCL ( bool & clcutili, const std::vector& clcurvePoints, LUTf & clCurve, int skip);
-
- static void curveWavContL ( bool & wavcontlutili, const std::vector& wavclcurvePoints, LUTf & wavclCurve,/* LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,*/int skip);
- static void curveDehaContL ( bool & dehacontlutili, const std::vector& dehaclcurvePoints, LUTf & dehaclCurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram);
- static void mapcurve ( bool & mapcontlutili, const std::vector& mapcurvePoints, LUTf & mapcurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram);
-
- static void curveToning ( const std::vector& curvePoints, LUTf & ToningCurve, int skip);
-
- static void complexsgnCurve ( bool & autili, bool & butili, bool & ccutili, bool & clcutili, const std::vector& acurvePoints,
- const std::vector& bcurvePoints, const std::vector& cccurvePoints, const std::vector& lccurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve,
+ static void complexCurvelocal(double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double cont, double lumare,
+ LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTf & lightCurveloc, float avg,
int skip = 1);
- static void complexLCurve (double br, double contr, const std::vector& curvePoints, const LUTu & histogram, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, int skip, bool & utili);
- static void curveLightBrightColor (
+ static void Curvelocalhl(double ecomp, double hlcompr, double hlcomprthresh, LUTf & hlCurve);
+
+ static void curveBW(const std::vector& curvePointsbw, const std::vector& curvePointsbw2, const LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw,
+ ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip);
+
+ static void curveCL(bool & clcutili, const std::vector& clcurvePoints, LUTf & clCurve, int skip);
+
+ static void curveWavContL(bool & wavcontlutili, const std::vector& wavclcurvePoints, LUTf & wavclCurve,/* LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,*/int skip);
+ static void curveDehaContL(bool & dehacontlutili, const std::vector& dehaclcurvePoints, LUTf & dehaclCurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram);
+ static void mapcurve(bool & mapcontlutili, const std::vector& mapcurvePoints, LUTf & mapcurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram);
+
+ static void curveToning(const std::vector& curvePoints, LUTf & ToningCurve, int skip);
+
+ static void curveLocal(bool & locallutili, const std::vector& curvePoints, LUTf & LocalLCurve, int skip);
+ static void curveCCLocal(bool & localcutili, const std::vector& curvePoints, LUTf & LocalCCurve, int skip);
+ static void curveskLocal(bool & localskutili, const std::vector& curvePoints, LUTf & LocalskCurve, int skip);
+ static void curveexLocal(bool & localexutili, const std::vector& curvePoints, LUTf & LocalexCurve, int skip);
+ static void curvemaskLocal(bool & localmaskutili, const std::vector& curvePoints, LUTf & LocalmaskCurve, int skip);
+
+ static void complexsgnCurve(bool & autili, bool & butili, bool & ccutili, bool & clcutili, const std::vector& acurvePoints,
+ const std::vector& bcurvePoints, const std::vector& cccurvePoints, const std::vector& lccurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve,
+ int skip = 1);
+
+ static void localLCurve(double br, double contr,/* const std::vector& curvePoints,*/ LUTu & histogram, LUTf & outCurve, int skip, bool & utili);
+
+ static void updatechroma(
+ const std::vector& cccurvePoints,
+ LUTu & histogramC, LUTu & outBeforeCCurveHistogramC,//for chroma
+ int skip = 1);
+ static void complexLCurve(double br, double contr, const std::vector& curvePoints, const LUTu & histogram, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, int skip, bool & utili);
+
+ static void curveLightBrightColor(
const std::vector& curvePoints,
const std::vector& curvePoints2,
const std::vector& curvePoints3,
@@ -382,7 +403,7 @@ public:
ColorAppearance & outColCurve2,
ColorAppearance & outColCurve3,
int skip = 1);
- static void RGBCurve (const std::vector& curvePoints, LUTf & outCurve, int skip);
+ static void RGBCurve(const std::vector& curvePoints, LUTf & outCurve, int skip);
};
@@ -420,23 +441,23 @@ protected:
double increment;
int nbr_points;
- static inline double p00 (double x, double prot)
+ static inline double p00(double x, double prot)
{
- return CurveFactory::clower (x, 2.0, prot);
+ return CurveFactory::clower(x, 2.0, prot);
}
- static inline double p11 (double x, double prot)
+ static inline double p11(double x, double prot)
{
- return CurveFactory::cupper (x, 2.0, prot);
+ return CurveFactory::cupper(x, 2.0, prot);
}
- static inline double p01 (double x, double prot)
+ static inline double p01(double x, double prot)
{
- return x <= 0.5 ? CurveFactory::clower (x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::cupper ((x - 0.5) * 2, 2.0, prot) * 0.5;
+ return x <= 0.5 ? CurveFactory::clower(x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::cupper((x - 0.5) * 2, 2.0, prot) * 0.5;
}
- static inline double p10 (double x, double prot)
+ static inline double p10(double x, double prot)
{
- return x <= 0.5 ? CurveFactory::cupper (x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::clower ((x - 0.5) * 2, 2.0, prot) * 0.5;
+ return x <= 0.5 ? CurveFactory::cupper(x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::clower((x - 0.5) * 2, 2.0, prot) * 0.5;
}
- static inline double pfull (double x, double prot, double sh, double hl)
+ static inline double pfull(double x, double prot, double sh, double hl)
{
return (1 - sh) * (1 - hl) * p00(x, prot) + sh * hl * p11(x, prot) + (1 - sh) * hl * p01(x, prot) + sh * (1 - hl) * p10(x, prot);
}
@@ -445,15 +466,15 @@ protected:
void fillDyByDx();
public:
- Curve ();
- virtual ~Curve () {};
- void AddPolygons ();
- int getSize () const; // return the number of control points
+ Curve();
+ virtual ~Curve() {};
+ void AddPolygons();
+ int getSize() const; // return the number of control points
void getControlPoint(int cpNum, double &x, double &y) const;
- virtual double getVal (double t) const = 0;
- virtual void getVal (const std::vector& t, std::vector& res) const = 0;
+ virtual double getVal(double t) const = 0;
+ virtual void getVal(const std::vector& t, std::vector& res) const = 0;
- virtual bool isIdentity () const = 0;
+ virtual bool isIdentity() const = 0;
};
class DiagonalCurve final : public Curve
@@ -462,23 +483,23 @@ class DiagonalCurve final : public Curve
protected:
DiagonalCurveType kind;
- void spline_cubic_set ();
+ void spline_cubic_set();
void catmull_rom_set();
- void NURBS_set ();
+ void NURBS_set();
public:
- explicit DiagonalCurve (const std::vector& points, int ppn = CURVES_MIN_POLY_POINTS);
- ~DiagonalCurve () override;
+ DiagonalCurve(const std::vector& points, int ppn = CURVES_MIN_POLY_POINTS);
+ ~DiagonalCurve() override;
- double getVal (double t) const override;
- void getVal (const std::vector& t, std::vector& res) const override;
- bool isIdentity () const override
+ double getVal(double t) const override;
+ void getVal(const std::vector& t, std::vector& res) const override;
+ bool isIdentity() const override
{
return kind == DCT_Empty;
};
};
-class FlatCurve final : public Curve, public rtengine::NonCopyable
+class FlatCurve final : public Curve
{
private:
@@ -488,17 +509,17 @@ private:
double identityValue;
bool periodic;
- void CtrlPoints_set ();
+ void CtrlPoints_set();
public:
- explicit FlatCurve (const std::vector