diff --git a/CMakeLists.txt b/CMakeLists.txt index cf20be270..aff360083 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,6 +183,7 @@ if (WIN32) endif (WIN32) # you may need lcms v1.xx for older version : pkg_check_modules (LCMS REQUIRED lcms<=1.99) pkg_check_modules (LCMS REQUIRED lcms2) +pkg_check_modules (EXPAT REQUIRED expat>=2.0) pkg_check_modules (IPTCDATA REQUIRED libiptcdata) find_package (JPEG REQUIRED) find_package (PNG REQUIRED) diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 3e63fcb55..78a4e2d3d 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -960,6 +960,8 @@ TP_LABCURVE_SATURATION;Sättigung TP_LENSGEOM_AUTOCROP;Auto-Schneiden TP_LENSGEOM_FILL;Auto-Füllen TP_LENSGEOM_LABEL;Objektivkorrekturen +TP_LENSPROFILE_FILEDLGFILTERLCP;Linsen-Korrekturdateien +TP_LENSPROFILE_LABEL;Linsen-Korrekturprofil TP_LUMADENOISE_EDGETOLERANCE;Kantentoleranz TP_LUMADENOISE_LABEL;Luminanz-Rauschfilter TP_LUMADENOISE_RADIUS;Radius diff --git a/rtdata/languages/default b/rtdata/languages/default index ca3cb76c6..4ec201921 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -960,6 +960,8 @@ TP_LABCURVE_SATURATION;Saturation TP_LENSGEOM_AUTOCROP; Auto Crop TP_LENSGEOM_FILL;Auto Fill TP_LENSGEOM_LABEL;Lens / Geometry +TP_LENSPROFILE_FILEDLGFILTERLCP;Lens correction files +TP_LENSPROFILE_LABEL;Lens Correction Profile TP_LUMADENOISE_EDGETOLERANCE;Edge Tolerance TP_LUMADENOISE_LABEL;Luminance Noise Reduction TP_LUMADENOISE_RADIUS;Radius diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index b4f854481..cc2a96f11 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -1,9 +1,9 @@ include_directories (${EXTRA_INCDIR} ${GTHREAD_INCLUDE_DIRS} ${GOBJECT_INCLUDE_DIRS} ${GLIB2_INCLUDE_DIRS} - ${GLIBMM_INCLUDE_DIRS} ${IPTCDATA_INCLUDE_DIRS} ${LCMS_INCLUDE_DIRS} + ${GLIBMM_INCLUDE_DIRS} ${IPTCDATA_INCLUDE_DIRS} ${LCMS_INCLUDE_DIRS} ${EXPAT_INCLUDE_DIRS} ${GTKMM_INCLUDE_DIRS} ${GTK_INCLUDE_DIRS}) link_directories (${CMAKE_CURRENT_SOURCE_DIR}/../rtexif ${EXTRA_LIBDIR} ${GTHREAD_LIBRARY_DIRS} ${GOBJECT_LIBRARY_DIRS} ${GLIB2_LIBRARY_DIRS} ${GLIBMM_LIBRARY_DIRS} - ${IPTCDATA_LIBRARY_DIRS} ${LCMS_LIBRARY_DIRS}) + ${IPTCDATA_LIBRARY_DIRS} ${LCMS_LIBRARY_DIRS} ${EXPAT_LIBRARY_DIRS}) set (RTENGINESOURCEFILES safegtk.cc colortemp.cc curves.cc flatcurves.cc diagonalcurves.cc dcraw.cc iccstore.cc dfmanager.cc ffmanager.cc rawimage.cc image8.cc image16.cc imagefloat.cc imagedata.cc imageio.cc improcfun.cc init.cc dcrop.cc @@ -14,7 +14,7 @@ set (RTENGINESOURCEFILES safegtk.cc colortemp.cc curves.cc flatcurves.cc diagona jpeg_memsrc.cc jdatasrc.cc PF_correct_RT.cc dirpyrLab_denoise.cc dirpyrLab_equalizer.cc dirpyr_equalizer.cc - calc_distort.cc dcp.cc + calc_distort.cc lcp.cc dcp.cc klt/convolve.cc klt/error.cc klt/klt.cc klt/klt_util.cc klt/pnmio.cc klt/pyramid.cc klt/selectGoodFeatures.cc klt/storeFeatures.cc klt/trackFeatures.cc klt/writeFeatures.cc ) @@ -28,5 +28,5 @@ ENDIF (BUILD_SHARED_LIBS) set_target_properties (rtengine PROPERTIES COMPILE_FLAGS "${RTENGINE_CXX_FLAGS}") target_link_libraries (rtengine rtexif ${EXTRA_LIB} ${GOBJECT_LIBRARIES} ${GTHREAD_LIBRARIES} - ${GLIB2_LIBRARIES} ${GLIBMM_LIBRARIES} ${LCMS_LIBRARIES} ${IPTCDATA_LIBRARIES} + ${GLIB2_LIBRARIES} ${GLIBMM_LIBRARIES} ${LCMS_LIBRARIES} ${EXPAT_LIBRARIES} ${IPTCDATA_LIBRARIES} ${JPEG_LIBRARIES} ${PNG_LIBRARIES} ${TIFF_LIBRARIES} ${ZLIB_LIBRARIES}) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index ff7ed811d..12132db5b 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -111,7 +111,8 @@ void Crop::update (int todo) { if (needstransform && !transCrop) 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->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->getRotateDegree()); if (transCrop) baseCrop = transCrop; diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 0f34e06dc..528bee047 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -68,7 +68,7 @@ class ImageSource : public InitialImage { virtual ~ImageSource () {} virtual int load (Glib::ustring fname, bool batch = false) =0; - virtual void preprocess (const RAWParams &raw){}; + virtual void preprocess (const RAWParams &raw, const LensProfParams &lensProf, const CoarseTransformParams& coarse){}; virtual void demosaic (const RAWParams &raw){}; virtual void flushRawData (){}; virtual void flushRGB (){}; @@ -91,6 +91,7 @@ class ImageSource : public InitialImage { virtual void getFullSize (int& w, int& h, int tr = TR_NONE) {} virtual void getSize (int tran, PreviewProps pp, int& w, int& h) {} + virtual int getRotateDegree() const { return 0; } virtual ImageData* getImageData () =0; virtual void setProgressListener (ProgressListener* pl) {} diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index ecf170a45..d53361f7e 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -122,7 +122,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) { progress ("Applying white balance, color correction & sRGB conversion...",100*readyphase/numofphases); if ( todo & M_PREPROC) { - imgsrc->preprocess( rp ); + imgsrc->preprocess( rp, params.lensProf, params.coarse ); imgsrc->getRAWHistogram( histRedRaw, histGreenRaw, histBlueRaw ); } @@ -193,7 +193,7 @@ 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); + ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, imgsrc->getMetaData()->getFocalLen(), imgsrc->getRotateDegree()); readyphase++; @@ -579,7 +579,7 @@ void ImProcCoordinator::saveInputICCReference (const Glib::ustring& fname) { ppar.icm.input = "(none)"; Imagefloat* im = new Imagefloat (fW, fH); Image16* im16 = new Image16 (fW, fH); - imgsrc->preprocess( ppar.raw ); + imgsrc->preprocess( ppar.raw, ppar.lensProf, ppar.coarse ); imgsrc->demosaic(ppar.raw ); //imgsrc->getImage (imgsrc->getWB(), 0, im, pp, ppar.hlrecovery, ppar.icm, ppar.raw); ColorTemp currWB = ColorTemp (params.wb.temperature, params.wb.green, params.wb.method); diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 09dcf6f4e..07278834a 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -27,6 +27,7 @@ #include "coord2d.h" #include "labimage.h" #include "LUT.h" +#include "lcp.h" namespace rtengine { @@ -43,10 +44,10 @@ class ImProcFunctions { bool multiThread; float g; - void simpltransform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH); + 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); - void transformSep (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, 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 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); @@ -56,6 +57,7 @@ class ImProcFunctions { bool needsRotation (); bool needsPerspective (); bool needsVignetting (); + bool needsLCP (); // static cmsUInt8Number* Mempro = NULL; @@ -120,7 +122,8 @@ class ImProcFunctions { void MunsellLch (float lum, float hue, float chrom, float memChprov, float &correction, int zone);//jacques: Munsell correction 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); + void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, + double focalLen, int rawRotationDeg); 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 6cea52924..b0c5ba6d8 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -176,18 +176,24 @@ bool ImProcFunctions::transCoord (int W, int H, int x, int y, int w, int h, int& return result; } -void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH) { +void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, + double focalLen, int rawRotationDeg) { + LCPMapper *pLCPMap=NULL; + if (needsLCP() && focalLen>0) { + LCPProfile *pLCPProf=lcpStore->getProfile(params->lensProf.lcpFile); + if (pLCPProf) pLCPMap=new LCPMapper(pLCPProf, focalLen, false, original->width, original->height, params->coarse, rawRotationDeg); + } - if (!(needsCA() || needsDistortion() || needsRotation() || needsPerspective()) && needsVignetting()) + 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); + transformNonSep (original, transformed, cx, cy, sx, sy, oW, oH, pLCPMap); else - simpltransform (original, transformed, cx, cy, sx, sy, oW, oH); + simpltransform (original, transformed, cx, cy, sx, sy, oW, oH, pLCPMap); } else - transformSep (original, transformed, cx, cy, sx, sy, oW, oH); + transformSep (original, transformed, cx, cy, sx, sy, oW, oH, pLCPMap); } void calcVignettingParams(int oW, int oH, const VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul) @@ -209,6 +215,7 @@ void calcVignettingParams(int oW, int oH, const VignettingParams& vignetting, do mul = (1.0-v) / tanh(b); } +// Transform vignetting only void ImProcFunctions::vignetting (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH) { double vig_w2; @@ -234,7 +241,8 @@ void ImProcFunctions::vignetting (Imagefloat* original, Imagefloat* transformed, } #include "cubint.cc" -void ImProcFunctions::transformNonSep (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH) { +// 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; @@ -247,14 +255,13 @@ void ImProcFunctions::transformNonSep (Imagefloat* original, Imagefloat* transfo 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); - bool dovign = params->vignetting.amount != 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; @@ -273,8 +280,11 @@ void ImProcFunctions::transformNonSep (Imagefloat* original, Imagefloat* transfo #pragma omp parallel for if (multiThread) for (int y=0; yheight; y++) { for (int x=0; xwidth; x++) { - double x_d = ascale * (x + cx - w2); // centering x coord & scale - double y_d = ascale * (y + cy - h2); // centering y coord & scale + 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 @@ -291,14 +301,18 @@ void ImProcFunctions::transformNonSep (Imagefloat* original, Imagefloat* transfo double Dy = x_d * sint + y_d * cost; // distortion correction - double r = sqrt(Dx*Dx + Dy*Dy) / maxRadius; - double s = 1.0 - a + a * r ; + 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 = sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy); + + double r2 = needsVignetting() ? sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy) : 0; // de-center Dx += w2; @@ -313,7 +327,7 @@ void ImProcFunctions::transformNonSep (Imagefloat* original, Imagefloat* transfo // multiplier for vignetting correction double vignmul = 1.0; - if (dovign) + 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 @@ -338,9 +352,9 @@ void ImProcFunctions::transformNonSep (Imagefloat* original, Imagefloat* transfo } } +// 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) { - +void ImProcFunctions::transformSep (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; @@ -367,14 +381,13 @@ void ImProcFunctions::transformSep (Imagefloat* original, Imagefloat* transforme chtrans[2] = transformed->b; // 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); - bool dovign = params->vignetting.amount != 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; @@ -395,8 +408,11 @@ void ImProcFunctions::transformSep (Imagefloat* original, Imagefloat* transforme #pragma omp parallel for if (multiThread) for (int y=0; yheight; y++) { for (int x=0; xwidth; x++) { - double x_d = ascale * (x + cx - w2); // centering x coord & scale - double y_d = ascale * (y + cy - h2); // centering y coord & scale + 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 @@ -413,12 +429,15 @@ void ImProcFunctions::transformSep (Imagefloat* original, Imagefloat* transforme double Dyc = x_d * sint + y_d * cost; // distortion correction - double r = sqrt(Dxc*Dxc + Dyc*Dyc) / maxRadius; - double s = 1.0 - a + a * r ; + double s = 1; + if (needsDist) { + double r = sqrt(Dxc*Dxc + Dyc*Dyc) / maxRadius; // sqrt is slow + s = 1.0 - a + a * r ; + } double vig_Dx = vig_x_d * cost - vig_y_d * sint; double vig_Dy = vig_x_d * sint + vig_y_d * cost; - double r2 = sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy); + double r2 = needsVignetting() ? sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy) : 0; for (int c=0; c<3; c++) { @@ -438,7 +457,7 @@ void ImProcFunctions::transformSep (Imagefloat* original, Imagefloat* transforme // multiplier for vignetting correction double vignmul = 1.0; - if (dovign) + 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 @@ -459,8 +478,8 @@ void ImProcFunctions::transformSep (Imagefloat* original, Imagefloat* transforme } } -void ImProcFunctions::simpltransform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH) { - +// 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) { double w2 = (double) oW / 2.0 - 0.5; double h2 = (double) oH / 2.0 - 0.5; @@ -473,14 +492,13 @@ void ImProcFunctions::simpltransform (Imagefloat* original, Imagefloat* transfor 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); - bool dovign = params->vignetting.amount != 0; - // auxiliary variables for vertical perspective correction double vpdeg = params->perspective.vertical / 100.0 * 45.0; double vpalpha = (90 - vpdeg) / 180.0 * RT_PI; @@ -499,8 +517,11 @@ void ImProcFunctions::simpltransform (Imagefloat* original, Imagefloat* transfor #pragma omp parallel for if (multiThread) for (int y=0; yheight; y++) { for (int x=0; xwidth; x++) { - double y_d = ascale * (y + cy - h2); // centering y coord & scale - double x_d = ascale * (x + cx - w2); // centering x coord & scale + double x_d=x,y_d=y; + if (pLCPMap) 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 @@ -517,14 +538,17 @@ void ImProcFunctions::simpltransform (Imagefloat* original, Imagefloat* transfor double Dy = x_d * sint + y_d * cost; // distortion correction - double r = sqrt(Dx*Dx + Dy*Dy) / maxRadius; - double s = 1.0 - a + a * r ; + 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 = sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy); + double r2 = needsVignetting() ? sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy) : 0; // de-center Dx += w2; @@ -539,7 +563,7 @@ void ImProcFunctions::simpltransform (Imagefloat* original, Imagefloat* transfor // multiplier for vignetting correction double vignmul = 1.0; - if (dovign) + 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 @@ -605,8 +629,12 @@ bool ImProcFunctions::needsVignetting () { return params->vignetting.amount; } +bool ImProcFunctions::needsLCP () { + return params->lensProf.lcpFile.length()>0; +} + bool ImProcFunctions::needsTransform () { - return needsCA () || needsDistortion () || needsRotation () || needsPerspective () || needsVignetting (); + return needsCA () || needsDistortion () || needsRotation () || needsPerspective () || needsVignetting () || needsLCP(); } diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc new file mode 100644 index 000000000..d3db3ae57 --- /dev/null +++ b/rtengine/lcp.cc @@ -0,0 +1,389 @@ +/* +* This file is part of RawTherapee. +* +* 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 +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* RawTherapee is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with RawTherapee. If not, see . +*/ +#include + +#include "lcp.h" +#include "safegtk.h" +#include "iccmatrices.h" +#include "iccstore.h" +#include "rawimagesource.h" +#include "improcfun.h" +#include "rt_math.h" + +#ifdef WIN32 +#include +// for GCC32 +#ifndef _WIN32_IE +#define _WIN32_IE 0x0600 +#endif +#include +#endif + + +using namespace std; +using namespace rtengine; +using namespace rtexif; + + +LCPModelCommon::LCPModelCommon() { + focLenX=focLenY=-1; imgXCenter=imgYCenter=0.5; + for (int i=0;i<5;i++) param[i]=0; + scaleFac=1; +} + +bool LCPModelCommon::empty() const { + return focLenX<0 && focLenY<0; +} + +void LCPModelCommon::print() const { + printf("focLen %g/%g; imgCenter %g/%g; scale %g\n",focLenX,focLenY,imgXCenter,imgYCenter,scaleFac); + printf("param: %g/%g/%g/%g/%g\n",param[0],param[1],param[2],param[3],param[4]); +} + +LCPPersModel::LCPPersModel() { + focLen=focDist=aperture=0; +} + +void LCPPersModel::print() const { + printf("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture); + printf("Base:\n"); base.print(); + if (!chromRG.empty()) { printf("ChromRG:\n"); chromRG.print(); } + if (!chromG.empty()) { printf("ChromG:\n"); chromG.print(); } + if (!chromBG.empty()) { printf("ChromBG:\n"); chromBG.print(); } + if (!vignette.empty()) { printf("Vignette:\n"); vignette.print(); } + printf("\n"); +} + +// if !vignette then geometric +LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, bool vignette, int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg) +{ + if (pProf==NULL) return; + + pProf-> calcBasePerspectiveParams(focalLength, vignette, mc); + + // determine in what the image with the RAW landscape in comparison (calibration target) + int rot = (coarse.rotate+rawRotationDeg) % 360; + + swapXY = (rot==90 || rot==270); + mirrorX = (rot==90 || rot==180); + 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; + + if (swapXY) { + x0 = (mirrorX ? 1-mc.imgYCenter : mc.imgYCenter) * fullWidth; + y0 = (mirrorY ? 1-mc.imgXCenter : mc.imgXCenter) * fullHeight; + fx = mc.focLenY * Dmax; + fy = mc.focLenX * Dmax; + } else { + x0 = (mirrorX ? 1-mc.imgXCenter : mc.imgXCenter) * fullWidth; + y0 = (mirrorY ? 1-mc.imgYCenter : mc.imgYCenter) * fullHeight; + fx = mc.focLenX * Dmax; + fy = mc.focLenY * Dmax; + } +} + + +void LCPMapper::correctDistortion(double& x, double& y) const { + double xd=(x-x0)/fx, yd=(y-y0)/fy; + + double rsqr = xd*xd+yd*yd; + double rsqrrsqr = rsqr*rsqr; // speed + double commonFac = (mc.param[2]*rsqrrsqr*rsqr + mc.param[1]*rsqrrsqr + mc.param[0]*rsqr + 1.) + + 2 * (mc.param[3] * yd + mc.param[4] * xd); + + double xnew = xd * commonFac + mc.param[4] * rsqr; + double ynew = yd * commonFac + mc.param[3] * rsqr; + + x = xnew * fx + x0; + y = ynew * fy + y0; +} + +float LCPMapper::correctVignette(double x, double y) const { + double xd=(x-x0)/fx, yd=(y-y0)/fy; + + double rsqr = xd*xd+yd*yd; + double rsqrrsqr = rsqr*rsqr; // speed + double param0Sqr = mc.param[0]*mc.param[0]; + + return 1. - mc.param[0]*rsqr + (param0Sqr - mc.param[1]) *rsqrrsqr + - (param0Sqr*mc.param[0] - 2*mc.param[0]*mc.param[1] + mc.param[2]) *rsqrrsqr*rsqr + + (param0Sqr*param0Sqr + mc.param[1]*mc.param[1] + + 2*mc.param[0]*mc.param[2] - 3*mc.param[0]*mc.param[0]*mc.param[1]) *rsqrrsqr*rsqrrsqr; +} + + +LCPProfile::LCPProfile(Glib::ustring fname) { + const int BufferSize=8192; + char buf[BufferSize]; + + XML_Parser parser = XML_ParserCreate(NULL); + if (!parser) throw "Couldn't allocate memory for XML parser"; + + XML_SetElementHandler(parser, XmlStartHandler, XmlEndHandler); + XML_SetCharacterDataHandler(parser, XmlTextHandler); + XML_SetUserData(parser, (void *)this); + + + isFisheye=inCamProfiles=firstLIDone=inPerspect=false; + + FILE *pFile = safe_g_fopen(fname, "rb"); + + bool done; + do { + int bytesRead = (int)fread(buf, 1, BufferSize, pFile); + done=feof(pFile); + if (XML_Parse(parser, buf, bytesRead, done) == XML_STATUS_ERROR) + throw "Invalid XML in LCP file"; + } while (!done); + + XML_ParserFree(parser); +} + + +void LCPProfile::calcBasePerspectiveParams(float focalLength, bool vignette, LCPModelCommon& corr) { + // find the frames with the least distance, focal length wise + LCPPersModel *pLow=NULL, *pHigh=NULL; + + std::list::const_iterator it; + for (it=persModels.begin(); it!=persModels.end(); ++it) { + float f=(*it)->focLen; + + if ((!vignette && !(*it)->base.empty()) || (vignette && !(*it)->vignette.empty())) { + if (f <= focalLength && (pLow ==NULL || f > pLow->focLen)) pLow= (*it); + if (f >= focalLength && (pHigh==NULL || f < pHigh->focLen)) pHigh=(*it); + } + } + + if (!pLow) pLow=pHigh; if (!pHigh) pHigh=pLow; + + // average out the factors, linear interpolation + float facLow = 0, facHigh=0; + if (pLow && pHigh && pLow->focLen < pHigh->focLen) { + facLow = (pHigh->focLen-focalLength) / (pHigh->focLen-pLow->focLen); + facHigh = (focalLength-pLow->focLen) / (pHigh->focLen-pLow->focLen); + } else { + facLow=pHigh?0:1; facHigh=pHigh?1:0; + } + + LCPModelCommon& mLow =(vignette?pLow->vignette :pLow->base); + LCPModelCommon& mHigh=(vignette?pHigh->vignette:pHigh->base); + + 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; + + for (int i=0;i<5;i++) corr.param[i]= facLow * mLow.param[i] + facHigh * mHigh.param[i]; +} + +void LCPProfile::print() const { + printf("=== Profile %s\n", profileName.c_str()); + printf("RAW: %i; Fisheye: %i\n",isRaw,isFisheye); + std::list::const_iterator it; + for (it=persModels.begin(); it!=persModels.end(); ++it) (*it)->print(); +} + +void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, const char **attr) { + LCPProfile *pProf=static_cast(pLCPProfile); + + // clean up tagname + char* src=strrchr(el,':'); + if (src==NULL) src=const_cast(el); else src++; + + strcpy(pProf->lastTag,src); + + if (!strcmp("CameraProfiles",src)) pProf->inCamProfiles=true; + if (!pProf->inCamProfiles) return; + + if (!strcmp("li",src)) { + pProf->pCurPersModel=new LCPPersModel(); + pProf->pCurCommon=&pProf->pCurPersModel->base; // iterated to next tags within persModel + return; + } + + if (!strcmp("PerspectiveModel",src)) { + pProf->firstLIDone=true; pProf->inPerspect=true; + return; + } else if (!strcmp("FisheyeModel",src)) { + pProf->firstLIDone=true; pProf->inPerspect=true; + pProf->isFisheye=true; // just misses third param, and different path, rest is the same + return; + } + + // Move pointer to general section + if (pProf->inPerspect) { + if (!strcmp("ChromaticRedGreenModel",src)) + pProf->pCurCommon=&pProf->pCurPersModel->chromRG; + else if (!strcmp("ChromaticGreenModel",src)) + pProf->pCurCommon=&pProf->pCurPersModel->chromG; + else if (!strcmp("ChromaticBlueGreenModel",src)) + pProf->pCurCommon=&pProf->pCurPersModel->chromBG; + else if (!strcmp("VignetteModel",src)) + pProf->pCurCommon=&pProf->pCurPersModel->vignette; + } +} + +void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, int len) { + LCPProfile *pProf=static_cast(pLCPProfile); + + if (!pProf->inCamProfiles) return; + + // Check if it contains non-whitespaces (there are several calls to this for one tag unfortunately) + bool onlyWhiteSpace=true; + int i=0; + while (ilastTag; + + //printf("%s : %s\n",tag,raw); + + // Common data section + if (!pProf->firstLIDone) { + // Generic tags are the same for all + if (!strcmp("ProfileName",tag)) + pProf->profileName=Glib::ustring(raw); + else if (!strcmp("Model",tag)) + pProf->camera=Glib::ustring(raw); + else if (!strcmp("Lens",tag)) + pProf->lens=Glib::ustring(raw); + else if (!strcmp("CameraPrettyName",tag)) + pProf->cameraPrettyName=Glib::ustring(raw); + else if (!strcmp("LensPrettyName",tag)) + pProf->lensPrettyName=Glib::ustring(raw); + else if (!strcmp("CameraRawProfile",tag)) + pProf->isRaw=!strcmp("True",raw); + } + + // --- Now all floating points. Must replace local dot characters + struct lconv * lc=localeconv(); + + char* p=raw; + while (*p) { + if (*p=='.') *p=lc->decimal_point[0]; + p++; + } + + // 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("ApertureValue",tag)) + pProf->pCurPersModel->aperture=atof(raw); + + // Section depended + if (!strcmp("FocalLengthX",tag)) + pProf->pCurCommon->focLenX=atof(raw); + else if (!strcmp("FocalLengthY",tag)) + pProf->pCurCommon->focLenY=atof(raw); + else if (!strcmp("ImageXCenter",tag)) + pProf->pCurCommon->imgXCenter=atof(raw); + else if (!strcmp("ImageYCenter",tag)) + pProf->pCurCommon->imgYCenter=atof(raw); + else if (!strcmp("ScaleFactor",tag)) + pProf->pCurCommon->scaleFac=atof(raw); + else if (!strcmp("RadialDistortParam1",tag) || !strcmp("VignetteModelParam1",tag)) + pProf->pCurCommon->param[0]=atof(raw); + else if (!strcmp("RadialDistortParam2",tag) || !strcmp("VignetteModelParam2",tag)) + pProf->pCurCommon->param[1]=atof(raw); + else if (!strcmp("RadialDistortParam3",tag) || !strcmp("VignetteModelParam3",tag)) + pProf->pCurCommon->param[2]=atof(raw); + else if (!strcmp("TangentialDistortParam1",tag)) + pProf->pCurCommon->param[3]=atof(raw); + else if (!strcmp("TangentialDistortParam2",tag)) + pProf->pCurCommon->param[4]=atof(raw); +} + +void XMLCALL LCPProfile::XmlEndHandler(void *pLCPProfile, const char *el) { + LCPProfile *pProf=static_cast(pLCPProfile); + + if (strstr(el,":CameraProfiles")) pProf->inCamProfiles=false; + + if (!pProf->inCamProfiles) return; + + if (strstr(el,":PerspectiveModel") || strstr(el,":FisheyeModel")) + pProf->inPerspect=false; + else if (strstr(el, ":li")) { + pProf->persModels.push_back(pProf->pCurPersModel); + pProf->pCurPersModel=NULL; + } +} + +// Generates as singleton +LCPStore* LCPStore::getInstance() +{ + static LCPStore* instance_ = 0; + if ( instance_ == 0 ) + { + static Glib::Mutex smutex_; + Glib::Mutex::Lock lock(smutex_); + if ( instance_ == 0 ) + { + instance_ = new LCPStore(); + } + } + return instance_; +} + +LCPProfile* LCPStore::getProfile (Glib::ustring filename) { + if (filename.length()==0) return NULL; + + Glib::Mutex::Lock lock(mtx); + + std::map::iterator r = profileCache.find (filename); + if (r!=profileCache.end()) return r->second; + + // Add profile + profileCache[filename]=new LCPProfile(filename); + //profileCache[filename]->print(); + return profileCache[filename]; +} + + +bool LCPStore::isValidLCPFileName(Glib::ustring filename) const { + if (!safe_file_test (filename, Glib::FILE_TEST_EXISTS) || safe_file_test (filename, Glib::FILE_TEST_IS_DIR)) return false; + size_t pos=filename.find_last_of ('.'); + return pos>0 && !filename.casefold().compare (pos, 4, ".lcp"); +} + + +Glib::ustring LCPStore::getDefaultCommonDirectory() const { + Glib::ustring dir; + +#ifdef WIN32 + WCHAR pathW[MAX_PATH]={0}; char pathA[MAX_PATH]; + + if (SHGetSpecialFolderPathW(NULL,pathW,CSIDL_COMMON_APPDATA,false)) { + char pathA[MAX_PATH]; + WideCharToMultiByte(CP_UTF8,0,pathW,-1,pathA,MAX_PATH,0,0); + Glib::ustring fullDir=Glib::ustring(pathA)+Glib::ustring("\\Adobe\\CameraRaw\\LensProfiles\\1.0"); + if (safe_file_test(fullDir, Glib::FILE_TEST_IS_DIR)) dir=fullDir; + } +#else + printf("Sorry, default LCP directory are currently only configured on Windows\n"); +#endif + + return dir; +} \ No newline at end of file diff --git a/rtengine/lcp.h b/rtengine/lcp.h new file mode 100644 index 000000000..abd8a3483 --- /dev/null +++ b/rtengine/lcp.h @@ -0,0 +1,116 @@ +/* +* This file is part of RawTherapee. +* +* 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 +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* RawTherapee is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with RawTherapee. If not, see . +*/ + +#ifndef _LCP_ +#define _LCP_ + +#include "imagefloat.h" +#include +#include +#include +#include +#include + +namespace rtengine { + // Perspective model common data, also used for Vignette and Fisheye + class LCPModelCommon { + public: + float focLenX, focLenY, imgXCenter, imgYCenter; + float param[5]; // k1..k5 + float scaleFac; + + LCPModelCommon(); + bool empty() const; // is it empty? + void print() const; // printf all values + }; + + class LCPPersModel { + public: + float focLen, focDist, aperture; // this is what it refers to + + LCPModelCommon base; // base perspective correction + LCPModelCommon chromRG, chromG, chromBG; // red/green, green, blue/green (may be empty) + LCPModelCommon vignette; // vignette (may be empty) + + LCPPersModel(); + void print() const; + }; + + + class LCPProfile { + // Temporary data for parsing + bool inCamProfiles,firstLIDone,inPerspect; + char lastTag[256]; + LCPPersModel* pCurPersModel; + LCPModelCommon* pCurCommon; + + static void XMLCALL XmlStartHandler(void *pLCPProfile, const char *el, const char **attr); + static void XMLCALL XmlTextHandler (void *pLCPProfile, const XML_Char *s, int len); + static void XMLCALL XmlEndHandler (void *pLCPProfile, const char *el); + + public: + // Common data + Glib::ustring profileName, lensPrettyName, cameraPrettyName, lens, camera; // lens/camera(=model) can be auto-matched with DNG + bool isRaw,isFisheye; + + // The correction frames + std::list persModels; + + LCPProfile(Glib::ustring fname); + + void calcBasePerspectiveParams(float focalLength, bool vignette, LCPModelCommon& corr); // Interpolates between the persModels frames + + void print() const; + }; + + class LCPStore { + Glib::Mutex mtx; + + + // Maps file name to profile as cache + std::map profileCache; + + public: + Glib::ustring getDefaultCommonDirectory() const; + bool isValidLCPFileName(Glib::ustring filename) const; + LCPProfile* getProfile(Glib::ustring filename); + + static LCPStore* getInstance(); + }; + +#define lcpStore LCPStore::getInstance() + + + // Once precalculated class to correct a point + class LCPMapper { + double x0,y0,fx,fy; + LCPModelCommon mc; + bool swapXY; + bool mirrorX, mirrorY; + + public: + // precalculates the mapper. + LCPMapper(LCPProfile* pProf, float focalLength, bool vignette, int fullWidth, int fullHeight, + const CoarseTransformParams& coarse, int rawRotationDeg); + + void correctDistortion(double& x, double& y) const; // MUST be the first stage + float correctVignette (double x, double y) const; // MUST be in RAW + }; +} +#endif diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 20c4c1539..d4ef5b198 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -106,7 +106,7 @@ enum ProcEvent { EvProfileChangeNotification=81, EvSHHighQuality=82, EvPerspCorr=83, - EvEqualizer=84, // obsolete + EvLCPFile=84, EvEqlEnabled=85,// obsolete EvIDNEnabled=86, EvIDNThresh=87, diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index bd49c6346..9a527c0c3 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -259,7 +259,6 @@ void ProcParams::setDefaults () { rotate.degree = 0; distortion.amount = 0; - distortion.uselensfun = false; perspective.horizontal = 0; perspective.vertical = 0; @@ -540,7 +539,9 @@ int ProcParams::save (Glib::ustring fname, Glib::ustring fname2, ParamsEdited* p // save distortion if (!pedited || pedited->distortion.amount) keyFile.set_double ("Distortion", "Amount", distortion.amount); - if (!pedited || pedited->distortion.uselensfun) keyFile.set_boolean ("Distortion", "UseLensFun", distortion.uselensfun); + + // lens profile + if (!pedited || pedited->lensProf.lcpFile) keyFile.set_string ("LensProfile", "LCPFile", lensProf.lcpFile); // save perspective correction if (!pedited || pedited->perspective.horizontal) keyFile.set_integer ("Perspective", "Horizontal", perspective.horizontal); @@ -939,7 +940,11 @@ if (keyFile.has_group ("Common Properties for Transformations")) { // load distortion if (keyFile.has_group ("Distortion")) { if (keyFile.has_key ("Distortion", "Amount")) { distortion.amount = keyFile.get_double ("Distortion", "Amount"); if (pedited) pedited->distortion.amount = true; } - if (keyFile.has_key ("Distortion", "UseLensFun")) { distortion.uselensfun = keyFile.get_boolean ("Distortion", "UseLensFun"); if (pedited) pedited->distortion.uselensfun = true; } +} + + // 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; } } // load perspective correction @@ -1234,8 +1239,8 @@ bool ProcParams::operator== (const ProcParams& other) { && coarse.vflip == other.coarse.vflip && rotate.degree == other.rotate.degree && commonTrans.autofill == other.commonTrans.autofill - && distortion.uselensfun == other.distortion.uselensfun && distortion.amount == other.distortion.amount + && lensProf.lcpFile == other.lensProf.lcpFile && 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 ec0025d73..44c90226f 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -326,10 +326,16 @@ class RotateParams { class DistortionParams { public: - bool uselensfun; double amount; }; +// Lens profile correction parameters +class LensProfParams { + +public: + Glib::ustring lcpFile; +}; + /** * Parameters of the perspective correction */ @@ -527,6 +533,7 @@ class ProcParams { CommonTransformParams commonTrans; ///< Common transformation parameters (autofill) RotateParams rotate; ///< Rotation parameters DistortionParams distortion; ///< Lens distortion correction parameters + LensProfParams lensProf; ///< Lens correction profile parameters PerspectiveParams perspective; ///< Perspective correction parameters CACorrParams cacorrection; ///< Lens c/a correction parameters VignettingParams vignetting; ///< Lens vignetting correction parameters diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 4c1dc0dba..7843eae23 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -953,7 +953,7 @@ int RawImageSource::load (Glib::ustring fname, bool batch) { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void RawImageSource::preprocess (const RAWParams &raw) +void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &lensProf, const CoarseTransformParams& coarse) { MyTime t1,t2; t1.set(); @@ -979,7 +979,9 @@ void RawImageSource::preprocess (const RAWParams &raw) } else { rif = ffm.searchFlatField( idata->getMake(), idata->getModel(),idata->getLens(),idata->getFocalLen(), idata->getFNumber(), idata->getDateTimeAsTS()); } - if( rif && settings->verbose) { + + bool hasFlatField = (rif!=NULL); + if( hasFlatField && settings->verbose) { printf( "Flat Field Correction:%s\n",rif->get_filename().c_str()); } copyOriginalPixels(raw, ri, rid, rif); @@ -1012,6 +1014,22 @@ void RawImageSource::preprocess (const RAWParams &raw) } } + // Correct vignetting of lens profile + if (!hasFlatField) { + LCPProfile *pLCPProf=lcpStore->getProfile(lensProf.lcpFile); + + if (pLCPProf) { + LCPMapper *pLCPMap=new LCPMapper(pLCPProf, idata->getFocalLen(), true, W, H, coarse, ri->get_rotateDegree()); + + #pragma omp parallel + for (int y=0; ycorrectVignette(x,y); + } + } + } + } + scaleColors( 0,0, W, H, raw);//+ + raw parameters for black level(raw.blackxx) defGain = 0.0;//log(initialGain) / log(2.0); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 5e49d15d4..63cb5720b 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -135,7 +135,7 @@ class RawImageSource : public ImageSource { ~RawImageSource (); int load (Glib::ustring fname, bool batch = false); - void preprocess (const RAWParams &raw); + void preprocess (const RAWParams &raw, const LensProfParams &lensProf, const CoarseTransformParams& coarse); void demosaic (const RAWParams &raw); void flushRawData (); void flushRGB (); @@ -160,6 +160,7 @@ class RawImageSource : public ImageSource { void getFullSize (int& w, int& h, int tr = TR_NONE); void getSize (int tran, PreviewProps pp, int& w, int& h); + int getRotateDegree() const { return ri->get_rotateDegree(); } ImageData* getImageData () { return idata; } void setProgressListener (ProgressListener* pl) { plistener = pl; } diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 1a060b977..fbc01e7e8 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -104,7 +104,7 @@ RESIZE, // EvResizeEnabled ALL, // EvProfileChangeNotification RETINEX, // EvShrHighQuality TRANSFORM, // EvPerspCorr -0, // EvEqualizer: obsolete +DARKFRAME, // EvLCPFile 0, // EvEqlEnabled:obsolete IMPULSEDENOISE, // EvIDNEnabled, IMPULSEDENOISE, // EvIDNThresh, diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 834d09e76..4dd5cc1f3 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -576,7 +576,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& myscale) { +IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rheight, TypeInterpolation interp, std::string camName, double focalLen, double& myscale) { // compute WB multipliers ColorTemp currWB = ColorTemp (params.wb.temperature, params.wb.green, params.wb.method); @@ -698,7 +698,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); + ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, focalLen, 0); // Raw rotate degree not detectable here delete baseImg; baseImg = trImg; } diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h index da2a04cde..fc6d080fa 100644 --- a/rtengine/rtthumbnail.h +++ b/rtengine/rtthumbnail.h @@ -70,7 +70,7 @@ namespace rtengine { static void cleanupGamma (); void init (); - IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, std::string camName, double& scale); + IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, std::string camName, double focalLen, 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 460379e61..0ea6a7f63 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -91,7 +91,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p ImProcFunctions ipf (¶ms, true); PreviewProps pp (0, 0, fw, fh, 1); - imgsrc->preprocess( params.raw); + imgsrc->preprocess( params.raw, params.lensProf, params.coarse); if (pl) pl->setProgress (0.20); imgsrc->demosaic( params.raw); if (pl) pl->setProgress (0.30); @@ -115,7 +115,7 @@ 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); + ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, imgsrc->getMetaData()->getFocalLen(), imgsrc->getRotateDegree()); delete baseImg; baseImg = trImg; } diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index a613cb47c..ec6d3afa1 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -8,7 +8,7 @@ set (BASESOURCEFILES coarsepanel.cc cacorrection.cc hlrec.cc chmixer.cc resize.cc icmpanel.cc crop.cc shadowshighlights.cc impulsedenoise.cc dirpyrdenoise.cc epd.cc - exifpanel.cc toolpanel.cc + exifpanel.cc toolpanel.cc lensprofile.cc sharpening.cc vibrance.cc rgbcurves.cc whitebalance.cc vignetting.cc rotate.cc distortion.cc crophandler.cc dirbrowser.cc @@ -40,9 +40,9 @@ if (WIN32) #set_target_properties (rth PROPERTIES LINK_FLAGS "-mwindows") else (WIN32) include_directories (${EXTRA_INCDIR} ${GLIB2_INCLUDE_DIRS} ${GLIBMM_INCLUDE_DIRS} - ${GTK_INCLUDE_DIRS} ${GTKMM_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${GIOMM_INCLUDE_DIRS} ${IPTCDATA_INCLUDE_DIRS} ${LCMS_INCLUDE_DIRS} ${GTHREAD_INCLUDE_DIRS} ${GOBJECT_INCLUDE_DIRS} ) + ${GTK_INCLUDE_DIRS} ${GTKMM_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${GIOMM_INCLUDE_DIRS} ${IPTCDATA_INCLUDE_DIRS} ${LCMS_INCLUDE_DIRS} ${EXPAT_INCLUDE_DIRS} ${GTHREAD_INCLUDE_DIRS} ${GOBJECT_INCLUDE_DIRS} ) link_directories (${EXTRA_LIBDIR} ${GLIB2_LIBRARY_DIRS} ${GLIBMM_LIBRARY_DIRS} - ${GTK_LIBRARY_DIRS} ${GTKMM_LIBRARY_DIRS} ${GIO_LIBRARY_DIRS} ${GIOMM_LIBRARY_DIRS} ${IPTCDATA_LIBRARY_DIRS} ${LCMS_LIBRARY_DIRS} ${GTHREAD_LIBRARY_DIRS} ${GOBJECT_LIBRARY_DIRS}) + ${GTK_LIBRARY_DIRS} ${GTKMM_LIBRARY_DIRS} ${GIO_LIBRARY_DIRS} ${GIOMM_LIBRARY_DIRS} ${IPTCDATA_LIBRARY_DIRS} ${LCMS_LIBRARY_DIRS} ${EXPAT_LIBRARY_DIRS} ${GTHREAD_LIBRARY_DIRS} ${GOBJECT_LIBRARY_DIRS}) endif (WIN32) # create config.h which defines where data are stored configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/config.h) @@ -53,6 +53,6 @@ set_target_properties (rth PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS}" OUTPUT_ #target_link_libraries (rth rtengine ${JPEG_LIBRARIES} ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${TIFF_LIBRARIES} ${EXTRA_LIB} ${GOBJECT_LIBRARIES} ${GTHREAD_LIBRARIES} # ${GLIB2_LIBRARIES} ${GLIBMM_LIBRARIES} ${GTK_LIBRARIES} ${GTKMM_LIBRARIES} ${GIO_LIBRARIES} ${GIOMM_LIBRARIES} ${LCMS_LIBRARIES} ${IPTCDATA_LIBRARIES}) target_link_libraries (rth rtengine ${JPEG_LIBRARIES} ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${TIFF_LIBRARIES} ${GOBJECT_LIBRARIES} ${GTHREAD_LIBRARIES} - ${GLIB2_LIBRARIES} ${GLIBMM_LIBRARIES} ${GTK_LIBRARIES} ${GTKMM_LIBRARIES} ${GIO_LIBRARIES} ${GIOMM_LIBRARIES} ${LCMS_LIBRARIES} ${IPTCDATA_LIBRARIES} ${EXTRA_LIB_RTGUI}) + ${GLIB2_LIBRARIES} ${GLIBMM_LIBRARIES} ${GTK_LIBRARIES} ${GTKMM_LIBRARIES} ${GIO_LIBRARIES} ${GIOMM_LIBRARIES} ${LCMS_LIBRARIES} ${EXPAT_LIBRARIES} ${IPTCDATA_LIBRARIES} ${EXTRA_LIB_RTGUI}) install (TARGETS rth DESTINATION ${BINDIR}) diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 02be0b244..49478aafc 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -134,8 +134,8 @@ void ParamsEdited::set (bool v) { coarse.vflip = v; commonTrans.autofill = v; rotate.degree = v; - distortion.uselensfun = v; distortion.amount = v; + lensProf.lcpFile = v; perspective.horizontal = v; perspective.vertical = v; cacorrection.red = v; @@ -327,8 +327,8 @@ void ParamsEdited::initFrom (const std::vector coarse.vflip = coarse.vflip && p.coarse.vflip == other.coarse.vflip; commonTrans.autofill = commonTrans.autofill && p.commonTrans.autofill == other.commonTrans.autofill; rotate.degree = rotate.degree && p.rotate.degree == other.rotate.degree; - distortion.uselensfun = distortion.uselensfun && p.distortion.uselensfun == other.distortion.uselensfun; distortion.amount = distortion.amount && p.distortion.amount == other.distortion.amount; + lensProf.lcpFile = lensProf.lcpFile && p.lensProf.lcpFile == other.lensProf.lcpFile; 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; @@ -519,8 +519,8 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten if (coarse.vflip) toEdit.coarse.vflip = mods.coarse.vflip ? !toEdit.coarse.vflip : toEdit.coarse.vflip; if (commonTrans.autofill) toEdit.commonTrans.autofill = mods.commonTrans.autofill; if (rotate.degree) toEdit.rotate.degree = dontforceSet && options.baBehav[ADDSET_ROTATE_DEGREE] ? toEdit.rotate.degree + mods.rotate.degree : mods.rotate.degree; - if (distortion.uselensfun) toEdit.distortion.uselensfun = mods.distortion.uselensfun; 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 (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 38a70b552..7385284ce 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -243,10 +243,14 @@ class RotateParamsEdited { class DistortionParamsEdited { public: - bool uselensfun; bool amount; }; +class LensProfParamsEdited { + public: + bool lcpFile; +}; + class PerspectiveParamsEdited { public: @@ -386,6 +390,7 @@ class ParamsEdited { CommonTransformParamsEdited commonTrans; RotateParamsEdited rotate; DistortionParamsEdited distortion; + LensProfParamsEdited lensProf; PerspectiveParamsEdited perspective; CACorrParamsEdited cacorrection; VignettingParamsEdited vignetting; diff --git a/rtgui/partialpastedlg.cc b/rtgui/partialpastedlg.cc index a30837ffa..28e6fdb60 100644 --- a/rtgui/partialpastedlg.cc +++ b/rtgui/partialpastedlg.cc @@ -70,6 +70,7 @@ PartialPasteDlg::PartialPasteDlg (Glib::ustring title) { distortion = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_DISTORTION"))); cacorr = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_CACORRECTION"))); vignetting = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_VIGNETTING"))); + lcp = Gtk::manage (new Gtk::CheckButton (M("TP_LENSPROFILE_LABEL"))); // options in composition: coarserot = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_COARSETRANS"))); @@ -149,6 +150,7 @@ PartialPasteDlg::PartialPasteDlg (Glib::ustring title) { vboxes[3]->pack_start (*distortion, Gtk::PACK_SHRINK, 2); vboxes[3]->pack_start (*cacorr, Gtk::PACK_SHRINK, 2); vboxes[3]->pack_start (*vignetting, Gtk::PACK_SHRINK, 2); + vboxes[3]->pack_start (*lcp, Gtk::PACK_SHRINK, 2); vboxes[4]->pack_start (*composition, Gtk::PACK_SHRINK, 2); vboxes[4]->pack_start (*hseps[4], Gtk::PACK_SHRINK, 2); @@ -260,6 +262,7 @@ PartialPasteDlg::PartialPasteDlg (Glib::ustring title) { distortionConn = distortion->signal_toggled().connect (sigc::bind (sigc::mem_fun(*lens, &Gtk::CheckButton::set_inconsistent), true)); cacorrConn = cacorr->signal_toggled().connect (sigc::bind (sigc::mem_fun(*lens, &Gtk::CheckButton::set_inconsistent), true)); vignettingConn = vignetting->signal_toggled().connect (sigc::bind (sigc::mem_fun(*lens, &Gtk::CheckButton::set_inconsistent), true)); + lcpConn = lcp->signal_toggled().connect (sigc::bind (sigc::mem_fun(*lens, &Gtk::CheckButton::set_inconsistent), true)); coarserotConn = coarserot->signal_toggled().connect (sigc::bind (sigc::mem_fun(*composition, &Gtk::CheckButton::set_inconsistent), true)); finerotConn = finerot->signal_toggled().connect (sigc::bind (sigc::mem_fun(*composition, &Gtk::CheckButton::set_inconsistent), true)); @@ -495,16 +498,19 @@ void PartialPasteDlg::lensToggled () { distortionConn.block (true); cacorrConn.block (true); vignettingConn.block (true); + lcpConn.block (true); lens->set_inconsistent (false); distortion->set_active (lens->get_active ()); cacorr->set_active (lens->get_active ()); vignetting->set_active (lens->get_active ()); + lcp->set_active (lens->get_active ()); distortionConn.block (false); cacorrConn.block (false); vignettingConn.block (false); + lcpConn.block (false); } void PartialPasteDlg::compositionToggled () { @@ -600,6 +606,7 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param if (!distortion->get_active ()) filterPE.distortion = falsePE.distortion; if (!cacorr->get_active ()) filterPE.cacorrection = falsePE.cacorrection; if (!vignetting->get_active ()) filterPE.vignetting = falsePE.vignetting; + if (!lcp->get_active ()) filterPE.lensProf = falsePE.lensProf; if (!coarserot->get_active ()) filterPE.coarse = falsePE.coarse; if (!finerot->get_active ()) filterPE.rotate = falsePE.rotate; diff --git a/rtgui/partialpastedlg.h b/rtgui/partialpastedlg.h index 21f0c70cd..56d4b88a1 100644 --- a/rtgui/partialpastedlg.h +++ b/rtgui/partialpastedlg.h @@ -66,6 +66,7 @@ class PartialPasteDlg : public Gtk::Dialog { Gtk::CheckButton* distortion; Gtk::CheckButton* cacorr; Gtk::CheckButton* vignetting; + Gtk::CheckButton* lcp; // options in composition: Gtk::CheckButton* coarserot; @@ -109,7 +110,7 @@ class PartialPasteDlg : public Gtk::Dialog { sigc::connection wbConn, exposureConn, hlrecConn, shConn, labcurveConn; sigc::connection sharpenConn, gradsharpenConn, microcontrastConn, impdenConn, dirpyrdenConn, waveqConn, defringeConn, epdConn, dirpyreqConn; sigc::connection vibranceConn, chmixerConn, hsveqConn, rgbcurvesConn; - sigc::connection distortionConn, cacorrConn, vignettingConn; + sigc::connection distortionConn, cacorrConn, vignettingConn, lcpConn; sigc::connection coarserotConn, finerotConn, cropConn, resizeConn, perspectiveConn, commonTransConn; sigc::connection exifchConn, iptcConn, icmConn, gamcsconn; sigc::connection df_fileConn, df_AutoSelectConn, ff_fileConn, ff_AutoSelectConn, ff_BlurRadiusConn, ff_BlurTypeConn; diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 7219df3fc..ca3b7ed26 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -468,7 +468,7 @@ rtengine::IImage8* Thumbnail::processThumbImage (const rtengine::procparams::Pro else { // Full thumbnail: apply profile - image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, scale ); + image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, cfs.focalLen, scale ); } tpp->getDimensions(lastW,lastH,lastScale); @@ -493,7 +493,7 @@ rtengine::IImage8* Thumbnail::upgradeThumbImage (const rtengine::procparams::Pro return 0; } - rtengine::IImage8* image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, scale ); + rtengine::IImage8* image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, cfs.focalLen, scale ); tpp->getDimensions(lastW,lastH,lastScale); delete tpp; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 7540316a1..8d4364580 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -52,6 +52,7 @@ ToolPanelCoordinator::ToolPanelCoordinator () : ipc(NULL) { lcurve = Gtk::manage (new LCurve ()); rgbcurves = Gtk::manage (new RGBCurves ()); lensgeom = Gtk::manage (new LensGeometry ()); + lensProf = Gtk::manage (new LensProfilePanel ()); distortion = Gtk::manage (new Distortion ()); rotate = Gtk::manage (new Rotate ()); vibrance = Gtk::manage (new Vibrance ()); @@ -97,6 +98,7 @@ ToolPanelCoordinator::ToolPanelCoordinator () : ipc(NULL) { addPanel (transformPanel, lensgeom, M("TP_LENSGEOM_LABEL")); toolPanels.push_back (lensgeom); addPanel (lensgeom->getPackBox(), rotate, M("TP_ROTATE_LABEL")); toolPanels.push_back (rotate); addPanel (lensgeom->getPackBox(), perspective, M("TP_PERSPECTIVE_LABEL")); toolPanels.push_back (perspective); + addPanel (lensgeom->getPackBox(), lensProf, M("TP_LENSPROFILE_LABEL")); toolPanels.push_back (lensProf); addPanel (lensgeom->getPackBox(), distortion, M("TP_DISTORTION_LABEL")); toolPanels.push_back (distortion); addPanel (lensgeom->getPackBox(), cacorrection, M("TP_CACORRECTION_LABEL")); toolPanels.push_back (cacorrection); addPanel (lensgeom->getPackBox(), vignetting, M("TP_VIGNETTING_LABEL")); toolPanels.push_back (vignetting); diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 7d51128e5..e3ed8c263 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -45,6 +45,7 @@ #include "chmixer.h" #include "hlrec.h" #include "cacorrection.h" +#include "lensprofile.h" #include "distortion.h" #include "perspective.h" #include "rotate.h" @@ -83,6 +84,7 @@ class ToolPanelCoordinator : public ToolPanelListener, WhiteBalance* whitebalance; Vignetting* vignetting; LensGeometry* lensgeom; + LensProfilePanel* lensProf; Rotate* rotate; Distortion* distortion; PerspCorrection* perspective;