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; }