diff --git a/AboutThisBuild.txt.in b/AboutThisBuild.txt.in
index ea3269c09..f48d39b50 100644
--- a/AboutThisBuild.txt.in
+++ b/AboutThisBuild.txt.in
@@ -7,6 +7,7 @@ Processor: ${PROC_LABEL}
System: ${SYSTEM}
Bit depth: ${PROC_BIT_DEPTH}
Gtkmm: V${GTKMM_VERSION}
+Lensfun: V${LENSFUN_VERSION}
Build type: ${BUILD_TYPE}
Build flags: ${CXX_FLAGS}
Link flags: ${LFLAGS}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 685b418bf..333e292f0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -272,6 +272,7 @@ pkg_check_modules (GIOMM REQUIRED giomm-2.4>=2.44)
pkg_check_modules (GTHREAD REQUIRED gthread-2.0>=2.44)
pkg_check_modules (GOBJECT REQUIRED gobject-2.0>=2.44)
pkg_check_modules (SIGC REQUIRED sigc++-2.0>=2.3.1)
+pkg_check_modules (LENSFUN REQUIRED lensfun>=0.2)
if(WIN32)
add_definitions(-DWIN32)
@@ -389,7 +390,8 @@ set(ABOUT_COMMAND_WITH_ARGS ${CMAKE_COMMAND}
-DBUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
-DGTKMM_VERSION:STRING=${GTKMM_VERSION}
-DOPTION_OMP:STRING=${OPTION_OMP}
- -DWITH_MYFILE_MMAP:STRING=${WITH_MYFILE_MMAP})
+ -DWITH_MYFILE_MMAP:STRING=${WITH_MYFILE_MMAP}
+ -DLENSFUN_VERSION:STRING=${LENSFUN_VERSION})
if(WIN32)
list(APPEND ABOUT_COMMAND_WITH_ARGS -DSYSTEM:STRING=Windows
diff --git a/rtdata/languages/default b/rtdata/languages/default
index 34c1bbf22..fc17e8571 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -281,9 +281,9 @@ HISTORY_MSG_30;RLD - Radius
HISTORY_MSG_31;RLD - Amount
HISTORY_MSG_32;RLD - Damping
HISTORY_MSG_33;RLD - Iterations
-HISTORY_MSG_34;LCP distortion correction
-HISTORY_MSG_35;LCP vignetting correction
-HISTORY_MSG_36;LCP CA correction
+HISTORY_MSG_34;Lens Correction - Distortion
+HISTORY_MSG_35;Lens Correction - Vignetting
+HISTORY_MSG_36;Lens Correction - CA
HISTORY_MSG_37;Exposure - Auto levels
HISTORY_MSG_38;White Balance - Method
HISTORY_MSG_39;WB - Temperature
@@ -332,7 +332,7 @@ HISTORY_MSG_81;Resize
HISTORY_MSG_82;Profile changed
HISTORY_MSG_83;S/H - Sharp mask
HISTORY_MSG_84;Perspective correction
-HISTORY_MSG_85;LCP
+HISTORY_MSG_85;Lens Correction - LCP file
HISTORY_MSG_86;RGB Curves - Luminosity mode
HISTORY_MSG_87;Impulse Noise Reduction
HISTORY_MSG_88;Impulse NR threshold
@@ -731,94 +731,97 @@ 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;Local L*a*b*
-HISTORY_MSG_486;Local - Bottom
-HISTORY_MSG_487;Local - Right
-HISTORY_MSG_488;Local - Center
-HISTORY_MSG_489;Local - Lightness/
-HISTORY_MSG_490;Local - Lightness
-HISTORY_MSG_491;Local - Contrast
-HISTORY_MSG_492;Local - Chrominance
-HISTORY_MSG_493;Local - Transition
-HISTORY_MSG_494;Local - Avoid color shift
-HISTORY_MSG_495;Local - Top
-HISTORY_MSG_496;Local - Left
-HISTORY_MSG_497;Local - Method
-HISTORY_MSG_498;Local - Color Inverse
-HISTORY_MSG_499;Local - Radius
-HISTORY_MSG_500;Local - Blur Inverse
-HISTORY_MSG_501;Local - Noise
-HISTORY_MSG_502;Local - Scope
-HISTORY_MSG_503;Local - Retinex method
-HISTORY_MSG_504;Local - Retinex strength
-HISTORY_MSG_505;Local - Retinex radius
-HISTORY_MSG_506;Local - Retinex contrast
-HISTORY_MSG_507;Local - Retinex Gain curve
-HISTORY_MSG_508;Local - Retinex chroma
-HISTORY_MSG_509;Local - Retinex Inverse
-HISTORY_MSG_510;Local - Hue scope
-HISTORY_MSG_511;Local - Spot
-HISTORY_MSG_512;Local - Blur Luminance only
-HISTORY_MSG_513;Local - Update GUI and Mip -1
-HISTORY_MSG_514;Local - Sh Radius
-HISTORY_MSG_515;Local - Sh Amount
-HISTORY_MSG_516;Local - Sh Damping
-HISTORY_MSG_517;Local - Sh Iterations
-HISTORY_MSG_518;Local - Sh Scope
-HISTORY_MSG_519;Local - Sh Inverse
-HISTORY_MSG_520;Local - Spot size
-HISTORY_MSG_521;Local - artifacts theshold
-HISTORY_MSG_522;Local - artifacts iterations
-HISTORY_MSG_523;Local - Quality
-HISTORY_MSG_524;Local - Noise lum f
-HISTORY_MSG_525;Local - Noise lum c
-HISTORY_MSG_526;Local - Noise chro f
-HISTORY_MSG_527;Local - Noise chro c
-HISTORY_MSG_528;Local - cbdl threshold
-HISTORY_MSG_529;Local - cbdl mult
-HISTORY_MSG_530;Local - cbdl scope
-HISTORY_MSG_531;Local - Blur scope
-HISTORY_MSG_532;Local - TM strength
-HISTORY_MSG_533;Local - TM gamma
-HISTORY_MSG_534;Local - TM edge stopping
-HISTORY_MSG_535;Local - TM scale
-HISTORY_MSG_536;Local - TM Reweighting
-HISTORY_MSG_537;Local - TM scope
-HISTORY_MSG_538;Local - Update GUI and Mip -2
-HISTORY_MSG_539;Local - Update GUI and Mip -3
-HISTORY_MSG_540;Local - LL Curve
-HISTORY_MSG_541;Local - Color and light
-HISTORY_MSG_542;Local - Blur and noise
-HISTORY_MSG_543;Local - Tone mapping
-HISTORY_MSG_544;Local - Retinex
-HISTORY_MSG_545;Local - Sharpening
-HISTORY_MSG_546;Local - CBDL
-HISTORY_MSG_547;Local - Denoise
-HISTORY_MSG_548;Local - LH Curve
-HISTORY_MSG_549;Local - Enable super
-HISTORY_MSG_550;Local - CC curve
-HISTORY_MSG_551;Local - curve method
-HISTORY_MSG_552;Local - hueref
-HISTORY_MSG_553;Local - chromaref
-HISTORY_MSG_554;Local - lumaref
-HISTORY_MSG_555;Local - H curve
-HISTORY_MSG_556;Local - Vibrance
-HISTORY_MSG_557;Local - Vib H curve
-HISTORY_MSG_558;Local - Vib Protect skin tones
-HISTORY_MSG_559;Local - Vib avoid colorshift
-HISTORY_MSG_560;Local - Vib link
-HISTORY_MSG_561;Local - Vib Pastel
-HISTORY_MSG_562;Local - Vib Saturated
-HISTORY_MSG_563;Local - Vib Threshold
-HISTORY_MSG_564;Local - Vib Scope
-HISTORY_MSG_565;Local - Exposure
-HISTORY_MSG_566;Local - Exp Compensation
-HISTORY_MSG_567;Local - Exp Hlcompr
-HISTORY_MSG_568;Local - Exp hlcomprthresh
-HISTORY_MSG_569;Local - Exp black
-HISTORY_MSG_570;Local - Exp Shcompr
-HISTORY_MSG_571;Local - Exp Scope
-HISTORY_MSG_572;Local - Exp Contrast curve
+HISTORY_MSG_485;Lens Correction
+HISTORY_MSG_486;Lens Correction - Camera
+HISTORY_MSG_487;Lens Correction - Lens
+HISTORY_MSG_488;Local L*a*b*
+HISTORY_MSG_489;Local - Bottom
+HISTORY_MSG_490;Local - Right
+HISTORY_MSG_491;Local - Center
+HISTORY_MSG_492;Local - Lightness/
+HISTORY_MSG_493;Local - Lightness
+HISTORY_MSG_494;Local - Contrast
+HISTORY_MSG_495;Local - Chrominance
+HISTORY_MSG_496;Local - Transition
+HISTORY_MSG_497;Local - Avoid color shift
+HISTORY_MSG_498;Local - Top
+HISTORY_MSG_499;Local - Left
+HISTORY_MSG_500;Local - Method
+HISTORY_MSG_501;Local - Color Inverse
+HISTORY_MSG_502;Local - Radius
+HISTORY_MSG_503;Local - Blur Inverse
+HISTORY_MSG_504;Local - Noise
+HISTORY_MSG_505;Local - Scope
+HISTORY_MSG_506;Local - Retinex method
+HISTORY_MSG_507;Local - Retinex strength
+HISTORY_MSG_508;Local - Retinex radius
+HISTORY_MSG_509;Local - Retinex contrast
+HISTORY_MSG_510;Local - Retinex Gain curve
+HISTORY_MSG_511;Local - Retinex chroma
+HISTORY_MSG_512;Local - Retinex Inverse
+HISTORY_MSG_513;Local - Hue scope
+HISTORY_MSG_514;Local - Spot
+HISTORY_MSG_515;Local - Blur Luminance only
+HISTORY_MSG_516;Local - Update GUI and Mip -1
+HISTORY_MSG_517;Local - Sh Radius
+HISTORY_MSG_518;Local - Sh Amount
+HISTORY_MSG_519;Local - Sh Damping
+HISTORY_MSG_520;Local - Sh Iterations
+HISTORY_MSG_521;Local - Sh Scope
+HISTORY_MSG_522;Local - Sh Inverse
+HISTORY_MSG_523;Local - Spot size
+HISTORY_MSG_524;Local - artifacts theshold
+HISTORY_MSG_525;Local - artifacts iterations
+HISTORY_MSG_526;Local - Quality
+HISTORY_MSG_527;Local - Noise lum f
+HISTORY_MSG_528;Local - Noise lum c
+HISTORY_MSG_529;Local - Noise chro f
+HISTORY_MSG_530;Local - Noise chro c
+HISTORY_MSG_531;Local - cbdl threshold
+HISTORY_MSG_532;Local - cbdl mult
+HISTORY_MSG_533;Local - cbdl scope
+HISTORY_MSG_534;Local - Blur scope
+HISTORY_MSG_535;Local - TM strength
+HISTORY_MSG_536;Local - TM gamma
+HISTORY_MSG_537;Local - TM edge stopping
+HISTORY_MSG_538;Local - TM scale
+HISTORY_MSG_539;Local - TM Reweighting
+HISTORY_MSG_540;Local - TM scope
+HISTORY_MSG_541;Local - Update GUI and Mip -2
+HISTORY_MSG_542;Local - Update GUI and Mip -3
+HISTORY_MSG_543;Local - LL Curve
+HISTORY_MSG_544;Local - Color and light
+HISTORY_MSG_545;Local - Blur and noise
+HISTORY_MSG_546;Local - Tone mapping
+HISTORY_MSG_547;Local - Retinex
+HISTORY_MSG_548;Local - Sharpening
+HISTORY_MSG_549;Local - CBDL
+HISTORY_MSG_550;Local - Denoise
+HISTORY_MSG_551;Local - LH Curve
+HISTORY_MSG_552;Local - Enable super
+HISTORY_MSG_553;Local - CC curve
+HISTORY_MSG_554;Local - curve method
+HISTORY_MSG_555;Local - hueref
+HISTORY_MSG_556;Local - chromaref
+HISTORY_MSG_557;Local - lumaref
+HISTORY_MSG_558;Local - H curve
+HISTORY_MSG_559;Local - Vibrance
+HISTORY_MSG_560;Local - Vib H curve
+HISTORY_MSG_561;Local - Vib Protect skin tones
+HISTORY_MSG_562;Local - Vib avoid colorshift
+HISTORY_MSG_563;Local - Vib link
+HISTORY_MSG_564;Local - Vib Pastel
+HISTORY_MSG_565;Local - Vib Saturated
+HISTORY_MSG_566;Local - Vib Threshold
+HISTORY_MSG_567;Local - Vib Scope
+HISTORY_MSG_568;Local - Exposure
+HISTORY_MSG_569;Local - Exp Compensation
+HISTORY_MSG_570;Local - Exp Hlcompr
+HISTORY_MSG_571;Local - Exp hlcomprthresh
+HISTORY_MSG_572;Local - Exp black
+HISTORY_MSG_573;Local - Exp Shcompr
+HISTORY_MSG_574;Local - Exp Scope
+HISTORY_MSG_575;Local - Exp Contrast curve
HISTORY_NEWSNAPSHOT;Add
HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s
HISTORY_SNAPSHOT;Snapshot
@@ -993,9 +996,9 @@ PARTIALPASTE_IMPULSEDENOISE;Impulse noise reduction
PARTIALPASTE_IPTCINFO;IPTC
PARTIALPASTE_LABCURVE;L*a*b* adjustments
PARTIALPASTE_LENSGROUP;Lens Related Settings
-PARTIALPASTE_LENSPROFILE;Lens correction profile
PARTIALPASTE_LOCALLAB;Local L*a*b*
PARTIALPASTE_LOCGROUP;Local
+PARTIALPASTE_LENSPROFILE;Profiled lens correction
PARTIALPASTE_METAGROUP;Metadata
PARTIALPASTE_PCVIGNETTE;Vignette filter
PARTIALPASTE_PERSPECTIVE;Perspective
@@ -1774,7 +1777,7 @@ TP_LABCURVE_RSTPRO_TOOLTIP;Works on the Chromaticity slider and the CC curve.
TP_LENSGEOM_AUTOCROP;Auto-Crop
TP_LENSGEOM_FILL;Auto-fill
TP_LENSGEOM_LABEL;Lens / Geometry
-TP_LENSPROFILE_LABEL;Lens Correction Profile
+TP_LENSPROFILE_LABEL;Profiled Lens Correction
TP_LENSPROFILE_USECA;Chromatic aberration correction
TP_LENSPROFILE_USEDIST;Distortion correction
TP_LENSPROFILE_USEVIGN;Vignetting correction
@@ -2359,3 +2362,7 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f
ZOOMPANEL_ZOOMFITSCREEN;Fit whole image to screen\nShortcut: f
ZOOMPANEL_ZOOMIN;Zoom In\nShortcut: +
ZOOMPANEL_ZOOMOUT;Zoom Out\nShortcut: -
+LENSPROFILE_CORRECTION_AUTOMATCH;Auto-matched correction parameters
+LENSPROFILE_CORRECTION_MANUAL;Manual correction parameters
+LENSPROFILE_CORRECTION_LCPFILE;LCP File
+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.
\ No newline at end of file
diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt
index b7a081d7c..32d7a4179 100644
--- a/rtengine/CMakeLists.txt
+++ b/rtengine/CMakeLists.txt
@@ -9,6 +9,7 @@ include_directories(${EXTRA_INCDIR}
${GTK_INCLUDE_DIRS}
${IPTCDATA_INCLUDE_DIRS}
${LCMS_INCLUDE_DIRS}
+ ${LENSFUN_INCLUDE_DIRS}
)
link_directories("${PROJECT_SOURCE_DIR}/rtexif"
@@ -110,6 +111,7 @@ set(RTENGINESOURCEFILES
slicer.cc
stdimagesource.cc
utils.cc
+ rtlensfun.cc
)
if(NOT WITH_SYSTEM_KLT)
@@ -154,6 +156,7 @@ target_link_libraries(rtengine rtexif
${PNG_LIBRARIES}
${TIFF_LIBRARIES}
${ZLIB_LIBRARIES}
+ ${LENSFUN_LIBRARIES}
)
install(FILES ${CAMCONSTSFILE} DESTINATION "${DATADIR}" PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
diff --git a/rtengine/ciecam02.cc b/rtengine/ciecam02.cc
index 488093060..625b77f05 100644
--- a/rtengine/ciecam02.cc
+++ b/rtengine/ciecam02.cc
@@ -178,6 +178,8 @@ void Ciecam02::curveJ (double br, double contr, int db, LUTf & outCurve, LUTu &
for (int i = 0; i < (db * 32768); i++) {
outCurve[i] = db * 32768.0 * dcurve[i];
}
+// printf("double out500=%f out15000=%f\n", outCurve[500], outCurve[15000]);
+
}
void Ciecam02::curveJfloat (float br, float contr, const LUTu & histogram, LUTf & outCurve)
@@ -268,6 +270,8 @@ void Ciecam02::curveJfloat (float br, float contr, const LUTu & histogram, LUTf
}
outCurve *= 32767.f;
+ //printf("out500=%f out15000=%f\n", outCurve[500], outCurve[15000]);
+ //outCurve.dump("brig");
}
/**
diff --git a/rtengine/clutstore.cc b/rtengine/clutstore.cc
index ba117a2fd..5731773a4 100644
--- a/rtengine/clutstore.cc
+++ b/rtengine/clutstore.cc
@@ -305,7 +305,7 @@ rtengine::CLUTStore& rtengine::CLUTStore::getInstance()
return instance;
}
-std::shared_ptr rtengine::CLUTStore::getClut(const Glib::ustring& filename)
+std::shared_ptr rtengine::CLUTStore::getClut(const Glib::ustring& filename) const
{
std::shared_ptr result;
diff --git a/rtengine/clutstore.h b/rtengine/clutstore.h
index 5e4930fa1..a43526f78 100644
--- a/rtengine/clutstore.h
+++ b/rtengine/clutstore.h
@@ -57,14 +57,14 @@ class CLUTStore final :
public:
static CLUTStore& getInstance();
- std::shared_ptr getClut(const Glib::ustring& filename);
+ std::shared_ptr getClut(const Glib::ustring& filename) const;
void clearCache();
private:
CLUTStore();
- Cache> cache;
+ mutable Cache> cache;
};
}
diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc
index a70c2d2dc..5bda02f86 100644
--- a/rtengine/dcraw.cc
+++ b/rtengine/dcraw.cc
@@ -2562,6 +2562,12 @@ void CLASS kodak_radc_load_raw()
((short *)buf)[i] = 2048;
for (row=0; row < height; row+=4) {
FORC3 mul[c] = getbits(6);
+ FORC3 {
+ if (!mul[c]) {
+ mul[c] = 1;
+ derror();
+ }
+ }
FORC3 {
val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c];
s = val > 65564 ? 10:12;
@@ -9936,4 +9942,4 @@ struct tiff_hdr {
/*RT*/#undef CLIP
#ifdef __GNUC__
#pragma GCC diagnostic pop
-#endif
\ No newline at end of file
+#endif
diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc
index f8f5271cc..48dd6a92b 100644
--- a/rtengine/dcrop.cc
+++ b/rtengine/dcrop.cc
@@ -708,9 +708,7 @@ void Crop::update (int todo)
if (needstransform)
parent->ipf.transform (baseCrop, transCrop, cropx / skip, cropy / skip, trafx / skip, trafy / skip, skips (parent->fw, skip), skips (parent->fh, skip), parent->getFullWidth(), parent->getFullHeight(),
- parent->imgsrc->getMetaData()->getFocalLen(), parent->imgsrc->getMetaData()->getFocalLen35mm(),
- parent->imgsrc->getMetaData()->getFocusDist(),
- parent->imgsrc->getMetaData()->getFNumber(),
+ parent->imgsrc->getMetaData(),
parent->imgsrc->getRotateDegree(), false);
else {
baseCrop->copyData (transCrop);
@@ -1754,8 +1752,9 @@ bool check_need_larger_crop_for_lcp_distortion (int fw, int fh, int x, int y, in
return false;
}
- return (params.lensProf.lcpFile.length() > 0 &&
- params.lensProf.useDist);
+ return (params.lensProf.useDist &&
+ (params.lensProf.useLensfun ||
+ params.lensProf.lcpFile.length() > 0));
}
} // namespace
diff --git a/rtengine/green_equil_RT.cc b/rtengine/green_equil_RT.cc
index 8b1136359..90c412871 100644
--- a/rtengine/green_equil_RT.cc
+++ b/rtengine/green_equil_RT.cc
@@ -3,9 +3,10 @@
// Green Equilibration via directional average
//
// copyright (c) 2008-2010 Emil Martinec
+// optimized for speed 2017 Ingo Weyrich
//
//
-// code dated: February 12, 2011
+// code dated: August 25, 2017
//
// green_equil_RT.cc is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -21,18 +22,72 @@
// along with this program. If not, see .
//
////////////////////////////////////////////////////////////////
-#define TS 256 // Tile size
#include
#include
#include
-
#include "rt_math.h"
#include "rawimagesource.h"
+#include "opthelper.h"
+
namespace rtengine
{
+void RawImageSource::green_equilibrate_global(array2D &rawData)
+{
+ // global correction
+ int ng1 = 0, ng2 = 0;
+ double avgg1 = 0., avgg2 = 0.;
+
+#ifdef _OPENMP
+ #pragma omp parallel for reduction(+: ng1, ng2, avgg1, avgg2) schedule(dynamic,16)
+#endif
+
+ for (int i = border; i < H - border; i++) {
+ double avgg = 0.;
+
+ for (int j = border + ((FC(i, border) & 1) ^ 1); j < W - border; j += 2) {
+ avgg += rawData[i][j];
+ }
+
+ int ng = (W - 2 * border + (FC(i, border) & 1)) / 2;
+
+ if (i & 1) {
+ avgg2 += avgg;
+ ng2 += ng;
+ } else {
+ avgg1 += avgg;
+ ng1 += ng;
+ }
+ }
+
+ // Avoid division by zero
+ if(ng1 == 0 || avgg1 == 0.0) {
+ ng1 = 1;
+ avgg1 = 1.0;
+ }
+ if(ng2 == 0 || avgg2 == 0.0) {
+ ng2 = 1;
+ avgg2 = 1.0;
+ }
+
+ double corrg1 = (avgg1 / ng1 + avgg2 / ng2) / 2.0 / (avgg1 / ng1);
+ double corrg2 = (avgg1 / ng1 + avgg2 / ng2) / 2.0 / (avgg2 / ng2);
+
+#ifdef _OPENMP
+ #pragma omp parallel for schedule(dynamic,16)
+#endif
+
+ for (int i = border; i < H - border; i++) {
+ double corrg = (i & 1) ? corrg2 : corrg1;
+
+ for (int j = border + ((FC(i, border) & 1) ^ 1); j < W - border; j += 2) {
+ rawData[i][j] *= corrg;
+ }
+ }
+}
+
//void green_equilibrate()//for dcraw implementation
void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
{
@@ -42,15 +97,29 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
int height = H, width = W;
// local variables
- float** rawptr = rawData;
- array2D cfa (width, height, rawptr);
- //array2D checker (width,height,ARRAY2D_CLEAR_DATA);
+ array2D cfa(width / 2 + (width & 1), height);
+#ifdef _OPENMP
+ #pragma omp parallel for schedule(dynamic,16)
+#endif
- //int verbose=1;
+ for (int i = 0; i < height; ++i) {
+ int j = (FC(i, 0) & 1) ^ 1;
+#ifdef __SSE2__
- static const float eps = 1.0; //tolerance to avoid dividing by zero
+ for (; j < width - 7; j += 8) {
+ STVFU(cfa[i][j >> 1], LC2VFU(rawData[i][j]));
+ }
+#endif
+
+ for (; j < width; j += 2) {
+ cfa[i][j >> 1] = rawData[i][j];
+ }
+ }
+
+ constexpr float eps = 1.f; //tolerance to avoid dividing by zero
+ const float thresh6 = 6 * thresh;
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Fill G interpolated values with border interpolation and input values
@@ -59,94 +128,118 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
//int counter, vtest;
//The green equilibration algorithm starts here
- /*
- #ifdef _OPENMP
- #pragma omp parallel for
- #endif
- for (int rr=1; rr < height-1; rr++)
- for (int cc=3-(FC(rr,2)&1); cc < width-2; cc+=2) {
-
- float pcorr = (cfa[rr+1][cc+1]-cfa[rr][cc])*(cfa[rr-1][cc-1]-cfa[rr][cc]);
- float mcorr = (cfa[rr-1][cc+1]-cfa[rr][cc])*(cfa[rr+1][cc-1]-cfa[rr][cc]);
-
- if (pcorr>0 && mcorr>0) {checker[rr][cc]=1;} else {checker[rr][cc]=0;}
-
- checker[rr][cc]=1;//test what happens if we always interpolate
- }
-
- counter=vtest=0;
- */
//now smooth the cfa data
#ifdef _OPENMP
- #pragma omp parallel for schedule(dynamic,16)
+ #pragma omp parallel
+#endif
+ {
+#ifdef __SSE2__
+ vfloat zd5v = F2V(0.5f);
+ vfloat onev = F2V(1.f);
+ vfloat threshv = F2V(thresh);
+ vfloat thresh6v = F2V(thresh6);
+ vfloat epsv = F2V(eps);
+#endif
+#ifdef _OPENMP
+ #pragma omp for schedule(dynamic,16)
#endif
- for (int rr = 4; rr < height - 4; rr++)
- for (int cc = 5 - (FC(rr, 2) & 1); cc < width - 6; cc += 2) {
- //if (checker[rr][cc]) {
- //%%%%%%%%%%%%%%%%%%%%%%
- //neighbor checking code from Manuel Llorens Garcia
- float o1_1 = cfa[(rr - 1)][cc - 1];
- float o1_2 = cfa[(rr - 1)][cc + 1];
- float o1_3 = cfa[(rr + 1)][cc - 1];
- float o1_4 = cfa[(rr + 1)][cc + 1];
- float o2_1 = cfa[(rr - 2)][cc];
- float o2_2 = cfa[(rr + 2)][cc];
- float o2_3 = cfa[(rr)][cc - 2];
- float o2_4 = cfa[(rr)][cc + 2];
+ for (int rr = 4; rr < height - 4; rr++) {
+ int cc = 5 - (FC(rr, 2) & 1);
+#ifdef __SSE2__
- float d1 = (o1_1 + o1_2 + o1_3 + o1_4) * 0.25f;
- float d2 = (o2_1 + o2_2 + o2_3 + o2_4) * 0.25f;
+ for (; cc < width - 12; cc += 8) {
+ //neighbour checking code from Manuel Llorens Garcia
+ vfloat o1_1 = LVFU(cfa[rr - 1][(cc - 1) >> 1]);
+ vfloat o1_2 = LVFU(cfa[rr - 1][(cc + 1) >> 1]);
+ vfloat o1_3 = LVFU(cfa[rr + 1][(cc - 1) >> 1]);
+ vfloat o1_4 = LVFU(cfa[rr + 1][(cc + 1) >> 1]);
+ vfloat o2_1 = LVFU(cfa[rr - 2][cc >> 1]);
+ vfloat o2_2 = LVFU(cfa[rr + 2][cc >> 1]);
+ vfloat o2_3 = LVFU(cfa[rr][(cc >> 1) - 1]);
+ vfloat o2_4 = LVFU(cfa[rr][(cc >> 1) + 1]);
- float c1 = (fabs(o1_1 - o1_2) + fabs(o1_1 - o1_3) + fabs(o1_1 - o1_4) + fabs(o1_2 - o1_3) + fabs(o1_3 - o1_4) + fabs(o1_2 - o1_4)) / 6.f;
- float c2 = (fabs(o2_1 - o2_2) + fabs(o2_1 - o2_3) + fabs(o2_1 - o2_4) + fabs(o2_2 - o2_3) + fabs(o2_3 - o2_4) + fabs(o2_2 - o2_4)) / 6.f;
- //%%%%%%%%%%%%%%%%%%%%%%
+ vfloat d1 = (o1_1 + o1_2 + o1_3 + o1_4);
+ vfloat d2 = (o2_1 + o2_2 + o2_3 + o2_4);
- //vote1=(checker[rr-2][cc]+checker[rr][cc-2]+checker[rr][cc+2]+checker[rr+2][cc]);
- //vote2=(checker[rr+1][cc-1]+checker[rr+1][cc+1]+checker[rr-1][cc-1]+checker[rr-1][cc+1]);
- //if ((vote1==0 || vote2==0) && (c1+c2)<2*thresh*fabs(d1-d2)) vtest++;
- //if (vote1>0 && vote2>0 && (c1+c2)<4*thresh*fabs(d1-d2)) {
- if ((c1 + c2) < 4 * thresh * fabs(d1 - d2)) {
- //pixel interpolation
- float gin = cfa[rr][cc];
+ vfloat c1 = (vabsf(o1_1 - o1_2) + vabsf(o1_1 - o1_3) + vabsf(o1_1 - o1_4) + vabsf(o1_2 - o1_3) + vabsf(o1_3 - o1_4) + vabsf(o1_2 - o1_4));
+ vfloat c2 = (vabsf(o2_1 - o2_2) + vabsf(o2_1 - o2_3) + vabsf(o2_1 - o2_4) + vabsf(o2_2 - o2_3) + vabsf(o2_3 - o2_4) + vabsf(o2_2 - o2_4));
- float gse = (cfa[rr + 1][cc + 1]) + 0.5f * (cfa[rr][cc] - cfa[rr + 2][cc + 2]);
- float gnw = (cfa[rr - 1][cc - 1]) + 0.5f * (cfa[rr][cc] - cfa[rr - 2][cc - 2]);
- float gne = (cfa[rr - 1][cc + 1]) + 0.5f * (cfa[rr][cc] - cfa[rr - 2][cc + 2]);
- float gsw = (cfa[rr + 1][cc - 1]) + 0.5f * (cfa[rr][cc] - cfa[rr + 2][cc - 2]);
+ vmask mask1 = vmaskf_lt(c1 + c2, thresh6v * vabsf(d1 - d2));
+ if (_mm_movemask_ps((vfloat)mask1)) { // if for any of the 4 pixels the condition is true, do the maths for all 4 pixels and mask the unused out at the end
+ //pixel interpolation
+ vfloat gin = LVFU(cfa[rr][cc >> 1]);
+ vfloat gmp2p2 = gin - LVFU(cfa[rr + 2][(cc >> 1) + 1]);
+ vfloat gmm2m2 = gin - LVFU(cfa[rr - 2][(cc >> 1) - 1]);
+ vfloat gmm2p2 = gin - LVFU(cfa[rr - 2][(cc >> 1) + 1]);
+ vfloat gmp2m2 = gin - LVFU(cfa[rr + 2][(cc >> 1) - 1]);
- float wtse = 1.0f / (eps + SQR(cfa[rr + 2][cc + 2] - cfa[rr][cc]) + SQR(cfa[rr + 3][cc + 3] - cfa[rr + 1][cc + 1]));
- float wtnw = 1.0f / (eps + SQR(cfa[rr - 2][cc - 2] - cfa[rr][cc]) + SQR(cfa[rr - 3][cc - 3] - cfa[rr - 1][cc - 1]));
- float wtne = 1.0f / (eps + SQR(cfa[rr - 2][cc + 2] - cfa[rr][cc]) + SQR(cfa[rr - 3][cc + 3] - cfa[rr - 1][cc + 1]));
- float wtsw = 1.0f / (eps + SQR(cfa[rr + 2][cc - 2] - cfa[rr][cc]) + SQR(cfa[rr + 3][cc - 3] - cfa[rr + 1][cc - 1]));
+ vfloat gse = o1_4 + zd5v * gmp2p2;
+ vfloat gnw = o1_1 + zd5v * gmm2m2;
+ vfloat gne = o1_2 + zd5v * gmm2p2;
+ vfloat gsw = o1_3 + zd5v * gmp2m2;
- float ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw);
+ vfloat wtse = onev / (epsv + SQRV(gmp2p2) + SQRV(LVFU(cfa[rr + 3][(cc + 3) >> 1]) - o1_4));
+ vfloat wtnw = onev / (epsv + SQRV(gmm2m2) + SQRV(LVFU(cfa[rr - 3][(cc - 3) >> 1]) - o1_1));
+ vfloat wtne = onev / (epsv + SQRV(gmm2p2) + SQRV(LVFU(cfa[rr - 3][(cc + 3) >> 1]) - o1_2));
+ vfloat wtsw = onev / (epsv + SQRV(gmp2m2) + SQRV(LVFU(cfa[rr + 3][(cc - 3) >> 1]) - o1_3));
- if ( ((ginterp - gin) < thresh * (ginterp + gin)) ) {
- rawData[rr][cc] = 0.5f * (ginterp + gin);
- //counter++;
+ vfloat ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw);
+
+ vfloat val = vself(vmaskf_lt(ginterp - gin, threshv * (ginterp + gin)), zd5v * (ginterp + gin), gin);
+ val = vself(mask1, val, gin);
+ STC2VFU(rawData[rr][cc], val);
}
-
}
- // }
+#endif
+
+ for (; cc < width - 6; cc += 2) {
+ //neighbour checking code from Manuel Llorens Garcia
+ float o1_1 = cfa[rr - 1][(cc - 1) >> 1];
+ float o1_2 = cfa[rr - 1][(cc + 1) >> 1];
+ float o1_3 = cfa[rr + 1][(cc - 1) >> 1];
+ float o1_4 = cfa[rr + 1][(cc + 1) >> 1];
+ float o2_1 = cfa[rr - 2][cc >> 1];
+ float o2_2 = cfa[rr + 2][cc >> 1];
+ float o2_3 = cfa[rr][(cc - 2) >> 1];
+ float o2_4 = cfa[rr][(cc + 2) >> 1];
+
+ float d1 = (o1_1 + o1_2) + (o1_3 + o1_4);
+ float d2 = (o2_1 + o2_2) + (o2_3 + o2_4);
+
+ float c1 = (fabs(o1_1 - o1_2) + fabs(o1_1 - o1_3) + fabs(o1_1 - o1_4) + fabs(o1_2 - o1_3) + fabs(o1_3 - o1_4) + fabs(o1_2 - o1_4));
+ float c2 = (fabs(o2_1 - o2_2) + fabs(o2_1 - o2_3) + fabs(o2_1 - o2_4) + fabs(o2_2 - o2_3) + fabs(o2_3 - o2_4) + fabs(o2_2 - o2_4));
+
+ if (c1 + c2 < thresh6 * fabs(d1 - d2)) {
+ //pixel interpolation
+ float gin = cfa[rr][cc >> 1];
+
+ float gmp2p2 = gin - cfa[rr + 2][(cc + 2) >> 1];
+ float gmm2m2 = gin - cfa[rr - 2][(cc - 2) >> 1];
+ float gmm2p2 = gin - cfa[rr - 2][(cc + 2) >> 1];
+ float gmp2m2 = gin - cfa[rr + 2][(cc - 2) >> 1];
+
+ float gse = o1_4 + 0.5f * gmp2p2;
+ float gnw = o1_1 + 0.5f * gmm2m2;
+ float gne = o1_2 + 0.5f * gmm2p2;
+ float gsw = o1_3 + 0.5f * gmp2m2;
+
+ float wtse = 1.f / (eps + SQR(gmp2p2) + SQR(cfa[rr + 3][(cc + 3) >> 1] - o1_4));
+ float wtnw = 1.f / (eps + SQR(gmm2m2) + SQR(cfa[rr - 3][(cc - 3) >> 1] - o1_1));
+ float wtne = 1.f / (eps + SQR(gmm2p2) + SQR(cfa[rr - 3][(cc + 3) >> 1] - o1_2));
+ float wtsw = 1.f / (eps + SQR(gmp2m2) + SQR(cfa[rr + 3][(cc - 3) >> 1] - o1_3));
+
+ float ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw);
+
+ if (ginterp - gin < thresh * (ginterp + gin)) {
+ rawData[rr][cc] = 0.5f * (ginterp + gin);
+ }
+ }
+ }
}
-
- //printf("pixfix count= %d; vtest= %d \n",counter,vtest);
- // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
- // done
- /*t2 = clock();
- dt = ((double)(t2-t1)) / CLOCKS_PER_SEC;
- if (verbose) {
- fprintf(stderr,_("elapsed time = %5.3fs\n"),dt);
- }*/
-
-
+ }
}
}
-
-#undef TS
diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc
index c8ab4c86f..a99864c7d 100644
--- a/rtengine/improccoordinator.cc
+++ b/rtengine/improccoordinator.cc
@@ -512,8 +512,8 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall)
}
if (needstransform)
- ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, fw, fh, imgsrc->getMetaData()->getFocalLen(),
- imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(), imgsrc->getMetaData()->getFNumber(), imgsrc->getRotateDegree(), false);
+ ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, fw, fh,
+ imgsrc->getMetaData(), imgsrc->getRotateDegree(), false);
else {
orig_prev->copyData (oprevi);
}
@@ -3820,10 +3820,10 @@ void ImProcCoordinator::getAutoCrop (double ratio, int &x, int &y, int &w, int &
MyMutex::MyLock lock (mProcessing);
- LCPMapper *pLCPMap = nullptr;
+ LensCorrection *pLCPMap = nullptr;
if (params.lensProf.lcpFile.length() && imgsrc->getMetaData()->getFocalLen() > 0) {
- LCPProfile *pLCPProf = lcpStore->getProfile (params.lensProf.lcpFile);
+ const std::shared_ptr pLCPProf = LCPStore::getInstance()->getProfile (params.lensProf.lcpFile);
if (pLCPProf) pLCPMap = new LCPMapper (pLCPProf, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(),
0, false, params.lensProf.useDist, fullw, fullh, params.coarse, imgsrc->getRotateDegree());
@@ -3921,8 +3921,8 @@ void ImProcCoordinator::saveInputICCReference (const Glib::ustring & fname, bool
if (ipf.needsTransform()) {
Imagefloat* trImg = new Imagefloat (fW, fH);
- ipf.transform (im, trImg, 0, 0, 0, 0, fW, fH, fW, fH, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(),
- imgsrc->getMetaData()->getFocusDist(), imgsrc->getMetaData()->getFNumber(), imgsrc->getRotateDegree(), true);
+ ipf.transform (im, trImg, 0, 0, 0, 0, fW, fH, fW, fH,
+ imgsrc->getMetaData(), imgsrc->getRotateDegree(), true);
delete im;
im = trImg;
}
diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc
index d04b7b539..aecbd22b1 100644
--- a/rtengine/improcfun.cc
+++ b/rtengine/improcfun.cc
@@ -847,6 +847,7 @@ void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh
} else if (curveMode == ColorAppearanceParams::TC_MODE_BRIGHT) {
//attention! Brightness curves are open - unlike Lightness or Lab or RGB==> rendering and algoritms will be different
float coef = ((aw + 4.f) * (4.f / c)) / 100.f;
+ float Qanc = Qpro;
float Qq = (float) Qpro * 327.68f * (1.f / coef);
float Qold100 = (float) Qpro / coef;
@@ -872,8 +873,15 @@ void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh
Qq = 0.7f * (Qq - Qold) + Qold; // not zero ==>artifacts
}
- Qpro = (double) (Qq * (coef) / 327.68f);
- Jpro = 100.* (Qpro * Qpro) / ((4.0 / c) * (4.0 / c) * (aw + 4.0) * (aw + 4.0));
+ if (Qold == 0.f) {
+ Qold = 0.001f;
+ }
+
+ Qpro = Qanc * (Qq / Qold);
+ Jpro = Jpro * SQR (Qq / Qold);
+
+// Qpro = (double) (Qq * (coef) / 327.68f);
+// Jpro = 100.* (Qpro * Qpro) / ((4.0 / c) * (4.0 / c) * (aw + 4.0) * (aw + 4.0));
t1B = true;
if (Jpro < 1.) {
@@ -928,6 +936,7 @@ void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh
}
} else if (curveMode2 == ColorAppearanceParams::TC_MODE_BRIGHT) { //
+ float Qanc = Qpro;
float coef = ((aw + 4.f) * (4.f / c)) / 100.f;
float Qq = (float) Qpro * 327.68f * (1.f / coef);
float Qold100 = (float) Qpro / coef;
@@ -954,8 +963,16 @@ void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh
Qq = 0.7f * (Qq - Qold) + Qold; // not zero ==>artifacts
}
- Qpro = (double) (Qq * (coef) / 327.68f);
- Jpro = 100.* (Qpro * Qpro) / ((4.0 / c) * (4.0 / c) * (aw + 4.0) * (aw + 4.0));
+ if (Qold == 0.f) {
+ Qold = 0.001f;
+ }
+
+ // Qpro = (float) (Qq * (coef) / 327.68f);
+ Qpro = Qanc * (Qq / Qold);
+ Jpro = Jpro * SQR (Qq / Qold);
+
+ // Qpro = (double) (Qq * (coef) / 327.68f);
+ // Jpro = 100.* (Qpro * Qpro) / ((4.0 / c) * (4.0 / c) * (aw + 4.0) * (aw + 4.0));
t2B = true;
if (t1L) { //to workaround the problem if we modify curve1-lightnees after curve2 brightness(the cat that bites its own tail!) in fact it's another type of curve only for this case
@@ -1682,10 +1699,11 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
}
}
- if (alg >= 2 && la < 200.f) {
- la = 200.f;
- }
-
+ /*
+ if (alg >= 2 && la < 200.f) {
+ la = 200.f;
+ }
+ */
const float la2 = float (params->colorappearance.adaplum);
// level of adaptation
@@ -1776,6 +1794,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
}
float sum = 0.f;
+ float sumQ = 0.f;
#ifdef _OPENMP
const int numThreads = min (max (width * height / 65536, 1), omp_get_max_threads());
@@ -1795,7 +1814,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
hist16Qthr.clear();
}
- #pragma omp for reduction(+:sum)
+ #pragma omp for reduction(+:sum,sumQ)
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++) { //rough correspondence between L and J
@@ -1840,11 +1859,26 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
hist16Jthr[ (int) ((koef * lab->L[i][j]))]++; //evaluate histogram luminance L # J
}
+ //estimation of wh only with La
+ float whestim = 500.f;
+
+ if (la < 200.f) {
+ whestim = 200.f;
+ } else if (la < 2500.f) {
+ whestim = 350.f;
+ } else {
+ whestim = 500.f;
+ }
+
if (needQ) {
- hist16Qthr[ (int) (sqrtf ((koef * (lab->L[i][j])) * 32768.f))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L
+ hist16Qthr[CLIP ((int) (32768.f * sqrt ((koef * (lab->L[i][j])) / 32768.f)))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L
+ //perhaps needs to introduce whestim ??
+ // hist16Qthr[ (int) (sqrtf ((koef * (lab->L[i][j])) * 32768.f))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L
}
sum += koef * lab->L[i][j]; //evaluate mean J to calculate Yb
+ sumQ += whestim * sqrt ((koef * (lab->L[i][j])) / 32768.f);
+ //can be used in case of...
}
#pragma omp critical
@@ -1858,14 +1892,16 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
}
}
+ float meanQ;
if (std::isnan (mean)) {
mean = (sum / ((height) * width)) / 327.68f; //for Yb for all image...if one day "pipette" we can adapt Yb for each zone
+ meanQ = (sumQ / ((height) * width));//in case of
+
}
}
-
//evaluate lightness, contrast
}
@@ -1938,6 +1974,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
float cz, wh, pfl;
Ciecam02::initcam1float (gamu, yb, pilot, f, la, xw, yw, zw, n, d, nbb, ncb, cz, aw, wh, pfl, fl, c);
+ //printf ("wh=%f \n", wh);
+
const float pow1 = pow_F ( 1.64f - pow_F ( 0.29f, n ), 0.73f );
float nj, nbbj, ncbj, czj, awj, flj;
Ciecam02::initcam2float (gamu, yb2, pilotout, f2, la2, xw2, yw2, zw2, nj, dj, nbbj, ncbj, czj, awj, flj);
@@ -1955,7 +1993,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
const bool LabPassOne = ! ((params->colorappearance.tonecie && (epdEnabled)) || (params->sharpening.enabled && settings->autocielab && execsharp)
|| (params->dirpyrequalizer.enabled && settings->autocielab) || (params->defringe.enabled && settings->autocielab) || (params->sharpenMicro.enabled && settings->autocielab)
|| (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl > 0 && settings->autocielab));
-
+ //printf("coQ=%f\n", coefQ);
if (needJ) {
if (!CAMBrightCurveJ) {
@@ -1976,7 +2014,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
if (CAMBrightCurveQ.dirty) {
Ciecam02::curveJfloat (params->colorappearance.qbright, params->colorappearance.qcontrast, hist16Q, CAMBrightCurveQ);//brightness and contrast Q
- CAMBrightCurveQ /= coefQ;
+ // CAMBrightCurveQ /= coefQ;
CAMBrightCurveQ.dirty = false;
}
}
@@ -2126,7 +2164,11 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
Qpro = QproFactor * sqrtf (Jpro);
Cpro = (spro * spro * Qpro) / (10000.0f);
} else if (alg == 2) {
- Qpro = CAMBrightCurveQ[ (float) (Qpro * coefQ)]; //brightness and contrast
+ //printf("Qp0=%f ", Qpro);
+
+ Qpro = CAMBrightCurveQ[ (float) (Qpro * coefQ)] / coefQ; //brightness and contrast
+ //printf("Qpaf=%f ", Qpro);
+
float Mp, sres;
Mp = Mpro / 100.0f;
Ciecam02::curvecolorfloat (mchr, Mp, sres, 2.5f);
@@ -2140,7 +2182,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
Qpro = (Qpro == 0.f ? epsil : Qpro); // avoid division by zero
spro = 100.0f * sqrtf ( Mpro / Qpro );
} else { /*if(alg == 3) */
- Qpro = CAMBrightCurveQ[ (float) (Qpro * coefQ)]; //brightness and contrast
+ Qpro = CAMBrightCurveQ[ (float) (Qpro * coefQ)] / coefQ; //brightness and contrast
float Mp, sres;
Mp = Mpro / 100.0f;
@@ -2217,6 +2259,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
} else if (curveMode == ColorAppearanceParams::TC_MODE_BRIGHT) {
//attention! Brightness curves are open - unlike Lightness or Lab or RGB==> rendering and algoritms will be different
float coef = ((aw + 4.f) * (4.f / c)) / 100.f;
+ float Qanc = Qpro;
float Qq = (float) Qpro * 327.68f * (1.f / coef);
float Qold100 = (float) Qpro / coef;
@@ -2242,8 +2285,13 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
Qq = 0.7f * (Qq - Qold) + Qold; // not zero ==>artifacts
}
- Qpro = (float) (Qq * (coef) / 327.68f);
- Jpro = 100.f * (Qpro * Qpro) / ((4.0f / c) * (4.0f / c) * (aw + 4.0f) * (aw + 4.0f));
+ if (Qold == 0.f) {
+ Qold = 0.001f;
+ }
+
+ Qpro = Qanc * (Qq / Qold);
+ // Jpro = 100.f * (Qpro * Qpro) / ((4.0f / c) * (4.0f / c) * (aw + 4.0f) * (aw + 4.0f));
+ Jpro = Jpro * SQR (Qq / Qold);
if (Jpro < 1.f) {
Jpro = 1.f;
@@ -2291,6 +2339,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
}
} else if (curveMode2 == ColorAppearanceParams::TC_MODE_BRIGHT) { //
+ float Qanc = Qpro;
+
float coef = ((aw + 4.f) * (4.f / c)) / 100.f;
float Qq = (float) Qpro * 327.68f * (1.f / coef);
float Qold100 = (float) Qpro / coef;
@@ -2317,8 +2367,15 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int
Qq = 0.7f * (Qq - Qold) + Qold; // not zero ==>artifacts
}
- Qpro = (float) (Qq * (coef) / 327.68f);
- Jpro = 100.f * (Qpro * Qpro) / ((4.0f / c) * (4.0f / c) * (aw + 4.0f) * (aw + 4.0f));
+ if (Qold == 0.f) {
+ Qold = 0.001f;
+ }
+
+ // Qpro = (float) (Qq * (coef) / 327.68f);
+ Qpro = Qanc * (Qq / Qold);
+ Jpro = Jpro * SQR (Qq / Qold);
+
+ // Jpro = 100.f * (Qpro * Qpro) / ((4.0f / c) * (4.0f / c) * (aw + 4.0f) * (aw + 4.0f));
if (t1L) { //to workaround the problem if we modify curve1-lightnees after curve2 brightness(the cat that bites its own tail!) in fact it's another type of curve only for this case
coef = 2.f; //adapt Q to J approximation
diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h
index a38adfa14..49b6b7d24 100644
--- a/rtengine/improcfun.h
+++ b/rtengine/improcfun.h
@@ -54,9 +54,13 @@ class ImProcFunctions
void calcVignettingParams (int oW, int oH, const VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul);
- void transformPreview (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LCPMapper *pLCPMap);
+ enum TransformMode {
+ TRANSFORM_PREVIEW,
+ TRANSFORM_HIGH_QUALITY,
+ TRANSFORM_HIGH_QUALITY_FULLIMAGE
+ };
void transformLuminanceOnly (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH, int fW, int fH);
- void transformHighQuality (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LCPMapper *pLCPMap, bool fullImage);
+ void transformGeneral(TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap);
void sharpenHaloCtrl (float** luminance, float** blurmap, float** base, int W, int H, const SharpeningParams &sharpenParam);
void sharpenHaloCtrl (LabImage* lab, float** blurmap, float** base, int W, int H, SharpeningParams &sharpenParam);
@@ -70,6 +74,7 @@ class ImProcFunctions
bool needsGradient ();
bool needsVignetting ();
bool needsLCP ();
+ bool needsLensfun();
// static cmsUInt8Number* Mempro = NULL;
@@ -236,8 +241,7 @@ public:
void colorCurve (LabImage* lold, LabImage* lnew);
void sharpening (LabImage* lab, float** buffer, SharpeningParams &sharpenParam);
void sharpeningcam (CieImage* ncie, float** buffer);
- void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH,
- double focalLen, double focalLen35mm, float focusDist, double fNumber, int rawRotationDeg, bool fullImage);
+ void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const ImageMetaData *metadata, int rawRotationDeg, bool fullImage);
float resizeScale (const ProcParams* params, int fw, int fh, int &imw, int &imh);
void lab2monitorRgb (LabImage* lab, Image8* image);
void resize (Image16* src, Image16* dst, float dScale);
@@ -381,11 +385,11 @@ public:
Image16* lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, bool bw, GammaValues *ga = nullptr);
// CieImage *ciec;
- bool transCoord (int W, int H, int x, int y, int w, int h, int& xv, int& yv, int& wv, int& hv, double ascaleDef = -1, const LCPMapper *pLCPMap = nullptr);
- bool transCoord (int W, int H, const std::vector &src, std::vector &red, std::vector &green, std::vector &blue, double ascaleDef = -1, const LCPMapper *pLCPMap = nullptr);
+ bool transCoord (int W, int H, int x, int y, int w, int h, int& xv, int& yv, int& wv, int& hv, double ascaleDef = -1, const LensCorrection *pLCPMap = nullptr);
+ bool transCoord (int W, int H, const std::vector &src, std::vector &red, std::vector &green, std::vector &blue, double ascaleDef = -1, const LensCorrection *pLCPMap = nullptr);
static void getAutoExp (const LUTu & histogram, int histcompr, double defgain, double clip, double& expcomp, int& bright, int& contr, int& black, int& hlcompr, int& hlcomprthresh);
static double getAutoDistor (const Glib::ustring& fname, int thumb_size);
- double getTransformAutoFill (int oW, int oH, const LCPMapper *pLCPMap = nullptr);
+ double getTransformAutoFill (int oW, int oH, const LensCorrection *pLCPMap = nullptr);
void rgb2lab (const Imagefloat &src, LabImage &dst, const Glib::ustring &workingSpace);
void lab2rgb (const LabImage &src, Imagefloat &dst, const Glib::ustring &workingSpace);
};
diff --git a/rtengine/init.cc b/rtengine/init.cc
index 2d157c762..7ef40f43a 100644
--- a/rtengine/init.cc
+++ b/rtengine/init.cc
@@ -30,6 +30,7 @@
#include "rtthumbnail.h"
#include "profilestore.h"
#include "../rtgui/threadutils.h"
+#include "rtlensfun.h"
namespace rtengine
{
@@ -50,6 +51,7 @@ int init (const Settings* s, Glib::ustring baseDir, Glib::ustring userSettingsDi
Color::init ();
PerceptualToneCurve::init ();
RawImageSource::init ();
+ LFDatabase::init();
delete lcmsMutex;
lcmsMutex = new MyMutex;
dfm.init( s->darkFramesPath );
diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc
index ee14c80c2..bfd01b301 100644
--- a/rtengine/iptransform.cc
+++ b/rtengine/iptransform.cc
@@ -24,6 +24,7 @@
#include "mytime.h"
#include "rt_math.h"
#include "sleef.c"
+#include "rtlensfun.h"
using namespace std;
@@ -86,18 +87,6 @@ float normn (float a, float b, int n)
}
-void correct_distortion(const rtengine::LCPMapper *lcp, double &x, double &y,
- int cx, int cy, double scale)
-{
- assert (lcp);
-
- x += cx;
- y += cy;
- lcp->correctDistortion(x, y, scale);
- x -= (cx * scale);
- y -= (cy * scale);
-}
-
}
namespace rtengine
@@ -107,7 +96,7 @@ namespace rtengine
#define CLIPTOC(a,b,c,d) ((a)>=(b)?((a)<=(c)?(a):(d=true,(c))):(d=true,(b)))
bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, std::vector &red, std::vector &green, std::vector &blue, double ascaleDef,
- const LCPMapper *pLCPMap)
+ const LensCorrection *pLCPMap)
{
bool clipped = false;
@@ -157,7 +146,7 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector &src,
double x_d = src[i].x, y_d = src[i].y;
if (pLCPMap && params->lensProf.useDist) {
- correct_distortion (pLCPMap, x_d, y_d, 0, 0, ascale);
+ pLCPMap->correctDistortion(x_d, y_d, 0, 0, ascale);
} else {
x_d *= ascale;
y_d *= ascale;
@@ -209,7 +198,7 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector &src,
}
// Transform all corners and critical sidelines of an image
-bool ImProcFunctions::transCoord (int W, int H, int x, int y, int w, int h, int& xv, int& yv, int& wv, int& hv, double ascaleDef, const LCPMapper *pLCPMap)
+bool ImProcFunctions::transCoord (int W, int H, int x, int y, int w, int h, int& xv, int& yv, int& wv, int& hv, double ascaleDef, const LensCorrection *pLCPMap)
{
const int DivisionsPerBorder = 32;
@@ -307,32 +296,44 @@ bool ImProcFunctions::transCoord (int W, int H, int x, int y, int w, int h, int&
}
void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH,
- double focalLen, double focalLen35mm, float focusDist, double fNumber, int rawRotationDeg, bool fullImage)
+ const ImageMetaData *metadata,
+ int rawRotationDeg, bool fullImage)
{
+ double focalLen = metadata->getFocalLen();
+ double focalLen35mm = metadata->getFocalLen35mm();
+ float focusDist = metadata->getFocusDist();
+ double fNumber = metadata->getFNumber();
- LCPMapper *pLCPMap = nullptr;
+ std::unique_ptr pLCPMap;
- if (needsLCP()) { // don't check focal length to allow distortion correction for lenses without chip
- LCPProfile *pLCPProf = lcpStore->getProfile (params->lensProf.lcpFile);
+ if (needsLensfun()) {
+ pLCPMap = std::move(LFDatabase::findModifier(params->lensProf, metadata, oW, oH, params->coarse, rawRotationDeg));
+ } else if (needsLCP()) { // don't check focal length to allow distortion correction for lenses without chip
+ const std::shared_ptr pLCPProf = LCPStore::getInstance()->getProfile (params->lensProf.lcpFile);
if (pLCPProf) {
- pLCPMap = new LCPMapper (pLCPProf, focalLen, focalLen35mm,
- focusDist, fNumber, false,
- params->lensProf.useDist,
- oW, oH, params->coarse, rawRotationDeg);
+ pLCPMap.reset(
+ new LCPMapper (pLCPProf, focalLen, focalLen35mm,
+ focusDist, fNumber, false,
+ params->lensProf.useDist,
+ oW, oH, params->coarse, rawRotationDeg
+ )
+ );
}
}
- if (! (needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP()) && (needsVignetting() || needsPCVignetting() || needsGradient())) {
+ if (! (needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsLensfun()) && (needsVignetting() || needsPCVignetting() || needsGradient())) {
transformLuminanceOnly (original, transformed, cx, cy, oW, oH, fW, fH);
- } else if (!needsCA() && scale != 1) {
- transformPreview (original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap);
} else {
- transformHighQuality (original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap, fullImage);
- }
-
- if (pLCPMap) {
- delete pLCPMap;
+ TransformMode mode;
+ if (!needsCA() && scale != 1) {
+ mode = TRANSFORM_PREVIEW;
+ } else if (!fullImage) {
+ mode = TRANSFORM_HIGH_QUALITY;
+ } else {
+ mode = TRANSFORM_HIGH_QUALITY_FULLIMAGE;
+ }
+ transformGeneral(mode, original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap.get());
}
}
@@ -721,9 +722,8 @@ void ImProcFunctions::transformLuminanceOnly (Imagefloat* original, Imagefloat*
}
}
-// Transform WITH scaling (opt.) and CA, cubic interpolation
-void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH,
- const LCPMapper *pLCPMap, bool fullImage)
+
+void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap)
{
double w2 = (double) oW / 2.0 - 0.5;
double h2 = (double) oH / 2.0 - 0.5;
@@ -781,17 +781,38 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr
oH * tan (hpalpha) * sqrt (SQR (4 * maxRadius) + SQR (oH * tan (hpalpha)))) / (SQR (maxRadius) * 8)));
double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos (hpteta), hptanpt = tan (hpteta);
- double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, true /*fullImage*/ ? pLCPMap : nullptr) : 1.0;
+ double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0;
// smaller crop images are a problem, so only when processing fully
- bool enableLCPCA = pLCPMap && params->lensProf.useCA && fullImage && pLCPMap->enableCA;
- bool enableLCPDist = pLCPMap && params->lensProf.useDist; // && fullImage;
+ bool enableLCPCA = false;
+ bool enableLCPDist = false;
+ bool enableCA = false;
- if (enableLCPCA) {
- enableLCPDist = false;
+ switch (mode) {
+ case ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE: {
+ enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable();
+ }
+ //no break on purpose
+
+ case ImProcFunctions::TRANSFORM_HIGH_QUALITY: {
+ enableLCPDist = pLCPMap && params->lensProf.useDist;
+ if (enableLCPCA) {
+ enableLCPDist = false;
+ }
+ enableCA = enableLCPCA || needsCA();
+ }
+ //no break on purpose
+
+ default:
+ case ImProcFunctions::TRANSFORM_PREVIEW: {
+ enableLCPDist = pLCPMap && params->lensProf.useDist;
+ break;
+ }
}
- bool enableCA = enableLCPCA || needsCA();
+ if (!enableCA) {
+ chDist[0] = 0.0;
+ }
// main cycle
bool darkening = (params->vignetting.amount <= 0.0);
@@ -802,7 +823,7 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr
double x_d = x, y_d = y;
if (enableLCPDist) {
- correct_distortion(pLCPMap, x_d, y_d, cx, cy, ascale); // must be first transform
+ pLCPMap->correctDistortion(x_d, y_d, cx, cy, ascale); // must be first transform
} else {
x_d *= ascale;
y_d *= ascale;
@@ -895,6 +916,10 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr
// all interpolation pixels inside image
if (enableCA) {
interpolateTransformChannelsCubic (chOrig[c], xc - 1, yc - 1, Dx, Dy, & (chTrans[c][y][x]), vignmul);
+ } else if (mode == ImProcFunctions::TRANSFORM_PREVIEW) {
+ transformed->r (y, x) = vignmul * (original->r (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->r (yc, xc + 1) * Dx * (1.0 - Dy) + original->r (yc + 1, xc) * (1.0 - Dx) * Dy + original->r (yc + 1, xc + 1) * Dx * Dy);
+ transformed->g (y, x) = vignmul * (original->g (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->g (yc, xc + 1) * Dx * (1.0 - Dy) + original->g (yc + 1, xc) * (1.0 - Dx) * Dy + original->g (yc + 1, xc + 1) * Dx * Dy);
+ transformed->b (y, x) = vignmul * (original->b (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->b (yc, xc + 1) * Dx * (1.0 - Dy) + original->b (yc + 1, xc) * (1.0 - Dx) * Dy + original->b (yc + 1, xc + 1) * Dx * Dy);
} else {
interpolateTransformCubic (original, xc - 1, yc - 1, Dx, Dy, & (transformed->r (y, x)), & (transformed->g (y, x)), & (transformed->b (y, x)), vignmul);
}
@@ -928,168 +953,8 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr
}
}
-// Transform WITH scaling, WITHOUT CA, simple (and fast) interpolation. Used for preview
-void ImProcFunctions::transformPreview (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LCPMapper *pLCPMap)
-{
- double w2 = (double) oW / 2.0 - 0.5;
- double h2 = (double) oH / 2.0 - 0.5;
-
- double vig_w2, vig_h2, maxRadius, v, b, mul;
- calcVignettingParams (oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul);
-
- struct grad_params gp;
-
- if (needsGradient()) {
- calcGradientParams (oW, oH, params->gradient, gp);
- }
-
- struct pcv_params pcv;
-
- if (needsPCVignetting()) {
- calcPCVignetteParams (fW, fH, oW, oH, params->pcvignette, params->crop, pcv);
- }
-
- // auxiliary variables for distortion correction
- bool needsDist = needsDistortion(); // for performance
- double distAmount = params->distortion.amount;
-
- // auxiliary variables for rotation
- double cost = cos (params->rotate.degree * rtengine::RT_PI / 180.0);
- double sint = sin (params->rotate.degree * rtengine::RT_PI / 180.0);
-
- // auxiliary variables for vertical perspective correction
- double vpdeg = params->perspective.vertical / 100.0 * 45.0;
- double vpalpha = (90 - vpdeg) / 180.0 * rtengine::RT_PI;
- double vpteta = fabs (vpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((vpdeg > 0 ? 1.0 : -1.0) * sqrt ((-oW * oW * tan (vpalpha) * tan (vpalpha) + (vpdeg > 0 ? 1.0 : -1.0) * oW * tan (vpalpha) * sqrt (16 * maxRadius * maxRadius + oW * oW * tan (vpalpha) * tan (vpalpha))) / (maxRadius * maxRadius * 8)));
- double vpcospt = (vpdeg >= 0 ? 1.0 : -1.0) * cos (vpteta), vptanpt = tan (vpteta);
-
- // auxiliary variables for horizontal perspective correction
- double hpdeg = params->perspective.horizontal / 100.0 * 45.0;
- double hpalpha = (90 - hpdeg) / 180.0 * rtengine::RT_PI;
- double hpteta = fabs (hpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((hpdeg > 0 ? 1.0 : -1.0) * sqrt ((-oH * oH * tan (hpalpha) * tan (hpalpha) + (hpdeg > 0 ? 1.0 : -1.0) * oH * tan (hpalpha) * sqrt (16 * maxRadius * maxRadius + oH * oH * tan (hpalpha) * tan (hpalpha))) / (maxRadius * maxRadius * 8)));
- double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos (hpteta), hptanpt = tan (hpteta);
-
- double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0;
-
- bool darkening = (params->vignetting.amount <= 0.0);
-
- // main cycle
- #pragma omp parallel for if (multiThread)
-
- for (int y = 0; y < transformed->getHeight(); y++) {
- for (int x = 0; x < transformed->getWidth(); x++) {
- double x_d = x, y_d = y;
-
- if (pLCPMap && params->lensProf.useDist) {
- correct_distortion(pLCPMap, x_d, y_d, cx, cy, ascale); // must be first transform
- } else {
- x_d *= ascale;
- y_d *= ascale;
- }
-
- x_d += ascale * (cx - w2); // centering x coord & scale
- y_d += ascale * (cy - h2); // centering y coord & scale
-
- double vig_x_d = 0., vig_y_d = 0.;
-
- if (needsVignetting()) {
- vig_x_d = ascale * (x + cx - vig_w2); // centering x coord & scale
- vig_y_d = ascale * (y + cy - vig_h2); // centering y coord & scale
- }
-
- if (needsPerspective()) {
- // horizontal perspective transformation
- y_d *= maxRadius / (maxRadius + x_d * hptanpt);
- x_d *= maxRadius * hpcospt / (maxRadius + x_d * hptanpt);
-
- // vertical perspective transformation
- x_d *= maxRadius / (maxRadius - y_d * vptanpt);
- y_d *= maxRadius * vpcospt / (maxRadius - y_d * vptanpt);
- }
-
- // rotate
- double Dx = x_d * cost - y_d * sint;
- double Dy = x_d * sint + y_d * cost;
-
- // distortion correction
- double s = 1;
-
- if (needsDist) {
- double r = sqrt (Dx * Dx + Dy * Dy) / maxRadius; // sqrt is slow
- s = 1.0 - distAmount + distAmount * r ;
- Dx *= s;
- Dy *= s;
- }
-
- double r2 = 0.;
-
- if (needsVignetting()) {
- double vig_Dx = vig_x_d * cost - vig_y_d * sint;
- double vig_Dy = vig_x_d * sint + vig_y_d * cost;
- r2 = sqrt (vig_Dx * vig_Dx + vig_Dy * vig_Dy);
- }
-
- // de-center
- Dx += w2;
- Dy += h2;
-
- // Extract integer and fractions of source screen coordinates
- int xc = (int)Dx;
- Dx -= (double)xc;
- xc -= sx;
- int yc = (int)Dy;
- Dy -= (double)yc;
- yc -= sy;
-
- // Convert only valid pixels
- if (yc >= 0 && yc < original->getHeight() && xc >= 0 && xc < original->getWidth()) {
-
- // multiplier for vignetting correction
- double vignmul = 1.0;
-
- if (needsVignetting()) {
- if (darkening) {
- vignmul /= std::max (v + mul * tanh (b * (maxRadius - s * r2) / maxRadius), 0.001);
- } else {
- vignmul = v + mul * tanh (b * (maxRadius - s * r2) / maxRadius);
- }
- }
-
- if (needsGradient()) {
- vignmul *= calcGradientFactor (gp, cx + x, cy + y);
- }
-
- if (needsPCVignetting()) {
- vignmul *= calcPCVignetteFactor (pcv, cx + x, cy + y);
- }
-
- if (yc < original->getHeight() - 1 && xc < original->getWidth() - 1) {
- // all interpolation pixels inside image
- transformed->r (y, x) = vignmul * (original->r (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->r (yc, xc + 1) * Dx * (1.0 - Dy) + original->r (yc + 1, xc) * (1.0 - Dx) * Dy + original->r (yc + 1, xc + 1) * Dx * Dy);
- transformed->g (y, x) = vignmul * (original->g (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->g (yc, xc + 1) * Dx * (1.0 - Dy) + original->g (yc + 1, xc) * (1.0 - Dx) * Dy + original->g (yc + 1, xc + 1) * Dx * Dy);
- transformed->b (y, x) = vignmul * (original->b (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->b (yc, xc + 1) * Dx * (1.0 - Dy) + original->b (yc + 1, xc) * (1.0 - Dx) * Dy + original->b (yc + 1, xc + 1) * Dx * Dy);
- } else {
- // edge pixels
- int y1 = LIM (yc, 0, original->getHeight() - 1);
- int y2 = LIM (yc + 1, 0, original->getHeight() - 1);
- int x1 = LIM (xc, 0, original->getWidth() - 1);
- int x2 = LIM (xc + 1, 0, original->getWidth() - 1);
- transformed->r (y, x) = vignmul * (original->r (y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->r (y1, x2) * Dx * (1.0 - Dy) + original->r (y2, x1) * (1.0 - Dx) * Dy + original->r (y2, x2) * Dx * Dy);
- transformed->g (y, x) = vignmul * (original->g (y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->g (y1, x2) * Dx * (1.0 - Dy) + original->g (y2, x1) * (1.0 - Dx) * Dy + original->g (y2, x2) * Dx * Dy);
- transformed->b (y, x) = vignmul * (original->b (y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->b (y1, x2) * Dx * (1.0 - Dy) + original->b (y2, x1) * (1.0 - Dx) * Dy + original->b (y2, x2) * Dx * Dy);
- }
- } else {
- // not valid (source pixel x,y not inside source image, etc.)
- transformed->r (y, x) = 0;
- transformed->g (y, x) = 0;
- transformed->b (y, x) = 0;
- }
- }
- }
-}
-
-double ImProcFunctions::getTransformAutoFill (int oW, int oH, const LCPMapper *pLCPMap)
+double ImProcFunctions::getTransformAutoFill (int oW, int oH, const LensCorrection *pLCPMap)
{
if (!needsCA() && !needsDistortion() && !needsRotation() && !needsPerspective() && (!params->lensProf.useDist || pLCPMap == nullptr)) {
return 1;
@@ -1150,12 +1015,17 @@ bool ImProcFunctions::needsVignetting ()
bool ImProcFunctions::needsLCP ()
{
- return params->lensProf.lcpFile.length() > 0;
+ return params->lensProf.lcpFile.length() > 0 && !needsLensfun();
+}
+
+bool ImProcFunctions::needsLensfun()
+{
+ return params->lensProf.useLensfun;
}
bool ImProcFunctions::needsTransform ()
{
- return needsCA () || needsDistortion () || needsRotation () || needsPerspective () || needsGradient () || needsPCVignetting () || needsVignetting () || needsLCP();
+ return needsCA () || needsDistortion () || needsRotation () || needsPerspective () || needsGradient () || needsPCVignetting () || needsVignetting () || needsLCP() || needsLensfun();
}
diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc
index c09d2d9be..3824d2b2f 100644
--- a/rtengine/lcp.cc
+++ b/rtengine/lcp.cc
@@ -20,27 +20,43 @@
#include
#include
-#include "lcp.h"
#include
#ifdef WIN32
-#include
#include
+#include
#endif
+#include "lcp.h"
+
#include "settings.h"
-using namespace std;
-using namespace rtengine;
-
-
-namespace rtengine {
+namespace rtengine
+{
extern const Settings* settings;
}
-LCPModelCommon::LCPModelCommon() :
+class rtengine::LCPProfile::LCPPersModel
+{
+public:
+ LCPPersModel();
+ bool hasModeData(LCPCorrectionMode mode) const;
+ void print() const;
+
+ float focLen;
+ float focDist;
+ float aperture; // this is what it refers to
+
+ LCPModelCommon base; // base perspective correction
+ LCPModelCommon chromRG;
+ LCPModelCommon chromG;
+ LCPModelCommon chromBG; // red/green, green, blue/green (may be empty)
+ LCPModelCommon vignette; // vignette (may be empty)
+};
+
+rtengine::LCPModelCommon::LCPModelCommon() :
foc_len_x(-1.0f),
foc_len_y(-1.0f),
img_center_x(0.5f),
@@ -59,20 +75,23 @@ LCPModelCommon::LCPModelCommon() :
{
}
-bool LCPModelCommon::empty() const
+bool rtengine::LCPModelCommon::empty() const
{
- return param[0] == 0.0f && param[1] == 0.0f && param[2] == 0.0f;
+ return
+ param[0] == 0.0f
+ && param[1] == 0.0f
+ && param[2] == 0.0f;
}
-void LCPModelCommon::print() const
+void rtengine::LCPModelCommon::print() const
{
- printf("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n", foc_len_x, foc_len_y, img_center_x, img_center_y, scale_factor, mean_error);
- printf("xy0 %g/%g fxy %g/%g\n", x0, y0, fx, fy);
- printf("param: %g/%g/%g/%g/%g\n", param[0], param[1], param[2], param[3], param[4]);
+ std::printf("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n", foc_len_x, foc_len_y, img_center_x, img_center_y, scale_factor, mean_error);
+ std::printf("xy0 %g/%g fxy %g/%g\n", x0, y0, fx, fy);
+ std::printf("param: %g/%g/%g/%g/%g\n", param[0], param[1], param[2], param[3], param[4]);
}
// weighted merge two parameters
-void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA)
+void rtengine::LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA)
{
const float facB = 1.0f - facA;
@@ -83,7 +102,7 @@ void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, flo
scale_factor = facA * a.scale_factor + facB * b.scale_factor;
mean_error = facA * a.mean_error + facB * b.mean_error;
- for (int i = 0; i < 5; i++) {
+ for (int i = 0; i < 5; ++i) {
param[i] = facA * a.param[i] + facB * b.param[i];
}
@@ -96,7 +115,16 @@ void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, flo
}
-void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY)
+void rtengine::LCPModelCommon::prepareParams(
+ int fullWidth,
+ int fullHeight,
+ float focalLength,
+ float focalLength35mm,
+ float sensorFormatFactor,
+ bool swapXY,
+ bool mirrorX,
+ bool mirrorY
+)
{
// Mention that the Adobe technical paper has a bug here, the DMAX is handled differently for focLen and imgCenter
const int Dmax = std::max(fullWidth, fullHeight);
@@ -125,60 +153,842 @@ void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLen
rfx = 1.0f / fx;
rfy = 1.0f / fy;
- //printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter);
+ //std::printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter);
}
-LCPPersModel::LCPPersModel()
+rtengine::LCPProfile::LCPPersModel::LCPPersModel() :
+ focLen(0.f),
+ focDist(0.f),
+ aperture(0.f)
{
- focLen = focDist = aperture = 0;
}
-// mode: 0=distortion, 1=vignette, 2=CA
-bool LCPPersModel::hasModeData(int mode) const
+bool rtengine::LCPProfile::LCPPersModel::hasModeData(LCPCorrectionMode mode) const
{
- return (mode == 0 && !vignette.empty() && !vignette.bad_error) || (mode == 1 && !base.empty() && !base.bad_error)
- || (mode == 2 && !chromRG.empty() && !chromG.empty() && !chromBG.empty() &&
- !chromRG.bad_error && !chromG.bad_error && !chromBG.bad_error);
+ switch (mode) {
+ case LCPCorrectionMode::VIGNETTE: {
+ return !vignette.empty() && !vignette.bad_error;
+ }
+
+ case LCPCorrectionMode::DISTORTION: {
+ return !base.empty() && !base.bad_error;
+ }
+
+ case LCPCorrectionMode::CA: {
+ return
+ !chromRG.empty()
+ && !chromG.empty()
+ && !chromBG.empty()
+ && !chromRG.bad_error
+ && !chromG.bad_error
+ && !chromBG.bad_error;
+ }
+ }
+
+ assert(false);
+ return false;
}
-void LCPPersModel::print() const
+void rtengine::LCPProfile::LCPPersModel::print() const
{
- printf("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture);
- printf("Base:\n");
+ std::printf("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture);
+ std::printf("Base:\n");
base.print();
if (!chromRG.empty()) {
- printf("ChromRG:\n");
+ std::printf("ChromRG:\n");
chromRG.print();
}
if (!chromG.empty()) {
- printf("ChromG:\n");
+ std::printf("ChromG:\n");
chromG.print();
}
if (!chromBG.empty()) {
- printf("ChromBG:\n");
+ std::printf("ChromBG:\n");
chromBG.print();
}
if (!vignette.empty()) {
- printf("Vignette:\n");
+ std::printf("Vignette:\n");
vignette.print();
}
- printf("\n");
+ std::printf("\n");
}
-// if !vignette then geometric and CA
-LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP,
- int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg) :useCADist(false), swapXY(false), isFisheye(false), enableCA(false)
+rtengine::LCPProfile::LCPProfile(const Glib::ustring& fname) :
+ isFisheye(false),
+ sensorFormatFactor(1.f),
+ persModelCount(0),
+ inCamProfiles(false),
+ firstLIDone(false),
+ inPerspect(false),
+ inAlternateLensID(false),
+ inAlternateLensNames(false),
+ lastTag{},
+ inInvalidTag{},
+ pCurPersModel(nullptr),
+ pCurCommon(nullptr),
+ aPersModel{}
{
- if (pProf == nullptr) {
+ const int BufferSize = 8192;
+ char buf[BufferSize];
+
+ XML_Parser parser = XML_ParserCreate(nullptr);
+
+ if (!parser) {
+ throw "Couldn't allocate memory for XML parser";
+ }
+
+ XML_SetElementHandler(parser, XmlStartHandler, XmlEndHandler);
+ XML_SetCharacterDataHandler(parser, XmlTextHandler);
+ XML_SetUserData(parser, static_cast(this));
+
+ FILE* const pFile = g_fopen(fname.c_str (), "rb");
+
+ if (pFile) {
+ bool done;
+
+ do {
+ int bytesRead = fread(buf, 1, BufferSize, pFile);
+ done = feof(pFile);
+
+ if (XML_Parse(parser, buf, bytesRead, done) == XML_STATUS_ERROR) {
+ XML_ParserFree(parser);
+ throw "Invalid XML in LCP file";
+ }
+ } while (!done);
+
+ fclose(pFile);
+ }
+
+ XML_ParserFree(parser);
+
+ if (settings->verbose) {
+ std::printf("Parsing %s\n", fname.c_str());
+ }
+ // Two phase filter: first filter out the very rough ones, that distord the average a lot
+ // force it, even if there are few frames (community profiles)
+ filterBadFrames(LCPCorrectionMode::VIGNETTE, 2.0, 0);
+ filterBadFrames(LCPCorrectionMode::CA, 2.0, 0);
+ // from the non-distorded, filter again on new average basis, but only if there are enough frames left
+ filterBadFrames(LCPCorrectionMode::VIGNETTE, 1.5, 50);
+ filterBadFrames(LCPCorrectionMode::CA, 1.5, 50);
+}
+
+rtengine::LCPProfile::~LCPProfile()
+{
+ delete pCurPersModel;
+
+ for (int i = 0; i < MaxPersModelCount; ++i) {
+ delete aPersModel[i];
+ }
+}
+
+void rtengine::LCPProfile::calcParams(
+ LCPCorrectionMode mode,
+ float focalLength,
+ float focusDist,
+ float aperture,
+ LCPModelCommon* pCorr1,
+ LCPModelCommon* pCorr2,
+ LCPModelCommon* pCorr3
+) const
+{
+ const float euler = std::exp(1.0);
+
+ // find the frames with the least distance, focal length wise
+ LCPPersModel* pLow = nullptr;
+ LCPPersModel* pHigh = nullptr;
+
+ const float focalLengthLog = std::log(focalLength); //, apertureLog=aperture>0 ? std::log(aperture) : 0;
+ const float focusDistLog = focusDist > 0 ? std::log(focusDist) + euler : 0;
+
+ // Pass 1: determining best focal length, if possible different focusDistances (for the focDist is not given case)
+ for (int pm = 0; pm < persModelCount; ++pm) {
+ const float f = aPersModel[pm]->focLen;
+
+ if (aPersModel[pm]->hasModeData(mode)) {
+ if (
+ f <= focalLength
+ && (
+ pLow == nullptr
+ || f > pLow->focLen
+ || (
+ focusDist == 0
+ && f == pLow->focLen
+ && pLow->focDist > aPersModel[pm]->focDist
+ )
+ )
+ ) {
+ pLow = aPersModel[pm];
+ }
+
+ if (
+ f >= focalLength
+ && (
+ pHigh == nullptr
+ || f < pHigh->focLen
+ || (
+ focusDist == 0
+ && f == pHigh->focLen
+ && pHigh->focDist < aPersModel[pm]->focDist
+ )
+ )
+ ) {
+ pHigh = aPersModel[pm];
+ }
+ }
+ }
+
+ if (!pLow) {
+ pLow = pHigh;
+ }
+ else if (!pHigh) {
+ pHigh = pLow;
+ }
+ else {
+ // Pass 2: We have some, so take the best aperture for vignette and best focus for CA and distortion
+ // there are usually several frame per focal length. In the end pLow will have both flen and apterure/focdis below the target,
+ // and vice versa pHigh
+ const float bestFocLenLow = pLow->focLen;
+ const float bestFocLenHigh = pHigh->focLen;
+
+ for (int pm = 0; pm < persModelCount; ++pm) {
+ const float aper = aPersModel[pm]->aperture; // float aperLog=std::log(aper);
+ const float focDist = aPersModel[pm]->focDist;
+ const float focDistLog = std::log(focDist) + euler;
+
+ double meanErr;
+
+ if (aPersModel[pm]->hasModeData(mode)) {
+ double lowMeanErr = 0.0;
+ double highMeanErr = 0.0;
+
+ switch (mode) {
+ case LCPCorrectionMode::VIGNETTE: {
+ meanErr = aPersModel[pm]->vignette.mean_error;
+ lowMeanErr = pLow->vignette.mean_error;
+ highMeanErr = pHigh->vignette.mean_error;
+ break;
+ }
+
+ case LCPCorrectionMode::DISTORTION: {
+ meanErr = aPersModel[pm]->base.mean_error;
+ lowMeanErr = pLow->base.mean_error;
+ highMeanErr = pHigh->base.mean_error;
+ break;
+ }
+
+ case LCPCorrectionMode::CA: {
+ meanErr = aPersModel[pm]->chromG.mean_error;
+ lowMeanErr = pLow->chromG.mean_error;
+ highMeanErr = pHigh->chromG.mean_error;
+ break;
+ }
+ }
+
+ if (aperture > 0 && mode != LCPCorrectionMode::CA) {
+ if (
+ aPersModel[pm]->focLen == bestFocLenLow
+ && (
+ (
+ aper == aperture
+ && lowMeanErr > meanErr
+ )
+ || (
+ aper >= aperture
+ && aper < pLow->aperture
+ && pLow->aperture > aperture
+ )
+ || (
+ aper <= aperture
+ && (
+ pLow->aperture > aperture
+ || fabs(aperture - aper) < fabs(aperture - pLow->aperture)
+ )
+ )
+ )
+ ) {
+ pLow = aPersModel[pm];
+ }
+
+ if (
+ aPersModel[pm]->focLen == bestFocLenHigh
+ && (
+ (
+ aper == aperture
+ && highMeanErr > meanErr
+ )
+ || (
+ aper <= aperture
+ && aper > pHigh->aperture
+ && pHigh->aperture < aperture
+ )
+ || (
+ aper >= aperture
+ && (
+ pHigh->aperture < aperture
+ || fabs(aperture - aper) < fabs(aperture - pHigh->aperture)
+ )
+ )
+ )
+ ) {
+ pHigh = aPersModel[pm];
+ }
+ }
+ else if (focusDist > 0 && mode != LCPCorrectionMode::VIGNETTE) {
+ // by focus distance
+ if (
+ aPersModel[pm]->focLen == bestFocLenLow
+ && (
+ (
+ focDist == focusDist
+ && lowMeanErr > meanErr
+ )
+ || (
+ focDist >= focusDist
+ && focDist < pLow->focDist
+ && pLow->focDist > focusDist
+ )
+ || (
+ focDist <= focusDist
+ && (
+ pLow->focDist > focusDist
+ || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (std::log(pLow->focDist) + euler))
+ )
+ )
+ )
+ ) {
+ pLow = aPersModel[pm];
+ }
+
+ if (
+ aPersModel[pm]->focLen == bestFocLenHigh
+ && (
+ (
+ focDist == focusDist
+ && highMeanErr > meanErr
+ )
+ || (
+ focDist <= focusDist
+ && focDist > pHigh->focDist
+ && pHigh->focDist < focusDist
+ )
+ || (
+ focDist >= focusDist
+ && (
+ pHigh->focDist < focusDist
+ || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (std::log(pHigh->focDist) + euler))
+ )
+ )
+ )
+ ) {
+ pHigh = aPersModel[pm];
+ }
+ }
+ else {
+ // no focus distance available, just error
+ if (aPersModel[pm]->focLen == bestFocLenLow && lowMeanErr > meanErr) {
+ pLow = aPersModel[pm];
+ }
+
+ if (aPersModel[pm]->focLen == bestFocLenHigh && highMeanErr > meanErr) {
+ pHigh = aPersModel[pm];
+ }
+ }
+
+ }
+ }
+ }
+
+ if (pLow != nullptr && pHigh != nullptr) {
+ // average out the factors, linear interpolation in logarithmic scale
+ float facLow = 0.5f;
+ bool focLenOnSpot = false; // pretty often, since max/min are often as frames in LCP
+
+ // There is as foclen range, take that as basis
+ if (pLow->focLen < pHigh->focLen) {
+ facLow = (std::log(pHigh->focLen) - focalLengthLog) / (std::log(pHigh->focLen) - std::log(pLow->focLen));
+ } else {
+ focLenOnSpot = pLow->focLen == pHigh->focLen && pLow->focLen == focalLength;
+ }
+
+ // and average the other factor if available
+ if (
+ mode == LCPCorrectionMode::VIGNETTE
+ && pLow->aperture < aperture
+ && pHigh->aperture > aperture
+ ) {
+ // Mix in aperture
+ const float facAperLow = (pHigh->aperture - aperture) / (pHigh->aperture - pLow->aperture);
+ facLow = focLenOnSpot ? facAperLow : (0.5 * facLow + 0.5 * facAperLow);
+ }
+ else if (
+ mode != LCPCorrectionMode::VIGNETTE
+ && focusDist > 0
+ && pLow->focDist < focusDist
+ && pHigh->focDist > focusDist
+ ) {
+ // focus distance for all else (if focus distance is given)
+ const float facDistLow = (std::log(pHigh->focDist) + euler - focusDistLog) / (std::log(pHigh->focDist) - std::log(pLow->focDist));
+ facLow = focLenOnSpot ? facDistLow : (0.8 * facLow + 0.2 * facDistLow);
+ }
+
+ switch (mode) {
+ case LCPCorrectionMode::VIGNETTE: {
+ pCorr1->merge(pLow->vignette, pHigh->vignette, facLow);
+ break;
+ }
+
+ case LCPCorrectionMode::DISTORTION: {
+ pCorr1->merge(pLow->base, pHigh->base, facLow);
+ break;
+ }
+
+ case LCPCorrectionMode::CA: {
+ pCorr1->merge(pLow->chromRG, pHigh->chromRG, facLow);
+ pCorr2->merge(pLow->chromG, pHigh->chromG, facLow);
+ pCorr3->merge(pLow->chromBG, pHigh->chromBG, facLow);
+ break;
+ }
+ }
+
+ if (settings->verbose) {
+ std::printf("LCP mode=%i, dist: %g found frames: Fno %g-%g; FocLen %g-%g; Dist %g-%g with weight %g\n", toUnderlying(mode), focusDist, pLow->aperture, pHigh->aperture, pLow->focLen, pHigh->focLen, pLow->focDist, pHigh->focDist, facLow);
+ }
+ } else {
+ if (settings->verbose) {
+ std::printf("Error: LCP file contained no %s parameters\n", mode == LCPCorrectionMode::VIGNETTE ? "vignette" : mode == LCPCorrectionMode::DISTORTION ? "distortion" : "CA" );
+ }
+ }
+}
+
+void rtengine::LCPProfile::print() const
+{
+ std::printf("=== Profile %s\n", profileName.c_str());
+ std::printf("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n", persModelCount, isRaw, isFisheye, sensorFormatFactor);
+
+ for (int pm = 0; pm < persModelCount; ++pm) {
+ aPersModel[pm]->print();
+ }
+}
+
+// from all frames not marked as bad already, take average and filter out frames with higher deviation than this if there are enough values
+int rtengine::LCPProfile::filterBadFrames(LCPCorrectionMode mode, double maxAvgDevFac, int minFramesLeft)
+{
+ // take average error, then calculated the maximum deviation allowed
+ double err = 0.0;
+ int count = 0;
+
+ for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; ++pm) {
+ if (aPersModel[pm]->hasModeData(mode)) {
+ ++count;
+ switch (mode) {
+ case LCPCorrectionMode::VIGNETTE: {
+ err += aPersModel[pm]->vignette.mean_error;
+ break;
+ }
+
+ case LCPCorrectionMode::DISTORTION: {
+ err += aPersModel[pm]->base.mean_error;
+ break;
+ }
+
+ case LCPCorrectionMode::CA: {
+ err += rtengine::max(aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error, aPersModel[pm]->chromBG.mean_error);
+ break;
+ }
+ }
+ }
+ }
+
+ // Only if we have enough frames, filter out errors
+ int filtered = 0;
+
+ if (count >= minFramesLeft) {
+ if (count > 0) {
+ err /= count;
+ }
+
+ // Now mark all the bad ones as bad, and hasModeData will return false;
+ for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; ++pm) {
+ if (aPersModel[pm]->hasModeData(mode)) {
+ switch (mode) {
+ case LCPCorrectionMode::VIGNETTE: {
+ if (aPersModel[pm]->vignette.mean_error > maxAvgDevFac * err) {
+ aPersModel[pm]->vignette.bad_error = true;
+ filtered++;
+ }
+ break;
+ }
+
+ case LCPCorrectionMode::DISTORTION: {
+ if (aPersModel[pm]->base.mean_error > maxAvgDevFac * err) {
+ aPersModel[pm]->base.bad_error = true;
+ filtered++;
+ }
+ break;
+ }
+
+ case LCPCorrectionMode::CA: {
+ if (
+ aPersModel[pm]->chromRG.mean_error > maxAvgDevFac * err
+ || aPersModel[pm]->chromG.mean_error > maxAvgDevFac * err
+ || aPersModel[pm]->chromBG.mean_error > maxAvgDevFac * err
+ ) {
+ aPersModel[pm]->chromRG.bad_error = true;
+ aPersModel[pm]->chromG.bad_error = true;
+ aPersModel[pm]->chromBG.bad_error = true;
+ ++filtered;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (settings->verbose && count) {
+ std::printf("Filtered %.1f%% frames for maxAvgDevFac %g leaving %i\n", filtered * 100.f / count, maxAvgDevFac, count - filtered);
+ }
+ }
+
+ return filtered;
+}
+
+void rtengine::LCPProfile::handle_text(const std::string& text)
+{
+ // Check if it contains non-whitespaces (there are several calls to this for one tag unfortunately)
+ bool onlyWhiteSpace = true;
+ for (auto c : text) {
+ if (!std::isspace(c)) {
+ onlyWhiteSpace = false;
+ break;
+ }
+ }
+
+ if (onlyWhiteSpace) {
return;
}
- useCADist = useCADistP;
+ LCPProfile* const pProf = this;
+
+ // convert to null terminated
+ const std::string tag = pProf->lastTag;
+
+ // Common data section
+ if (!pProf->firstLIDone) {
+ // Generic tags are the same for all
+ if (tag == "ProfileName") {
+ pProf->profileName = text;
+ } else if (tag == "Model") {
+ pProf->camera = text;
+ } else if (tag == "Lens") {
+ pProf->lens = text;
+ } else if (tag == "CameraPrettyName") {
+ pProf->cameraPrettyName = text;
+ } else if (tag == "LensPrettyName") {
+ pProf->lensPrettyName = text;
+ } else if (tag == "CameraRawProfile") {
+ pProf->isRaw = text == "True";
+ }
+ }
+
+ // Locale should be already set
+ assert(std::atof("1.2345") == 1.2345);
+
+ if (!pProf->firstLIDone) {
+ if (tag == "SensorFormatFactor") {
+ pProf->sensorFormatFactor = std::atof(text.c_str());
+ }
+ }
+
+ // Perspective model base data
+ if (tag == "FocalLength") {
+ pProf->pCurPersModel->focLen = std::atof(text.c_str());
+ } else if (tag == "FocusDistance") {
+ double focDist = std::atof(text.c_str());
+ pProf->pCurPersModel->focDist = focDist < 10000 ? focDist : 10000;
+ } else if (tag == "ApertureValue") {
+ pProf->pCurPersModel->aperture = std::atof(text.c_str());
+ }
+
+ // Section depended
+ if (tag == "FocalLengthX") {
+ pProf->pCurCommon->foc_len_x = std::atof(text.c_str());
+ } else if (tag == "FocalLengthY") {
+ pProf->pCurCommon->foc_len_y = std::atof(text.c_str());
+ } else if (tag == "ImageXCenter") {
+ pProf->pCurCommon->img_center_x = std::atof(text.c_str());
+ } else if (tag == "ImageYCenter") {
+ pProf->pCurCommon->img_center_y = std::atof(text.c_str());
+ } else if (tag == "ScaleFactor") {
+ pProf->pCurCommon->scale_factor = std::atof(text.c_str());
+ } else if (tag == "ResidualMeanError") {
+ pProf->pCurCommon->mean_error = std::atof(text.c_str());
+ } else if (tag == "RadialDistortParam1" || tag == "VignetteModelParam1") {
+ pProf->pCurCommon->param[0] = std::atof(text.c_str());
+ } else if (tag == "RadialDistortParam2" || tag == "VignetteModelParam2") {
+ pProf->pCurCommon->param[1] = std::atof(text.c_str());
+ } else if (tag == "RadialDistortParam3" || tag == "VignetteModelParam3") {
+ pProf->pCurCommon->param[2] = std::atof(text.c_str());
+ } else if (tag == "RadialDistortParam4" || tag == "TangentialDistortParam1") {
+ pProf->pCurCommon->param[3] = std::atof(text.c_str());
+ } else if (tag == "RadialDistortParam5" || tag == "TangentialDistortParam2") {
+ pProf->pCurCommon->param[4] = std::atof(text.c_str());
+ }
+}
+
+void XMLCALL rtengine::LCPProfile::XmlStartHandler(void* pLCPProfile, const char* el, const char** attr)
+{
+ LCPProfile* const pProf = static_cast(pLCPProfile);
+
+ bool parseAttr = false;
+
+ if (*pProf->inInvalidTag) {
+ return; // We ignore everything in dirty tag till it's gone
+ }
+
+ // clean up tagname
+ const char* src = strrchr(el, ':');
+
+ if (src == nullptr) {
+ src = el;
+ } else {
+ ++src;
+ }
+
+ strcpy(pProf->lastTag, src);
+
+ const std::string src_str = src;
+
+ if (src_str == "VignetteModelPiecewiseParam") {
+ strcpy(pProf->inInvalidTag, src);
+ }
+
+ if (src_str == "CameraProfiles") {
+ pProf->inCamProfiles = true;
+ }
+
+ if (src_str == "AlternateLensIDs") {
+ pProf->inAlternateLensID = true;
+ }
+
+ if (src_str == "AlternateLensNames") {
+ pProf->inAlternateLensNames = true;
+ }
+
+ if (
+ !pProf->inCamProfiles
+ || pProf->inAlternateLensID
+ || pProf->inAlternateLensNames
+ ) {
+ return;
+ }
+
+ if (src_str == "li") {
+ pProf->pCurPersModel = new LCPPersModel();
+ pProf->pCurCommon = &pProf->pCurPersModel->base; // iterated to next tags within persModel
+ return;
+ }
+
+ if (src_str == "PerspectiveModel") {
+ pProf->firstLIDone = true;
+ pProf->inPerspect = true;
+ return;
+ } else if (src_str == "FisheyeModel") {
+ pProf->firstLIDone = true;
+ pProf->inPerspect = true;
+ pProf->isFisheye = true; // just misses third param, and different path, rest is the same
+ return;
+ } else if (src_str == "Description") {
+ parseAttr = true;
+ }
+
+ // Move pointer to general section
+ if (pProf->inPerspect) {
+ if (src_str == "ChromaticRedGreenModel") {
+ pProf->pCurCommon = &pProf->pCurPersModel->chromRG;
+ parseAttr = true;
+ } else if (src_str == "ChromaticGreenModel") {
+ pProf->pCurCommon = &pProf->pCurPersModel->chromG;
+ parseAttr = true;
+ } else if (src_str == "ChromaticBlueGreenModel") {
+ pProf->pCurCommon = &pProf->pCurPersModel->chromBG;
+ parseAttr = true;
+ } else if (src_str == "VignetteModel") {
+ pProf->pCurCommon = &pProf->pCurPersModel->vignette;
+ parseAttr = true;
+ }
+ }
+
+ // some profiles (espc. Pentax) have a different structure that is attributes based
+ // simulate tags by feeding them in
+ if (parseAttr && attr != nullptr) {
+ for (int i = 0; attr[i]; i += 2) {
+ const char* nameStart = strrchr(attr[i], ':');
+
+ if (nameStart == nullptr) {
+ nameStart = attr[i];
+ } else {
+ ++nameStart;
+ }
+
+ strncpy(pProf->lastTag, nameStart, 255);
+
+ pProf->handle_text(attr[i + 1]);
+ }
+ }
+}
+
+void XMLCALL rtengine::LCPProfile::XmlTextHandler(void* pLCPProfile, const XML_Char* s, int len)
+{
+ LCPProfile* const pProf = static_cast(pLCPProfile);
+
+ if (
+ !pProf->inCamProfiles
+ || pProf->inAlternateLensID
+ || pProf->inAlternateLensNames
+ || *pProf->inInvalidTag
+ ) {
+ return;
+ }
+
+ for (int i = 0; i < len; ++i) {
+ pProf->textbuf << s[i];
+ }
+}
+
+void XMLCALL rtengine::LCPProfile::XmlEndHandler(void* pLCPProfile, const char* el)
+{
+ LCPProfile* const pProf = static_cast(pLCPProfile);
+
+ pProf->handle_text(pProf->textbuf.str());
+ pProf->textbuf.str("");
+
+ // We ignore everything in dirty tag till it's gone
+ if (*pProf->inInvalidTag) {
+ if (std::strstr(el, pProf->inInvalidTag)) {
+ *pProf->inInvalidTag = 0;
+ }
+
+ return;
+ }
+
+ if (std::strstr(el, ":CameraProfiles")) {
+ pProf->inCamProfiles = false;
+ }
+
+ if (std::strstr(el, ":AlternateLensIDs")) {
+ pProf->inAlternateLensID = false;
+ }
+
+ if (std::strstr(el, ":AlternateLensNames")) {
+ pProf->inAlternateLensNames = false;
+ }
+
+ if (
+ !pProf->inCamProfiles
+ || pProf->inAlternateLensID
+ || pProf->inAlternateLensNames
+ ) {
+ return;
+ }
+
+ if (std::strstr(el, ":PerspectiveModel") || std::strstr(el, ":FisheyeModel")) {
+ pProf->inPerspect = false;
+ } else if (std::strstr(el, ":li")) {
+ pProf->aPersModel[pProf->persModelCount] = pProf->pCurPersModel;
+ pProf->pCurPersModel = nullptr;
+ ++pProf->persModelCount;
+ }
+}
+
+// Generates as singleton
+rtengine::LCPStore* rtengine::LCPStore::getInstance()
+{
+ static LCPStore instance_;
+ return &instance_;
+}
+
+bool rtengine::LCPStore::isValidLCPFileName(const Glib::ustring& filename) const
+{
+ if (!Glib::file_test(filename, Glib::FILE_TEST_EXISTS) || Glib::file_test (filename, Glib::FILE_TEST_IS_DIR)) {
+ return false;
+ }
+
+ const size_t pos = filename.find_last_of ('.');
+ return pos > 0 && !filename.casefold().compare(pos, 4, ".lcp");
+}
+
+std::shared_ptr rtengine::LCPStore::getProfile(const Glib::ustring& filename) const
+{
+ if (filename.length() == 0 || !isValidLCPFileName(filename)) {
+ return nullptr;
+ }
+
+ std::shared_ptr res;
+ if (!cache.get(filename, res)) {
+ res.reset(new LCPProfile(filename));
+ cache.set(filename, res);
+ }
+
+ return res;
+}
+
+Glib::ustring rtengine::LCPStore::getDefaultCommonDirectory() const
+{
+ Glib::ustring dir;
+
+#ifdef WIN32
+ WCHAR pathW[MAX_PATH] = {0};
+
+ if (SHGetSpecialFolderPathW(NULL, pathW, CSIDL_COMMON_APPDATA, false)) {
+ char pathA[MAX_PATH];
+ WideCharToMultiByte(CP_UTF8, 0, pathW, -1, pathA, MAX_PATH, 0, 0);
+ Glib::ustring fullDir = Glib::ustring(pathA) + Glib::ustring("\\Adobe\\CameraRaw\\LensProfiles\\1.0");
+
+ if (Glib::file_test (fullDir, Glib::FILE_TEST_IS_DIR)) {
+ dir = fullDir;
+ }
+ }
+
+#endif
+
+ // TODO: Add Mac paths here
+
+ return dir;
+}
+
+rtengine::LCPStore::LCPStore(unsigned int _cache_size) :
+ cache(_cache_size)
+{
+}
+
+// if !vignette then geometric and CA
+rtengine::LCPMapper::LCPMapper(
+ const std::shared_ptr& pProf,
+ float focalLength,
+ float focalLength35mm,
+ float focusDist,
+ float aperture,
+ bool vignette,
+ bool useCADistP,
+ int fullWidth,
+ int fullHeight,
+ const CoarseTransformParams& coarse,
+ int rawRotationDeg
+) :
+ enableCA(false),
+ useCADist(useCADistP),
+ swapXY(false),
+ isFisheye(false)
+{
+ if (!pProf) {
+ return;
+ }
// determine in what the image with the RAW landscape in comparison (calibration target)
// in vignetting, the rotation has not taken place yet
@@ -188,84 +998,97 @@ LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm
rot = (coarse.rotate + rawRotationDeg) % 360;
}
- swapXY = (rot == 90 || rot == 270);
- bool mirrorX = (rot == 90 || rot == 180);
- bool mirrorY = (rot == 180 || rot == 270);
+ swapXY = (rot == 90 || rot == 270);
+
+ const bool mirrorX = (rot == 90 || rot == 180);
+ const bool mirrorY = (rot == 180 || rot == 270);
if (settings->verbose) {
- printf("Vign: %i, fullWidth: %i/%i, focLen %g SwapXY: %i / MirX/Y %i / %i on rot:%i from %i\n",vignette, fullWidth, fullHeight, focalLength, swapXY, mirrorX, mirrorY, rot, rawRotationDeg);
+ std::printf("Vign: %i, fullWidth: %i/%i, focLen %g SwapXY: %i / MirX/Y %i / %i on rot:%i from %i\n",vignette, fullWidth, fullHeight, focalLength, swapXY, mirrorX, mirrorY, rot, rawRotationDeg);
}
- pProf->calcParams(vignette ? 0 : 1, focalLength, focusDist, aperture, &mc, nullptr, nullptr);
+ pProf->calcParams(vignette ? LCPCorrectionMode::VIGNETTE : LCPCorrectionMode::DISTORTION, focalLength, focusDist, aperture, &mc, nullptr, nullptr);
mc.prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY);
if (!vignette) {
- pProf->calcParams(2, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]);
+ pProf->calcParams(LCPCorrectionMode::CA, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]);
- for (int i = 0; i < 3; i++) {
+ for (int i = 0; i < 3; ++i) {
chrom[i].prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY);
}
}
- enableCA = !vignette && focusDist > 0;
+ enableCA = !vignette && focusDist > 0.f;
isFisheye = pProf->isFisheye;
}
-void LCPMapper::correctDistortion(double& x, double& y, double scale) const
+bool rtengine::LCPMapper::isCACorrectionAvailable() const
{
+ return enableCA;
+}
+
+void rtengine::LCPMapper::correctDistortion(double &x, double &y, int cx, int cy, double scale) const
+{
+ x += cx;
+ y += cy;
+
if (isFisheye) {
- double u = x * scale;
- double v = y * scale;
- double u0 = mc.x0 * scale;
- double v0 = mc.y0 * scale;
- double du = (u - u0);
- double dv = (v - v0);
- double fx = mc.fx;
- double fy = mc.fy;
- double k1 = mc.param[0];
- double k2 = mc.param[1];
- double r = sqrt(du * du + dv * dv);
- double f = sqrt(fx*fy / (scale * scale));
- double th = atan2(r, f);
- double th2 = th * th;
- double cfact = (((k2 * th2 + k1) * th2 + 1) * th) / r;
- double ud = cfact * fx * du + u0;
- double vd = cfact * fy * dv + v0;
+ const double u = x * scale;
+ const double v = y * scale;
+ const double u0 = mc.x0 * scale;
+ const double v0 = mc.y0 * scale;
+ const double du = (u - u0);
+ const double dv = (v - v0);
+ const double fx = mc.fx;
+ const double fy = mc.fy;
+ const double k1 = mc.param[0];
+ const double k2 = mc.param[1];
+ const double r = sqrt(du * du + dv * dv);
+ const double f = sqrt(fx*fy / (scale * scale));
+ const double th = atan2(r, f);
+ const double th2 = th * th;
+ const double cfact = (((k2 * th2 + k1) * th2 + 1) * th) / r;
+ const double ud = cfact * fx * du + u0;
+ const double vd = cfact * fy * dv + v0;
x = ud;
y = vd;
} else {
x *= scale;
y *= scale;
- double x0 = mc.x0 * scale;
- double y0 = mc.y0 * scale;
- double xd = (x - x0) / mc.fx, yd = (y - y0) / mc.fy;
+ const double x0 = mc.x0 * scale;
+ const double y0 = mc.y0 * scale;
+ const double xd = (x - x0) / mc.fx, yd = (y - y0) / mc.fy;
const LCPModelCommon::Param aDist = mc.param;
- double rsqr = xd * xd + yd * yd;
- double xfac = aDist[swapXY ? 3 : 4], yfac = aDist[swapXY ? 4 : 3];
+ const double rsqr = xd * xd + yd * yd;
+ const double xfac = aDist[swapXY ? 3 : 4], yfac = aDist[swapXY ? 4 : 3];
- double commonFac = (((aDist[2] * rsqr + aDist[1]) * rsqr + aDist[0]) * rsqr + 1.)
+ const double commonFac = (((aDist[2] * rsqr + aDist[1]) * rsqr + aDist[0]) * rsqr + 1.)
+ 2. * (yfac * yd + xfac * xd);
- double xnew = xd * commonFac + xfac * rsqr;
- double ynew = yd * commonFac + yfac * rsqr;
+ const double xnew = xd * commonFac + xfac * rsqr;
+ const double ynew = yd * commonFac + yfac * rsqr;
x = xnew * mc.fx + x0;
y = ynew * mc.fy + y0;
}
+
+ x -= cx * scale;
+ y -= cy * scale;
}
-void LCPMapper::correctCA(double& x, double& y, int channel) const
+void rtengine::LCPMapper::correctCA(double& x, double& y, int channel) const
{
if (!enableCA) {
return;
}
- double rsqr, xgreen, ygreen;
+ double xgreen, ygreen;
// First calc the green channel like normal distortion
// the other are just deviations from it
- double xd = (x - chrom[1].x0) / chrom[1].fx, yd = (y - chrom[1].y0) / chrom[1].fy;
+ double xd = (x - chrom[1].x0) / chrom[1].fx;
+ double yd = (y - chrom[1].y0) / chrom[1].fy;
// Green contains main distortion, just like base
if (useCADist) {
@@ -291,18 +1114,18 @@ void LCPMapper::correctCA(double& x, double& y, int channel) const
// others are diffs from green
xd = xgreen;
yd = ygreen;
- rsqr = xd * xd + yd * yd;
+ const double rsqr = xd * xd + yd * yd;
const LCPModelCommon::Param aCA = chrom[channel].param;
- double xfac = aCA[swapXY ? 3 : 4], yfac = aCA[swapXY ? 4 : 3];
- double commonSum = 1. + rsqr * (aCA[0] + rsqr * (aCA[1] + aCA[2] * rsqr)) + 2. * (yfac * yd + xfac * xd);
+ const double xfac = aCA[swapXY ? 3 : 4], yfac = aCA[swapXY ? 4 : 3];
+ const double commonSum = 1. + rsqr * (aCA[0] + rsqr * (aCA[1] + aCA[2] * rsqr)) + 2. * (yfac * yd + xfac * xd);
x = (chrom[channel].scale_factor * ( xd * commonSum + xfac * rsqr )) * chrom[channel].fx + chrom[channel].x0;
y = (chrom[channel].scale_factor * ( yd * commonSum + yfac * rsqr )) * chrom[channel].fy + chrom[channel].y0;
}
}
-SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) const
+SSEFUNCTION void rtengine::LCPMapper::processVignetteLine(int width, int y, float* line) const
{
// No need for swapXY, since vignette is in RAW and always before rotation
float yd = ((float)y - mc.y0) * mc.rfy;
@@ -321,9 +1144,9 @@ SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) c
vfloat xv = _mm_setr_ps(0.f, 1.f, 2.f, 3.f);
for (; x < width-3; x+=4) {
- vfloat xdv = (xv - x0v) * rfxv;
- vfloat rsqr = xdv * xdv + ydv;
- vfloat vignFactorv = rsqr * (p0 + rsqr * (p1 - p2 * rsqr + p3 * rsqr * rsqr));
+ const vfloat xdv = (xv - x0v) * rfxv;
+ const vfloat rsqr = xdv * xdv + ydv;
+ const vfloat vignFactorv = rsqr * (p0 + rsqr * (p1 - p2 * rsqr + p3 * rsqr * rsqr));
vfloat valv = LVFU(line[x]);
valv += valv * vselfzero(vmaskf_gt(valv, zerov), vignFactorv);
STVFU(line[x], valv);
@@ -332,24 +1155,24 @@ SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) c
#endif // __SSE2__
for (; x < width; x++) {
if (line[x] > 0) {
- float xd = ((float)x - mc.x0) * mc.rfx;
+ const float xd = ((float)x - mc.x0) * mc.rfx;
const LCPModelCommon::VignParam vignParam = mc.vign_param;
- float rsqr = xd * xd + yd;
+ const float rsqr = xd * xd + yd;
line[x] += line[x] * rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr));
}
}
}
-SSEFUNCTION void LCPMapper::processVignetteLine3Channels(int width, int y, float *line) const
+SSEFUNCTION void rtengine::LCPMapper::processVignetteLine3Channels(int width, int y, float* line) const
{
// No need for swapXY, since vignette is in RAW and always before rotation
float yd = ((float)y - mc.y0) * mc.rfy;
yd *= yd;
const LCPModelCommon::VignParam vignParam = mc.vign_param;
for (int x = 0; x < width; x++) {
- float xd = ((float)x - mc.x0) * mc.rfx;
- float rsqr = xd * xd + yd;
- float vignetteFactor = rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr));
+ const float xd = ((float)x - mc.x0) * mc.rfx;
+ const float rsqr = xd * xd + yd;
+ const float vignetteFactor = rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr));
for(int c = 0;c < 3; ++c) {
if (line[3*x+c] > 0) {
line[3*x+c] += line[3*x+c] * vignetteFactor;
@@ -357,623 +1180,3 @@ SSEFUNCTION void LCPMapper::processVignetteLine3Channels(int width, int y, float
}
}
}
-
-
-LCPProfile::LCPProfile(const Glib::ustring &fname)
-{
- for (int i = 0; i < MaxPersModelCount; i++) {
- aPersModel[i] = nullptr;
- }
- pCurPersModel = nullptr;
-
- const int BufferSize = 8192;
- char buf[BufferSize];
-
- XML_Parser parser = XML_ParserCreate(nullptr);
-
- if (!parser) {
- throw "Couldn't allocate memory for XML parser";
- }
-
- XML_SetElementHandler(parser, XmlStartHandler, XmlEndHandler);
- XML_SetCharacterDataHandler(parser, XmlTextHandler);
- XML_SetUserData(parser, (void *)this);
-
-
- isFisheye = inCamProfiles = firstLIDone = inPerspect = inAlternateLensID = inAlternateLensNames = false;
- sensorFormatFactor = 1;
-
- persModelCount = 0;
- *inInvalidTag = 0;
-
- FILE *pFile = g_fopen(fname.c_str (), "rb");
-
- if(pFile) {
- bool done;
-
- do {
- int bytesRead = (int)fread(buf, 1, BufferSize, pFile);
- done = feof(pFile);
-
- if (XML_Parse(parser, buf, bytesRead, done) == XML_STATUS_ERROR) {
- throw "Invalid XML in LCP file";
- }
- } while (!done);
-
- fclose(pFile);
- }
-
- XML_ParserFree(parser);
-
- if (settings->verbose) {
- printf("Parsing %s\n", fname.c_str());
- }
- // Two phase filter: first filter out the very rough ones, that distord the average a lot
- // force it, even if there are few frames (community profiles)
-// filterBadFrames(2.0, 0);
- // from the non-distorded, filter again on new average basis, but only if there are enough frames left
-// filterBadFrames(1.5, 100);
-}
-
-
-LCPProfile::~LCPProfile()
-{
- if (pCurPersModel) {
- delete pCurPersModel;
- }
- for (int i = 0; i < MaxPersModelCount; i++) {
- if (aPersModel[i]) {
- delete aPersModel[i];
- }
- }
-}
-
-// from all frames not marked as bad already, take average and filter out frames with higher deviation than this if there are enough values
-int LCPProfile::filterBadFrames(double maxAvgDevFac, int minFramesLeft)
-{
- // take average error per type, then calculated the maximum deviation allowed
- double errBase = 0, errChrom = 0, errVignette = 0;
- int baseCount = 0, chromCount = 0, vignetteCount = 0;
-
- for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) {
- if (aPersModel[pm]->hasModeData(0)) {
- errVignette += aPersModel[pm]->vignette.mean_error;
- vignetteCount++;
- }
-
- if (aPersModel[pm]->hasModeData(1)) {
- errBase += aPersModel[pm]->base.mean_error;
- baseCount++;
- }
-
- if (aPersModel[pm]->hasModeData(2)) {
- errChrom += rtengine::max(aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error, aPersModel[pm]->chromBG.mean_error);
- chromCount++;
- }
- }
-
- // Only if we have enough frames, filter out errors
- int filtered = 0;
-
- if (baseCount + chromCount + vignetteCount >= minFramesLeft) {
- if (baseCount > 0) {
- errBase /= (double)baseCount;
- }
-
- if (chromCount > 0) {
- errChrom /= (double)chromCount;
- }
-
- if (vignetteCount > 0) {
- errVignette /= (double)vignetteCount;
- }
-
- // Now mark all the bad ones as bad, and hasModeData will return false;
- for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) {
- if (aPersModel[pm]->hasModeData(0) && aPersModel[pm]->vignette.mean_error > maxAvgDevFac * errVignette) {
- aPersModel[pm]->vignette.bad_error = true;
- filtered++;
- }
-
- if (aPersModel[pm]->hasModeData(1) && aPersModel[pm]->base.mean_error > maxAvgDevFac * errBase) {
- aPersModel[pm]->base.bad_error = true;
- filtered++;
- }
-
- if (aPersModel[pm]->hasModeData(2) &&
- (aPersModel[pm]->chromRG.mean_error > maxAvgDevFac * errChrom || aPersModel[pm]->chromG.mean_error > maxAvgDevFac * errChrom
- || aPersModel[pm]->chromBG.mean_error > maxAvgDevFac * errChrom)) {
- aPersModel[pm]->chromRG.bad_error = aPersModel[pm]->chromG.bad_error = aPersModel[pm]->chromBG.bad_error = true;
- filtered++;
- }
- }
-
- if (settings->verbose) {
- printf("Filtered %.1f%% frames for maxAvgDevFac %g leaving %i\n", filtered*100./(baseCount+chromCount+vignetteCount), maxAvgDevFac, baseCount+chromCount+vignetteCount-filtered);
- }
- }
-
- return filtered;
-}
-
-
-// mode: 0=vignette, 1=distortion, 2=CA
-void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const
-{
- float euler = exp(1.0);
-
- // find the frames with the least distance, focal length wise
- LCPPersModel *pLow = nullptr, *pHigh = nullptr;
-
- float focalLengthLog = log(focalLength); //, apertureLog=aperture>0 ? log(aperture) : 0;
- float focusDistLog = focusDist > 0 ? log(focusDist) + euler : 0;
-
- // Pass 1: determining best focal length, if possible different focusDistances (for the focDist is not given case)
- for (int pm = 0; pm < persModelCount; pm++) {
- float f = aPersModel[pm]->focLen;
-
- if (aPersModel[pm]->hasModeData(mode)) {
- if (f <= focalLength && (pLow == nullptr || f > pLow->focLen || (focusDist == 0 && f == pLow->focLen && pLow->focDist > aPersModel[pm]->focDist))) {
- pLow = aPersModel[pm];
- }
-
- if (f >= focalLength && (pHigh == nullptr || f < pHigh->focLen || (focusDist == 0 && f == pHigh->focLen && pHigh->focDist < aPersModel[pm]->focDist))) {
- pHigh = aPersModel[pm];
- }
- }
- }
-
- if (!pLow) {
- pLow = pHigh;
- } else if (!pHigh) {
- pHigh = pLow;
- } else {
- // Pass 2: We have some, so take the best aperture for vignette and best focus for CA and distortion
- // there are usually several frame per focal length. In the end pLow will have both flen and apterure/focdis below the target,
- // and vice versa pHigh
- float bestFocLenLow = pLow->focLen, bestFocLenHigh = pHigh->focLen;
-
- for (int pm = 0; pm < persModelCount; pm++) {
- float aper = aPersModel[pm]->aperture; // float aperLog=log(aper);
- float focDist = aPersModel[pm]->focDist;
- float focDistLog = log(focDist) + euler;
- double meanErr;
- if (aPersModel[pm]->hasModeData(mode)) {
- double lowMeanErr, highMeanErr;
- switch (mode) {
- case 0:
- meanErr = aPersModel[pm]->vignette.mean_error;
- lowMeanErr = pLow->vignette.mean_error;
- highMeanErr = pHigh->vignette.mean_error;
- break;
- case 1:
- meanErr = aPersModel[pm]->base.mean_error;
- lowMeanErr = pLow->base.mean_error;
- highMeanErr = pHigh->base.mean_error;
- break;
- default: //case 2:
- meanErr = aPersModel[pm]->chromG.mean_error;
- lowMeanErr = pLow->chromG.mean_error;
- highMeanErr = pHigh->chromG.mean_error;
- break;
- }
-
- if (aperture > 0 && mode != 2) {
- if (aPersModel[pm]->focLen == bestFocLenLow && (
- (aper == aperture && lowMeanErr > meanErr)
- || (aper >= aperture && aper < pLow->aperture && pLow->aperture > aperture)
- || (aper <= aperture && (pLow->aperture > aperture || fabs(aperture - aper) < fabs(aperture - pLow->aperture))))) {
- pLow = aPersModel[pm];
- }
-
- if (aPersModel[pm]->focLen == bestFocLenHigh && (
- (aper == aperture && highMeanErr > meanErr)
- || (aper <= aperture && aper > pHigh->aperture && pHigh->aperture < aperture)
- || (aper >= aperture && (pHigh->aperture < aperture || fabs(aperture - aper) < fabs(aperture - pHigh->aperture))))) {
- pHigh = aPersModel[pm];
- }
- } else if (focusDist > 0 && mode != 0) {
- // by focus distance
- if (aPersModel[pm]->focLen == bestFocLenLow && (
- (focDist == focusDist && lowMeanErr > meanErr)
- || (focDist >= focusDist && focDist < pLow->focDist && pLow->focDist > focusDist)
- || (focDist <= focusDist && (pLow->focDist > focusDist || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (log(pLow->focDist) + euler)))))) {
- pLow = aPersModel[pm];
- }
-
- if (aPersModel[pm]->focLen == bestFocLenHigh && (
- (focDist == focusDist && highMeanErr > meanErr)
- || (focDist <= focusDist && focDist > pHigh->focDist && pHigh->focDist < focusDist)
- || (focDist >= focusDist && (pHigh->focDist < focusDist || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (log(pHigh->focDist) + euler)))))) {
- pHigh = aPersModel[pm];
- }
- } else {
- // no focus distance available, just error
- if (aPersModel[pm]->focLen == bestFocLenLow && lowMeanErr > meanErr) {
- pLow = aPersModel[pm];
- }
-
- if (aPersModel[pm]->focLen == bestFocLenHigh && highMeanErr > meanErr) {
- pHigh = aPersModel[pm];
- }
- }
-
- }
- }
- }
-
- if (pLow != nullptr && pHigh != nullptr) {
- // average out the factors, linear interpolation in logarithmic scale
- float facLow = 0.5;
- bool focLenOnSpot = false; // pretty often, since max/min are often as frames in LCP
-
- // There is as foclen range, take that as basis
- if (pLow->focLen < pHigh->focLen) {
- facLow = (log(pHigh->focLen) - focalLengthLog) / (log(pHigh->focLen) - log(pLow->focLen));
- } else {
- focLenOnSpot = pLow->focLen == pHigh->focLen && pLow->focLen == focalLength;
- }
-
- // and average the other factor if available
- if (mode == 0 && pLow->aperture < aperture && pHigh->aperture > aperture) {
- // Mix in aperture
- float facAperLow = (pHigh->aperture - aperture) / (pHigh->aperture - pLow->aperture);
- facLow = focLenOnSpot ? facAperLow : (0.5 * facLow + 0.5 * facAperLow);
- } else if (mode != 0 && focusDist > 0 && pLow->focDist < focusDist && pHigh->focDist > focusDist) {
- // focus distance for all else (if focus distance is given)
- float facDistLow = (log(pHigh->focDist) + euler - focusDistLog) / (log(pHigh->focDist) - log(pLow->focDist));
- facLow = focLenOnSpot ? facDistLow : (0.8 * facLow + 0.2 * facDistLow);
- }
-
- switch (mode) {
- case 0: // vignette
- pCorr1->merge(pLow->vignette, pHigh->vignette, facLow);
- break;
-
- case 1: // distortion
- pCorr1->merge(pLow->base, pHigh->base, facLow);
- break;
-
- case 2: // CA
- pCorr1->merge(pLow->chromRG, pHigh->chromRG, facLow);
- pCorr2->merge(pLow->chromG, pHigh->chromG, facLow);
- pCorr3->merge(pLow->chromBG, pHigh->chromBG, facLow);
- break;
- }
-
- if (settings->verbose) {
- printf("LCP mode=%i, dist: %g found frames: Fno %g-%g; FocLen %g-%g; Dist %g-%g with weight %g\n", mode, focusDist, pLow->aperture, pHigh->aperture, pLow->focLen, pHigh->focLen, pLow->focDist, pHigh->focDist, facLow);
- }
- } else {
- if (settings->verbose) {
- printf("Error: LCP file contained no %s parameters\n", mode == 0 ? "vignette" : mode == 1 ? "distortion" : "CA" );
- }
- }
-}
-
-void LCPProfile::print() const
-{
- printf("=== Profile %s\n", profileName.c_str());
- printf("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n", persModelCount, isRaw, isFisheye, sensorFormatFactor);
-
- for (int pm = 0; pm < persModelCount; pm++) {
- aPersModel[pm]->print();
- }
-}
-
-void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, const char **attr)
-{
- LCPProfile *pProf = static_cast(pLCPProfile);
- bool parseAttr = false;
-
- if (*pProf->inInvalidTag) {
- return; // We ignore everything in dirty tag till it's gone
- }
-
- // clean up tagname
- const char* src = strrchr(el, ':');
-
- if (src == nullptr) {
- src = const_cast(el);
- } else {
- src++;
- }
-
- strcpy(pProf->lastTag, src);
-
- if (!strcmp("VignetteModelPiecewiseParam", src)) {
- strcpy(pProf->inInvalidTag, src);
- }
-
- if (!strcmp("CameraProfiles", src)) {
- pProf->inCamProfiles = true;
- }
-
- if (!strcmp("AlternateLensIDs", src)) {
- pProf->inAlternateLensID = true;
- }
-
- if (!strcmp("AlternateLensNames", src)) {
- pProf->inAlternateLensNames = true;
- }
-
- if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames) {
- return;
- }
-
- if (!strcmp("li", src)) {
- pProf->pCurPersModel = new LCPPersModel();
- pProf->pCurCommon = &pProf->pCurPersModel->base; // iterated to next tags within persModel
- return;
- }
-
- if (!strcmp("PerspectiveModel", src)) {
- pProf->firstLIDone = true;
- pProf->inPerspect = true;
- return;
- } else if (!strcmp("FisheyeModel", src)) {
- pProf->firstLIDone = true;
- pProf->inPerspect = true;
- pProf->isFisheye = true; // just misses third param, and different path, rest is the same
- return;
- } else if (!strcmp("Description", src)) {
- parseAttr = true;
- }
-
- // Move pointer to general section
- if (pProf->inPerspect) {
- if (!strcmp("ChromaticRedGreenModel", src)) {
- pProf->pCurCommon = &pProf->pCurPersModel->chromRG;
- parseAttr = true;
- } else if (!strcmp("ChromaticGreenModel", src)) {
- pProf->pCurCommon = &pProf->pCurPersModel->chromG;
- parseAttr = true;
- } else if (!strcmp("ChromaticBlueGreenModel", src)) {
- pProf->pCurCommon = &pProf->pCurPersModel->chromBG;
- parseAttr = true;
- } else if (!strcmp("VignetteModel", src)) {
- pProf->pCurCommon = &pProf->pCurPersModel->vignette;
- parseAttr = true;
- }
- }
-
- // some profiles (espc. Pentax) have a different structure that is attributes based
- // simulate tags by feeding them in
- if (parseAttr && attr != nullptr) {
- for (int i = 0; attr[i]; i += 2) {
- const char* nameStart = strrchr(attr[i], ':');
-
- if (nameStart == nullptr) {
- nameStart = const_cast(attr[i]);
- } else {
- nameStart++;
- }
-
- strncpy(pProf->lastTag, nameStart, 255);
-
- pProf->handle_text(attr[i+1]);
- //XmlTextHandler(pLCPProfile, attr[i + 1], strlen(attr[i + 1]));
- }
- }
-}
-
-void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, int len)
-{
- LCPProfile *pProf = static_cast(pLCPProfile);
-
- if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames || *pProf->inInvalidTag) {
- return;
- }
-
- for (int i = 0; i < len; ++i) {
- pProf->textbuf << s[i];
- }
-}
-
-
-void LCPProfile::handle_text(std::string text)
-{
- // Check if it contains non-whitespaces (there are several calls to this for one tag unfortunately)
- bool onlyWhiteSpace = true;
- for (size_t i = 0; i < text.size(); ++i) {
- if (!isspace(text[i])) {
- onlyWhiteSpace = false;
- break;
- }
- }
-
- if (onlyWhiteSpace) {
- return;
- }
-
- LCPProfile *pProf = this;
-
- // convert to null terminated
- char* tag = pProf->lastTag;
-
- const char* raw = text.c_str();
-
- // Common data section
- if (!pProf->firstLIDone) {
- // Generic tags are the same for all
- if (!strcmp("ProfileName", tag)) {
- pProf->profileName = Glib::ustring(raw);
- } else if (!strcmp("Model", tag)) {
- pProf->camera = Glib::ustring(raw);
- } else if (!strcmp("Lens", tag)) {
- pProf->lens = Glib::ustring(raw);
- } else if (!strcmp("CameraPrettyName", tag)) {
- pProf->cameraPrettyName = Glib::ustring(raw);
- } else if (!strcmp("LensPrettyName", tag)) {
- pProf->lensPrettyName = Glib::ustring(raw);
- } else if (!strcmp("CameraRawProfile", tag)) {
- pProf->isRaw = !strcmp("True", raw);
- }
- }
-
- // --- Now all floating points. Must replace local dot characters
- // WARNING: called by different threads, that may run on different local settings,
- // so don't use system params
- if (atof("1,2345") == 1.2345) {
- for (size_t i = 0; i < text.size(); ++i) {
- if (text[i] == '.') {
- text[i] = ',';
- }
- }
- raw = text.c_str();
- }
-
- if (!pProf->firstLIDone) {
- if (!strcmp("SensorFormatFactor", tag)) {
- pProf->sensorFormatFactor = atof(raw);
- }
- }
-
- // Perspective model base data
- if (!strcmp("FocalLength", tag)) {
- pProf->pCurPersModel->focLen = atof(raw);
- } else if (!strcmp("FocusDistance", tag)) {
- double focDist = atof(raw);
- pProf->pCurPersModel->focDist = focDist < 10000 ? focDist : 10000;
- } else if (!strcmp("ApertureValue", tag)) {
- pProf->pCurPersModel->aperture = atof(raw);
- }
-
- // Section depended
- if (!strcmp("FocalLengthX", tag)) {
- pProf->pCurCommon->foc_len_x = atof(raw);
- } else if (!strcmp("FocalLengthY", tag)) {
- pProf->pCurCommon->foc_len_y = atof(raw);
- } else if (!strcmp("ImageXCenter", tag)) {
- pProf->pCurCommon->img_center_x = atof(raw);
- } else if (!strcmp("ImageYCenter", tag)) {
- pProf->pCurCommon->img_center_y = atof(raw);
- } else if (!strcmp("ScaleFactor", tag)) {
- pProf->pCurCommon->scale_factor = atof(raw);
- } else if (!strcmp("ResidualMeanError", tag)) {
- pProf->pCurCommon->mean_error = atof(raw);
- } else if (!strcmp("RadialDistortParam1", tag) || !strcmp("VignetteModelParam1", tag)) {
- pProf->pCurCommon->param[0] = atof(raw);
- } else if (!strcmp("RadialDistortParam2", tag) || !strcmp("VignetteModelParam2", tag)) {
- pProf->pCurCommon->param[1] = atof(raw);
- } else if (!strcmp("RadialDistortParam3", tag) || !strcmp("VignetteModelParam3", tag)) {
- pProf->pCurCommon->param[2] = atof(raw);
- } else if (!strcmp("RadialDistortParam4", tag) || !strcmp("TangentialDistortParam1", tag)) {
- pProf->pCurCommon->param[3] = atof(raw);
- } else if (!strcmp("RadialDistortParam5", tag) || !strcmp("TangentialDistortParam2", tag)) {
- pProf->pCurCommon->param[4] = atof(raw);
- }
-}
-
-void XMLCALL LCPProfile::XmlEndHandler(void *pLCPProfile, const char *el)
-{
- LCPProfile *pProf = static_cast(pLCPProfile);
-
- pProf->handle_text(pProf->textbuf.str());
- pProf->textbuf.str("");
-
- // We ignore everything in dirty tag till it's gone
- if (*pProf->inInvalidTag) {
- if (strstr(el, pProf->inInvalidTag)) {
- *pProf->inInvalidTag = 0;
- }
-
- return;
- }
-
- if (strstr(el, ":CameraProfiles")) {
- pProf->inCamProfiles = false;
- }
-
- if (strstr(el, ":AlternateLensIDs")) {
- pProf->inAlternateLensID = false;
- }
-
- if (strstr(el, ":AlternateLensNames")) {
- pProf->inAlternateLensNames = false;
- }
-
- if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames) {
- return;
- }
-
- if (strstr(el, ":PerspectiveModel") || strstr(el, ":FisheyeModel")) {
- pProf->inPerspect = false;
- } else if (strstr(el, ":li")) {
- pProf->aPersModel[pProf->persModelCount] = pProf->pCurPersModel;
- pProf->pCurPersModel = nullptr;
- pProf->persModelCount++;
- }
-}
-
-// Generates as singleton
-LCPStore* LCPStore::getInstance()
-{
- static LCPStore instance_;
- return &instance_;
-}
-
-
-LCPStore::~LCPStore()
-{
- for (auto &p : profileCache) {
- delete p.second;
- }
-}
-
-
-LCPProfile* LCPStore::getProfile (Glib::ustring filename)
-{
- if (filename.length() == 0 || !isValidLCPFileName(filename)) {
- return nullptr;
- }
-
- MyMutex::MyLock lock(mtx);
-
- std::map::iterator r = profileCache.find (filename);
-
- if (r != profileCache.end()) {
- return r->second;
- }
-
- // Add profile (if exists)
- profileCache[filename] = new LCPProfile(filename);
- if (settings->verbose) {
- profileCache[filename]->print();
- }
- return profileCache[filename];
-}
-
-bool LCPStore::isValidLCPFileName(Glib::ustring filename) const
-{
- if (!Glib::file_test (filename, Glib::FILE_TEST_EXISTS) || Glib::file_test (filename, Glib::FILE_TEST_IS_DIR)) {
- return false;
- }
-
- size_t pos = filename.find_last_of ('.');
- return pos > 0 && !filename.casefold().compare (pos, 4, ".lcp");
-}
-
-Glib::ustring LCPStore::getDefaultCommonDirectory() const
-{
- Glib::ustring dir;
-
-#ifdef WIN32
- WCHAR pathW[MAX_PATH] = {0};
-
- if (SHGetSpecialFolderPathW(NULL, pathW, CSIDL_COMMON_APPDATA, false)) {
- char pathA[MAX_PATH];
- WideCharToMultiByte(CP_UTF8, 0, pathW, -1, pathA, MAX_PATH, 0, 0);
- Glib::ustring fullDir = Glib::ustring(pathA) + Glib::ustring("\\Adobe\\CameraRaw\\LensProfiles\\1.0");
-
- if (Glib::file_test (fullDir, Glib::FILE_TEST_IS_DIR)) {
- dir = fullDir;
- }
- }
-
-#endif
-
- // TODO: Add Mac paths here
-
- return dir;
-}
diff --git a/rtengine/lcp.h b/rtengine/lcp.h
index f7164117f..17eb892c8 100644
--- a/rtengine/lcp.h
+++ b/rtengine/lcp.h
@@ -21,27 +21,45 @@
#include
#include