diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch
index 0a80db39e..e9ef8b134 100644
--- a/rtdata/languages/Deutsch
+++ b/rtdata/languages/Deutsch
@@ -254,9 +254,9 @@ HISTORY_MSG_30;Dekonvolution Radius
HISTORY_MSG_31;Dekonvolution Stärke
HISTORY_MSG_32;Dekonvolution\nDämpfung
HISTORY_MSG_33;Dekonvolution\nIterationen
-HISTORY_MSG_34;Farbbeschneidungen\nverhindern
-HISTORY_MSG_35;Sättigung begrenzen
-HISTORY_MSG_36;Sättigungsgrenzwert
+HISTORY_MSG_34;LCP Verzerrung
+HISTORY_MSG_35;LCP Vignettierung
+HISTORY_MSG_36;LCP CA
HISTORY_MSG_37;Farbverstärkung
HISTORY_MSG_38;Weißabgleich Methode
HISTORY_MSG_39;Weißabgleich Farbtemp.
@@ -962,6 +962,9 @@ TP_LENSGEOM_FILL;Auto-Füllen
TP_LENSGEOM_LABEL;Objektivkorrekturen
TP_LENSPROFILE_FILEDLGFILTERLCP;Linsen-Korrekturdateien
TP_LENSPROFILE_LABEL;Linsen-Korrekturprofil
+TP_LENSPROFILE_USECA;CA korrigieren
+TP_LENSPROFILE_USEDIST;Verzerrung korrigieren
+TP_LENSPROFILE_USEVIGN;Vignettierung korrigieren
TP_LUMADENOISE_EDGETOLERANCE;Kantentoleranz
TP_LUMADENOISE_LABEL;Luminanz-Rauschfilter
TP_LUMADENOISE_RADIUS;Radius
diff --git a/rtdata/languages/default b/rtdata/languages/default
index 8b137d04f..c236122c2 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -254,9 +254,9 @@ HISTORY_MSG_30;Deconvolution Radius
HISTORY_MSG_31;Deconvolution Amount
HISTORY_MSG_32;Deconvolution Damping
HISTORY_MSG_33;Deconvolution Iterations
-HISTORY_MSG_34;Avoid Color Clipping
-HISTORY_MSG_35;Saturation Limiter
-HISTORY_MSG_36;Saturation Limit
+HISTORY_MSG_34;LCP use disortion
+HISTORY_MSG_35;LCP use vignette
+HISTORY_MSG_36;LCP use CA
HISTORY_MSG_37;Color Boost
HISTORY_MSG_38;White Balance Method
HISTORY_MSG_39;Color Temperature
@@ -964,6 +964,9 @@ TP_LENSGEOM_FILL;Auto Fill
TP_LENSGEOM_LABEL;Lens / Geometry
TP_LENSPROFILE_FILEDLGFILTERLCP;Lens correction files
TP_LENSPROFILE_LABEL;Lens Correction Profile
+TP_LENSPROFILE_USECA;Use CA correction
+TP_LENSPROFILE_USEDIST;Use distortion correction
+TP_LENSPROFILE_USEVIGN;Use vignette correction
TP_LUMADENOISE_EDGETOLERANCE;Edge Tolerance
TP_LUMADENOISE_LABEL;Luminance Noise Reduction
TP_LUMADENOISE_RADIUS;Radius
diff --git a/rtdata/profiles/BW-1.pp3 b/rtdata/profiles/BW-1.pp3
index 8473ad4b3..33871c25f 100644
--- a/rtdata/profiles/BW-1.pp3
+++ b/rtdata/profiles/BW-1.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/BW-2.pp3 b/rtdata/profiles/BW-2.pp3
index d383c4353..b8dad1af3 100644
--- a/rtdata/profiles/BW-2.pp3
+++ b/rtdata/profiles/BW-2.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/BW-3.pp3 b/rtdata/profiles/BW-3.pp3
index 3905aa3b5..83fa02c8a 100644
--- a/rtdata/profiles/BW-3.pp3
+++ b/rtdata/profiles/BW-3.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/BW-4.pp3 b/rtdata/profiles/BW-4.pp3
index 5d5acf2ae..7e82be051 100644
--- a/rtdata/profiles/BW-4.pp3
+++ b/rtdata/profiles/BW-4.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/Default-ISO-High.pp3 b/rtdata/profiles/Default-ISO-High.pp3
index 63203cdfe..a4924cf14 100644
--- a/rtdata/profiles/Default-ISO-High.pp3
+++ b/rtdata/profiles/Default-ISO-High.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/Default-ISO-Medium.pp3 b/rtdata/profiles/Default-ISO-Medium.pp3
index fabe7abb8..b95c73150 100644
--- a/rtdata/profiles/Default-ISO-Medium.pp3
+++ b/rtdata/profiles/Default-ISO-Medium.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/Default.pp3 b/rtdata/profiles/Default.pp3
index f00bed60d..b0e0145aa 100644
--- a/rtdata/profiles/Default.pp3
+++ b/rtdata/profiles/Default.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/Highkey-1.pp3 b/rtdata/profiles/Highkey-1.pp3
index d53a14d3a..607b1f73c 100644
--- a/rtdata/profiles/Highkey-1.pp3
+++ b/rtdata/profiles/Highkey-1.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/Natural-1.pp3 b/rtdata/profiles/Natural-1.pp3
index a13827497..c9fccf283 100644
--- a/rtdata/profiles/Natural-1.pp3
+++ b/rtdata/profiles/Natural-1.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/Natural-2.pp3 b/rtdata/profiles/Natural-2.pp3
index 038c603f8..66e312ff9 100644
--- a/rtdata/profiles/Natural-2.pp3
+++ b/rtdata/profiles/Natural-2.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/Neutral.pp3 b/rtdata/profiles/Neutral.pp3
index 718ff6ed2..04f1a3dde 100644
--- a/rtdata/profiles/Neutral.pp3
+++ b/rtdata/profiles/Neutral.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/Punchy-1.pp3 b/rtdata/profiles/Punchy-1.pp3
index f6ecbcfec..397e96515 100644
--- a/rtdata/profiles/Punchy-1.pp3
+++ b/rtdata/profiles/Punchy-1.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtdata/profiles/Punchy-2.pp3 b/rtdata/profiles/Punchy-2.pp3
index 6ca304b40..d8ce0f2bd 100644
--- a/rtdata/profiles/Punchy-2.pp3
+++ b/rtdata/profiles/Punchy-2.pp3
@@ -135,7 +135,12 @@ Degree=0
[Distortion]
Amount=0
-UseLensFun=false
+
+[LensProfile]
+LCPFile=
+UseDistortion=true
+UseVignette=true
+UseCA=false
[Perspective]
Horizontal=0
diff --git a/rtengine/cubint.cc b/rtengine/cubint.cc
index 0c65b7fcc..d730c6299 100644
--- a/rtengine/cubint.cc
+++ b/rtengine/cubint.cc
@@ -16,9 +16,10 @@
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see .
*/
-#define A (-0.85)
-inline void cubint (Imagefloat* src, int xs, int ys, double Dx, double Dy, float *r, float *g, float *b, double mul) {
+// currently not in use any more
+inline void interpolateTransformCubic (Imagefloat* src, int xs, int ys, double Dx, double Dy, float *r, float *g, float *b, double mul) {
+ const double A=-0.85;
double w[4];
diff --git a/rtengine/cubintch.cc b/rtengine/cubintch.cc
index 528641247..7bc55e089 100644
--- a/rtengine/cubintch.cc
+++ b/rtengine/cubintch.cc
@@ -17,12 +17,13 @@
* along with RawTherapee. If not, see .
*/
-inline void cubintch (float** src, int xs, int ys, double Dx, double Dy, float *r, double mul) {
+inline void interpolateTransformChannelsCubic (float** src, int xs, int ys, double Dx, double Dy, float *r, double mul) {
+ const double A=-0.85;
double w[4];
{
- register double t1, t2;
+ double t1, t2;
t1 = -A*(Dx-1.0)*Dx;
t2 = (3.0-2.0*Dx)*Dx*Dx;
w[3] = t1*Dx;
diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc
index eedd8fdc1..2b58b0c6a 100644
--- a/rtengine/dcrop.cc
+++ b/rtengine/dcrop.cc
@@ -112,7 +112,8 @@ void Crop::update (int todo) {
transCrop = new Imagefloat (cropw, croph);
if ((todo & M_TRANSFORM) && needstransform)
parent->ipf.transform (baseCrop, transCrop, cropx/skip, cropy/skip, trafx/skip, trafy/skip, SKIPS(parent->fw,skip), SKIPS(parent->fh,skip),
- parent->imgsrc->getMetaData()->getFocalLen(), parent->imgsrc->getMetaData()->getFocalLen35mm(), parent->imgsrc->getRotateDegree());
+ parent->imgsrc->getMetaData()->getFocalLen(), parent->imgsrc->getMetaData()->getFocalLen35mm(),
+ parent->imgsrc->getMetaData()->getFocusDist(), parent->imgsrc->getRotateDegree(), false);
if (transCrop)
baseCrop = transCrop;
diff --git a/rtengine/imagedata.cc b/rtengine/imagedata.cc
index b79c227c9..4a41209cc 100644
--- a/rtengine/imagedata.cc
+++ b/rtengine/imagedata.cc
@@ -109,6 +109,7 @@ void ImageData::extractInfo () {
shutter = 0;
aperture = 0;
focal_len = focal_len35mm = 0;
+ focus_dist = 0;
iso_speed = 0;
memset (&time, 0, sizeof(time));
timeStamp = 0;
@@ -171,6 +172,17 @@ void ImageData::extractInfo () {
focal_len = exif->getTag ("FocalLength")->toDouble ();
if (exif->getTag ("FocalLengthIn35mmFilm"))
focal_len35mm = exif->getTag ("FocalLengthIn35mmFilm")->toDouble ();
+ rtexif::Tag* pDst=exif->getTag("SubjectDistance"); // EXIF, set by Adobe. MakerNote ones are scattered and partly encrypted
+ if (pDst) {
+ int num, denom;
+ pDst->toRational(num,denom);
+ if ((denom==1 && num>=10000) || num<0 || denom<0)
+ focus_dist=10000; // infinity
+ else if (denom>0) {
+ focus_dist=(float)num/denom;
+ }
+ }
+
if (exif->getTag ("ISOSpeedRatings"))
iso_speed = exif->getTag ("ISOSpeedRatings")->toDouble ();
if (exif->getTag ("DateTimeOriginal")) {
diff --git a/rtengine/imagedata.h b/rtengine/imagedata.h
index 02dc63cee..3074c91b2 100644
--- a/rtengine/imagedata.h
+++ b/rtengine/imagedata.h
@@ -41,6 +41,7 @@ class ImageData : public ImageMetaData {
int iso_speed;
double aperture;
double focal_len, focal_len35mm;
+ float focus_dist; // dist: 0=unknown, 10000=infinity
double shutter;
double expcomp;
std::string make, model, serial;
@@ -66,6 +67,7 @@ class ImageData : public ImageMetaData {
double getFNumber () const { return aperture; }
double getFocalLen () const { return focal_len; }
double getFocalLen35mm () const { return focal_len35mm; }
+ float getFocusDist () const { return focus_dist; }
double getShutterSpeed () const { return shutter; }
double getExpComp () const { return expcomp; }
std::string getMake () const { return make; }
diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc
index 05735b497..4d55f6fa8 100644
--- a/rtengine/improccoordinator.cc
+++ b/rtengine/improccoordinator.cc
@@ -193,7 +193,8 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) {
if (needstransform && orig_prev==oprevi)
oprevi = new Imagefloat (pW, pH);
if ((todo & M_TRANSFORM) && needstransform)
- ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getRotateDegree());
+ ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, imgsrc->getMetaData()->getFocalLen(),
+ imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(), imgsrc->getRotateDegree(), false);
readyphase++;
diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h
index 77a37de66..4355c1d77 100644
--- a/rtengine/improcfun.h
+++ b/rtengine/improcfun.h
@@ -44,10 +44,12 @@ class ImProcFunctions {
bool multiThread;
float g;
- void simpltransform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, const LCPMapper *pLCPMap);
- void vignetting (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH);
- void transformNonSep (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, const LCPMapper *pLCPMap);
- void transformSep (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, const LCPMapper *pLCPMap);
+ 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, const LCPMapper *pLCPMap);
+ void transformVignetteOnly (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH);
+ void transformHighQuality (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, const LCPMapper *pLCPMap, bool fullImage);
+
void sharpenHaloCtrl (LabImage* lab, float** blurmap, float** base, int W, int H);
void firstAnalysisThread(Imagefloat* original, Glib::ustring wprofile, unsigned int* histogram, int row_from, int row_to);
void dcdamping (float** aI, float** aO, float damping, int W, int H);
@@ -123,7 +125,7 @@ class ImProcFunctions {
void colorCurve (LabImage* lold, LabImage* lnew);
void sharpening (LabImage* lab, float** buffer);
void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH,
- double focalLen, double focalLen35mm, int rawRotationDeg);
+ double focalLen, double focalLen35mm, float focusDist, int rawRotationDeg, bool fullImage);
void lab2monitorRgb (LabImage* lab, Image8* image);
void resize (Image16* src, Image16* dst, float dScale);
void deconvsharpening (LabImage* lab, float** buffer);
diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc
index 4556222e0..cecd7dcf5 100644
--- a/rtengine/iptransform.cc
+++ b/rtengine/iptransform.cc
@@ -177,28 +177,27 @@ 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,
- double focalLen, double focalLen35mm, int rawRotationDeg) {
+ double focalLen, double focalLen35mm, float focusDist, int rawRotationDeg, bool fullImage) {
+
LCPMapper *pLCPMap=NULL;
if (needsLCP() && focalLen>0) {
LCPProfile *pLCPProf=lcpStore->getProfile(params->lensProf.lcpFile);
- if (pLCPProf) pLCPMap=new LCPMapper(pLCPProf, focalLen, focalLen35mm, 0, false, original->width, original->height, params->coarse, rawRotationDeg);
+ if (pLCPProf) pLCPMap=new LCPMapper(pLCPProf, focalLen, focalLen35mm, focusDist, 0, false, params->lensProf.useDist,
+ original->width, original->height, params->coarse, rawRotationDeg);
}
if (!(needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP()) && needsVignetting())
- vignetting (original, transformed, cx, cy, oW, oH);
- else if (!needsCA()) {
- if (scale==1)
- transformNonSep (original, transformed, cx, cy, sx, sy, oW, oH, pLCPMap);
+ transformVignetteOnly (original, transformed, cx, cy, oW, oH);
+ else if (!needsCA() && scale!=1)
+ transformPreview (original, transformed, cx, cy, sx, sy, oW, oH, pLCPMap);
else
- simpltransform (original, transformed, cx, cy, sx, sy, oW, oH, pLCPMap);
- }
- else
- transformSep (original, transformed, cx, cy, sx, sy, oW, oH, pLCPMap);
+ transformHighQuality (original, transformed, cx, cy, sx, sy, oW, oH, pLCPMap, fullImage);
if (pLCPMap) delete pLCPMap;
}
-void calcVignettingParams(int oW, int oH, const VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul)
+// helper function
+void ImProcFunctions::calcVignettingParams(int oW, int oH, const VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul)
{
// vignette center is a point with coordinates between -1 and +1
double x = vignetting.centerX / 100.0;
@@ -218,14 +217,9 @@ void calcVignettingParams(int oW, int oH, const VignettingParams& vignetting, do
}
// Transform vignetting only
-void ImProcFunctions::vignetting (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH) {
+void ImProcFunctions::transformVignetteOnly (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH) {
- double vig_w2;
- double vig_h2;
- double maxRadius;
- double v;
- double b;
- double mul;
+ double vig_w2, vig_h2, maxRadius, v, b, mul;
calcVignettingParams(oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul);
#pragma omp parallel for if (multiThread)
@@ -242,149 +236,36 @@ void ImProcFunctions::vignetting (Imagefloat* original, Imagefloat* transformed,
}
}
-#include "cubint.cc"
-// Transform WITHOUT scaling or CA, cubic interpolation
-void ImProcFunctions::transformNonSep (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, const LCPMapper *pLCPMap) {
- double w2 = (double) oW / 2.0 - 0.5;
- double h2 = (double) oH / 2.0 - 0.5;
-
- double vig_w2;
- double vig_h2;
- double maxRadius;
- double v;
- double b;
- double mul;
- calcVignettingParams(oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul);
-
- // auxiliary variables for distortion correction
- bool needsDist = needsDistortion(); // for performance
- double a = params->distortion.amount;
-
- // auxiliary variables for rotation
- double cost = cos(params->rotate.degree * RT_PI/180.0);
- double sint = sin(params->rotate.degree * RT_PI/180.0);
-
- // auxiliary variables for vertical perspective correction
- double vpdeg = params->perspective.vertical / 100.0 * 45.0;
- double vpalpha = (90.0 - vpdeg) / 180.0 * RT_PI;
- double vpteta = fabs(vpalpha-RT_PI/2)<1e-3 ? 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.0 - hpdeg) / 180.0 * RT_PI;
- double hpteta = fabs(hpalpha-RT_PI/2)<1e-3 ? 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) : 1.0;
-
- // main cycle
- #pragma omp parallel for if (multiThread)
- for (int y=0; yheight; y++) {
- for (int x=0; xwidth; x++) {
- double x_d=x,y_d=y;
- if (pLCPMap) pLCPMap->correctDistortion(x_d,y_d); // must be first transform
-
- x_d = ascale * (x_d + cx - w2); // centering x coord & scale
- y_d = ascale * (y_d + cy - h2); // centering y coord & scale
- double vig_x_d = ascale * (x + cx - vig_w2); // centering x coord & scale
- double vig_y_d = ascale * (y + cy - vig_h2); // centering y coord & scale
-
- // horizontal perspective transformation
- y_d = y_d * maxRadius / (maxRadius + x_d*hptanpt);
- x_d = x_d * maxRadius * hpcospt / (maxRadius + x_d*hptanpt);
-
- // vertical perspective transformation
- x_d = x_d * maxRadius / (maxRadius - y_d*vptanpt);
- y_d = 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 - a + a * r ;
- Dx *= s;
- Dy *= s;
- }
-
- double vig_Dx = vig_x_d * cost - vig_y_d * sint;
- double vig_Dy = vig_x_d * sint + vig_y_d * cost;
-
- double r2 = needsVignetting() ? sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy) : 0;
-
- // 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 && ycheight && xc>=0 && xcwidth) {
-
- // multiplier for vignetting correction
- double vignmul = 1.0;
- if (needsVignetting())
- vignmul /= (v + mul * tanh (b*(maxRadius-s*r2) / maxRadius));
-
- if (yc > 0 && yc < original->height-2 && xc > 0 && xc < original->width-2) // all interpolation pixels inside image
- cubint (original, xc-1, yc-1, Dx, Dy, &(transformed->r[y][x]), &(transformed->g[y][x]), &(transformed->b[y][x]), vignmul);
- else { // edge pixels
- int y1 = LIM(yc, 0, original->height-1);
- int y2 = LIM(yc+1, 0, original->height-1);
- int x1 = LIM(xc, 0, original->width-1);
- int x2 = LIM(xc+1, 0, original->width-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;
- }
- }
- }
-}
-
// Transform WITH scaling (opt.) and CA, cubic interpolation
#include "cubintch.cc"
-void ImProcFunctions::transformSep (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, const LCPMapper *pLCPMap) {
+void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH,
+ const LCPMapper *pLCPMap, bool fullImage) {
+
double w2 = (double) oW / 2.0 - 0.5;
double h2 = (double) oH / 2.0 - 0.5;
- double vig_w2;
- double vig_h2;
- double maxRadius;
- double v;
- double b;
- double mul;
+ double vig_w2,vig_h2,maxRadius,v,b,mul;
calcVignettingParams(oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul);
- // auxiliary variables for c/a correction
- double cdist[3];
- cdist[0] = params->cacorrection.red;
- cdist[1] = 0.0;
- cdist[2] = params->cacorrection.blue;
- float** chorig[3];
- chorig[0] = original->r;
- chorig[1] = original->g;
- chorig[2] = original->b;
- float** chtrans[3];
- chtrans[0] = transformed->r;
- chtrans[1] = transformed->g;
- chtrans[2] = transformed->b;
+ float** chOrig[3];
+ chOrig[0] = original->r;
+ chOrig[1] = original->g;
+ chOrig[2] = original->b;
+
+ float** chTrans[3];
+ chTrans[0] = transformed->r;
+ chTrans[1] = transformed->g;
+ chTrans[2] = transformed->b;
+
+ // auxiliary variables for c/a correction
+ double chDist[3];
+ chDist[0] = params->cacorrection.red;
+ chDist[1] = 0.0;
+ chDist[2] = params->cacorrection.blue;
// auxiliary variables for distortion correction
bool needsDist = needsDistortion(); // for performance
- double a = params->distortion.amount;
+ double distAmount = params->distortion.amount;
// auxiliary variables for rotation
double cost = cos(params->rotate.degree * RT_PI/180.0);
@@ -406,25 +287,36 @@ void ImProcFunctions::transformSep (Imagefloat* original, Imagefloat* transforme
double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH) : 1.0;
+ // smaller crop images are a problem, so only when processing fully
+ bool enableLCPCA = pLCPMap && params->lensProf.useCA && fullImage;
+ bool enableLCPDist = pLCPMap && params->lensProf.useDist && fullImage;
+ if (enableLCPCA) enableLCPDist=false;
+
// main cycle
#pragma omp parallel for if (multiThread)
for (int y=0; yheight; y++) {
for (int x=0; xwidth; x++) {
double x_d=x,y_d=y;
- if (pLCPMap) pLCPMap->correctDistortion(x_d,y_d); // must be first transform
+ if (enableLCPDist) pLCPMap->correctDistortion(x_d,y_d); // must be first transform
x_d = ascale * (x_d + cx - w2); // centering x coord & scale
y_d = ascale * (y_d + cy - h2); // centering y coord & scale
- double vig_x_d = ascale * (x + cx - vig_w2); // centering x coord & scale
- double vig_y_d = ascale * (y + cy - vig_h2); // centering y coord & scale
+ double vig_x_d, vig_y_d;
+ 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 = y_d * maxRadius / (maxRadius + x_d*hptanpt);
- x_d = x_d * maxRadius * hpcospt / (maxRadius + x_d*hptanpt);
+ y_d *= maxRadius / (maxRadius + x_d*hptanpt);
+ x_d *= maxRadius * hpcospt / (maxRadius + x_d*hptanpt);
// vertical perspective transformation
- x_d = x_d * maxRadius / (maxRadius - y_d*vptanpt);
- y_d = y_d * maxRadius * vpcospt / (maxRadius - y_d*vptanpt);
+ x_d *= maxRadius / (maxRadius - y_d*vptanpt);
+ y_d *= maxRadius * vpcospt / (maxRadius - y_d*vptanpt);
+ }
// rotate
double Dxc = x_d * cost - y_d * sint;
@@ -434,21 +326,25 @@ void ImProcFunctions::transformSep (Imagefloat* original, Imagefloat* transforme
double s = 1;
if (needsDist) {
double r = sqrt(Dxc*Dxc + Dyc*Dyc) / maxRadius; // sqrt is slow
- s = 1.0 - a + a * r ;
+ s = 1.0 - distAmount + distAmount * r ;
}
+ double r2;
+ if (needsVignetting()) {
double vig_Dx = vig_x_d * cost - vig_y_d * sint;
double vig_Dy = vig_x_d * sint + vig_y_d * cost;
- double r2 = needsVignetting() ? sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy) : 0;
+ r2=sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy);
+ }
for (int c=0; c<3; c++) {
-
- double Dx = Dxc * (s + cdist[c]);
- double Dy = Dyc * (s + cdist[c]);
+ double Dx = Dxc * (s + chDist[c]);
+ double Dy = Dyc * (s + chDist[c]);
// de-center
- Dx += w2;
- Dy += h2;
+ Dx += w2; Dy += h2;
+
+ // LCP CA
+ if (enableLCPCA) pLCPMap->correctCA(Dx,Dy,c);
// Extract integer and fractions of source screen coordinates
int xc = (int)Dx; Dx -= (double)xc; xc -= sx;
@@ -462,40 +358,38 @@ void ImProcFunctions::transformSep (Imagefloat* original, Imagefloat* transforme
if (needsVignetting())
vignmul /= (v + mul * tanh (b*(maxRadius-s*r2) / maxRadius));
- if (yc > 0 && yc < original->height-2 && xc > 0 && xc < original->width-2) // all interpolation pixels inside image
- cubintch (chorig[c], xc-1, yc-1, Dx, Dy, &(chtrans[c][y][x]), vignmul);
- else { // edge pixels
+ if (yc > 0 && yc < original->height-2 && xc > 0 && xc < original->width-2) {
+ // all interpolation pixels inside image
+ interpolateTransformChannelsCubic (chOrig[c], xc-1, yc-1, Dx, Dy, &(chTrans[c][y][x]), vignmul);
+ } else {
+ // edge pixels
int y1 = LIM(yc, 0, original->height-1);
int y2 = LIM(yc+1, 0, original->height-1);
int x1 = LIM(xc, 0, original->width-1);
int x2 = LIM(xc+1, 0, original->width-1);
- chtrans[c][y][x] = vignmul*(chorig[c][y1][x1]*(1.0-Dx)*(1.0-Dy) + chorig[c][y1][x2]*Dx*(1.0-Dy) + chorig[c][y2][x1]*(1.0-Dx)*Dy + chorig[c][y2][x2]*Dx*Dy);
+ chTrans[c][y][x] = vignmul * (chOrig[c][y1][x1]*(1.0-Dx)*(1.0-Dy) + chOrig[c][y1][x2]*Dx*(1.0-Dy) + chOrig[c][y2][x1]*(1.0-Dx)*Dy + chOrig[c][y2][x2]*Dx*Dy);
}
}
else
// not valid (source pixel x,y not inside source image, etc.)
- chtrans[c][y][x] = 0;
+ chTrans[c][y][x] = 0;
}
}
}
}
// Transform WITH scaling, WITHOUT CA, simple (and fast) interpolation. Used for preview
-void ImProcFunctions::simpltransform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, const LCPMapper *pLCPMap) {
+void ImProcFunctions::transformPreview (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, const LCPMapper *pLCPMap) {
+
double w2 = (double) oW / 2.0 - 0.5;
double h2 = (double) oH / 2.0 - 0.5;
- double vig_w2;
- double vig_h2;
- double maxRadius;
- double v;
- double b;
- double mul;
+ double vig_w2, vig_h2, maxRadius, v, b, mul;
calcVignettingParams(oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul);
// auxiliary variables for distortion correction
bool needsDist = needsDistortion(); // for performance
- double a = params->distortion.amount;
+ double distAmount = params->distortion.amount;
// auxiliary variables for rotation
double cost = cos(params->rotate.degree * RT_PI/180.0);
@@ -520,20 +414,26 @@ void ImProcFunctions::simpltransform (Imagefloat* original, Imagefloat* transfor
for (int y=0; yheight; y++) {
for (int x=0; xwidth; x++) {
double x_d=x,y_d=y;
- if (pLCPMap) pLCPMap->correctDistortion(x_d,y_d); // must be first transform
+ if (pLCPMap && params->lensProf.useDist) pLCPMap->correctDistortion(x_d,y_d); // must be first transform
y_d = ascale * (y_d + cy - h2); // centering y coord & scale
x_d = ascale * (x_d + cx - w2); // centering x coord & scale
- double vig_x_d = ascale * (x + cx - vig_w2); // centering x coord & scale
- double vig_y_d = ascale * (y + cy - vig_h2); // centering y coord & scale
+ double vig_x_d, vig_y_d;
+ 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 = y_d * maxRadius / (maxRadius + x_d*hptanpt);
- x_d = x_d * maxRadius * hpcospt / (maxRadius + x_d*hptanpt);
+ y_d *= maxRadius / (maxRadius + x_d*hptanpt);
+ x_d *= maxRadius * hpcospt / (maxRadius + x_d*hptanpt);
// vertical perspective transformation
- x_d = x_d * maxRadius / (maxRadius - y_d*vptanpt);
- y_d = y_d * maxRadius * vpcospt / (maxRadius - y_d*vptanpt);
+ x_d *= maxRadius / (maxRadius - y_d*vptanpt);
+ y_d *= maxRadius * vpcospt / (maxRadius - y_d*vptanpt);
+ }
// rotate
double Dx = x_d * cost - y_d * sint;
@@ -543,18 +443,20 @@ void ImProcFunctions::simpltransform (Imagefloat* original, Imagefloat* transfor
double s = 1;
if (needsDist) {
double r = sqrt(Dx*Dx + Dy*Dy) / maxRadius; // sqrt is slow
- s = 1.0 - a + a * r ;
+ s = 1.0 - distAmount + distAmount * r ;
Dx *= s;
Dy *= s;
}
+ double r2;
+ if (needsVignetting()) {
double vig_Dx = vig_x_d * cost - vig_y_d * sint;
double vig_Dy = vig_x_d * sint + vig_y_d * cost;
- double r2 = needsVignetting() ? sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy) : 0;
+ r2=sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy);
+ }
// de-center
- Dx += w2;
- Dy += h2;
+ Dx += w2; Dy += h2;
// Extract integer and fractions of source screen coordinates
int xc = (int)Dx; Dx -= (double)xc; xc -= sx;
@@ -568,12 +470,14 @@ void ImProcFunctions::simpltransform (Imagefloat* original, Imagefloat* transfor
if (needsVignetting())
vignmul /= (v + mul * tanh (b*(maxRadius-s*r2) / maxRadius));
- if (yc < original->height-1 && xc < original->width-1) { // all interpolation pixels inside image
+ if (yc < original->height-1 && xc < original->width-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
+ else {
+ // edge pixels
int y1 = LIM(yc, 0, original->height-1);
int y2 = LIM(yc+1, 0, original->height-1);
int x1 = LIM(xc, 0, original->width-1);
diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc
index b7665775a..439387d77 100644
--- a/rtengine/lcp.cc
+++ b/rtengine/lcp.cc
@@ -43,6 +43,7 @@ using namespace rtexif;
LCPModelCommon::LCPModelCommon() {
focLenX=focLenY=-1; imgXCenter=imgYCenter=0.5;
+ x0=y0=fx=fy=0;
for (int i=0;i<5;i++) param[i]=0;
scaleFac=1;
}
@@ -53,13 +54,59 @@ bool LCPModelCommon::empty() const {
void LCPModelCommon::print() const {
printf("focLen %g/%g; imgCenter %g/%g; scale %g\n",focLenX,focLenY,imgXCenter,imgYCenter,scaleFac);
+ 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]);
}
+// weightened merge two parameters
+void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA) {
+ float facB=1-facA;
+
+ focLenX = facA * a.focLenX + facB * b.focLenX;
+ focLenY = facA * a.focLenY + facB * b.focLenY;
+ imgXCenter = facA * a.imgXCenter + facB * b.imgXCenter;
+ imgYCenter = facA * a.imgYCenter + facB * b.imgYCenter;
+ scaleFac = facA * a.scaleFac + facB * b.scaleFac;
+
+ for (int i=0;i<5;i++) param[i]= facA * a.param[i] + facB * b.param[i];
+}
+
+void 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
+ int Dmax=fullWidth; if (fullHeight>fullWidth) Dmax=fullHeight;
+
+ // correct focLens
+ if (focLenX<0) { // they may not be given
+ // and 35mm may not be given either
+ if (focalLength35mm<1) focalLength35mm = focalLength*sensorFormatFactor;
+
+ focLenX=focLenY=focalLength / ( 35*focalLength/focalLength35mm); // focLen must be calculated in pixels
+ }
+
+ if (swapXY) {
+ x0 = (mirrorX ? 1.-imgYCenter : imgYCenter) * fullWidth;
+ y0 = (mirrorY ? 1.-imgXCenter : imgXCenter) * fullHeight;
+ fx = focLenY * Dmax;
+ fy = focLenX * Dmax;
+ } else {
+ x0 = (mirrorX ? 1.-imgXCenter : imgXCenter) * fullWidth;
+ y0 = (mirrorY ? 1.-imgYCenter : imgYCenter) * fullHeight;
+ fx = focLenX * Dmax;
+ fy = focLenY * Dmax;
+ }
+ //printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter);
+}
+
LCPPersModel::LCPPersModel() {
focLen=focDist=aperture=0;
}
+// mode: 0=distortion, 1=vignette, 2=CA
+bool LCPPersModel::hasModeData(int mode) const {
+ return (mode==0 && !vignette.empty()) || (mode==1 && !base.empty())
+ || (mode==2 && !chromRG.empty() && !chromG.empty() && !chromBG.empty());
+}
+
void LCPPersModel::print() const {
printf("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture);
printf("Base:\n"); base.print();
@@ -70,72 +117,102 @@ void LCPPersModel::print() const {
printf("\n");
}
-// if !vignette then geometric
-LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float aperture, bool vignette, int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg)
+// 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)
{
if (pProf==NULL) return;
- pProf-> calcParams(focalLength, aperture, vignette, mc);
+ useCADist=useCADistP;
// determine in what the image with the RAW landscape in comparison (calibration target)
- int rot = (coarse.rotate+rawRotationDeg) % 360;
+ // in vignetting, the rotation has not taken place yet
+ int rot = 0;
+ if (rawRotationDeg>=0) rot=(coarse.rotate+rawRotationDeg) % 360;
- bool swapXY = (rot==90 || rot==270);
+ swapXY = (rot==90 || rot==270);
bool mirrorX = (rot==90 || rot==180);
bool mirrorY = (rot==180 || rot==270);
-
- // Mention that the Adobe technical paper has a bug here, the DMAX is handled differently for focLen and imgCenter
- int Dmax=fullWidth; if (fullHeight>fullWidth) Dmax=fullHeight;
-
- // correct focLens
- float focLenX=mc.focLenX; float focLenY=mc.focLenY;
- if (focLenX<0) { // they may not be given
- // and 35mm may not be given either
- if (focalLength35mm<1) focalLength35mm = focalLength*pProf->sensorFormatFactor;
-
- focLenX=focLenY=focalLength / ( 35*focalLength/focalLength35mm); // focLen must be calculated in pixels
+ //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, NULL, NULL);
+ 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]);
+ for (int i=0;i<3;i++) chrom[i].prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY);
}
-
- if (swapXY) {
- x0 = (mirrorX ? 1-mc.imgYCenter : mc.imgYCenter) * fullWidth;
- y0 = (mirrorY ? 1-mc.imgXCenter : mc.imgXCenter) * fullHeight;
- fx = focLenY * Dmax;
- fy = focLenX * Dmax;
- } else {
- x0 = (mirrorX ? 1-mc.imgXCenter : mc.imgXCenter) * fullWidth;
- y0 = (mirrorY ? 1-mc.imgYCenter : mc.imgYCenter) * fullHeight;
- fx = focLenX * Dmax;
- fy = focLenY * Dmax;
- }
-
- //printf("\nMapping Dmax=%i f=%g/f35=%g vignette=%i using\n",Dmax, focalLength, focalLength35mm, vignette);
- //mc.print();
}
void LCPMapper::correctDistortion(double& x, double& y) const {
- double xd=(x-x0)/fx, yd=(y-y0)/fy;
+ double xd=(x-mc.x0)/mc.fx, yd=(y-mc.y0)/mc.fy;
+ const float* aDist = mc.param;
double rsqr = xd*xd+yd*yd;
- double commonFac = (((mc.param[2]*rsqr + mc.param[1])*rsqr + mc.param[0])*rsqr + 1.)
- + 2. * (mc.param[3] * yd + mc.param[4] * xd);
+ double xfac=aDist[swapXY?3:4], yfac=aDist[swapXY?4:3];
- double xnew = xd * commonFac + mc.param[4] * rsqr;
- double ynew = yd * commonFac + mc.param[3] * rsqr;
+ double commonFac = (((aDist[2]*rsqr + aDist[1])*rsqr + aDist[0])*rsqr + 1.)
+ + 2. * (yfac * yd + xfac * xd);
- x = xnew * fx + x0;
- y = ynew * fy + y0;
+ double xnew = xd * commonFac + xfac * rsqr;
+ double ynew = yd * commonFac + yfac * rsqr;
+
+ x = xnew * mc.fx + mc.x0;
+ y = ynew * mc.fy + mc.y0;
}
-float LCPMapper::correctVignette(int x, int y) const {
- double xd=((double)x-x0)/fx, yd=((double)y-y0)/fy;
+void LCPMapper::correctCA(double& x, double& y, int channel) const {
+ double rsqr, 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;
+
+ // Green contains main distortion, just like base
+ if (useCADist) {
+ const float* aDist = chrom[1].param;
double rsqr = xd*xd+yd*yd;
- double param0Sqr = mc.param[0]*mc.param[0];
+ double xfac=aDist[swapXY?3:4], yfac=aDist[swapXY?4:3];
- return 1. + rsqr * (-mc.param[0] + rsqr * ((param0Sqr - mc.param[1])
- - (param0Sqr*mc.param[0] - 2.*mc.param[0]*mc.param[1] + mc.param[2]) *rsqr
- + (param0Sqr*param0Sqr + mc.param[1]*mc.param[1]
- + 2.*mc.param[0]*mc.param[2] - 3.*param0Sqr*mc.param[1]) *rsqr*rsqr));
+ double commonFac = (((aDist[2]*rsqr + aDist[1])*rsqr + aDist[0])*rsqr + 1.)
+ + 2. * (yfac * yd + xfac * xd);
+
+ xgreen = xd * commonFac + aDist[4] * rsqr;
+ ygreen = yd * commonFac + aDist[3] * rsqr;
+ } else {
+ xgreen=xd; ygreen=yd;
+ }
+
+ if (channel==1) {
+ // green goes directly
+ x = xgreen * chrom[1].fx + chrom[1].x0;
+ y = ygreen * chrom[1].fy + chrom[1].y0;
+ } else {
+ // others are diffs from green
+ xd=xgreen; yd=ygreen;
+ rsqr=xd*xd+yd*yd;
+
+ const float* 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);
+
+ x = (chrom[channel].scaleFac * ( xd * commonSum + xfac*rsqr )) * chrom[channel].fx + chrom[channel].x0;
+ y = (chrom[channel].scaleFac * ( yd * commonSum + yfac*rsqr )) * chrom[channel].fy + chrom[channel].y0;
+ }
+}
+
+float LCPMapper::calcVignetteFac(int x, int y) const {
+ // No need for swapXY, since vignette is in RAW and always before rotation
+ double xd=((double)x-mc.x0)/mc.fx, yd=((double)y-mc.y0)/mc.fy;
+
+ const float* aVig= mc.param;
+ double rsqr = xd*xd+yd*yd;
+ double param0Sqr = aVig[0]*aVig[0];
+
+ return 1. + rsqr * (-aVig[0] + rsqr * ((param0Sqr - aVig[1])
+ - (param0Sqr*aVig[0] - 2.*aVig[0]*aVig[1] + aVig[2]) *rsqr
+ + (param0Sqr*param0Sqr + aVig[1]*aVig[1]
+ + 2.*aVig[0]*aVig[2] - 3.*param0Sqr*aVig[1]) *rsqr*rsqr));
}
LCPProfile::LCPProfile(Glib::ustring fname) {
@@ -165,24 +242,31 @@ LCPProfile::LCPProfile(Glib::ustring fname) {
throw "Invalid XML in LCP file";
} while (!done);
+ fclose(pFile);
+
XML_ParserFree(parser);
}
-void LCPProfile::calcParams(float focalLength, float aperture, bool vignette, LCPModelCommon& corr) const {
+
+// 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=NULL, *pHigh=NULL;
- float focalLengthLog=log(focalLength), apertureLog=aperture>0 ? log(aperture) : 0;
+ 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 apertures
+ // Pass 1: determining best focal length, if possible different focusDistances (for the focDist is not given case)
for (int pm=0;pmfocLen;
- if ((!vignette && !aPersModel[pm]->base.empty()) || (vignette && !aPersModel[pm]->vignette.empty())) {
- if (f <= focalLength && (pLow==NULL || f > pLow->focLen || (f==pLow->focLen && pLow->aperture>aPersModel[pm]->aperture))) {
+ if (aPersModel[pm]->hasModeData(mode)) {
+ if (f <= focalLength && (pLow==NULL || f > pLow->focLen || (focusDist==0 && f==pLow->focLen && pLow->focDist>aPersModel[pm]->focDist))) {
pLow=aPersModel[pm];
}
- if (f >= focalLength && (pHigh==NULL || f < pHigh->focLen || (f==pHigh->focLen && pHigh->apertureaperture))) {
+ if (f >= focalLength && (pHigh==NULL || f < pHigh->focLen || (focusDist==0 && f==pHigh->focLen && pHigh->focDistfocDist))) {
pHigh=aPersModel[pm];
}
}
@@ -192,56 +276,83 @@ void LCPProfile::calcParams(float focalLength, float aperture, bool vignette, LC
pLow=pHigh;
else if (!pHigh)
pHigh=pLow;
- else if (vignette) {
- // We have some, so take the best aperture for vignette (unimportant for distortion)
+ else if (mode==0 || focusDist>0) {
+ // 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;pmaperture);
+ float aper=aPersModel[pm]->aperture; // float aperLog=log(aper);
+ float focDist=aPersModel[pm]->focDist; float focDistLog=log(focDist)+euler;
- if ((!vignette && !aPersModel[pm]->base.empty()) || (vignette && !aPersModel[pm]->vignette.empty())) {
- if (fabs(aPersModel[pm]->focLen-bestFocLenLow)<0.01 && ((aperLog<=apertureLog && pLow->aperture>aperture)
- || (aperLog<=apertureLog && fabs(apertureLog-aperLog)aperture))))) {
+ if (aPersModel[pm]->hasModeData(mode)) {
+ if (mode==0) {
+ // by aperture, and max out focus distance
+ // tests showed doing this by log(aperture) is not as advisable
+ if (aPersModel[pm]->focLen==bestFocLenLow && ((aper>=aperture && aperaperture && pLow->aperture > aperture)
+ || (aper<=aperture && (pLow->aperture>aperture || fabs(aperture-aper)aperture))))) {
pLow=aPersModel[pm];
}
- if (fabs(aPersModel[pm]->focLen-bestFocLenHigh)<0.01 && ((aperLog>=apertureLog && pHigh->aperture=apertureLog && fabs(apertureLog-aperLog)aperture))))) {
+ if (aPersModel[pm]->focLen==bestFocLenHigh && ((aper<=aperture && aper>pHigh->aperture && pHigh->aperture < aperture)
+ || (aper>=aperture && (pHigh->apertureaperture))))) {
pHigh=aPersModel[pm];
}
+ } else {
+ // by focus distance
+ if (aPersModel[pm]->focLen==bestFocLenLow && ((focDist>=focusDist && focDistfocDist && pLow->focDist > focusDist)
+ || (focDist<=focusDist && (pLow->focDist>focusDist || fabs(focusDistLog-focDistLog)focDist)+euler)))))) {
+ pLow=aPersModel[pm];
+ }
+ if (aPersModel[pm]->focLen==bestFocLenHigh && ((focDist<=focusDist && focDist>pHigh->focDist && pHigh->focDist < focusDist)
+ || (focDist>=focusDist && (pHigh->focDistfocDist)+euler)))))) {
+ pHigh=aPersModel[pm];
+ }
+ }
}
}
}
if (pLow!=NULL && pHigh!=NULL) {
// average out the factors, linear interpolation in logarithmic scale
- float facLow=0, facHigh=0;
+ 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) {
- float diff = log(pHigh->focLen) - log(pLow->focLen);
- facLow = (log(pHigh->focLen)-focalLengthLog) / diff;
- facHigh = (focalLengthLog-log(pLow->focLen)) / diff;
- } else if (pLow->aperture < aperture && pHigh->aperture > aperture) {
- // FocLen is the same, take aperture (espc. used for vignetting)
- float diff = log(pHigh->aperture) - log(pLow->aperture);
- facLow = (log(pHigh->aperture)-apertureLog) / diff;
- facHigh = (apertureLog-log(pLow->aperture)) / diff;
+ facLow = (log(pHigh->focLen)-focalLengthLog) / (log(pHigh->focLen) - log(pLow->focLen));
} else {
- facLow=facHigh=0.5;
+ focLenOnSpot=pLow->focLen==pHigh->focLen && pLow->focLen==focalLength;
}
- LCPModelCommon& mLow =(vignette?pLow->vignette :pLow->base);
- LCPModelCommon& mHigh=(vignette?pHigh->vignette:pHigh->base);
+ // 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);
+ }
- corr.focLenX = facLow * mLow.focLenX + facHigh * mHigh.focLenX;
- corr.focLenY = facLow * mLow.focLenY + facHigh * mHigh.focLenY;
- corr.imgXCenter = facLow * mLow.imgXCenter + facHigh * mHigh.imgXCenter;
- corr.imgYCenter = facLow * mLow.imgYCenter + facHigh * mHigh.imgYCenter;
- corr.scaleFac = facLow * mLow.scaleFac + facHigh * mHigh.scaleFac;
+ switch (mode) {
+ case 0: // vignette
+ pCorr1->merge(pLow->vignette, pHigh->vignette, facLow);
+ break;
- for (int i=0;i<5;i++) corr.param[i]= facLow * mLow.param[i] + facHigh * mHigh.param[i];
+ case 1: // distortion
+ pCorr1->merge(pLow->base, pHigh->base, facLow);
+ break;
- //printf("LCP ( %i frames) vignette=%i for Fno %g - %g; FocLen %g - %g with fac %g - %g:\n", persModelCount, vignette, pLow->aperture, pHigh->aperture, pLow->focLen, pHigh->focLen, facLow, facHigh);
+ 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;
+ }
+
+ //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 printf("Error: LCP file contained no parameters\n");
}
@@ -325,8 +436,6 @@ void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, in
char raw[len+1]; memcpy(raw,s,len); raw[len]=0;
char* tag=pProf->lastTag;
- //printf("%s : %s\n",tag,raw);
-
// Common data section
if (!pProf->firstLIDone) {
// Generic tags are the same for all
@@ -363,8 +472,10 @@ void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, in
// Perspective model base data
if (!strcmp("FocalLength",tag))
pProf->pCurPersModel->focLen=atof(raw);
- else if (!strcmp("FocusDistance",tag))
- pProf->pCurPersModel->focDist=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);
@@ -383,12 +494,14 @@ void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, in
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))
+ else if (!strcmp("RadialDistortParam3",tag) || !strcmp("VignetteModelParam3",tag))
pProf->pCurCommon->param[2]=atof(raw);
- else if (!strcmp("TangentialDistortParam1",tag))
+ else if (!strcmp("RadialDistortParam4",tag) || !strcmp("TangentialDistortParam1",tag))
pProf->pCurCommon->param[3]=atof(raw);
- else if (!strcmp("TangentialDistortParam2",tag))
+ else if (!strcmp("RadialDistortParam5",tag) || !strcmp("TangentialDistortParam2",tag))
pProf->pCurCommon->param[4]=atof(raw);
+ else if (!strcmp("RadialDistortParam6",tag) || !strcmp("TangentialDistortParam3",tag))
+ pProf->pCurCommon->param[5]=atof(raw);
}
void XMLCALL LCPProfile::XmlEndHandler(void *pLCPProfile, const char *el) {
diff --git a/rtengine/lcp.h b/rtengine/lcp.h
index b50f71082..bd2364c6c 100644
--- a/rtengine/lcp.h
+++ b/rtengine/lcp.h
@@ -32,12 +32,16 @@ namespace rtengine {
class LCPModelCommon {
public:
float focLenX, focLenY, imgXCenter, imgYCenter;
- float param[5]; // k1..k5
- float scaleFac;
+ float param[5]; // k1..k5, resp. alpha1..5
+ float scaleFac; // alpha0
+
+ double x0,y0,fx,fy; // prepared params
LCPModelCommon();
- bool empty() const; // is it empty?
+ bool empty() const; // is it empty
void print() const; // printf all values
+ void merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA);
+ void prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY);
};
class LCPPersModel {
@@ -49,6 +53,7 @@ namespace rtengine {
LCPModelCommon vignette; // vignette (may be empty)
LCPPersModel();
+ bool hasModeData(int mode) const;
void print() const;
};
@@ -76,7 +81,7 @@ namespace rtengine {
LCPProfile(Glib::ustring fname);
- void calcParams(float focalLength, float aperture, bool vignette, LCPModelCommon& corr) const; // Interpolates between the persModels frames
+ void calcParams(int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const; // Interpolates between the persModels frames
void print() const;
};
@@ -100,16 +105,20 @@ namespace rtengine {
// Once precalculated class to correct a point
class LCPMapper {
- double x0,y0,fx,fy;
+
+ bool useCADist; // should the distortion in the CA info be used?
+ bool swapXY;
LCPModelCommon mc;
+ LCPModelCommon chrom[3]; // in order RedGreen/Green/BlueGreen
public:
// precalculates the mapper.
- LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float aperture, bool vignette, int fullWidth, int fullHeight,
+ LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, int fullWidth, int fullHeight,
const CoarseTransformParams& coarse, int rawRotationDeg);
void correctDistortion(double& x, double& y) const; // MUST be the first stage
- float correctVignette (int x, int y) const; // MUST be in RAW
+ void correctCA(double& x, double& y, int channel) const;
+ float calcVignetteFac (int x, int y) const; // MUST be in RAW
};
}
#endif
diff --git a/rtengine/procevents.h b/rtengine/procevents.h
index d4ef5b198..10b5dfe31 100644
--- a/rtengine/procevents.h
+++ b/rtengine/procevents.h
@@ -55,9 +55,9 @@ enum ProcEvent {
EvShrDAmount=30,
EvShrDDamping=31,
EvShrDIterations=32,
- EvCBAvoidClip=33, // obsolete
- EvCBSatLimiter=34,// obsolete
- EvCBSatLimit=35, // obsolete
+ EvLCPUseDist=33,
+ EvLCPUseVign=34,
+ EvLCPUseCA=35,
EvCBBoost=36, // obsolete
EvWBMethod=37,
EvWBTemp=38,
diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc
index 12ffc4a18..d246d6672 100644
--- a/rtengine/procparams.cc
+++ b/rtengine/procparams.cc
@@ -275,6 +275,10 @@ void ProcParams::setDefaults () {
vignetting.centerX = 0;
vignetting.centerY = 0;
+ lensProf.lcpFile="";
+ lensProf.useDist=lensProf.useVign=true;
+ lensProf.useCA=false;
+
chmixer.red[0] = 100;
chmixer.red[1] = 0;
chmixer.red[2] = 0;
@@ -542,6 +546,9 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, ParamsEdited* p
// lens profile
if (!pedited || pedited->lensProf.lcpFile) keyFile.set_string ("LensProfile", "LCPFile", lensProf.lcpFile);
+ if (!pedited || pedited->lensProf.useDist) keyFile.set_boolean ("LensProfile", "UseDistortion", lensProf.useDist);
+ if (!pedited || pedited->lensProf.useVign) keyFile.set_boolean ("LensProfile", "UseVignette", lensProf.useDist);
+ if (!pedited || pedited->lensProf.useCA) keyFile.set_boolean ("LensProfile", "UseCA", lensProf.useDist);
// save perspective correction
if (!pedited || pedited->perspective.horizontal) keyFile.set_integer ("Perspective", "Horizontal", perspective.horizontal);
@@ -948,6 +955,9 @@ if (keyFile.has_group ("Distortion")) {
// lens profile
if (keyFile.has_group ("LensProfile")) {
if (keyFile.has_key ("LensProfile", "LCPFile")) { lensProf.lcpFile = keyFile.get_string ("LensProfile", "LCPFile"); if (pedited) pedited->lensProf.lcpFile = true; }
+ if (keyFile.has_key ("LensProfile", "UseDistortion")) { lensProf.useDist = keyFile.get_boolean ("LensProfile", "UseDistortion"); if (pedited) pedited->lensProf.useDist = true; }
+ if (keyFile.has_key ("LensProfile", "UseVignette")) { lensProf.useVign = keyFile.get_boolean ("LensProfile", "UseVignette"); if (pedited) pedited->lensProf.useVign = true; }
+ if (keyFile.has_key ("LensProfile", "UseCA")) { lensProf.useCA = keyFile.get_boolean ("LensProfile", "UseCA"); if (pedited) pedited->lensProf.useCA = true; }
}
// load perspective correction
@@ -1244,6 +1254,9 @@ bool ProcParams::operator== (const ProcParams& other) {
&& commonTrans.autofill == other.commonTrans.autofill
&& distortion.amount == other.distortion.amount
&& lensProf.lcpFile == other.lensProf.lcpFile
+ && lensProf.useDist == other.lensProf.useDist
+ && lensProf.useVign == other.lensProf.useVign
+ && lensProf.useCA == other.lensProf.useCA
&& perspective.horizontal == other.perspective.horizontal
&& perspective.vertical == other.perspective.vertical
&& cacorrection.red == other.cacorrection.red
diff --git a/rtengine/procparams.h b/rtengine/procparams.h
index 44c90226f..d5e198281 100644
--- a/rtengine/procparams.h
+++ b/rtengine/procparams.h
@@ -334,6 +334,7 @@ class LensProfParams {
public:
Glib::ustring lcpFile;
+ bool useDist, useVign, useCA;
};
/**
diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc
index 253f74ebe..001db81dd 100644
--- a/rtengine/rawimagesource.cc
+++ b/rtengine/rawimagesource.cc
@@ -1017,16 +1017,16 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le
scaleColors( 0,0, W, H, raw);//+ + raw parameters for black level(raw.blackxx)
// Correct vignetting of lens profile
- if (!hasFlatField) {
+ if (!hasFlatField && lensProf.useVign) {
LCPProfile *pLCPProf=lcpStore->getProfile(lensProf.lcpFile);
if (pLCPProf) {
- LCPMapper map(pLCPProf, idata->getFocalLen(), idata->getFocalLen35mm(), idata->getFNumber(), true, W, H, coarse, ri->get_rotateDegree());
+ LCPMapper map(pLCPProf, idata->getFocalLen(), idata->getFocalLen35mm(), idata->getFocusDist(), idata->getFNumber(), true, false, W, H, coarse, -1);
#pragma omp parallel for
for (int y=0; y0) rawData[y][x]*=map.correctVignette(x,y);
+ if (rawData[y][x]>0) rawData[y][x] *= map.calcVignetteFac(x,y);
}
}
}
diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc
index fbc01e7e8..7229ff43a 100644
--- a/rtengine/refreshmap.cc
+++ b/rtengine/refreshmap.cc
@@ -53,9 +53,9 @@ SHARPENING, // EvShrDRadius,
SHARPENING, // EvShrDAmount,
SHARPENING, // EvShrDDamping,
SHARPENING, // EvShrDIterations,
-0, // EvCBAvoidClip: obsolete
-0, // EvCBSatLimiter: obsolete
-0, // EvCBSatLimit: obsolete
+TRANSFORM, // EvLCPUseDist,
+DARKFRAME, // EvLCPUseVign,
+TRANSFORM, // EvLCPUseCA,
0, // EvCBBoost: obsolete
WHITEBALANCE, // EvWBMethod,
WHITEBALANCE, // EvWBTemp,
diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h
index 3e00c4419..3740c787f 100644
--- a/rtengine/rtengine.h
+++ b/rtengine/rtengine.h
@@ -71,6 +71,8 @@ namespace rtengine {
virtual double getFocalLen () const =0;
/** @return the focal length in 35mm used at the exposure */
virtual double getFocalLen35mm () const =0;
+ /** @return the focus distance in meters, 0=unknown, 10000=infinity */
+ virtual float getFocusDist () const =0;
/** @return the shutter speed */
virtual double getShutterSpeed () const =0;
/** @return the exposure compensation */
diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc
index 45547d403..8527081a7 100644
--- a/rtengine/rtthumbnail.cc
+++ b/rtengine/rtthumbnail.cc
@@ -577,7 +577,7 @@ IImage8* Thumbnail::quickProcessImage (const procparams::ProcParams& params, int
// Full thumbnail processing, second stage if complete profile exists
IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rheight, TypeInterpolation interp, std::string camName,
- double focalLen, double focalLen35mm, double& myscale) {
+ double focalLen, double focalLen35mm, float focusDist, double& myscale) {
// compute WB multipliers
ColorTemp currWB = ColorTemp (params.wb.temperature, params.wb.green, params.wb.method);
@@ -699,7 +699,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei
// perform transform
if (ipf.needsTransform()) {
Imagefloat* trImg = new Imagefloat (fw, fh);
- ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, focalLen, focalLen35mm, 0); // Raw rotate degree not detectable here
+ ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, focalLen, focalLen35mm, focusDist, 0, true); // Raw rotate degree not detectable here
delete baseImg;
baseImg = trImg;
}
diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h
index a705c008d..0df052da7 100644
--- a/rtengine/rtthumbnail.h
+++ b/rtengine/rtthumbnail.h
@@ -70,7 +70,8 @@ namespace rtengine {
static void cleanupGamma ();
void init ();
- IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, std::string camName, double focalLen, double focalLen35mm, double& scale);
+ IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, std::string camName,
+ double focalLen, double focalLen35mm, float focusDist, double& scale);
IImage8* quickProcessImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, double& scale);
int getImageWidth (const procparams::ProcParams& pparams, int rheight, float &ratio);
void getDimensions (int& w, int& h, double& scaleFac);
diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc
index 969194580..270fa3b22 100644
--- a/rtengine/simpleprocess.cc
+++ b/rtengine/simpleprocess.cc
@@ -115,7 +115,8 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p
// perform transform (excepted resizing)
if (ipf.needsTransform()) {
Imagefloat* trImg = new Imagefloat (fw, fh);
- ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getRotateDegree());
+ ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(),
+ imgsrc->getMetaData()->getFocusDist(), imgsrc->getRotateDegree(), true);
delete baseImg;
baseImg = trImg;
}
diff --git a/rtgui/cacheimagedata.cc b/rtgui/cacheimagedata.cc
index 6fa0b5828..9b207a160 100644
--- a/rtgui/cacheimagedata.cc
+++ b/rtgui/cacheimagedata.cc
@@ -70,6 +70,8 @@ int CacheImageData::load (const Glib::ustring& fname) {
if (keyFile.has_key ("ExifInfo", "FocalLen")) focalLen = keyFile.get_double ("ExifInfo", "FocalLen");
if (keyFile.has_key ("ExifInfo", "FocalLen35mm")) focalLen35mm = keyFile.get_double ("ExifInfo", "FocalLen35mm");
else focalLen35mm=focalLen; // prevent crashes on old files
+ if (keyFile.has_key ("ExifInfo", "FocusDist")) focusDist = keyFile.get_double ("ExifInfo", "FocusDist");
+ else focusDist=0;
if (keyFile.has_key ("ExifInfo", "ISO")) iso = keyFile.get_integer ("ExifInfo", "ISO");
if (keyFile.has_key ("ExifInfo", "ExpComp")) expcomp = keyFile.get_string ("ExifInfo", "ExpComp");
}
@@ -129,6 +131,7 @@ int CacheImageData::save (const Glib::ustring& fname) {
keyFile.set_double ("ExifInfo", "Shutter", shutter);
keyFile.set_double ("ExifInfo", "FocalLen", focalLen);
keyFile.set_double ("ExifInfo", "FocalLen35mm", focalLen35mm);
+ keyFile.set_double ("ExifInfo", "FocusDist", focusDist);
keyFile.set_integer ("ExifInfo", "ISO", iso);
keyFile.set_string ("ExifInfo", "ExpComp", expcomp);
}
diff --git a/rtgui/cacheimagedata.h b/rtgui/cacheimagedata.h
index 5feeca1ea..4c31cba77 100644
--- a/rtgui/cacheimagedata.h
+++ b/rtgui/cacheimagedata.h
@@ -50,6 +50,7 @@ class CacheImageData {
double fnumber;
double shutter;
double focalLen,focalLen35mm;
+ float focusDist;
unsigned iso;
Glib::ustring lens;
Glib::ustring camera;
diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc
index 2793d4ee9..5a3c4bbbf 100644
--- a/rtgui/lensprofile.cc
+++ b/rtgui/lensprofile.cc
@@ -1,7 +1,7 @@
/*
* This file is part of RawTherapee.
*
-* Copyright (c) 2011 Oliver Duis
+* Copyright (c) 2012 Oliver Duis
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -58,24 +58,56 @@ LensProfilePanel::LensProfilePanel () : Gtk::VBox(), FoldableToolPanel(this)
pack_start(*hbLCPFile, Gtk::PACK_SHRINK, 4);
+ ckbUseDist = Gtk::manage (new Gtk::CheckButton (M("TP_LENSPROFILE_USEDIST")));
+ pack_start (*ckbUseDist, Gtk::PACK_SHRINK, 4);
+ ckbUseVign = Gtk::manage (new Gtk::CheckButton (M("TP_LENSPROFILE_USEVIGN")));
+ pack_start (*ckbUseVign, Gtk::PACK_SHRINK, 4);
+ ckbUseCA = Gtk::manage (new Gtk::CheckButton (M("TP_LENSPROFILE_USECA")));
+ pack_start (*ckbUseCA, Gtk::PACK_SHRINK, 4);
+
conLCPFile = fcbLCPFile->signal_file_set().connect( sigc::mem_fun(*this, &LensProfilePanel::onLCPFileChanged), true);
btnReset->signal_clicked().connect( sigc::mem_fun(*this, &LensProfilePanel::onLCPFileReset), true);
+ ckbUseDist->signal_toggled().connect( sigc::mem_fun(*this, &LensProfilePanel::onUseDistChanged) );
+ ckbUseVign->signal_toggled().connect( sigc::mem_fun(*this, &LensProfilePanel::onUseVignChanged) );
+ ckbUseCA->signal_toggled().connect( sigc::mem_fun(*this, &LensProfilePanel::onUseCAChanged) );
+
+ allowFocusDep=true;
}
void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited)
{
disableListener ();
- if (pp->lensProf.lcpFile.length()>0 && lcpStore->isValidLCPFileName(pp->lensProf.lcpFile))
+ if (pp->lensProf.lcpFile.length()>0 && lcpStore->isValidLCPFileName(pp->lensProf.lcpFile)) {
fcbLCPFile->set_filename (pp->lensProf.lcpFile);
- else
+ updateDisabled(true);
+ } else {
fcbLCPFile->unselect_filename(fcbLCPFile->get_filename());
+ updateDisabled(false);
+ }
- lcpFileChanged = false;
+ ckbUseDist->set_active (pp->lensProf.useDist);
+ ckbUseVign->set_active (pp->lensProf.useVign);
+ ckbUseCA->set_active (pp->lensProf.useCA);
+
+ lcpFileChanged=useDistChanged=useVignChanged=useCAChanged=false;
enableListener ();
}
+void LensProfilePanel::setRawMeta(bool raw, const rtengine::ImageMetaData* pMeta) {
+ if (!raw || pMeta->getFocusDist()<=0) {
+ disableListener();
+
+ // CA is very focus layer dependend, otherwise it might even worsen things
+ allowFocusDep=false;
+ ckbUseCA->set_active(false);
+ ckbUseCA->set_sensitive(false);
+
+ enableListener();
+ }
+}
+
void LensProfilePanel::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pedited)
{
if (lcpStore->isValidLCPFileName(fcbLCPFile->get_filename()))
@@ -83,12 +115,23 @@ void LensProfilePanel::write( rtengine::procparams::ProcParams* pp, ParamsEdited
else
pp->lensProf.lcpFile = "";
- if (pedited) pedited->lensProf.lcpFile = lcpFileChanged;
+ pp->lensProf.useDist = ckbUseDist->get_active();
+ pp->lensProf.useVign = ckbUseVign->get_active();
+ pp->lensProf.useCA = ckbUseCA->get_active();
+
+ if (pedited) {
+ pedited->lensProf.lcpFile = lcpFileChanged;
+ pedited->lensProf.useDist = useDistChanged;
+ pedited->lensProf.useVign = useVignChanged;
+ pedited->lensProf.useCA = useCAChanged;
+ }
}
void LensProfilePanel::onLCPFileChanged()
{
lcpFileChanged=true;
+ updateDisabled(lcpStore->isValidLCPFileName(fcbLCPFile->get_filename()));
+
if (listener)
listener->panelChanged (EvLCPFile, Glib::path_get_basename(fcbLCPFile->get_filename()));
}
@@ -98,7 +141,30 @@ void LensProfilePanel::onLCPFileReset()
lcpFileChanged=true;
fcbLCPFile->unselect_filename(fcbLCPFile->get_filename());
+ updateDisabled(false);
if (listener)
listener->panelChanged (EvLCPFile, M("GENERAL_NONE"));
}
+
+void LensProfilePanel::onUseDistChanged()
+{
+ useDistChanged=true;
+ if (listener) listener->panelChanged (EvLCPUseDist, ckbUseDist->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED"));
+}
+void LensProfilePanel::onUseVignChanged()
+{
+ useVignChanged=true;
+ if (listener) listener->panelChanged (EvLCPUseVign, ckbUseVign->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED"));
+}
+void LensProfilePanel::onUseCAChanged()
+{
+ useCAChanged=true;
+ if (listener) listener->panelChanged (EvLCPUseCA, ckbUseCA->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED"));
+}
+
+void LensProfilePanel::updateDisabled(bool enable) {
+ ckbUseDist->set_sensitive(enable);
+ ckbUseVign->set_sensitive(enable);
+ ckbUseCA->set_sensitive(enable && allowFocusDep);
+}
diff --git a/rtgui/lensprofile.h b/rtgui/lensprofile.h
index 8aedb43be..e25555a24 100644
--- a/rtgui/lensprofile.h
+++ b/rtgui/lensprofile.h
@@ -1,7 +1,7 @@
/*
* This file is part of RawTherapee.
*
-* Copyright (c) 2011 Oliver Duis
+* Copyright (c) 2012 Oliver Duis
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,11 +28,14 @@ class LensProfilePanel : public Gtk::VBox, public FoldableToolPanel {
protected:
MyFileChooserButton *fcbLCPFile;
+ Gtk::CheckButton *ckbUseDist, *ckbUseVign, *ckbUseCA;
Gtk::HBox *hbLCPFile;
Gtk::Button *btnReset;
Gtk::Label *lLCPFileHead;
- bool lcpFileChanged;
- sigc::connection conLCPFile;
+ bool lcpFileChanged,useDistChanged,useVignChanged,useCAChanged;
+ sigc::connection conLCPFile, conUseDist, conUseVign, conUseCA;
+ void updateDisabled(bool enable);
+ bool allowFocusDep;
public:
@@ -40,9 +43,13 @@ public:
void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited=NULL);
void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited=NULL);
+ void setRawMeta (bool raw, const rtengine::ImageMetaData* pMeta);
void onLCPFileChanged ();
void onLCPFileReset ();
+ void onUseDistChanged();
+ void onUseVignChanged();
+ void onUseCAChanged();
};
#endif
diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc
index 0620170f6..ffad8e753 100644
--- a/rtgui/paramsedited.cc
+++ b/rtgui/paramsedited.cc
@@ -136,6 +136,9 @@ void ParamsEdited::set (bool v) {
rotate.degree = v;
distortion.amount = v;
lensProf.lcpFile = v;
+ lensProf.useDist = v;
+ lensProf.useVign = v;
+ lensProf.useCA = v;
perspective.horizontal = v;
perspective.vertical = v;
cacorrection.red = v;
@@ -329,6 +332,9 @@ void ParamsEdited::initFrom (const std::vector
rotate.degree = rotate.degree && p.rotate.degree == other.rotate.degree;
distortion.amount = distortion.amount && p.distortion.amount == other.distortion.amount;
lensProf.lcpFile = lensProf.lcpFile && p.lensProf.lcpFile == other.lensProf.lcpFile;
+ lensProf.useDist = lensProf.useDist && p.lensProf.useDist == other.lensProf.useDist;
+ lensProf.useVign = lensProf.useVign && p.lensProf.useVign == other.lensProf.useVign;
+ lensProf.useCA = lensProf.useCA && p.lensProf.useCA == other.lensProf.useCA;
perspective.horizontal = perspective.horizontal && p.perspective.horizontal == other.perspective.horizontal;
perspective.vertical = perspective.vertical && p.perspective.vertical == other.perspective.vertical;
cacorrection.red = cacorrection.red && p.cacorrection.red == other.cacorrection.red;
@@ -521,6 +527,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten
if (rotate.degree) toEdit.rotate.degree = dontforceSet && options.baBehav[ADDSET_ROTATE_DEGREE] ? toEdit.rotate.degree + mods.rotate.degree : mods.rotate.degree;
if (distortion.amount) toEdit.distortion.amount = dontforceSet && options.baBehav[ADDSET_DIST_AMOUNT] ? toEdit.distortion.amount + mods.distortion.amount : mods.distortion.amount;
if (lensProf.lcpFile) toEdit.lensProf.lcpFile = mods.lensProf.lcpFile;
+ if (lensProf.useDist) toEdit.lensProf.useDist = mods.lensProf.useDist;
+ if (lensProf.useVign) toEdit.lensProf.useVign = mods.lensProf.useVign;
+ if (lensProf.useCA) toEdit.lensProf.useCA = mods.lensProf.useCA;
+
if (perspective.horizontal) toEdit.perspective.horizontal = dontforceSet && options.baBehav[ADDSET_PERSPECTIVE] ? toEdit.perspective.horizontal + mods.perspective.horizontal : mods.perspective.horizontal;
if (perspective.vertical) toEdit.perspective.vertical = dontforceSet && options.baBehav[ADDSET_PERSPECTIVE] ? toEdit.perspective.vertical + mods.perspective.vertical : mods.perspective.vertical;
if (cacorrection.red) toEdit.cacorrection.red = dontforceSet && options.baBehav[ADDSET_CA] ? toEdit.cacorrection.red + mods.cacorrection.red : mods.cacorrection.red;
diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h
index f9d840820..c4804333e 100644
--- a/rtgui/paramsedited.h
+++ b/rtgui/paramsedited.h
@@ -248,7 +248,7 @@ class DistortionParamsEdited {
class LensProfParamsEdited {
public:
- bool lcpFile;
+ bool lcpFile,useDist,useVign,useCA;
bool isUnchanged() const;
};
diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc
index 377efd4f4..a2aadaa91 100644
--- a/rtgui/thumbnail.cc
+++ b/rtgui/thumbnail.cc
@@ -469,7 +469,7 @@ rtengine::IImage8* Thumbnail::processThumbImage (const rtengine::procparams::Pro
else
{
// Full thumbnail: apply profile
- image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, cfs.focalLen, cfs.focalLen35mm, scale );
+ image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, cfs.focalLen, cfs.focalLen35mm, cfs.focusDist, scale );
}
tpp->getDimensions(lastW,lastH,lastScale);
@@ -494,7 +494,7 @@ rtengine::IImage8* Thumbnail::upgradeThumbImage (const rtengine::procparams::Pro
return 0;
}
- rtengine::IImage8* image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, cfs.focalLen, cfs.focalLen35mm, scale );
+ rtengine::IImage8* image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, cfs.focalLen, cfs.focalLen35mm, cfs.focusDist, scale );
tpp->getDimensions(lastW,lastH,lastScale);
delete tpp;
@@ -574,6 +574,7 @@ int Thumbnail::infoFromImage (const Glib::ustring& fname, rtengine::RawMetaDataL
cfs.fnumber = idata->getFNumber ();
cfs.focalLen = idata->getFocalLen ();
cfs.focalLen35mm = idata->getFocalLen35mm ();
+ cfs.focusDist = idata->getFocusDist ();
cfs.iso = idata->getISOSpeed ();
cfs.expcomp = idata->expcompToString (idata->getExpComp(), false); // do not mask Zero expcomp
cfs.year = 1900 + idata->getDateTime().tm_year;
diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc
index 3822a4275..0ab09566e 100644
--- a/rtgui/toolpanelcoord.cc
+++ b/rtgui/toolpanelcoord.cc
@@ -356,6 +356,8 @@ void ToolPanelCoordinator::initImage (rtengine::StagedImageProcessor* ipc_, bool
}
icm->setRawMeta (raw, (const rtengine::ImageData*)pMetaData);
+ lensProf->setRawMeta (raw, pMetaData);
+
hlrecovery->setRaw (raw);
hasChanged = true;
}