From f409f1333ec9e29cb0c9d4cbc50383e8bbac7f7e Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 22 Aug 2017 01:21:09 +0200 Subject: [PATCH 01/52] Speedup for green equilibration --- rtengine/green_equil_RT.cc | 183 ++++++++++++++++++++++--------------- rtengine/rawimagesource.cc | 51 ++++++----- 2 files changed, 136 insertions(+), 98 deletions(-) diff --git a/rtengine/green_equil_RT.cc b/rtengine/green_equil_RT.cc index 8b1136359..53943e9a8 100644 --- a/rtengine/green_equil_RT.cc +++ b/rtengine/green_equil_RT.cc @@ -30,6 +30,8 @@ #include "rt_math.h" #include "rawimagesource.h" +#include "opthelper.h" + namespace rtengine { @@ -42,15 +44,25 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData) int height = H, width = W; // local variables - float** rawptr = rawData; - array2D cfa (width, height, rawptr); - //array2D checker (width,height,ARRAY2D_CLEAR_DATA); + array2D cfa(width / 2 + (width & 1), height); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + for(int i = 0; i < height; ++i) { + int j = (FC(i,0) & 1) ^ 1; +#ifdef __SSE2__ + for(; j < width - 7; j += 8) { + STVFU(cfa[i][j>>1], LC2VFU(rawData[i][j])); + } +#endif + for(; j < width; j += 2) { + cfa[i][j>>1] = rawData[i][j]; + } + } - //int verbose=1; - - static const float eps = 1.0; //tolerance to avoid dividing by zero - + constexpr float eps = 1.f; //tolerance to avoid dividing by zero + const float thresh6 = 6 * thresh; // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // Fill G interpolated values with border interpolation and input values @@ -59,93 +71,116 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData) //int counter, vtest; //The green equilibration algorithm starts here - /* - #ifdef _OPENMP - #pragma omp parallel for - #endif - for (int rr=1; rr < height-1; rr++) - for (int cc=3-(FC(rr,2)&1); cc < width-2; cc+=2) { - - float pcorr = (cfa[rr+1][cc+1]-cfa[rr][cc])*(cfa[rr-1][cc-1]-cfa[rr][cc]); - float mcorr = (cfa[rr-1][cc+1]-cfa[rr][cc])*(cfa[rr+1][cc-1]-cfa[rr][cc]); - - if (pcorr>0 && mcorr>0) {checker[rr][cc]=1;} else {checker[rr][cc]=0;} - - checker[rr][cc]=1;//test what happens if we always interpolate - } - - counter=vtest=0; - */ //now smooth the cfa data #ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) + #pragma omp parallel +#endif +{ +#ifdef __SSE2__ + vfloat zd5v = F2V(0.5f); + vfloat onev = F2V(1.f); + vfloat threshv = F2V(thresh); + vfloat thresh6v = F2V(thresh6); + vfloat epsv = F2V(eps); +#endif +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) #endif - for (int rr = 4; rr < height - 4; rr++) - for (int cc = 5 - (FC(rr, 2) & 1); cc < width - 6; cc += 2) { - //if (checker[rr][cc]) { - //%%%%%%%%%%%%%%%%%%%%%% - //neighbor checking code from Manuel Llorens Garcia - float o1_1 = cfa[(rr - 1)][cc - 1]; - float o1_2 = cfa[(rr - 1)][cc + 1]; - float o1_3 = cfa[(rr + 1)][cc - 1]; - float o1_4 = cfa[(rr + 1)][cc + 1]; - float o2_1 = cfa[(rr - 2)][cc]; - float o2_2 = cfa[(rr + 2)][cc]; - float o2_3 = cfa[(rr)][cc - 2]; - float o2_4 = cfa[(rr)][cc + 2]; + for (int rr = 4; rr < height - 4; rr++) { + int cc = 5 - (FC(rr, 2) & 1); +#ifdef __SSE2__ + for (; cc < width - 12; cc += 8) { + //neighbour checking code from Manuel Llorens Garcia + vfloat o1_1 = LVFU(cfa[rr - 1][(cc - 1)>>1]); + vfloat o1_2 = LVFU(cfa[rr - 1][(cc + 1)>>1]); + vfloat o1_3 = LVFU(cfa[rr + 1][(cc - 1)>>1]); + vfloat o1_4 = LVFU(cfa[rr + 1][(cc + 1)>>1]); + vfloat o2_1 = LVFU(cfa[rr - 2][cc>>1]); + vfloat o2_2 = LVFU(cfa[rr + 2][cc>>1]); + vfloat o2_3 = LVFU(cfa[rr][(cc - 2)>>1]); + vfloat o2_4 = LVFU(cfa[rr][(cc + 2)>>1]); - float d1 = (o1_1 + o1_2 + o1_3 + o1_4) * 0.25f; - float d2 = (o2_1 + o2_2 + o2_3 + o2_4) * 0.25f; + vfloat d1 = (o1_1 + o1_2 + o1_3 + o1_4); + vfloat d2 = (o2_1 + o2_2 + o2_3 + o2_4); - float c1 = (fabs(o1_1 - o1_2) + fabs(o1_1 - o1_3) + fabs(o1_1 - o1_4) + fabs(o1_2 - o1_3) + fabs(o1_3 - o1_4) + fabs(o1_2 - o1_4)) / 6.f; - float c2 = (fabs(o2_1 - o2_2) + fabs(o2_1 - o2_3) + fabs(o2_1 - o2_4) + fabs(o2_2 - o2_3) + fabs(o2_3 - o2_4) + fabs(o2_2 - o2_4)) / 6.f; - //%%%%%%%%%%%%%%%%%%%%%% + vfloat c1 = (vabsf(o1_1 - o1_2) + vabsf(o1_1 - o1_3) + vabsf(o1_1 - o1_4) + vabsf(o1_2 - o1_3) + vabsf(o1_3 - o1_4) + vabsf(o1_2 - o1_4)); + vfloat c2 = (vabsf(o2_1 - o2_2) + vabsf(o2_1 - o2_3) + vabsf(o2_1 - o2_4) + vabsf(o2_2 - o2_3) + vabsf(o2_3 - o2_4) + vabsf(o2_2 - o2_4)); - //vote1=(checker[rr-2][cc]+checker[rr][cc-2]+checker[rr][cc+2]+checker[rr+2][cc]); - //vote2=(checker[rr+1][cc-1]+checker[rr+1][cc+1]+checker[rr-1][cc-1]+checker[rr-1][cc+1]); - //if ((vote1==0 || vote2==0) && (c1+c2)<2*thresh*fabs(d1-d2)) vtest++; - //if (vote1>0 && vote2>0 && (c1+c2)<4*thresh*fabs(d1-d2)) { - if ((c1 + c2) < 4 * thresh * fabs(d1 - d2)) { + vmask mask1 = vmaskf_lt(c1 + c2, thresh6v * vabsf(d1 - d2)); + if(_mm_movemask_ps((vfloat)mask1)) { // if for any of the 4 pixels the condition is true, do the maths for all 4 pixels and mask the unused out at the end //pixel interpolation - float gin = cfa[rr][cc]; + vfloat gin = LVFU(cfa[rr][cc>>1]); - float gse = (cfa[rr + 1][cc + 1]) + 0.5f * (cfa[rr][cc] - cfa[rr + 2][cc + 2]); - float gnw = (cfa[rr - 1][cc - 1]) + 0.5f * (cfa[rr][cc] - cfa[rr - 2][cc - 2]); - float gne = (cfa[rr - 1][cc + 1]) + 0.5f * (cfa[rr][cc] - cfa[rr - 2][cc + 2]); - float gsw = (cfa[rr + 1][cc - 1]) + 0.5f * (cfa[rr][cc] - cfa[rr + 2][cc - 2]); + vfloat gmp2p2 = gin - LVFU(cfa[rr + 2][(cc + 2)>>1]); + vfloat gmm2m2 = gin - LVFU(cfa[rr - 2][(cc - 2)>>1]); + vfloat gmm2p2 = gin - LVFU(cfa[rr - 2][(cc + 2)>>1]); + vfloat gmp2m2 = gin - LVFU(cfa[rr + 2][(cc - 2)>>1]); + vfloat gse = o1_4 + zd5v * gmp2p2; + vfloat gnw = o1_1 + zd5v * gmm2m2; + vfloat gne = o1_2 + zd5v * gmm2p2; + vfloat gsw = o1_3 + zd5v * gmp2m2; + vfloat wtse = onev / (epsv + SQRV(gmp2p2) + SQRV(LVFU(cfa[rr + 3][(cc + 3)>>1]) - o1_4)); + vfloat wtnw = onev / (epsv + SQRV(gmm2m2) + SQRV(LVFU(cfa[rr - 3][(cc - 3)>>1]) - o1_1)); + vfloat wtne = onev / (epsv + SQRV(gmm2p2) + SQRV(LVFU(cfa[rr - 3][(cc + 3)>>1]) - o1_2)); + vfloat wtsw = onev / (epsv + SQRV(gmp2m2) + SQRV(LVFU(cfa[rr + 3][(cc - 3)>>1]) - o1_3)); - float wtse = 1.0f / (eps + SQR(cfa[rr + 2][cc + 2] - cfa[rr][cc]) + SQR(cfa[rr + 3][cc + 3] - cfa[rr + 1][cc + 1])); - float wtnw = 1.0f / (eps + SQR(cfa[rr - 2][cc - 2] - cfa[rr][cc]) + SQR(cfa[rr - 3][cc - 3] - cfa[rr - 1][cc - 1])); - float wtne = 1.0f / (eps + SQR(cfa[rr - 2][cc + 2] - cfa[rr][cc]) + SQR(cfa[rr - 3][cc + 3] - cfa[rr - 1][cc + 1])); - float wtsw = 1.0f / (eps + SQR(cfa[rr + 2][cc - 2] - cfa[rr][cc]) + SQR(cfa[rr + 3][cc - 3] - cfa[rr + 1][cc - 1])); + vfloat ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw); + + vfloat val = vself(vmaskf_lt(ginterp - gin, threshv * (ginterp + gin)), zd5v * (ginterp + gin), gin); + val = vself(mask1, val, gin); + STC2VFU(rawData[rr][cc], val); + } + } +#endif + for (; cc < width - 6; cc += 2) { + //neighbour checking code from Manuel Llorens Garcia + float o1_1 = cfa[rr - 1][(cc - 1)>>1]; + float o1_2 = cfa[rr - 1][(cc + 1)>>1]; + float o1_3 = cfa[rr + 1][(cc - 1)>>1]; + float o1_4 = cfa[rr + 1][(cc + 1)>>1]; + float o2_1 = cfa[rr - 2][cc>>1]; + float o2_2 = cfa[rr + 2][cc>>1]; + float o2_3 = cfa[rr][(cc - 2)>>1]; + float o2_4 = cfa[rr][(cc + 2)>>1]; + + float d1 = (o1_1 + o1_2) + (o1_3 + o1_4); + float d2 = (o2_1 + o2_2) + (o2_3 + o2_4); + + float c1 = (fabs(o1_1 - o1_2) + fabs(o1_1 - o1_3) + fabs(o1_1 - o1_4) + fabs(o1_2 - o1_3) + fabs(o1_3 - o1_4) + fabs(o1_2 - o1_4)); + float c2 = (fabs(o2_1 - o2_2) + fabs(o2_1 - o2_3) + fabs(o2_1 - o2_4) + fabs(o2_2 - o2_3) + fabs(o2_3 - o2_4) + fabs(o2_2 - o2_4)); + + if (c1 + c2 < thresh6 * fabs(d1 - d2)) { + //pixel interpolation + float gin = cfa[rr][cc>>1]; + + float gmp2p2 = gin - cfa[rr + 2][(cc + 2)>>1]; + float gmm2m2 = gin - cfa[rr - 2][(cc - 2)>>1]; + float gmm2p2 = gin - cfa[rr - 2][(cc + 2)>>1]; + float gmp2m2 = gin - cfa[rr + 2][(cc - 2)>>1]; + + float gse = o1_4 + 0.5f * gmp2p2; + float gnw = o1_1 + 0.5f * gmm2m2; + float gne = o1_2 + 0.5f * gmm2p2; + float gsw = o1_3 + 0.5f * gmp2m2; + + float wtse = 1.f / (eps + SQR(gmp2p2) + SQR(cfa[rr + 3][(cc + 3)>>1] - o1_4)); + float wtnw = 1.f / (eps + SQR(gmm2m2) + SQR(cfa[rr - 3][(cc - 3)>>1] - o1_1)); + float wtne = 1.f / (eps + SQR(gmm2p2) + SQR(cfa[rr - 3][(cc + 3)>>1] - o1_2)); + float wtsw = 1.f / (eps + SQR(gmp2m2) + SQR(cfa[rr + 3][(cc - 3)>>1] - o1_3)); float ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw); - if ( ((ginterp - gin) < thresh * (ginterp + gin)) ) { + if (ginterp - gin < thresh * (ginterp + gin)) { rawData[rr][cc] = 0.5f * (ginterp + gin); - //counter++; } } - - // } } - - //printf("pixfix count= %d; vtest= %d \n",counter,vtest); - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - - // done - /*t2 = clock(); - dt = ((double)(t2-t1)) / CLOCKS_PER_SEC; - if (verbose) { - fprintf(stderr,_("elapsed time = %5.3fs\n"),dt); - }*/ - - + } +} } } diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 148d17ee9..9418de0ba 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1915,43 +1915,46 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le // check if it is an olympus E camera, if yes, compute G channel pre-compensation factors if ( ri->getSensorType() == ST_BAYER && (raw.bayersensor.greenthresh || (((idata->getMake().size() >= 7 && idata->getMake().substr(0, 7) == "OLYMPUS" && idata->getModel()[0] == 'E') || (idata->getMake().size() >= 9 && idata->getMake().substr(0, 9) == "Panasonic")) && raw.bayersensor.method != RAWParams::BayerSensor::methodstring[ RAWParams::BayerSensor::vng4])) ) { + StopWatch stop1("gel part one"); // global correction - int ng1 = 0, ng2 = 0, i = 0; + int ng1 = 0, ng2 = 0; double avgg1 = 0., avgg2 = 0.; #ifdef _OPENMP - #pragma omp parallel for default(shared) private(i) reduction(+: ng1, ng2, avgg1, avgg2) + #pragma omp parallel for reduction(+: ng1, ng2, avgg1, avgg2) schedule(dynamic,16) #endif - for (i = border; i < H - border; i++) - for (int j = border; j < W - border; j++) - if (ri->ISGREEN(i, j)) { - if (i & 1) { - avgg2 += rawData[i][j]; - ng2++; - } else { - avgg1 += rawData[i][j]; - ng1++; - } - } - - double corrg1 = ((double)avgg1 / ng1 + (double)avgg2 / ng2) / 2.0 / ((double)avgg1 / ng1); - double corrg2 = ((double)avgg1 / ng1 + (double)avgg2 / ng2) / 2.0 / ((double)avgg2 / ng2); + for (int i = border; i < H - border; i++) { + double avgg = 0.; + for (int j = border + ((FC(i, border) & 1) ^ 1); j < W - border; j += 2) { + avgg += rawData[i][j]; + } + int ng = (W - 2 * border + (FC(i, border) & 1)) / 2; + if (i & 1) { + avgg2 += avgg; + ng2 += ng; + } else { + avgg1 += avgg; + ng1 += ng; + } + } + double corrg1 = (avgg1 / ng1 + avgg2 / ng2) / 2.0 / (avgg1 / ng1); + double corrg2 = (avgg1 / ng1 + avgg2 / ng2) / 2.0 / (avgg2 / ng2); #ifdef _OPENMP - #pragma omp parallel for default(shared) + #pragma omp parallel for schedule(dynamic,16) #endif - for (int i = border; i < H - border; i++) - for (int j = border; j < W - border; j++) - if (ri->ISGREEN(i, j)) { - float currData; - currData = (float)(rawData[i][j] * ((i & 1) ? corrg2 : corrg1)); - rawData[i][j] = (currData); - } + for (int i = border; i < H - border; i++) { + double corrg = (i & 1) ? corrg2 : corrg1; + for (int j = border + ((FC(i, border) & 1) ^ 1); j < W - border; j += 2) { + rawData[i][j] *= corrg; + } + } } if ( ri->getSensorType() == ST_BAYER && raw.bayersensor.greenthresh > 0) { + StopWatch Stop1("gel part two"); if (plistener) { plistener->setProgressStr ("Green equilibrate..."); plistener->setProgress (0.0); From ed354a592649bed150ae14e7e6b1d368e09a4878 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 25 Aug 2017 21:39:00 +0200 Subject: [PATCH 02/52] Moved green equilibration pass one from to green_equil_RT.cc, removed timing code. Fixes #4034 --- rtengine/green_equil_RT.cc | 240 ++++++++++++++++++++++--------------- rtengine/rawimagesource.cc | 45 ++----- rtengine/rawimagesource.h | 1 + 3 files changed, 153 insertions(+), 133 deletions(-) diff --git a/rtengine/green_equil_RT.cc b/rtengine/green_equil_RT.cc index 53943e9a8..91093550d 100644 --- a/rtengine/green_equil_RT.cc +++ b/rtengine/green_equil_RT.cc @@ -3,9 +3,10 @@ // Green Equilibration via directional average // // copyright (c) 2008-2010 Emil Martinec +// optimized for speed 2017 Ingo Weyrich // // -// code dated: February 12, 2011 +// code dated: August 25, 2017 // // green_equil_RT.cc is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,13 +22,11 @@ // along with this program. If not, see . // //////////////////////////////////////////////////////////////// -#define TS 256 // Tile size #include #include #include - #include "rt_math.h" #include "rawimagesource.h" #include "opthelper.h" @@ -35,6 +34,50 @@ namespace rtengine { +void RawImageSource::green_equilibrate_global(array2D &rawData) +{ + // global correction + int ng1 = 0, ng2 = 0; + double avgg1 = 0., avgg2 = 0.; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+: ng1, ng2, avgg1, avgg2) schedule(dynamic,16) +#endif + + for (int i = border; i < H - border; i++) { + double avgg = 0.; + + for (int j = border + ((FC(i, border) & 1) ^ 1); j < W - border; j += 2) { + avgg += rawData[i][j]; + } + + int ng = (W - 2 * border + (FC(i, border) & 1)) / 2; + + if (i & 1) { + avgg2 += avgg; + ng2 += ng; + } else { + avgg1 += avgg; + ng1 += ng; + } + } + + double corrg1 = (avgg1 / ng1 + avgg2 / ng2) / 2.0 / (avgg1 / ng1); + double corrg2 = (avgg1 / ng1 + avgg2 / ng2) / 2.0 / (avgg2 / ng2); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int i = border; i < H - border; i++) { + double corrg = (i & 1) ? corrg2 : corrg1; + + for (int j = border + ((FC(i, border) & 1) ^ 1); j < W - border; j += 2) { + rawData[i][j] *= corrg; + } + } +} + //void green_equilibrate()//for dcraw implementation void RawImageSource::green_equilibrate(float thresh, array2D &rawData) { @@ -49,15 +92,19 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData) #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif - for(int i = 0; i < height; ++i) { - int j = (FC(i,0) & 1) ^ 1; + + for (int i = 0; i < height; ++i) { + int j = (FC(i, 0) & 1) ^ 1; #ifdef __SSE2__ - for(; j < width - 7; j += 8) { - STVFU(cfa[i][j>>1], LC2VFU(rawData[i][j])); + + for (; j < width - 7; j += 8) { + STVFU(cfa[i][j >> 1], LC2VFU(rawData[i][j])); } + #endif - for(; j < width; j += 2) { - cfa[i][j>>1] = rawData[i][j]; + + for (; j < width; j += 2) { + cfa[i][j >> 1] = rawData[i][j]; } } @@ -75,113 +122,114 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData) #ifdef _OPENMP #pragma omp parallel #endif -{ + { #ifdef __SSE2__ - vfloat zd5v = F2V(0.5f); - vfloat onev = F2V(1.f); - vfloat threshv = F2V(thresh); - vfloat thresh6v = F2V(thresh6); - vfloat epsv = F2V(eps); + vfloat zd5v = F2V(0.5f); + vfloat onev = F2V(1.f); + vfloat threshv = F2V(thresh); + vfloat thresh6v = F2V(thresh6); + vfloat epsv = F2V(eps); #endif #ifdef _OPENMP - #pragma omp for schedule(dynamic,16) + #pragma omp for schedule(dynamic,16) #endif - for (int rr = 4; rr < height - 4; rr++) { - int cc = 5 - (FC(rr, 2) & 1); + for (int rr = 4; rr < height - 4; rr++) { + int cc = 5 - (FC(rr, 2) & 1); #ifdef __SSE2__ - for (; cc < width - 12; cc += 8) { - //neighbour checking code from Manuel Llorens Garcia - vfloat o1_1 = LVFU(cfa[rr - 1][(cc - 1)>>1]); - vfloat o1_2 = LVFU(cfa[rr - 1][(cc + 1)>>1]); - vfloat o1_3 = LVFU(cfa[rr + 1][(cc - 1)>>1]); - vfloat o1_4 = LVFU(cfa[rr + 1][(cc + 1)>>1]); - vfloat o2_1 = LVFU(cfa[rr - 2][cc>>1]); - vfloat o2_2 = LVFU(cfa[rr + 2][cc>>1]); - vfloat o2_3 = LVFU(cfa[rr][(cc - 2)>>1]); - vfloat o2_4 = LVFU(cfa[rr][(cc + 2)>>1]); - vfloat d1 = (o1_1 + o1_2 + o1_3 + o1_4); - vfloat d2 = (o2_1 + o2_2 + o2_3 + o2_4); + for (; cc < width - 12; cc += 8) { + //neighbour checking code from Manuel Llorens Garcia + vfloat o1_1 = LVFU(cfa[rr - 1][(cc - 1) >> 1]); + vfloat o1_2 = LVFU(cfa[rr - 1][(cc + 1) >> 1]); + vfloat o1_3 = LVFU(cfa[rr + 1][(cc - 1) >> 1]); + vfloat o1_4 = LVFU(cfa[rr + 1][(cc + 1) >> 1]); + vfloat o2_1 = LVFU(cfa[rr - 2][cc >> 1]); + vfloat o2_2 = LVFU(cfa[rr + 2][cc >> 1]); + vfloat o2_3 = LVFU(cfa[rr][(cc >> 1) - 1]); + vfloat o2_4 = LVFU(cfa[rr][(cc >> 1) + 1]); - vfloat c1 = (vabsf(o1_1 - o1_2) + vabsf(o1_1 - o1_3) + vabsf(o1_1 - o1_4) + vabsf(o1_2 - o1_3) + vabsf(o1_3 - o1_4) + vabsf(o1_2 - o1_4)); - vfloat c2 = (vabsf(o2_1 - o2_2) + vabsf(o2_1 - o2_3) + vabsf(o2_1 - o2_4) + vabsf(o2_2 - o2_3) + vabsf(o2_3 - o2_4) + vabsf(o2_2 - o2_4)); + vfloat d1 = (o1_1 + o1_2 + o1_3 + o1_4); + vfloat d2 = (o2_1 + o2_2 + o2_3 + o2_4); - vmask mask1 = vmaskf_lt(c1 + c2, thresh6v * vabsf(d1 - d2)); - if(_mm_movemask_ps((vfloat)mask1)) { // if for any of the 4 pixels the condition is true, do the maths for all 4 pixels and mask the unused out at the end - //pixel interpolation - vfloat gin = LVFU(cfa[rr][cc>>1]); + vfloat c1 = (vabsf(o1_1 - o1_2) + vabsf(o1_1 - o1_3) + vabsf(o1_1 - o1_4) + vabsf(o1_2 - o1_3) + vabsf(o1_3 - o1_4) + vabsf(o1_2 - o1_4)); + vfloat c2 = (vabsf(o2_1 - o2_2) + vabsf(o2_1 - o2_3) + vabsf(o2_1 - o2_4) + vabsf(o2_2 - o2_3) + vabsf(o2_3 - o2_4) + vabsf(o2_2 - o2_4)); - vfloat gmp2p2 = gin - LVFU(cfa[rr + 2][(cc + 2)>>1]); - vfloat gmm2m2 = gin - LVFU(cfa[rr - 2][(cc - 2)>>1]); - vfloat gmm2p2 = gin - LVFU(cfa[rr - 2][(cc + 2)>>1]); - vfloat gmp2m2 = gin - LVFU(cfa[rr + 2][(cc - 2)>>1]); + vmask mask1 = vmaskf_lt(c1 + c2, thresh6v * vabsf(d1 - d2)); - vfloat gse = o1_4 + zd5v * gmp2p2; - vfloat gnw = o1_1 + zd5v * gmm2m2; - vfloat gne = o1_2 + zd5v * gmm2p2; - vfloat gsw = o1_3 + zd5v * gmp2m2; + if (_mm_movemask_ps((vfloat)mask1)) { // if for any of the 4 pixels the condition is true, do the maths for all 4 pixels and mask the unused out at the end + //pixel interpolation + vfloat gin = LVFU(cfa[rr][cc >> 1]); - vfloat wtse = onev / (epsv + SQRV(gmp2p2) + SQRV(LVFU(cfa[rr + 3][(cc + 3)>>1]) - o1_4)); - vfloat wtnw = onev / (epsv + SQRV(gmm2m2) + SQRV(LVFU(cfa[rr - 3][(cc - 3)>>1]) - o1_1)); - vfloat wtne = onev / (epsv + SQRV(gmm2p2) + SQRV(LVFU(cfa[rr - 3][(cc + 3)>>1]) - o1_2)); - vfloat wtsw = onev / (epsv + SQRV(gmp2m2) + SQRV(LVFU(cfa[rr + 3][(cc - 3)>>1]) - o1_3)); + vfloat gmp2p2 = gin - LVFU(cfa[rr + 2][(cc >> 1) + 1]); + vfloat gmm2m2 = gin - LVFU(cfa[rr - 2][(cc >> 1) - 1]); + vfloat gmm2p2 = gin - LVFU(cfa[rr - 2][(cc >> 1) + 1]); + vfloat gmp2m2 = gin - LVFU(cfa[rr + 2][(cc >> 1) - 1]); - vfloat ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw); + vfloat gse = o1_4 + zd5v * gmp2p2; + vfloat gnw = o1_1 + zd5v * gmm2m2; + vfloat gne = o1_2 + zd5v * gmm2p2; + vfloat gsw = o1_3 + zd5v * gmp2m2; - vfloat val = vself(vmaskf_lt(ginterp - gin, threshv * (ginterp + gin)), zd5v * (ginterp + gin), gin); - val = vself(mask1, val, gin); - STC2VFU(rawData[rr][cc], val); - } - } -#endif - for (; cc < width - 6; cc += 2) { - //neighbour checking code from Manuel Llorens Garcia - float o1_1 = cfa[rr - 1][(cc - 1)>>1]; - float o1_2 = cfa[rr - 1][(cc + 1)>>1]; - float o1_3 = cfa[rr + 1][(cc - 1)>>1]; - float o1_4 = cfa[rr + 1][(cc + 1)>>1]; - float o2_1 = cfa[rr - 2][cc>>1]; - float o2_2 = cfa[rr + 2][cc>>1]; - float o2_3 = cfa[rr][(cc - 2)>>1]; - float o2_4 = cfa[rr][(cc + 2)>>1]; + vfloat wtse = onev / (epsv + SQRV(gmp2p2) + SQRV(LVFU(cfa[rr + 3][(cc + 3) >> 1]) - o1_4)); + vfloat wtnw = onev / (epsv + SQRV(gmm2m2) + SQRV(LVFU(cfa[rr - 3][(cc - 3) >> 1]) - o1_1)); + vfloat wtne = onev / (epsv + SQRV(gmm2p2) + SQRV(LVFU(cfa[rr - 3][(cc + 3) >> 1]) - o1_2)); + vfloat wtsw = onev / (epsv + SQRV(gmp2m2) + SQRV(LVFU(cfa[rr + 3][(cc - 3) >> 1]) - o1_3)); - float d1 = (o1_1 + o1_2) + (o1_3 + o1_4); - float d2 = (o2_1 + o2_2) + (o2_3 + o2_4); + vfloat ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw); - float c1 = (fabs(o1_1 - o1_2) + fabs(o1_1 - o1_3) + fabs(o1_1 - o1_4) + fabs(o1_2 - o1_3) + fabs(o1_3 - o1_4) + fabs(o1_2 - o1_4)); - float c2 = (fabs(o2_1 - o2_2) + fabs(o2_1 - o2_3) + fabs(o2_1 - o2_4) + fabs(o2_2 - o2_3) + fabs(o2_3 - o2_4) + fabs(o2_2 - o2_4)); - - if (c1 + c2 < thresh6 * fabs(d1 - d2)) { - //pixel interpolation - float gin = cfa[rr][cc>>1]; - - float gmp2p2 = gin - cfa[rr + 2][(cc + 2)>>1]; - float gmm2m2 = gin - cfa[rr - 2][(cc - 2)>>1]; - float gmm2p2 = gin - cfa[rr - 2][(cc + 2)>>1]; - float gmp2m2 = gin - cfa[rr + 2][(cc - 2)>>1]; - - float gse = o1_4 + 0.5f * gmp2p2; - float gnw = o1_1 + 0.5f * gmm2m2; - float gne = o1_2 + 0.5f * gmm2p2; - float gsw = o1_3 + 0.5f * gmp2m2; - - float wtse = 1.f / (eps + SQR(gmp2p2) + SQR(cfa[rr + 3][(cc + 3)>>1] - o1_4)); - float wtnw = 1.f / (eps + SQR(gmm2m2) + SQR(cfa[rr - 3][(cc - 3)>>1] - o1_1)); - float wtne = 1.f / (eps + SQR(gmm2p2) + SQR(cfa[rr - 3][(cc + 3)>>1] - o1_2)); - float wtsw = 1.f / (eps + SQR(gmp2m2) + SQR(cfa[rr + 3][(cc - 3)>>1] - o1_3)); - - float ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw); - - if (ginterp - gin < thresh * (ginterp + gin)) { - rawData[rr][cc] = 0.5f * (ginterp + gin); + vfloat val = vself(vmaskf_lt(ginterp - gin, threshv * (ginterp + gin)), zd5v * (ginterp + gin), gin); + val = vself(mask1, val, gin); + STC2VFU(rawData[rr][cc], val); } + } +#endif + + for (; cc < width - 6; cc += 2) { + //neighbour checking code from Manuel Llorens Garcia + float o1_1 = cfa[rr - 1][(cc - 1) >> 1]; + float o1_2 = cfa[rr - 1][(cc + 1) >> 1]; + float o1_3 = cfa[rr + 1][(cc - 1) >> 1]; + float o1_4 = cfa[rr + 1][(cc + 1) >> 1]; + float o2_1 = cfa[rr - 2][cc >> 1]; + float o2_2 = cfa[rr + 2][cc >> 1]; + float o2_3 = cfa[rr][(cc - 2) >> 1]; + float o2_4 = cfa[rr][(cc + 2) >> 1]; + + float d1 = (o1_1 + o1_2) + (o1_3 + o1_4); + float d2 = (o2_1 + o2_2) + (o2_3 + o2_4); + + float c1 = (fabs(o1_1 - o1_2) + fabs(o1_1 - o1_3) + fabs(o1_1 - o1_4) + fabs(o1_2 - o1_3) + fabs(o1_3 - o1_4) + fabs(o1_2 - o1_4)); + float c2 = (fabs(o2_1 - o2_2) + fabs(o2_1 - o2_3) + fabs(o2_1 - o2_4) + fabs(o2_2 - o2_3) + fabs(o2_3 - o2_4) + fabs(o2_2 - o2_4)); + + if (c1 + c2 < thresh6 * fabs(d1 - d2)) { + //pixel interpolation + float gin = cfa[rr][cc >> 1]; + + float gmp2p2 = gin - cfa[rr + 2][(cc + 2) >> 1]; + float gmm2m2 = gin - cfa[rr - 2][(cc - 2) >> 1]; + float gmm2p2 = gin - cfa[rr - 2][(cc + 2) >> 1]; + float gmp2m2 = gin - cfa[rr + 2][(cc - 2) >> 1]; + + float gse = o1_4 + 0.5f * gmp2p2; + float gnw = o1_1 + 0.5f * gmm2m2; + float gne = o1_2 + 0.5f * gmm2p2; + float gsw = o1_3 + 0.5f * gmp2m2; + + float wtse = 1.f / (eps + SQR(gmp2p2) + SQR(cfa[rr + 3][(cc + 3) >> 1] - o1_4)); + float wtnw = 1.f / (eps + SQR(gmm2m2) + SQR(cfa[rr - 3][(cc - 3) >> 1] - o1_1)); + float wtne = 1.f / (eps + SQR(gmm2p2) + SQR(cfa[rr - 3][(cc + 3) >> 1] - o1_2)); + float wtsw = 1.f / (eps + SQR(gmp2m2) + SQR(cfa[rr + 3][(cc - 3) >> 1] - o1_3)); + + float ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw); + + if (ginterp - gin < thresh * (ginterp + gin)) { + rawData[rr][cc] = 0.5f * (ginterp + gin); + } + } } } } } } -} - -#undef TS diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 9418de0ba..77710229a 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1913,48 +1913,19 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } } - // check if it is an olympus E camera, if yes, compute G channel pre-compensation factors + // check if it is an olympus E camera or green equilibration is enabled. If yes, compute G channel pre-compensation factors if ( ri->getSensorType() == ST_BAYER && (raw.bayersensor.greenthresh || (((idata->getMake().size() >= 7 && idata->getMake().substr(0, 7) == "OLYMPUS" && idata->getModel()[0] == 'E') || (idata->getMake().size() >= 9 && idata->getMake().substr(0, 9) == "Panasonic")) && raw.bayersensor.method != RAWParams::BayerSensor::methodstring[ RAWParams::BayerSensor::vng4])) ) { - StopWatch stop1("gel part one"); // global correction - int ng1 = 0, ng2 = 0; - double avgg1 = 0., avgg2 = 0.; - -#ifdef _OPENMP - #pragma omp parallel for reduction(+: ng1, ng2, avgg1, avgg2) schedule(dynamic,16) -#endif - - for (int i = border; i < H - border; i++) { - double avgg = 0.; - for (int j = border + ((FC(i, border) & 1) ^ 1); j < W - border; j += 2) { - avgg += rawData[i][j]; - } - int ng = (W - 2 * border + (FC(i, border) & 1)) / 2; - if (i & 1) { - avgg2 += avgg; - ng2 += ng; - } else { - avgg1 += avgg; - ng1 += ng; - } - } - double corrg1 = (avgg1 / ng1 + avgg2 / ng2) / 2.0 / (avgg1 / ng1); - double corrg2 = (avgg1 / ng1 + avgg2 / ng2) / 2.0 / (avgg2 / ng2); - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) -#endif - - for (int i = border; i < H - border; i++) { - double corrg = (i & 1) ? corrg2 : corrg1; - for (int j = border + ((FC(i, border) & 1) ^ 1); j < W - border; j += 2) { - rawData[i][j] *= corrg; + if(numFrames == 4) { + for(int i = 0; i < 4; ++i) { + green_equilibrate_global(*rawDataFrames[i]); } + } else { + green_equilibrate_global(rawData); } } if ( ri->getSensorType() == ST_BAYER && raw.bayersensor.greenthresh > 0) { - StopWatch Stop1("gel part two"); if (plistener) { plistener->setProgressStr ("Green equilibrate..."); plistener->setProgress (0.0); @@ -1962,10 +1933,10 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le if(numFrames == 4) { for(int i = 0; i < 4; ++i) { - green_equilibrate(0.01 * (raw.bayersensor.greenthresh), *rawDataFrames[i]); + green_equilibrate(0.01 * raw.bayersensor.greenthresh, *rawDataFrames[i]); } } else { - green_equilibrate(0.01 * (raw.bayersensor.greenthresh), rawData); + green_equilibrate(0.01 * raw.bayersensor.greenthresh, rawData); } } diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index bc2589408..8ce27f289 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -233,6 +233,7 @@ protected: void cfa_linedn (float linenoiselevel);//Emil's line denoise + void green_equilibrate_global (array2D &rawData); void green_equilibrate (float greenthresh, array2D &rawData);//Emil's green equilibration void nodemosaic(bool bw); From 2c257d22150d287fc45ca819f06f0a6aecc6a346 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 3 Sep 2017 20:48:49 +0200 Subject: [PATCH 03/52] started refactoring ImProcFunctions::transform in preparation for lensfun integration --- rtengine/improcfun.h | 8 +- rtengine/iptransform.cc | 207 +++++++--------------------------------- 2 files changed, 41 insertions(+), 174 deletions(-) diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 309e5c029..05bd2081d 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -54,9 +54,13 @@ class ImProcFunctions void calcVignettingParams (int oW, int oH, const VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul); - void transformPreview (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LCPMapper *pLCPMap); + enum TransformMode { + TRANSFORM_PREVIEW, + TRANSFORM_HIGH_QUALITY, + TRANSFORM_HIGH_QUALITY_FULLIMAGE + }; void transformLuminanceOnly (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH, int fW, int fH); - void transformHighQuality (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LCPMapper *pLCPMap, bool fullImage); + void transformGeneral(TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LCPMapper *pLCPMap); void sharpenHaloCtrl (float** luminance, float** blurmap, float** base, int W, int H, const SharpeningParams &sharpenParam); void sharpenHaloCtrl (LabImage* lab, float** blurmap, float** base, int W, int H, SharpeningParams &sharpenParam); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index ee14c80c2..3ee42a684 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -325,10 +325,16 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, if (! (needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP()) && (needsVignetting() || needsPCVignetting() || needsGradient())) { transformLuminanceOnly (original, transformed, cx, cy, oW, oH, fW, fH); - } else if (!needsCA() && scale != 1) { - transformPreview (original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap); } else { - transformHighQuality (original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap, fullImage); + TransformMode mode; + if (!needsCA() && scale != 1) { + mode = TRANSFORM_PREVIEW; + } else if (!fullImage) { + mode = TRANSFORM_HIGH_QUALITY; + } else { + mode = TRANSFORM_HIGH_QUALITY_FULLIMAGE; + } + transformGeneral(mode, original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap); } if (pLCPMap) { @@ -721,9 +727,8 @@ void ImProcFunctions::transformLuminanceOnly (Imagefloat* original, Imagefloat* } } -// Transform WITH scaling (opt.) and CA, cubic interpolation -void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, - const LCPMapper *pLCPMap, bool fullImage) + +void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LCPMapper *pLCPMap) { double w2 = (double) oW / 2.0 - 0.5; double h2 = (double) oH / 2.0 - 0.5; @@ -781,17 +786,31 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr oH * tan (hpalpha) * sqrt (SQR (4 * maxRadius) + SQR (oH * tan (hpalpha)))) / (SQR (maxRadius) * 8))); double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos (hpteta), hptanpt = tan (hpteta); - double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, true /*fullImage*/ ? pLCPMap : nullptr) : 1.0; + double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0; // smaller crop images are a problem, so only when processing fully - bool enableLCPCA = pLCPMap && params->lensProf.useCA && fullImage && pLCPMap->enableCA; - bool enableLCPDist = pLCPMap && params->lensProf.useDist; // && fullImage; + bool enableLCPCA = false; + bool enableLCPDist = false; + bool enableCA = false; - if (enableLCPCA) { - enableLCPDist = false; + switch (mode) { + case ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE: + enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->enableCA; + // no break on purpose + case ImProcFunctions::TRANSFORM_HIGH_QUALITY: + enableLCPDist = pLCPMap && params->lensProf.useDist; + if (enableLCPCA) { + enableLCPDist = false; + } + enableCA = enableLCPCA || needsCA(); + default: // ImProcFunctions::TRANSFORM_PREVIEW + enableLCPDist = pLCPMap && params->lensProf.useDist; + break; } - bool enableCA = enableLCPCA || needsCA(); + if (!enableCA) { + chDist[0] = 0.0; + } // main cycle bool darkening = (params->vignetting.amount <= 0.0); @@ -895,6 +914,10 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr // all interpolation pixels inside image if (enableCA) { interpolateTransformChannelsCubic (chOrig[c], xc - 1, yc - 1, Dx, Dy, & (chTrans[c][y][x]), vignmul); + } else if (mode == ImProcFunctions::TRANSFORM_PREVIEW) { + transformed->r (y, x) = vignmul * (original->r (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->r (yc, xc + 1) * Dx * (1.0 - Dy) + original->r (yc + 1, xc) * (1.0 - Dx) * Dy + original->r (yc + 1, xc + 1) * Dx * Dy); + transformed->g (y, x) = vignmul * (original->g (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->g (yc, xc + 1) * Dx * (1.0 - Dy) + original->g (yc + 1, xc) * (1.0 - Dx) * Dy + original->g (yc + 1, xc + 1) * Dx * Dy); + transformed->b (y, x) = vignmul * (original->b (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->b (yc, xc + 1) * Dx * (1.0 - Dy) + original->b (yc + 1, xc) * (1.0 - Dx) * Dy + original->b (yc + 1, xc + 1) * Dx * Dy); } else { interpolateTransformCubic (original, xc - 1, yc - 1, Dx, Dy, & (transformed->r (y, x)), & (transformed->g (y, x)), & (transformed->b (y, x)), vignmul); } @@ -928,166 +951,6 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr } } -// Transform WITH scaling, WITHOUT CA, simple (and fast) interpolation. Used for preview -void ImProcFunctions::transformPreview (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LCPMapper *pLCPMap) -{ - - double w2 = (double) oW / 2.0 - 0.5; - double h2 = (double) oH / 2.0 - 0.5; - - double vig_w2, vig_h2, maxRadius, v, b, mul; - calcVignettingParams (oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul); - - struct grad_params gp; - - if (needsGradient()) { - calcGradientParams (oW, oH, params->gradient, gp); - } - - struct pcv_params pcv; - - if (needsPCVignetting()) { - calcPCVignetteParams (fW, fH, oW, oH, params->pcvignette, params->crop, pcv); - } - - // auxiliary variables for distortion correction - bool needsDist = needsDistortion(); // for performance - double distAmount = params->distortion.amount; - - // auxiliary variables for rotation - double cost = cos (params->rotate.degree * rtengine::RT_PI / 180.0); - double sint = sin (params->rotate.degree * rtengine::RT_PI / 180.0); - - // auxiliary variables for vertical perspective correction - double vpdeg = params->perspective.vertical / 100.0 * 45.0; - double vpalpha = (90 - vpdeg) / 180.0 * rtengine::RT_PI; - double vpteta = fabs (vpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((vpdeg > 0 ? 1.0 : -1.0) * sqrt ((-oW * oW * tan (vpalpha) * tan (vpalpha) + (vpdeg > 0 ? 1.0 : -1.0) * oW * tan (vpalpha) * sqrt (16 * maxRadius * maxRadius + oW * oW * tan (vpalpha) * tan (vpalpha))) / (maxRadius * maxRadius * 8))); - double vpcospt = (vpdeg >= 0 ? 1.0 : -1.0) * cos (vpteta), vptanpt = tan (vpteta); - - // auxiliary variables for horizontal perspective correction - double hpdeg = params->perspective.horizontal / 100.0 * 45.0; - double hpalpha = (90 - hpdeg) / 180.0 * rtengine::RT_PI; - double hpteta = fabs (hpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((hpdeg > 0 ? 1.0 : -1.0) * sqrt ((-oH * oH * tan (hpalpha) * tan (hpalpha) + (hpdeg > 0 ? 1.0 : -1.0) * oH * tan (hpalpha) * sqrt (16 * maxRadius * maxRadius + oH * oH * tan (hpalpha) * tan (hpalpha))) / (maxRadius * maxRadius * 8))); - double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos (hpteta), hptanpt = tan (hpteta); - - double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0; - - bool darkening = (params->vignetting.amount <= 0.0); - - // main cycle - #pragma omp parallel for if (multiThread) - - for (int y = 0; y < transformed->getHeight(); y++) { - for (int x = 0; x < transformed->getWidth(); x++) { - double x_d = x, y_d = y; - - if (pLCPMap && params->lensProf.useDist) { - correct_distortion(pLCPMap, x_d, y_d, cx, cy, ascale); // must be first transform - } else { - x_d *= ascale; - y_d *= ascale; - } - - x_d += ascale * (cx - w2); // centering x coord & scale - y_d += ascale * (cy - h2); // centering y coord & scale - - double vig_x_d = 0., vig_y_d = 0.; - - if (needsVignetting()) { - vig_x_d = ascale * (x + cx - vig_w2); // centering x coord & scale - vig_y_d = ascale * (y + cy - vig_h2); // centering y coord & scale - } - - if (needsPerspective()) { - // horizontal perspective transformation - y_d *= maxRadius / (maxRadius + x_d * hptanpt); - x_d *= maxRadius * hpcospt / (maxRadius + x_d * hptanpt); - - // vertical perspective transformation - x_d *= maxRadius / (maxRadius - y_d * vptanpt); - y_d *= maxRadius * vpcospt / (maxRadius - y_d * vptanpt); - } - - // rotate - double Dx = x_d * cost - y_d * sint; - double Dy = x_d * sint + y_d * cost; - - // distortion correction - double s = 1; - - if (needsDist) { - double r = sqrt (Dx * Dx + Dy * Dy) / maxRadius; // sqrt is slow - s = 1.0 - distAmount + distAmount * r ; - Dx *= s; - Dy *= s; - } - - double r2 = 0.; - - if (needsVignetting()) { - double vig_Dx = vig_x_d * cost - vig_y_d * sint; - double vig_Dy = vig_x_d * sint + vig_y_d * cost; - r2 = sqrt (vig_Dx * vig_Dx + vig_Dy * vig_Dy); - } - - // de-center - Dx += w2; - Dy += h2; - - // Extract integer and fractions of source screen coordinates - int xc = (int)Dx; - Dx -= (double)xc; - xc -= sx; - int yc = (int)Dy; - Dy -= (double)yc; - yc -= sy; - - // Convert only valid pixels - if (yc >= 0 && yc < original->getHeight() && xc >= 0 && xc < original->getWidth()) { - - // multiplier for vignetting correction - double vignmul = 1.0; - - if (needsVignetting()) { - if (darkening) { - vignmul /= std::max (v + mul * tanh (b * (maxRadius - s * r2) / maxRadius), 0.001); - } else { - vignmul = v + mul * tanh (b * (maxRadius - s * r2) / maxRadius); - } - } - - if (needsGradient()) { - vignmul *= calcGradientFactor (gp, cx + x, cy + y); - } - - if (needsPCVignetting()) { - vignmul *= calcPCVignetteFactor (pcv, cx + x, cy + y); - } - - if (yc < original->getHeight() - 1 && xc < original->getWidth() - 1) { - // all interpolation pixels inside image - transformed->r (y, x) = vignmul * (original->r (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->r (yc, xc + 1) * Dx * (1.0 - Dy) + original->r (yc + 1, xc) * (1.0 - Dx) * Dy + original->r (yc + 1, xc + 1) * Dx * Dy); - transformed->g (y, x) = vignmul * (original->g (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->g (yc, xc + 1) * Dx * (1.0 - Dy) + original->g (yc + 1, xc) * (1.0 - Dx) * Dy + original->g (yc + 1, xc + 1) * Dx * Dy); - transformed->b (y, x) = vignmul * (original->b (yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->b (yc, xc + 1) * Dx * (1.0 - Dy) + original->b (yc + 1, xc) * (1.0 - Dx) * Dy + original->b (yc + 1, xc + 1) * Dx * Dy); - } else { - // edge pixels - int y1 = LIM (yc, 0, original->getHeight() - 1); - int y2 = LIM (yc + 1, 0, original->getHeight() - 1); - int x1 = LIM (xc, 0, original->getWidth() - 1); - int x2 = LIM (xc + 1, 0, original->getWidth() - 1); - transformed->r (y, x) = vignmul * (original->r (y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->r (y1, x2) * Dx * (1.0 - Dy) + original->r (y2, x1) * (1.0 - Dx) * Dy + original->r (y2, x2) * Dx * Dy); - transformed->g (y, x) = vignmul * (original->g (y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->g (y1, x2) * Dx * (1.0 - Dy) + original->g (y2, x1) * (1.0 - Dx) * Dy + original->g (y2, x2) * Dx * Dy); - transformed->b (y, x) = vignmul * (original->b (y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->b (y1, x2) * Dx * (1.0 - Dy) + original->b (y2, x1) * (1.0 - Dx) * Dy + original->b (y2, x2) * Dx * Dy); - } - } else { - // not valid (source pixel x,y not inside source image, etc.) - transformed->r (y, x) = 0; - transformed->g (y, x) = 0; - transformed->b (y, x) = 0; - } - } - } -} double ImProcFunctions::getTransformAutoFill (int oW, int oH, const LCPMapper *pLCPMap) { From f7b857eb9e0d4f68fba9ae3936eb4a306f663adf Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 5 Sep 2017 23:42:17 +0200 Subject: [PATCH 04/52] added wrapper classes for lensfun types --- rtengine/rtlensfun.cc | 225 ++++++++++++++++++++++++++++++++++++++++++ rtengine/rtlensfun.h | 85 ++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 rtengine/rtlensfun.cc create mode 100644 rtengine/rtlensfun.h diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc new file mode 100644 index 000000000..1b8009ee1 --- /dev/null +++ b/rtengine/rtlensfun.cc @@ -0,0 +1,225 @@ +/* -*- C++ -*- + * + * This file is part of RawTherapee. + * + * Copyright (c) 2017 Alberto Griggio + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +#include "rtlensfun.h" + +namespace rtengine { + +//----------------------------------------------------------------------------- +// LFModifier +//----------------------------------------------------------------------------- + +bool LFModifier::ok() const +{ + return data_.get(); +} + + +void LFModifier::correctDistortion(double &x, double &y) +{ + if (!ok()) { + return; + } + + float pos[2]; + data_->ApplyGeometryDistortion(x, y, 1, 1, pos); + x = pos[0]; + y = pos[1]; +} + + +void LFModifier::processVignetteLine(int width, int y, float *line) +{ + // TODO +} + + +void LFModifier::processVignetteLine3Channels(int width, int y, float *line) +{ + // TODO +} + + +//----------------------------------------------------------------------------- +// LFCamera +//----------------------------------------------------------------------------- + +bool LFCamera::ok() const +{ + return data_.get(); +} + + +Glib::ustring LFCamera::getMake() const +{ + if (ok()) { + return data_->Maker; + } else { + return ""; + } +} + + +Glib::ustring LFCamera::getModel() const +{ + if (ok()) { + return data_->Model; + } else { + return ""; + } +} + + +float LFCamera::getCropFactor() const +{ + if (ok()) { + return data_->CropFactor; + } else { + return 0; + } +} + + +Glib::ustring LFCamera::getDisplayString() const +{ + if (ok()) { + return Glib::ustring::compose("%1 %2", getMake(), getModel()); + } else { + return "---"; + } +} + + +//----------------------------------------------------------------------------- +// LFLens +//----------------------------------------------------------------------------- + +bool LFLens::ok() const +{ + return data_->get(); +} + + +Glib::ustring LFLens::getDisplayString() const +{ + if (ok()) { + return Glib::ustring::compose("%1 %2", data_->Maker, data_->Model); + } else { + return "---"; + } +} + + +//----------------------------------------------------------------------------- +// LFDatabase +//----------------------------------------------------------------------------- + +LFDatabase LFDatabase::instance_; + + +bool LFDatabase::init() +{ + instance_.data_.reset(new lfDatabase()); + return instance_.data_->Load() != LF_NO_ERROR; +} + + +LFDatabase *LFDatabase::getInstance() +{ + return &instance_; +} + + +std::vector LFDatabase::getCameras() +{ + auto cams = data_->GetCameras(); + std::vector ret; + while (*cams) { + ret.emplace_back(LFCamera()); + ret.back().data_.reset(new lfCamera(**cams)); + ++cams; + } + return ret; +} + + +std::vector getLenses(const LFCamera &camera) +{ + auto lenses = data_->FindLenses(*camera.data_->get(), NULL, "", LF_SEARCH_LOOSE | LF_SEARCH_SORT_AND_UNIQUIFY); + std::vector ret; + while (*lenses) { + ret.emplace_back(LFLens()); + ret.back().data_.reset(new lfLens(**lenses)); + ++lenses; + } + lf_free(lenses); + return ret; +} + + +LFCamera LFDatabase::findCamera(const Glib::ustring &make, const Glib::ustring &model) +{ + LFCamera ret; + auto found = data_->FindCamerasExt(make.c_str(), model.c_str(), LF_SEARCH_LOOSE); + if (found) { + ret.data_.reset(new lfCamera(*found[0])); + lf_free(found); + } + return ret; +} + + +LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) +{ + LFLens ret; + auto found = data_->FindLenses(camera.data_.get(), NULL, name.c_str(), LF_SEARCH_LOOSE); + if (!found) { + // try to split the maker from the model of the lens + Glib::ustring make, model; + auto i = name.find_first_of(' '); + if (i != Glib::ustring::npos) { + make = name.substr(0, i); + model = name.substr(i+1); + found = data_->FindLenses(camera.data_.get(), make.c_str(), model.c_str(), LF_SEARCH_LOOSE); + } + } + if (found) { + ret.data_.reset(new lfLens(*found[0])); + lf_free(found); + } + return ret; +} + + +LFModifier LFDatabase::getModifier(const LFCamera &camera, const LFLens &lens, + int width, int height, float focalLen, + float aperture) +{ + LFModifier ret; + if (camera.ok() && lens.ok()) { + lfModifier *mod = lfModifier::Create(lens.data_.get(), camera.getCropFactor(), width, height); + mod->Initialize(lens.data_.get(), LF_PF_F32, focalLen, aperture, 1000, 1, LF_RECTILINEAR, LF_MODIFY_VIGNETTING | LF_MODIFY_DISTORTION, false); + ret.data_.reset(mod); + } + return ret; +} + + +} // namespace rtengine diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h new file mode 100644 index 000000000..aaa49c8e0 --- /dev/null +++ b/rtengine/rtlensfun.h @@ -0,0 +1,85 @@ +/* -*- C++ -*- + * + * This file is part of RawTherapee. + * + * Copyright (c) 2017 Alberto Griggio + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +#pragma once + +#include +#include +#include + +namespace rtengine { + +class LFModifier { +public: + bool ok() const; + + void correctDistortion(double &x, double &y); + void processVignetteLine(int width, int y, float *line); + void processVignetteLine3Channels(int width, int y, float *line); + +private: + friend class LFDatabase; + std::shared_ptr data_; +}; + +class LFCamera { +public: + bool ok() const; + + Glib::ustring getMake() const; + Glib::ustring getModel() const; + float getCropFactor() const; + + Glib::ustring getDisplayString() const; + +private: + friend class LFDatabase; + std::shared_ptr data_; +}; + +class LFLens { +public: + bool ok() const; + + Glib::ustring getDisplayString() const; +private: + friend class LFDatabase; + std::shared_ptr data_; +}; + +class LFDatabase { +public: + static bool init(); + static LFDatabase *getInstance(); + + std::vector getCameras(); + std::vector getLenses(const LFCamera &camera); + LFCamera findCamera(const Glib::ustring &make, const Glib::ustring &model); + LFLens findLens(const LFCamera &camera, const Glib::ustring &name); + LFModifier getModifier(const LFCamera &camera, const LFLens &lens, + int width, int height, + float focalLen, float aperture); + +private: + static LFDatabase instance_; + std::shared_ptr data_; +}; + +} // namespace rtengine From b4d3caf9c6b1cbfdf02561463d237c1926a977ad Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 6 Sep 2017 00:15:41 +0200 Subject: [PATCH 05/52] changed signature of ImProcFunctions::transform to take as input an ImageMetaData pointer --- rtengine/dcrop.cc | 7 ++++--- rtengine/improccoordinator.cc | 10 +++++++--- rtengine/improcfun.h | 5 +++-- rtengine/iptransform.cc | 8 +++++++- rtengine/rtengine.h | 1 + rtengine/rtthumbnail.cc | 27 +++++++++++++++++++-------- rtengine/rtthumbnail.h | 5 +++-- rtengine/simpleprocess.cc | 8 +++++--- rtgui/cacheimagedata.h | 33 ++++++++++++++++++++++++++++----- rtgui/thumbnail.cc | 6 ++++-- 10 files changed, 81 insertions(+), 29 deletions(-) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index df75e7a0e..acd73c0a3 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -698,9 +698,10 @@ void Crop::update (int todo) if (needstransform) parent->ipf.transform (baseCrop, transCrop, cropx / skip, cropy / skip, trafx / skip, trafy / skip, skips (parent->fw, skip), skips (parent->fh, skip), parent->getFullWidth(), parent->getFullHeight(), - parent->imgsrc->getMetaData()->getFocalLen(), parent->imgsrc->getMetaData()->getFocalLen35mm(), - parent->imgsrc->getMetaData()->getFocusDist(), - parent->imgsrc->getMetaData()->getFNumber(), + parent->imgsrc->getMetaData(), + // parent->imgsrc->getMetaData()->getFocalLen(), parent->imgsrc->getMetaData()->getFocalLen35mm(), + // parent->imgsrc->getMetaData()->getFocusDist(), + // parent->imgsrc->getMetaData()->getFNumber(), parent->imgsrc->getRotateDegree(), false); else { baseCrop->copyData (transCrop); diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index d5032cd46..b14db1588 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -401,7 +401,9 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) if (needstransform) ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, fw, fh, imgsrc->getMetaData()->getFocalLen(), - imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(), imgsrc->getMetaData()->getFNumber(), imgsrc->getRotateDegree(), false); + imgsrc->getMetaData(), + // imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(), imgsrc->getMetaData()->getFNumber(), + imgsrc->getRotateDegree(), false); else { orig_prev->copyData (oprevi); } @@ -1219,8 +1221,10 @@ void ImProcCoordinator::saveInputICCReference (const Glib::ustring& fname, bool if (ipf.needsTransform()) { Imagefloat* trImg = new Imagefloat (fW, fH); - ipf.transform (im, trImg, 0, 0, 0, 0, fW, fH, fW, fH, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), - imgsrc->getMetaData()->getFocusDist(), imgsrc->getMetaData()->getFNumber(), imgsrc->getRotateDegree(), true); + ipf.transform (im, trImg, 0, 0, 0, 0, fW, fH, fW, fH, + imgsrc->getMetaData(), + // imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(), imgsrc->getMetaData()->getFNumber(), + imgsrc->getRotateDegree(), true); delete im; im = trImg; } diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 05bd2081d..983295c38 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -239,8 +239,9 @@ public: void colorCurve (LabImage* lold, LabImage* lnew); void sharpening (LabImage* lab, float** buffer, SharpeningParams &sharpenParam); void sharpeningcam (CieImage* ncie, float** buffer); - void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, - double focalLen, double focalLen35mm, float focusDist, double fNumber, int rawRotationDeg, bool fullImage); + /* void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, */ + /* double focalLen, double focalLen35mm, float focusDist, double fNumber, int rawRotationDeg, bool fullImage); */ + void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const ImageMetaData *metadata, int rawRotationDeg, bool fullImage); float resizeScale (const ProcParams* params, int fw, int fh, int &imw, int &imh); void lab2monitorRgb (LabImage* lab, Image8* image); void resize (Image16* src, Image16* dst, float dScale); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 3ee42a684..6d34c5935 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -307,8 +307,14 @@ bool ImProcFunctions::transCoord (int W, int H, int x, int y, int w, int h, int& } void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, - double focalLen, double focalLen35mm, float focusDist, double fNumber, int rawRotationDeg, bool fullImage) + const ImageMetaData *metadata, + //double focalLen, double focalLen35mm, float focusDist, double fNumber, + int rawRotationDeg, bool fullImage) { + double focalLen = metadata->getFocalLen(); + double focalLen35mm = metadata->getFocalLen35mm(); + float focusDist = metadata->getFocusDist(); + double fNumber = metadata->getFNumber(); LCPMapper *pLCPMap = nullptr; diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index 9ba794a1f..c78d8ef40 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -48,6 +48,7 @@ class IImage8; class IImage16; class IImagefloat; + /** * This class represents provides functions to obtain exif and IPTC metadata information * from the image file diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 511b17f9f..52314ceaf 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -954,9 +954,19 @@ 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, float focusDist, float shutter, float fnumber, float iso, std::string expcomp_, double& myscale) +// IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rheight, TypeInterpolation interp, std::string camName, +// double focalLen, double focalLen35mm, float focusDist, float shutter, float fnumber, float iso, std::string expcomp_, double& myscale) +IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rheight, TypeInterpolation interp, const ImageMetaData *metadata, double& myscale) { + std::string camName = metadata->getCamera(); + double focalLen = metadata->getFocalLen(); + double focalLen35mm = metadata->getFocalLen35mm(); + float focusDist = metadata->getFocusDist(); + float shutter = metadata->getShutterSpeed(); + float fnumber = metadata->getFNumber(); + float iso = metadata->getISOSpeed(); + float fcomp = metadata->getExpComp(); + // check if the WB's equalizer value has changed if (wbEqual < (params.wb.equal - 5e-4) || wbEqual > (params.wb.equal + 5e-4) || wbTempBias < (params.wb.tempBias - 5e-4) || wbTempBias > (params.wb.tempBias + 5e-4)) { wbEqual = params.wb.equal; @@ -1079,7 +1089,8 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei int origFH; double tscale = 0.0; getDimensions (origFW, origFH, tscale); - ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, origFW * tscale + 0.5, origFH * tscale + 0.5, focalLen, focalLen35mm, focusDist, fnumber, 0, true); // Raw rotate degree not detectable here + // ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, origFW * tscale + 0.5, origFH * tscale + 0.5, focalLen, focalLen35mm, focusDist, fnumber, 0, true); // Raw rotate degree not detectable here + ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, origFW * tscale + 0.5, origFH * tscale + 0.5, metadata, 0, true); // Raw rotate degree not detectable here delete baseImg; baseImg = trImg; } @@ -1278,11 +1289,11 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei float fnum = fnumber;// F number float fiso = iso;// ISO float fspeed = shutter;//speed - char * writ = new char[expcomp_.size() + 1];//convert expcomp_ to char - std::copy (expcomp_.begin(), expcomp_.end(), writ); - writ[expcomp_.size()] = '\0'; - float fcomp = atof (writ); //compensation + - - delete[] writ; + // char * writ = new char[expcomp_.size() + 1];//convert expcomp_ to char + // std::copy (expcomp_.begin(), expcomp_.end(), writ); + // writ[expcomp_.size()] = '\0'; + // float fcomp = atof (writ); //compensation + - + // delete[] writ; float adap; if (fnum < 0.3f || fiso < 5.f || fspeed < 0.00001f) diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h index c40a226c4..4fc3466c8 100644 --- a/rtengine/rtthumbnail.h +++ b/rtengine/rtthumbnail.h @@ -71,8 +71,9 @@ public: void init (); - IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, std::string camName, - double focalLen, double focalLen35mm, float focusDist, float shutter, float fnumber, float iso, std::string expcomp_, double& scale); + // IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, std::string camName, + // double focalLen, double focalLen35mm, float focusDist, float shutter, float fnumber, float iso, std::string expcomp_, double& scale); + IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, const ImageMetaData *metadata, 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 14ad333e4..fabcf2158 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -813,9 +813,11 @@ private: // perform transform (excepted resizing) if (ipf.needsTransform()) { Imagefloat* trImg = new Imagefloat (fw, fh); - ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, fw, fh, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), - imgsrc->getMetaData()->getFocusDist(), - imgsrc->getMetaData()->getFNumber(), + ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, fw, fh, + imgsrc->getMetaData(), + // imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), + // imgsrc->getMetaData()->getFocusDist(), + // imgsrc->getMetaData()->getFNumber(), imgsrc->getRotateDegree(), true); delete baseImg; baseImg = trImg; diff --git a/rtgui/cacheimagedata.h b/rtgui/cacheimagedata.h index f655bd88b..378325630 100644 --- a/rtgui/cacheimagedata.h +++ b/rtgui/cacheimagedata.h @@ -21,8 +21,9 @@ #include #include "options.h" +#include "rtengine.h" -class CacheImageData +class CacheImageData: public rtengine::ImageMetaData { public: @@ -76,9 +77,31 @@ public: int load (const Glib::ustring& fname); int save (const Glib::ustring& fname); - Glib::ustring getCamera() const - { - return Glib::ustring(camMake + " " + camModel); - } + // Glib::ustring getCamera() const + // { + // return Glib::ustring(camMake + " " + camModel); + // } + + //------------------------------------------------------------------------- + // ImageMetaData interface + //------------------------------------------------------------------------- + + bool hasExif() const { return false; } + const rtexif::TagDirectory *getExifData() const { return NULL; } + bool hasIPTC() const { return false; } + const procparams::IPTCPairs getIPTCData () const { return procparams::IPTCPairs(); } + struct tm getDateTime () const { struct tm ret; return ret; } + time_t getDateTimeAsTS() const { time_t ret; return ret; } + int getISOSpeed() const { return iso; } + double getFNumber() const { return fnumber; } + double getFocalLen() const { return focalLen; } + double getFocalLen35mm() const { return focalLen35mm; } + float getFocusDist() const { return focusDist; } + double getShutterSpeed() const { return shutter; } + double getExpComp() const { return atof(expcomp.c_str()); } + std::string getMake() const { return camMake; } + std::string getModel() const { return camModel; } + std::string getLens() const { return lens; } + std::string getOrientation() const { return ""; } // TODO }; #endif diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 9db7c69b6..2d67a4a03 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -602,7 +602,8 @@ rtengine::IImage8* Thumbnail::processThumbImage (const rtengine::procparams::Pro image = tpp->quickProcessImage (pparams, h, rtengine::TI_Nearest, scale); } else { // Full thumbnail: apply profile - image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.getCamera(), cfs.focalLen, cfs.focalLen35mm, cfs.focusDist, cfs.shutter, cfs.fnumber, cfs.iso, cfs.expcomp, scale ); + // image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.getCamera(), cfs.focalLen, cfs.focalLen35mm, cfs.focusDist, cfs.shutter, cfs.fnumber, cfs.iso, cfs.expcomp, scale ); + image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, &cfs, scale ); } tpp->getDimensions(lastW, lastH, lastScale); @@ -627,7 +628,8 @@ rtengine::IImage8* Thumbnail::upgradeThumbImage (const rtengine::procparams::Pro return nullptr; } - rtengine::IImage8* image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.getCamera(), cfs.focalLen, cfs.focalLen35mm, cfs.focusDist, cfs.shutter, cfs.fnumber, cfs.iso, cfs.expcomp, scale ); + // rtengine::IImage8* image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.getCamera(), cfs.focalLen, cfs.focalLen35mm, cfs.focusDist, cfs.shutter, cfs.fnumber, cfs.iso, cfs.expcomp, scale ); + rtengine::IImage8* image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, &cfs, scale ); tpp->getDimensions(lastW, lastH, lastScale); delete tpp; From 5656d16e64a04c4829f1b75a937c59e3963f0e5d Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 6 Sep 2017 15:27:54 +0200 Subject: [PATCH 06/52] LCP: filter out bad vignetting correction entries Candidate fix for regression #4062 --- rtengine/lcp.cc | 139 ++++++++++++++++++++++++++---------------------- rtengine/lcp.h | 12 +++-- 2 files changed, 83 insertions(+), 68 deletions(-) diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index c09d2d9be..048ba9dcf 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -134,11 +134,20 @@ LCPPersModel::LCPPersModel() } // mode: 0=distortion, 1=vignette, 2=CA -bool LCPPersModel::hasModeData(int mode) const +bool LCPPersModel::hasModeData(LCPCorrectionMode mode) const { - return (mode == 0 && !vignette.empty() && !vignette.bad_error) || (mode == 1 && !base.empty() && !base.bad_error) - || (mode == 2 && !chromRG.empty() && !chromG.empty() && !chromBG.empty() && - !chromRG.bad_error && !chromG.bad_error && !chromBG.bad_error); + switch (mode) { + case LCP_MODE_VIGNETTE: + return !vignette.empty() && !vignette.bad_error; + case LCP_MODE_DISTORTION: + return !base.empty() && !base.bad_error; + case LCP_MODE_CA: + return !chromRG.empty() && !chromG.empty() && !chromBG.empty() && + !chromRG.bad_error && !chromG.bad_error && !chromBG.bad_error; + default: + assert(false); + return false; + } } void LCPPersModel::print() const @@ -195,11 +204,11 @@ LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm printf("Vign: %i, fullWidth: %i/%i, focLen %g SwapXY: %i / MirX/Y %i / %i on rot:%i from %i\n",vignette, fullWidth, fullHeight, focalLength, swapXY, mirrorX, mirrorY, rot, rawRotationDeg); } - pProf->calcParams(vignette ? 0 : 1, focalLength, focusDist, aperture, &mc, nullptr, nullptr); + pProf->calcParams(vignette ? LCP_MODE_VIGNETTE : LCP_MODE_DISTORTION, focalLength, focusDist, aperture, &mc, nullptr, nullptr); mc.prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); if (!vignette) { - pProf->calcParams(2, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]); + pProf->calcParams(LCP_MODE_CA, 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); @@ -410,9 +419,11 @@ LCPProfile::LCPProfile(const Glib::ustring &fname) } // Two phase filter: first filter out the very rough ones, that distord the average a lot // force it, even if there are few frames (community profiles) -// filterBadFrames(2.0, 0); + filterBadFrames(LCP_MODE_VIGNETTE, 2.0, 0); + filterBadFrames(LCP_MODE_CA, 2.0, 0); // from the non-distorded, filter again on new average basis, but only if there are enough frames left -// filterBadFrames(1.5, 100); + filterBadFrames(LCP_MODE_VIGNETTE, 1.5, 50); + filterBadFrames(LCP_MODE_CA, 1.5, 50); } @@ -429,67 +440,66 @@ LCPProfile::~LCPProfile() } // from all frames not marked as bad already, take average and filter out frames with higher deviation than this if there are enough values -int LCPProfile::filterBadFrames(double maxAvgDevFac, int minFramesLeft) +int LCPProfile::filterBadFrames(LCPCorrectionMode mode, double maxAvgDevFac, int minFramesLeft) { - // take average error per type, then calculated the maximum deviation allowed - double errBase = 0, errChrom = 0, errVignette = 0; - int baseCount = 0, chromCount = 0, vignetteCount = 0; + // take average error, then calculated the maximum deviation allowed + double err = 0; + int count = 0; for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) { - if (aPersModel[pm]->hasModeData(0)) { - errVignette += aPersModel[pm]->vignette.mean_error; - vignetteCount++; - } - - if (aPersModel[pm]->hasModeData(1)) { - errBase += aPersModel[pm]->base.mean_error; - baseCount++; - } - - if (aPersModel[pm]->hasModeData(2)) { - errChrom += rtengine::max(aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error, aPersModel[pm]->chromBG.mean_error); - chromCount++; + if (aPersModel[pm]->hasModeData(mode)) { + count++; + switch (mode) { + case LCP_MODE_VIGNETTE: + err += aPersModel[pm]->vignette.mean_error; + break; + case LCP_MODE_DISTORTION: + err += aPersModel[pm]->base.mean_error; + break; + case LCP_MODE_CA: + err += rtengine::max(aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error, aPersModel[pm]->chromBG.mean_error); + break; + } } } // Only if we have enough frames, filter out errors int filtered = 0; - if (baseCount + chromCount + vignetteCount >= minFramesLeft) { - if (baseCount > 0) { - errBase /= (double)baseCount; - } - - if (chromCount > 0) { - errChrom /= (double)chromCount; - } - - if (vignetteCount > 0) { - errVignette /= (double)vignetteCount; + if (count >= minFramesLeft) { + if (count > 0) { + err /= (double)count; } // Now mark all the bad ones as bad, and hasModeData will return false; for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) { - if (aPersModel[pm]->hasModeData(0) && aPersModel[pm]->vignette.mean_error > maxAvgDevFac * errVignette) { - aPersModel[pm]->vignette.bad_error = true; - filtered++; - } - - if (aPersModel[pm]->hasModeData(1) && aPersModel[pm]->base.mean_error > maxAvgDevFac * errBase) { - aPersModel[pm]->base.bad_error = true; - filtered++; - } - - if (aPersModel[pm]->hasModeData(2) && - (aPersModel[pm]->chromRG.mean_error > maxAvgDevFac * errChrom || aPersModel[pm]->chromG.mean_error > maxAvgDevFac * errChrom - || aPersModel[pm]->chromBG.mean_error > maxAvgDevFac * errChrom)) { - aPersModel[pm]->chromRG.bad_error = aPersModel[pm]->chromG.bad_error = aPersModel[pm]->chromBG.bad_error = true; - filtered++; + if (aPersModel[pm]->hasModeData(mode)) { + switch (mode) { + case LCP_MODE_VIGNETTE: + if (aPersModel[pm]->vignette.mean_error > maxAvgDevFac * err) { + aPersModel[pm]->vignette.bad_error = true; + filtered++; + } + break; + case LCP_MODE_DISTORTION: + if (aPersModel[pm]->base.mean_error > maxAvgDevFac * err) { + aPersModel[pm]->base.bad_error = true; + filtered++; + } + break; + case LCP_MODE_CA: + if ((aPersModel[pm]->chromRG.mean_error > maxAvgDevFac * err || aPersModel[pm]->chromG.mean_error > maxAvgDevFac * err + || aPersModel[pm]->chromBG.mean_error > maxAvgDevFac * err)) { + aPersModel[pm]->chromRG.bad_error = aPersModel[pm]->chromG.bad_error = aPersModel[pm]->chromBG.bad_error = true; + filtered++; + } + break; + } } } if (settings->verbose) { - printf("Filtered %.1f%% frames for maxAvgDevFac %g leaving %i\n", filtered*100./(baseCount+chromCount+vignetteCount), maxAvgDevFac, baseCount+chromCount+vignetteCount-filtered); + printf("Filtered %.1f%% frames for maxAvgDevFac %g leaving %i\n", filtered*100./count, maxAvgDevFac, count-filtered); } } @@ -497,8 +507,7 @@ int LCPProfile::filterBadFrames(double maxAvgDevFac, int minFramesLeft) } -// 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 +void LCPProfile::calcParams(LCPCorrectionMode mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const { float euler = exp(1.0); @@ -541,24 +550,24 @@ void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float if (aPersModel[pm]->hasModeData(mode)) { double lowMeanErr, highMeanErr; switch (mode) { - case 0: + case LCP_MODE_VIGNETTE: meanErr = aPersModel[pm]->vignette.mean_error; lowMeanErr = pLow->vignette.mean_error; highMeanErr = pHigh->vignette.mean_error; break; - case 1: + case LCP_MODE_DISTORTION: meanErr = aPersModel[pm]->base.mean_error; lowMeanErr = pLow->base.mean_error; highMeanErr = pHigh->base.mean_error; break; - default: //case 2: + default: // LCP_MODE_CA meanErr = aPersModel[pm]->chromG.mean_error; lowMeanErr = pLow->chromG.mean_error; highMeanErr = pHigh->chromG.mean_error; break; } - if (aperture > 0 && mode != 2) { + if (aperture > 0 && mode != LCP_MODE_CA) { if (aPersModel[pm]->focLen == bestFocLenLow && ( (aper == aperture && lowMeanErr > meanErr) || (aper >= aperture && aper < pLow->aperture && pLow->aperture > aperture) @@ -572,7 +581,7 @@ void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float || (aper >= aperture && (pHigh->aperture < aperture || fabs(aperture - aper) < fabs(aperture - pHigh->aperture))))) { pHigh = aPersModel[pm]; } - } else if (focusDist > 0 && mode != 0) { + } else if (focusDist > 0 && mode != LCP_MODE_VIGNETTE) { // by focus distance if (aPersModel[pm]->focLen == bestFocLenLow && ( (focDist == focusDist && lowMeanErr > meanErr) @@ -615,26 +624,26 @@ void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float } // and average the other factor if available - if (mode == 0 && pLow->aperture < aperture && pHigh->aperture > aperture) { + if (mode == LCP_MODE_VIGNETTE && 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) { + } else if (mode != LCP_MODE_VIGNETTE && focusDist > 0 && pLow->focDist < focusDist && pHigh->focDist > focusDist) { // focus distance for all else (if focus distance is given) float facDistLow = (log(pHigh->focDist) + euler - focusDistLog) / (log(pHigh->focDist) - log(pLow->focDist)); facLow = focLenOnSpot ? facDistLow : (0.8 * facLow + 0.2 * facDistLow); } switch (mode) { - case 0: // vignette + case LCP_MODE_VIGNETTE: pCorr1->merge(pLow->vignette, pHigh->vignette, facLow); break; - case 1: // distortion + case LCP_MODE_DISTORTION: pCorr1->merge(pLow->base, pHigh->base, facLow); break; - case 2: // CA + case LCP_MODE_CA: pCorr1->merge(pLow->chromRG, pHigh->chromRG, facLow); pCorr2->merge(pLow->chromG, pHigh->chromG, facLow); pCorr3->merge(pLow->chromBG, pHigh->chromBG, facLow); @@ -646,7 +655,7 @@ void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float } } else { if (settings->verbose) { - printf("Error: LCP file contained no %s parameters\n", mode == 0 ? "vignette" : mode == 1 ? "distortion" : "CA" ); + printf("Error: LCP file contained no %s parameters\n", mode == LCP_MODE_VIGNETTE ? "vignette" : mode == LCP_MODE_DISTORTION ? "distortion" : "CA" ); } } } diff --git a/rtengine/lcp.h b/rtengine/lcp.h index f7164117f..291710c5f 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -33,6 +33,12 @@ namespace rtengine { +enum LCPCorrectionMode { + LCP_MODE_VIGNETTE = 0, + LCP_MODE_DISTORTION = 1, + LCP_MODE_CA = 2 +}; + // Perspective model common data, also used for Vignette and Fisheye class LCPModelCommon final { @@ -76,7 +82,7 @@ public: LCPModelCommon vignette; // vignette (may be empty) LCPPersModel(); - bool hasModeData(int mode) const; + bool hasModeData(LCPCorrectionMode mode) const; void print() const; }; @@ -93,7 +99,7 @@ class LCPProfile static void XMLCALL XmlTextHandler (void *pLCPProfile, const XML_Char *s, int len); static void XMLCALL XmlEndHandler (void *pLCPProfile, const char *el); - int filterBadFrames(double maxAvgDevFac, int minFramesLeft); + int filterBadFrames(LCPCorrectionMode mode, double maxAvgDevFac, int minFramesLeft); void handle_text(std::string text); std::ostringstream textbuf; @@ -112,7 +118,7 @@ public: explicit LCPProfile(const Glib::ustring &fname); ~LCPProfile(); - void calcParams(int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const; // Interpolates between the persModels frames + void calcParams(LCPCorrectionMode mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const; // Interpolates between the persModels frames void print() const; }; From dde94bc6d3ccfa2ebbb0f7ce8a1a6f7ddc362911 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 7 Sep 2017 00:53:03 +0200 Subject: [PATCH 07/52] added basic integration of lensfun so far only distortion correction, and no GUI yet --- CMakeLists.txt | 1 + rtengine/CMakeLists.txt | 3 + rtengine/dcrop.cc | 5 +- rtengine/improccoordinator.cc | 5 +- rtengine/improcfun.h | 9 +- rtengine/init.cc | 2 + rtengine/iptransform.cc | 65 ++++++++++--- rtengine/lcp.cc | 8 +- rtengine/lcp.h | 18 +++- rtengine/procparams.cc | 61 ++++++++++++ rtengine/procparams.h | 6 ++ rtengine/rtlensfun.cc | 178 ++++++++++++++++++++++------------ rtengine/rtlensfun.h | 48 ++++++--- rtengine/rtthumbnail.cc | 3 - rtgui/CMakeLists.txt | 4 + rtgui/cacheimagedata.h | 6 +- rtgui/paramsedited.cc | 10 ++ rtgui/paramsedited.h | 1 + 18 files changed, 320 insertions(+), 113 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 685b418bf..e506499b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,6 +272,7 @@ pkg_check_modules (GIOMM REQUIRED giomm-2.4>=2.44) pkg_check_modules (GTHREAD REQUIRED gthread-2.0>=2.44) pkg_check_modules (GOBJECT REQUIRED gobject-2.0>=2.44) pkg_check_modules (SIGC REQUIRED sigc++-2.0>=2.3.1) +pkg_check_modules (LENSFUN REQUIRED lensfun>=0.2) if(WIN32) add_definitions(-DWIN32) diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index a813d4156..32d99cb8f 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -9,6 +9,7 @@ include_directories(${EXTRA_INCDIR} ${GTK_INCLUDE_DIRS} ${IPTCDATA_INCLUDE_DIRS} ${LCMS_INCLUDE_DIRS} + ${LENSFUN_INCLUDE_DIRS} ) link_directories("${PROJECT_SOURCE_DIR}/rtexif" @@ -109,6 +110,7 @@ set(RTENGINESOURCEFILES slicer.cc stdimagesource.cc utils.cc + rtlensfun.cc ) if(NOT WITH_SYSTEM_KLT) @@ -153,6 +155,7 @@ target_link_libraries(rtengine rtexif ${PNG_LIBRARIES} ${TIFF_LIBRARIES} ${ZLIB_LIBRARIES} + ${LENSFUN_LIBRARIES} ) install(FILES ${CAMCONSTSFILE} DESTINATION "${DATADIR}" PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index acd73c0a3..92416917d 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -1086,8 +1086,9 @@ bool check_need_larger_crop_for_lcp_distortion (int fw, int fh, int x, int y, in return false; } - return (params.lensProf.lcpFile.length() > 0 && - params.lensProf.useDist); + return (params.lensProf.useDist && + (params.lensProf.useLensfun || + params.lensProf.lcpFile.length() > 0)); } } // namespace diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index b14db1588..9f53ab81d 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -400,8 +400,9 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) } if (needstransform) - ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, fw, fh, imgsrc->getMetaData()->getFocalLen(), + ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, fw, fh, imgsrc->getMetaData(), + // imgsrc->getMetaData()->getFocalLen(), // imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(), imgsrc->getMetaData()->getFNumber(), imgsrc->getRotateDegree(), false); else { @@ -1120,7 +1121,7 @@ void ImProcCoordinator::getAutoCrop (double ratio, int &x, int &y, int &w, int & MyMutex::MyLock lock (mProcessing); - LCPMapper *pLCPMap = nullptr; + LensCorrection *pLCPMap = nullptr; if (params.lensProf.lcpFile.length() && imgsrc->getMetaData()->getFocalLen() > 0) { LCPProfile *pLCPProf = lcpStore->getProfile (params.lensProf.lcpFile); diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 983295c38..1bcca4d37 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -60,7 +60,7 @@ class ImProcFunctions TRANSFORM_HIGH_QUALITY_FULLIMAGE }; void transformLuminanceOnly (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH, int fW, int fH); - void transformGeneral(TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LCPMapper *pLCPMap); + void transformGeneral(TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap); void sharpenHaloCtrl (float** luminance, float** blurmap, float** base, int W, int H, const SharpeningParams &sharpenParam); void sharpenHaloCtrl (LabImage* lab, float** blurmap, float** base, int W, int H, SharpeningParams &sharpenParam); @@ -74,6 +74,7 @@ class ImProcFunctions bool needsGradient (); bool needsVignetting (); bool needsLCP (); + bool needsLensfun(); // static cmsUInt8Number* Mempro = NULL; inline void interpolateTransformCubic (Imagefloat* src, int xs, int ys, double Dx, double Dy, float *r, float *g, float *b, double mul) @@ -352,11 +353,11 @@ public: Image16* lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, bool bw, GammaValues *ga = nullptr); // CieImage *ciec; - bool transCoord (int W, int H, int x, int y, int w, int h, int& xv, int& yv, int& wv, int& hv, double ascaleDef = -1, const LCPMapper *pLCPMap = nullptr); - bool transCoord (int W, int H, const std::vector &src, std::vector &red, std::vector &green, std::vector &blue, double ascaleDef = -1, const LCPMapper *pLCPMap = nullptr); + bool transCoord (int W, int H, int x, int y, int w, int h, int& xv, int& yv, int& wv, int& hv, double ascaleDef = -1, const LensCorrection *pLCPMap = nullptr); + bool transCoord (int W, int H, const std::vector &src, std::vector &red, std::vector &green, std::vector &blue, double ascaleDef = -1, const LensCorrection *pLCPMap = nullptr); static void getAutoExp (const LUTu & histogram, int histcompr, double defgain, double clip, double& expcomp, int& bright, int& contr, int& black, int& hlcompr, int& hlcomprthresh); static double getAutoDistor (const Glib::ustring& fname, int thumb_size); - double getTransformAutoFill (int oW, int oH, const LCPMapper *pLCPMap = nullptr); + double getTransformAutoFill (int oW, int oH, const LensCorrection *pLCPMap = nullptr); void rgb2lab (const Imagefloat &src, LabImage &dst, const Glib::ustring &workingSpace); void lab2rgb (const LabImage &src, Imagefloat &dst, const Glib::ustring &workingSpace); }; diff --git a/rtengine/init.cc b/rtengine/init.cc index 2d157c762..7ef40f43a 100644 --- a/rtengine/init.cc +++ b/rtengine/init.cc @@ -30,6 +30,7 @@ #include "rtthumbnail.h" #include "profilestore.h" #include "../rtgui/threadutils.h" +#include "rtlensfun.h" namespace rtengine { @@ -50,6 +51,7 @@ int init (const Settings* s, Glib::ustring baseDir, Glib::ustring userSettingsDi Color::init (); PerceptualToneCurve::init (); RawImageSource::init (); + LFDatabase::init(); delete lcmsMutex; lcmsMutex = new MyMutex; dfm.init( s->darkFramesPath ); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 6d34c5935..a054cc57a 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -24,6 +24,7 @@ #include "mytime.h" #include "rt_math.h" #include "sleef.c" +#include "rtlensfun.h" using namespace std; @@ -86,16 +87,18 @@ float normn (float a, float b, int n) } -void correct_distortion(const rtengine::LCPMapper *lcp, double &x, double &y, +inline void correct_distortion(const rtengine::LensCorrection *lcp, double &x, double &y, int cx, int cy, double scale) { assert (lcp); - x += cx; - y += cy; - lcp->correctDistortion(x, y, scale); - x -= (cx * scale); - y -= (cy * scale); + // x += cx; + // y += cy; + // std::cout << "DIST: x=" << x << ", y=" << y; + lcp->correctDistortion(x, y, cx, cy, scale); + // std::cout << " --> pos[0]=" << x << ", pos[1]=" << y << std::endl; + // x -= (cx * scale); + // y -= (cy * scale); } } @@ -107,7 +110,7 @@ namespace rtengine #define CLIPTOC(a,b,c,d) ((a)>=(b)?((a)<=(c)?(a):(d=true,(c))):(d=true,(b))) bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, std::vector &red, std::vector &green, std::vector &blue, double ascaleDef, - const LCPMapper *pLCPMap) + const LensCorrection *pLCPMap) { bool clipped = false; @@ -209,7 +212,7 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, } // Transform all corners and critical sidelines of an image -bool ImProcFunctions::transCoord (int W, int H, int x, int y, int w, int h, int& xv, int& yv, int& wv, int& hv, double ascaleDef, const LCPMapper *pLCPMap) +bool ImProcFunctions::transCoord (int W, int H, int x, int y, int w, int h, int& xv, int& yv, int& wv, int& hv, double ascaleDef, const LensCorrection *pLCPMap) { const int DivisionsPerBorder = 32; @@ -316,9 +319,30 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, float focusDist = metadata->getFocusDist(); double fNumber = metadata->getFNumber(); - LCPMapper *pLCPMap = nullptr; + LensCorrection *pLCPMap = nullptr; - if (needsLCP()) { // don't check focal length to allow distortion correction for lenses without chip + if (needsLensfun()) { + const LFDatabase *db = LFDatabase::getInstance(); + Glib::ustring make, model, lens; + if (params->lensProf.lfAutoMatch) { + make = metadata->getMake(); + model = metadata->getModel(); + lens = metadata->getLens(); + } else { + make = params->lensProf.lfCameraMake; + model = params->lensProf.lfCameraModel; + lens = params->lensProf.lfLens; + } + LFCamera c = db->findCamera(make, model); + LFLens l = db->findLens(c, lens); + pLCPMap = db->getModifier(c, l, fW, fH, focalLen, fNumber, focusDist); + + std::cout << "LENSFUN:\n" + << " camera: " << c.getDisplayString() << "\n" + << " lens: " << l.getDisplayString() << "\n" + << " correction? " << (pLCPMap ? "yes" : "no") << std::endl; + + } else if (needsLCP()) { // don't check focal length to allow distortion correction for lenses without chip LCPProfile *pLCPProf = lcpStore->getProfile (params->lensProf.lcpFile); if (pLCPProf) { @@ -329,7 +353,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, } } - if (! (needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP()) && (needsVignetting() || needsPCVignetting() || needsGradient())) { + if (! (needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsLensfun()) && (needsVignetting() || needsPCVignetting() || needsGradient())) { transformLuminanceOnly (original, transformed, cx, cy, oW, oH, fW, fH); } else { TransformMode mode; @@ -734,7 +758,7 @@ void ImProcFunctions::transformLuminanceOnly (Imagefloat* original, Imagefloat* } -void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LCPMapper *pLCPMap) +void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imagefloat *original, Imagefloat *transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const LensCorrection *pLCPMap) { double w2 = (double) oW / 2.0 - 0.5; double h2 = (double) oH / 2.0 - 0.5; @@ -801,7 +825,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag switch (mode) { case ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE: - enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->enableCA; + enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->supportsCA(); // no break on purpose case ImProcFunctions::TRANSFORM_HIGH_QUALITY: enableLCPDist = pLCPMap && params->lensProf.useDist; @@ -958,12 +982,16 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag } -double ImProcFunctions::getTransformAutoFill (int oW, int oH, const LCPMapper *pLCPMap) +double ImProcFunctions::getTransformAutoFill (int oW, int oH, const LensCorrection *pLCPMap) { if (!needsCA() && !needsDistortion() && !needsRotation() && !needsPerspective() && (!params->lensProf.useDist || pLCPMap == nullptr)) { return 1; } + if (pLCPMap && !pLCPMap->supportsAutoFill()) { + return 1; + } + double scaleU = 2, scaleL = 0.001; // upper and lower border, iterate inbetween do { @@ -1019,12 +1047,17 @@ bool ImProcFunctions::needsVignetting () bool ImProcFunctions::needsLCP () { - return params->lensProf.lcpFile.length() > 0; + return params->lensProf.lcpFile.length() > 0 && !needsLensfun(); +} + +bool ImProcFunctions::needsLensfun() +{ + return params->lensProf.useLensfun; } bool ImProcFunctions::needsTransform () { - return needsCA () || needsDistortion () || needsRotation () || needsPerspective () || needsGradient () || needsPCVignetting () || needsVignetting () || needsLCP(); + return needsCA () || needsDistortion () || needsRotation () || needsPerspective () || needsGradient () || needsPCVignetting () || needsVignetting () || needsLCP() || needsLensfun(); } diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index c09d2d9be..92a3a3ce8 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -210,8 +210,11 @@ LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm isFisheye = pProf->isFisheye; } -void LCPMapper::correctDistortion(double& x, double& y, double scale) const +void LCPMapper::correctDistortion(double &x, double &y, int cx, int cy, double scale) const { + x += cx; + y += cy; + if (isFisheye) { double u = x * scale; double v = y * scale; @@ -253,6 +256,9 @@ void LCPMapper::correctDistortion(double& x, double& y, double scale) const x = xnew * mc.fx + x0; y = ynew * mc.fy + y0; } + + x -= cx * scale; + y -= cy * scale; } void LCPMapper::correctCA(double& x, double& y, int channel) const diff --git a/rtengine/lcp.h b/rtengine/lcp.h index f7164117f..1a4b677d3 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -136,8 +136,20 @@ public: #define lcpStore LCPStore::getInstance() +class LensCorrection { +public: + virtual ~LensCorrection() {} + virtual void correctDistortion(double &x, double &y, int cx, int cy, double scale) const = 0; + virtual bool supportsAutoFill() const = 0; + virtual bool supportsCA() const = 0; + virtual void correctCA(double &x, double &y, int channel) const = 0; + virtual void processVignetteLine(int width, int y, float *line) const = 0; + virtual void processVignetteLine3Channels(int width, int y, float *line) const = 0; +}; + + // Once precalculated class to correct a point -class LCPMapper +class LCPMapper: public LensCorrection { bool useCADist; // should the distortion in the CA info be used? @@ -153,7 +165,9 @@ public: 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, double scale) const; // MUST be the first stage + void correctDistortion(double &x, double &y, int cx, int cy, double scale) const; // MUST be the first stage + bool supportsCA() const { return enableCA; } + bool supportsAutoFill() const { return true; } void correctCA(double& x, double& y, int channel) const; void processVignetteLine(int width, int y, float *line) const; void processVignetteLine3Channels(int width, int y, float *line) const; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index e1a3002e2..29b2cce84 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -922,6 +922,11 @@ void LensProfParams::setDefaults() lcpFile = ""; useDist = useVign = true; useCA = false; + useLensfun = false; + lfAutoMatch = true; + lfCameraMake = ""; + lfCameraModel = ""; + lfLens = ""; } void CoarseTransformParams::setDefaults() @@ -2565,6 +2570,22 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("LensProfile", "UseCA", lensProf.useCA); } + if (!pedited || pedited->lensProf.useLensfun) { + keyFile.set_boolean("LensProfile", "UseLensfun", lensProf.useLensfun); + } + if (!pedited || pedited->lensProf.lfAutoMatch) { + keyFile.set_boolean("LensProfile", "LFAutoMatch", lensProf.lfAutoMatch); + } + if (!pedited || pedited->lensProf.lfCameraMake) { + keyFile.set_string("LensProfile", "LFCameraMake", lensProf.lfCameraMake); + } + if (!pedited || pedited->lensProf.lfCameraModel) { + keyFile.set_string("LensProfile", "LFCameraModel", lensProf.lfCameraModel); + } + if (!pedited || pedited->lensProf.lfLens) { + keyFile.set_string("LensProfile", "LFLens", lensProf.lfLens); + } + // save perspective correction if (!pedited || pedited->perspective.horizontal) { keyFile.set_double ("Perspective", "Horizontal", perspective.horizontal); @@ -5832,6 +5853,41 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) pedited->lensProf.useCA = true; } } + + if (keyFile.has_key("LensProfile", "UseLensfun")) { + lensProf.useLensfun = keyFile.get_boolean("LensProfile", "UseLensfun"); + if (pedited) { + pedited->lensProf.useLensfun = true; + } + } + + if (keyFile.has_key("LensProfile", "LFAutoMatch")) { + lensProf.lfAutoMatch = keyFile.get_boolean("LensProfile", "LFAutoMatch"); + if (pedited) { + pedited->lensProf.lfAutoMatch = true; + } + } + + if (keyFile.has_key("LensProfile", "LFCameraMake")) { + lensProf.lfCameraMake = keyFile.get_string("LensProfile", "LFCameraMake"); + if (pedited) { + pedited->lensProf.lfCameraMake = true; + } + } + + if (keyFile.has_key("LensProfile", "LFCameraModel")) { + lensProf.lfCameraModel = keyFile.get_string("LensProfile", "LFCameraModel"); + if (pedited) { + pedited->lensProf.lfCameraModel = true; + } + } + + if (keyFile.has_key("LensProfile", "LFLens")) { + lensProf.lfLens = keyFile.get_string("LensProfile", "LFLens"); + if (pedited) { + pedited->lensProf.lfLens = true; + } + } } // load perspective correction @@ -8432,6 +8488,11 @@ bool ProcParams::operator== (const ProcParams& other) && lensProf.useDist == other.lensProf.useDist && lensProf.useVign == other.lensProf.useVign && lensProf.useCA == other.lensProf.useCA + && lensProf.useLensfun == other.lensProf.useLensfun + && lensProf.lfAutoMatch == other.lensProf.lfAutoMatch + && lensProf.lfCameraMake == other.lensProf.lfCameraMake + && lensProf.lfCameraModel == other.lensProf.lfCameraModel + && lensProf.lfLens == other.lensProf.lfLens && perspective.horizontal == other.perspective.horizontal && perspective.vertical == other.perspective.vertical && gradient.enabled == other.gradient.enabled diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 53561ce16..c7bedf611 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -831,6 +831,11 @@ class LensProfParams public: Glib::ustring lcpFile; bool useDist, useVign, useCA; + bool useLensfun; + bool lfAutoMatch; + Glib::ustring lfCameraMake; + Glib::ustring lfCameraModel; + Glib::ustring lfLens; LensProfParams() { @@ -839,6 +844,7 @@ public: void setDefaults(); }; + /** * Parameters of the perspective correction */ diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 1b8009ee1..7d376269d 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -26,32 +26,46 @@ namespace rtengine { // LFModifier //----------------------------------------------------------------------------- -bool LFModifier::ok() const +LFModifier::LFModifier(lfModifier *m): + data_(m) { - return data_.get(); } -void LFModifier::correctDistortion(double &x, double &y) +LFModifier::~LFModifier() +{ + if (data_) { + data_->Destroy(); + } +} + +bool LFModifier::ok() const +{ + return data_; +} + + +void LFModifier::correctDistortion(double &x, double &y, int cx, int cy, double scale) const { if (!ok()) { return; } float pos[2]; - data_->ApplyGeometryDistortion(x, y, 1, 1, pos); - x = pos[0]; - y = pos[1]; + if (data_->ApplyGeometryDistortion(x+cx, y+cy, 1, 1, pos)) { + x = pos[0] - cx; + y = pos[1] - cy; + } } -void LFModifier::processVignetteLine(int width, int y, float *line) +void LFModifier::processVignetteLine(int width, int y, float *line) const { // TODO } -void LFModifier::processVignetteLine3Channels(int width, int y, float *line) +void LFModifier::processVignetteLine3Channels(int width, int y, float *line) const { // TODO } @@ -61,15 +75,21 @@ void LFModifier::processVignetteLine3Channels(int width, int y, float *line) // LFCamera //----------------------------------------------------------------------------- +LFCamera::LFCamera(): + data_(nullptr) +{ +} + + bool LFCamera::ok() const { - return data_.get(); + return data_; } Glib::ustring LFCamera::getMake() const { - if (ok()) { + if (data_) { return data_->Maker; } else { return ""; @@ -79,7 +99,7 @@ Glib::ustring LFCamera::getMake() const Glib::ustring LFCamera::getModel() const { - if (ok()) { + if (data_) { return data_->Model; } else { return ""; @@ -89,7 +109,7 @@ Glib::ustring LFCamera::getModel() const float LFCamera::getCropFactor() const { - if (ok()) { + if (data_) { return data_->CropFactor; } else { return 0; @@ -99,7 +119,7 @@ float LFCamera::getCropFactor() const Glib::ustring LFCamera::getDisplayString() const { - if (ok()) { + if (data_) { return Glib::ustring::compose("%1 %2", getMake(), getModel()); } else { return "---"; @@ -111,15 +131,21 @@ Glib::ustring LFCamera::getDisplayString() const // LFLens //----------------------------------------------------------------------------- +LFLens::LFLens(): + data_(nullptr) +{ +} + + bool LFLens::ok() const { - return data_->get(); + return data_; } Glib::ustring LFLens::getDisplayString() const { - if (ok()) { + if (data_) { return Glib::ustring::compose("%1 %2", data_->Maker, data_->Model); } else { return "---"; @@ -136,87 +162,111 @@ LFDatabase LFDatabase::instance_; bool LFDatabase::init() { - instance_.data_.reset(new lfDatabase()); + instance_.data_ = lfDatabase::Create(); return instance_.data_->Load() != LF_NO_ERROR; } -LFDatabase *LFDatabase::getInstance() +LFDatabase::LFDatabase(): + data_(nullptr) +{ +} + + +LFDatabase::~LFDatabase() +{ + if (data_) { + data_->Destroy(); + } +} + + +const LFDatabase *LFDatabase::getInstance() { return &instance_; } -std::vector LFDatabase::getCameras() +std::vector LFDatabase::getCameras() const { - auto cams = data_->GetCameras(); std::vector ret; - while (*cams) { - ret.emplace_back(LFCamera()); - ret.back().data_.reset(new lfCamera(**cams)); - ++cams; - } + if (data_) { + auto cams = data_->GetCameras(); + while (*cams) { + ret.emplace_back(LFCamera()); + ret.back().data_ = *cams; + ++cams; + } + } return ret; } -std::vector getLenses(const LFCamera &camera) +std::vector LFDatabase::getLenses(const LFCamera &camera) const { - auto lenses = data_->FindLenses(*camera.data_->get(), NULL, "", LF_SEARCH_LOOSE | LF_SEARCH_SORT_AND_UNIQUIFY); std::vector ret; - while (*lenses) { - ret.emplace_back(LFLens()); - ret.back().data_.reset(new lfLens(**lenses)); - ++lenses; + if (data_) { + auto lenses = data_->FindLenses(camera.data_, NULL, "", LF_SEARCH_LOOSE /*| LF_SEARCH_SORT_AND_UNIQUIFY*/); + while (*lenses) { + ret.emplace_back(LFLens()); + ret.back().data_ = *lenses; + ++lenses; + } + lf_free(lenses); } - lf_free(lenses); return ret; } -LFCamera LFDatabase::findCamera(const Glib::ustring &make, const Glib::ustring &model) +LFCamera LFDatabase::findCamera(const Glib::ustring &make, const Glib::ustring &model) const { LFCamera ret; - auto found = data_->FindCamerasExt(make.c_str(), model.c_str(), LF_SEARCH_LOOSE); - if (found) { - ret.data_.reset(new lfCamera(*found[0])); - lf_free(found); - } - return ret; -} - - -LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) -{ - LFLens ret; - auto found = data_->FindLenses(camera.data_.get(), NULL, name.c_str(), LF_SEARCH_LOOSE); - if (!found) { - // try to split the maker from the model of the lens - Glib::ustring make, model; - auto i = name.find_first_of(' '); - if (i != Glib::ustring::npos) { - make = name.substr(0, i); - model = name.substr(i+1); - found = data_->FindLenses(camera.data_.get(), make.c_str(), model.c_str(), LF_SEARCH_LOOSE); + if (data_) { + auto found = data_->FindCamerasExt(make.c_str(), model.c_str(), LF_SEARCH_LOOSE); + if (found) { + ret.data_ = found[0]; + lf_free(found); } } - if (found) { - ret.data_.reset(new lfLens(*found[0])); - lf_free(found); + return ret; +} + + +LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) const +{ + LFLens ret; + if (data_) { + auto found = data_->FindLenses(camera.data_, NULL, name.c_str(), LF_SEARCH_LOOSE); + if (!found) { + // try to split the maker from the model of the lens + Glib::ustring make, model; + auto i = name.find_first_of(' '); + if (i != Glib::ustring::npos) { + make = name.substr(0, i); + model = name.substr(i+1); + found = data_->FindLenses(camera.data_, make.c_str(), model.c_str(), LF_SEARCH_LOOSE); + } + } + if (found) { + ret.data_ = found[0]; + lf_free(found); + } } return ret; } -LFModifier LFDatabase::getModifier(const LFCamera &camera, const LFLens &lens, - int width, int height, float focalLen, - float aperture) +LFModifier *LFDatabase::getModifier(const LFCamera &camera, const LFLens &lens, + int width, int height, float focalLen, + float aperture, float focusDist) const { - LFModifier ret; - if (camera.ok() && lens.ok()) { - lfModifier *mod = lfModifier::Create(lens.data_.get(), camera.getCropFactor(), width, height); - mod->Initialize(lens.data_.get(), LF_PF_F32, focalLen, aperture, 1000, 1, LF_RECTILINEAR, LF_MODIFY_VIGNETTING | LF_MODIFY_DISTORTION, false); - ret.data_.reset(mod); + LFModifier *ret = nullptr; + if (data_) { + if (camera.ok() && lens.ok()) { + lfModifier *mod = lfModifier::Create(lens.data_, camera.getCropFactor(), width, height); + mod->Initialize(lens.data_, LF_PF_F32, focalLen, aperture, focusDist > 0 ? focusDist : 1000, 0.0, LF_RECTILINEAR, LF_MODIFY_VIGNETTING | LF_MODIFY_DISTORTION, false); + ret = new LFModifier(mod); + } } return ret; } diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index aaa49c8e0..434f821fd 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -23,24 +23,34 @@ #include #include #include +#include "lcp.h" namespace rtengine { -class LFModifier { +class LFModifier: public LensCorrection { public: + ~LFModifier(); bool ok() const; - void correctDistortion(double &x, double &y); - void processVignetteLine(int width, int y, float *line); - void processVignetteLine3Channels(int width, int y, float *line); + void correctDistortion(double &x, double &y, int cx, int cy, double scale) const; + bool supportsAutoFill() const { return false; } + bool supportsCA() const { return false; } + void correctCA(double &x, double &y, int channel) const {} + void processVignetteLine(int width, int y, float *line) const; + void processVignetteLine3Channels(int width, int y, float *line) const; private: + explicit LFModifier(lfModifier *m); + LFModifier(const LFModifier &); + LFModifier &operator=(const LFModifier &); + friend class LFDatabase; - std::shared_ptr data_; + lfModifier *data_; }; class LFCamera { public: + LFCamera(); bool ok() const; Glib::ustring getMake() const; @@ -51,35 +61,41 @@ public: private: friend class LFDatabase; - std::shared_ptr data_; + const lfCamera *data_; }; class LFLens { public: + LFLens(); bool ok() const; Glib::ustring getDisplayString() const; private: friend class LFDatabase; - std::shared_ptr data_; + const lfLens *data_; }; class LFDatabase { public: static bool init(); - static LFDatabase *getInstance(); + static const LFDatabase *getInstance(); - std::vector getCameras(); - std::vector getLenses(const LFCamera &camera); - LFCamera findCamera(const Glib::ustring &make, const Glib::ustring &model); - LFLens findLens(const LFCamera &camera, const Glib::ustring &name); - LFModifier getModifier(const LFCamera &camera, const LFLens &lens, - int width, int height, - float focalLen, float aperture); + ~LFDatabase(); + + std::vector getCameras() const; + std::vector getLenses(const LFCamera &camera) const; + LFCamera findCamera(const Glib::ustring &make, const Glib::ustring &model) const; + LFLens findLens(const LFCamera &camera, const Glib::ustring &name) const; + LFModifier *getModifier(const LFCamera &camera, const LFLens &lens, + int width, int height, + float focalLen, float aperture, float focusDist) const; private: + LFDatabase(); + LFDatabase(const LFDatabase &); + LFDatabase &operator=(const LFDatabase &); static LFDatabase instance_; - std::shared_ptr data_; + lfDatabase *data_; }; } // namespace rtengine diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 52314ceaf..a5d951de2 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -959,9 +959,6 @@ IImage8* Thumbnail::quickProcessImage (const procparams::ProcParams& params, int IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rheight, TypeInterpolation interp, const ImageMetaData *metadata, double& myscale) { std::string camName = metadata->getCamera(); - double focalLen = metadata->getFocalLen(); - double focalLen35mm = metadata->getFocalLen35mm(); - float focusDist = metadata->getFocusDist(); float shutter = metadata->getShutterSpeed(); float fnumber = metadata->getFNumber(); float iso = metadata->getISOSpeed(); diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 05afd9af5..377b1bcbe 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -169,6 +169,7 @@ if(WIN32) ${GLIBMM_INCLUDE_DIRS} ${GTKMM_INCLUDE_DIRS} ${GTK_INCLUDE_DIRS} + ${LENSFUN_INCLUDE_DIRS} ) link_directories(. "${PROJECT_SOURCE_DIR}/rtexif" ${EXTRA_LIBDIR} @@ -194,6 +195,7 @@ else() ${GTK_INCLUDE_DIRS} ${IPTCDATA_INCLUDE_DIRS} ${LCMS_INCLUDE_DIRS} + ${LENSFUN_INCLUDE_DIRS} ) link_directories(${EXTRA_LIBDIR} ${CANBERRA-GTK_LIBRARY_DIRS} @@ -251,6 +253,7 @@ target_link_libraries(rth rtengine ${PNG_LIBRARIES} ${TIFF_LIBRARIES} ${ZLIB_LIBRARIES} + ${LENSFUN_LIBRARIES} ) target_link_libraries(rth-cli rtengine @@ -270,6 +273,7 @@ target_link_libraries(rth-cli rtengine ${PNG_LIBRARIES} ${TIFF_LIBRARIES} ${ZLIB_LIBRARIES} + ${LENSFUN_LIBRARIES} ) # Install executables diff --git a/rtgui/cacheimagedata.h b/rtgui/cacheimagedata.h index 378325630..0af6d4fcf 100644 --- a/rtgui/cacheimagedata.h +++ b/rtgui/cacheimagedata.h @@ -21,7 +21,7 @@ #include #include "options.h" -#include "rtengine.h" +#include "../rtengine/rtengine.h" class CacheImageData: public rtengine::ImageMetaData { @@ -89,9 +89,9 @@ public: bool hasExif() const { return false; } const rtexif::TagDirectory *getExifData() const { return NULL; } bool hasIPTC() const { return false; } - const procparams::IPTCPairs getIPTCData () const { return procparams::IPTCPairs(); } + const rtengine::procparams::IPTCPairs getIPTCData () const { return rtengine::procparams::IPTCPairs(); } struct tm getDateTime () const { struct tm ret; return ret; } - time_t getDateTimeAsTS() const { time_t ret; return ret; } + time_t getDateTimeAsTS() const { return time_t(-1); } int getISOSpeed() const { return iso; } double getFNumber() const { return fnumber; } double getFocalLen() const { return focalLen; } diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index ea9248ddc..588006eca 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -291,6 +291,11 @@ void ParamsEdited::set (bool v) lensProf.useDist = v; lensProf.useVign = v; lensProf.useCA = v; + lensProf.useLensfun = v; + lensProf.lfAutoMatch = v; + lensProf.lfCameraMake = v; + lensProf.lfCameraModel = v; + lensProf.lfLens = v; perspective.horizontal = v; perspective.vertical = v; gradient.enabled = v; @@ -825,6 +830,11 @@ void ParamsEdited::initFrom (const std::vector 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; + lensProf.useLensfun = lensProf.useLensfun && p.lensProf.useLensfun == other.lensProf.useLensfun; + lensProf.lfAutoMatch = lensProf.lfAutoMatch && p.lensProf.lfAutoMatch == other.lensProf.lfAutoMatch; + lensProf.lfCameraMake = lensProf.lfCameraMake && p.lensProf.lfCameraMake == other.lensProf.lfCameraMake; + lensProf.lfCameraModel = lensProf.lfCameraModel && p.lensProf.lfCameraModel == other.lensProf.lfCameraModel; + lensProf.lfLens = lensProf.lfLens && p.lensProf.lfLens == other.lensProf.lfLens; perspective.horizontal = perspective.horizontal && p.perspective.horizontal == other.perspective.horizontal; perspective.vertical = perspective.vertical && p.perspective.vertical == other.perspective.vertical; gradient.enabled = gradient.enabled && p.gradient.enabled == other.gradient.enabled; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 35e3c80b8..c30134f86 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -428,6 +428,7 @@ class LensProfParamsEdited { public: bool lcpFile, useDist, useVign, useCA; + bool useLensfun, lfAutoMatch, lfCameraMake, lfCameraModel, lfLens; bool isUnchanged() const; }; From 18f3bd6f4555f04b7c3f48ef2323ed5117f6eab7 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 7 Sep 2017 09:17:28 +0200 Subject: [PATCH 08/52] added vignette correction via lensfun --- rtengine/rawimagesource.cc | 28 +++++++++++++++++++++++++--- rtengine/rtlensfun.cc | 4 ++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 148d17ee9..c78dcff5f 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -33,6 +33,7 @@ #include "dcp.h" #include "rt_math.h" #include "improcfun.h" +#include "rtlensfun.h" #ifdef _OPENMP #include #endif @@ -1855,11 +1856,32 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le // Correct vignetting of lens profile if (!hasFlatField && lensProf.useVign) { - LCPProfile *pLCPProf = lcpStore->getProfile(lensProf.lcpFile); + std::unique_ptr pmap; + if (lensProf.useLensfun) { + const LFDatabase *db = LFDatabase::getInstance(); + Glib::ustring make, model, lens; + if (lensProf.lfAutoMatch) { + make = idata->getMake(); + model = idata->getModel(); + lens = idata->getLens(); + } else { + make = lensProf.lfCameraMake; + model = lensProf.lfCameraModel; + lens = lensProf.lfLens; + } + LFCamera c = db->findCamera(make, model); + LFLens l = db->findLens(c, lens); + pmap.reset(db->getModifier(c, l, W, H, idata->getFocalLen(), idata->getFNumber(), idata->getFocusDist())); + } else { + LCPProfile *pLCPProf = lcpStore->getProfile(lensProf.lcpFile); - if (pLCPProf) { // don't check focal length to allow distortion correction for lenses without chip, also pass dummy focal length 1 in case of 0 - LCPMapper map(pLCPProf, max(idata->getFocalLen(), 1.0), idata->getFocalLen35mm(), idata->getFocusDist(), idata->getFNumber(), true, false, W, H, coarse, -1); + if (pLCPProf) { // don't check focal length to allow distortion correction for lenses without chip, also pass dummy focal length 1 in case of 0 + pmap.reset(new LCPMapper(pLCPProf, max(idata->getFocalLen(), 1.0), idata->getFocalLen35mm(), idata->getFocusDist(), idata->getFNumber(), true, false, W, H, coarse, -1)); + } + } + if (pmap) { + LensCorrection &map = *pmap; if (ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS || ri->get_colors() == 1) { if(numFrames == 4) { for(int i = 0; i < 4; ++i) { diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 7d376269d..38e68a4df 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -61,13 +61,13 @@ void LFModifier::correctDistortion(double &x, double &y, int cx, int cy, double void LFModifier::processVignetteLine(int width, int y, float *line) const { - // TODO + data_->ApplyColorModification(line, 0, y, width, 1, LF_CR_1(INTENSITY), 0); } void LFModifier::processVignetteLine3Channels(int width, int y, float *line) const { - // TODO + data_->ApplyColorModification(line, 0, y, width, 1, LF_CR_3(RED, GREEN, BLUE), 0); } From 940577ce459d868b399d57033f6d8922275a0026 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 7 Sep 2017 09:57:37 +0200 Subject: [PATCH 09/52] handle coarse rotation in lensfun distortion correction --- rtengine/iptransform.cc | 21 +------------ rtengine/rawimagesource.cc | 15 +-------- rtengine/rtlensfun.cc | 63 ++++++++++++++++++++++++++++++++++---- rtengine/rtlensfun.h | 10 ++++-- 4 files changed, 66 insertions(+), 43 deletions(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index a054cc57a..d36e2b5b9 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -322,26 +322,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, LensCorrection *pLCPMap = nullptr; if (needsLensfun()) { - const LFDatabase *db = LFDatabase::getInstance(); - Glib::ustring make, model, lens; - if (params->lensProf.lfAutoMatch) { - make = metadata->getMake(); - model = metadata->getModel(); - lens = metadata->getLens(); - } else { - make = params->lensProf.lfCameraMake; - model = params->lensProf.lfCameraModel; - lens = params->lensProf.lfLens; - } - LFCamera c = db->findCamera(make, model); - LFLens l = db->findLens(c, lens); - pLCPMap = db->getModifier(c, l, fW, fH, focalLen, fNumber, focusDist); - - std::cout << "LENSFUN:\n" - << " camera: " << c.getDisplayString() << "\n" - << " lens: " << l.getDisplayString() << "\n" - << " correction? " << (pLCPMap ? "yes" : "no") << std::endl; - + pLCPMap = LFDatabase::findModifier(params->lensProf, metadata, fW, fH, params->coarse, rawRotationDeg); } else if (needsLCP()) { // don't check focal length to allow distortion correction for lenses without chip LCPProfile *pLCPProf = lcpStore->getProfile (params->lensProf.lcpFile); diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index c78dcff5f..de0ab3a9e 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1858,20 +1858,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le if (!hasFlatField && lensProf.useVign) { std::unique_ptr pmap; if (lensProf.useLensfun) { - const LFDatabase *db = LFDatabase::getInstance(); - Glib::ustring make, model, lens; - if (lensProf.lfAutoMatch) { - make = idata->getMake(); - model = idata->getModel(); - lens = idata->getLens(); - } else { - make = lensProf.lfCameraMake; - model = lensProf.lfCameraModel; - lens = lensProf.lfLens; - } - LFCamera c = db->findCamera(make, model); - LFLens l = db->findLens(c, lens); - pmap.reset(db->getModifier(c, l, W, H, idata->getFocalLen(), idata->getFNumber(), idata->getFocusDist())); + pmap.reset(LFDatabase::findModifier(lensProf, idata, W, H, coarse, -1)); } else { LCPProfile *pLCPProf = lcpStore->getProfile(lensProf.lcpFile); diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 38e68a4df..f546fe445 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -19,15 +19,20 @@ */ #include "rtlensfun.h" +#include "settings.h" +#include namespace rtengine { +extern const Settings *settings; + //----------------------------------------------------------------------------- // LFModifier //----------------------------------------------------------------------------- -LFModifier::LFModifier(lfModifier *m): - data_(m) +LFModifier::LFModifier(lfModifier *m, bool swap_xy): + data_(m), + swap_xy_(swap_xy) { } @@ -52,9 +57,17 @@ void LFModifier::correctDistortion(double &x, double &y, int cx, int cy, double } float pos[2]; - if (data_->ApplyGeometryDistortion(x+cx, y+cy, 1, 1, pos)) { + float xx = x + cx; + float yy = y + cy; + if (swap_xy_) { + std::swap(xx, yy); + } + if (data_->ApplyGeometryDistortion(xx, yy, 1, 1, pos)) { x = pos[0] - cx; y = pos[1] - cy; + if (swap_xy_) { + std::swap(x, y); + } } } @@ -257,19 +270,57 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c LFModifier *LFDatabase::getModifier(const LFCamera &camera, const LFLens &lens, - int width, int height, float focalLen, - float aperture, float focusDist) const + float focalLen, float aperture, float focusDist, + int width, int height, bool swap_xy) const { LFModifier *ret = nullptr; if (data_) { if (camera.ok() && lens.ok()) { lfModifier *mod = lfModifier::Create(lens.data_, camera.getCropFactor(), width, height); mod->Initialize(lens.data_, LF_PF_F32, focalLen, aperture, focusDist > 0 ? focusDist : 1000, 0.0, LF_RECTILINEAR, LF_MODIFY_VIGNETTING | LF_MODIFY_DISTORTION, false); - ret = new LFModifier(mod); + ret = new LFModifier(mod, swap_xy); } } return ret; } + + +LFModifier *LFDatabase::findModifier(const LensProfParams &lensProf, const ImageMetaData *idata, int width, int height, const CoarseTransformParams &coarse, int rawRotationDeg) +{ + const LFDatabase *db = getInstance(); + Glib::ustring make, model, lens; + if (lensProf.lfAutoMatch) { + make = idata->getMake(); + model = idata->getModel(); + lens = idata->getLens(); + } else { + make = lensProf.lfCameraMake; + model = lensProf.lfCameraModel; + lens = lensProf.lfLens; + } + LFCamera c = db->findCamera(make, model); + LFLens l = db->findLens(c, lens); + bool swap_xy = false; + if (rawRotationDeg >= 0) { + int rot = (coarse.rotate + rawRotationDeg) % 360; + swap_xy = (rot == 90 || rot == 270); + if (swap_xy) { + std::swap(width, height); + } + } + + LFModifier *ret = db->getModifier(c, l, idata->getFocalLen(), idata->getFNumber(), idata->getFocusDist(), width, height, swap_xy); + + + if (settings->verbose) { + std::cout << "LENSFUN:\n" + << " camera: " << c.getDisplayString() << "\n" + << " lens: " << l.getDisplayString() << "\n" + << " correction? " << (ret ? "yes" : "no") << std::endl; + } + + return ret; +} } // namespace rtengine diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index 434f821fd..4e3280de9 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -24,6 +24,7 @@ #include #include #include "lcp.h" +#include "procparams.h" namespace rtengine { @@ -40,12 +41,13 @@ public: void processVignetteLine3Channels(int width, int y, float *line) const; private: - explicit LFModifier(lfModifier *m); + explicit LFModifier(lfModifier *m, bool rotateXY); LFModifier(const LFModifier &); LFModifier &operator=(const LFModifier &); friend class LFDatabase; lfModifier *data_; + bool swap_xy_; }; class LFCamera { @@ -87,8 +89,10 @@ public: LFCamera findCamera(const Glib::ustring &make, const Glib::ustring &model) const; LFLens findLens(const LFCamera &camera, const Glib::ustring &name) const; LFModifier *getModifier(const LFCamera &camera, const LFLens &lens, - int width, int height, - float focalLen, float aperture, float focusDist) const; + float focalLen, float aperture, float focusDist, + int width, int height, bool swap_xy) const; + + static LFModifier *findModifier(const LensProfParams &lensProf, const ImageMetaData *idata, int width, int height, const CoarseTransformParams &coarse, int rawRotationDeg); private: LFDatabase(); From 30f62b05609367b56d5e812690c7f29495dd4540 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 7 Sep 2017 16:45:41 +0200 Subject: [PATCH 10/52] added GUI for lensfun --- rtengine/procevents.h | 4 + rtengine/refreshmap.cc | 6 +- rtengine/rtlensfun.cc | 5 +- rtengine/rtlensfun.h | 2 +- rtgui/lensprofile.cc | 282 ++++++++++++++++++++++++++++++++++++++++- rtgui/lensprofile.h | 42 ++++++ 6 files changed, 330 insertions(+), 11 deletions(-) diff --git a/rtengine/procevents.h b/rtengine/procevents.h index cf7f50704..4623b600b 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -511,6 +511,10 @@ enum ProcEvent { EvCATgreensc = 481, EvCATybscen = 482, EvCATAutoyb = 483, + // profiled lens correction new events + EvLensCorrMode = 484, + EvLensCorrLensfunCamera = 488, + EvLensCorrLensfunLens = 489, NUMOFEVENTS diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 5400a2e47..74eda6110 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -510,8 +510,10 @@ int refreshmap[rtengine::NUMOFEVENTS] = { LUMINANCECURVE, // EvCATtempsc LUMINANCECURVE, // EvCATgreensc LUMINANCECURVE, // EvCATybscen - LUMINANCECURVE // EvCATAutoyb - + LUMINANCECURVE, // EvCATAutoyb + DARKFRAME, // EvLensCorrMode + DARKFRAME, // EvLensCorrLensfunCamera + DARKFRAME // EvLensCorrLensfunLens }; diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index f546fe445..31392698d 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -215,17 +215,16 @@ std::vector LFDatabase::getCameras() const } -std::vector LFDatabase::getLenses(const LFCamera &camera) const +std::vector LFDatabase::getLenses() const { std::vector ret; if (data_) { - auto lenses = data_->FindLenses(camera.data_, NULL, "", LF_SEARCH_LOOSE /*| LF_SEARCH_SORT_AND_UNIQUIFY*/); + auto lenses = data_->GetLenses(); while (*lenses) { ret.emplace_back(LFLens()); ret.back().data_ = *lenses; ++lenses; } - lf_free(lenses); } return ret; } diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index 4e3280de9..d02535623 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -85,7 +85,7 @@ public: ~LFDatabase(); std::vector getCameras() const; - std::vector getLenses(const LFCamera &camera) const; + std::vector getLenses() const; LFCamera findCamera(const Glib::ustring &make, const Glib::ustring &model) const; LFLens findLens(const LFCamera &camera, const Glib::ustring &name) const; LFModifier *getModifier(const LFCamera &camera, const LFLens &lens, diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 0855ef03f..82e156199 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -22,15 +22,66 @@ #include "../rtengine/lcp.h" #include #include "rtimage.h" +#include "../rtengine/rtlensfun.h" +#include +#include using namespace rtengine; using namespace rtengine::procparams; -LensProfilePanel::LensProfilePanel () : FoldableToolPanel(this, "lensprof", M("TP_LENSPROFILE_LABEL")), lcpFileChanged(false), useDistChanged(false), useVignChanged(false), useCAChanged(false), isRaw(true), lensgeomLcpFill(nullptr) +LensProfilePanel::LensProfilePanel () : + FoldableToolPanel(this, "lensprof", M("TP_LENSPROFILE_LABEL")), + lcpFileChanged(false), + useDistChanged(false), + useVignChanged(false), + useCAChanged(false), + isRaw(true), + lensgeomLcpFill(nullptr), + useLensfunChanged(false), + lensfunAutoChanged(false), + lensfunCameraChanged(false), + lensfunLensChanged(false) { - hbLCPFile = Gtk::manage(new Gtk::HBox()); + corrOff = Gtk::manage(new Gtk::RadioButton(M("LENSPROFILE_CORRECTION_OFF"))); + pack_start(*corrOff); - lLCPFileHead = Gtk::manage(new Gtk::Label(M("GENERAL_FILE"))); + corrGroup = corrOff->get_group(); + + corrLensfunAuto = Gtk::manage(new Gtk::RadioButton(corrGroup, M("LENSPROFILE_CORRECTION_AUTOMATCH"))); + pack_start(*corrLensfunAuto); + + corrLensfunManual = Gtk::manage(new Gtk::RadioButton(corrGroup, M("LENSPROFILE_CORRECTION_MANUAL"))); + pack_start(*corrLensfunManual); + + lensfunCameraModel = Gtk::TreeStore::create(lensfunModelCam); + lensfunLensModel = Gtk::TreeStore::create(lensfunModelLens); + + lensfunCameras = Gtk::manage(new MyComboBox()); + lensfunCameras->set_model(lensfunCameraModel); + lensfunCameras->pack_start(lensfunModelCam.model); + lensfunLenses = Gtk::manage(new MyComboBox()); + lensfunLenses->set_model(lensfunLensModel); + lensfunLenses->pack_start(lensfunModelLens.lens); + + Gtk::HBox *hb = Gtk::manage(new Gtk::HBox()); + hb->pack_start(*Gtk::manage(new Gtk::Label(M("LENSFUN_CAMERA"))), Gtk::PACK_SHRINK, 4); + hb->pack_start(*lensfunCameras); + pack_start(*hb); + + fillLensfunCameras(); + + hb = Gtk::manage(new Gtk::HBox()); + hb->pack_start(*Gtk::manage(new Gtk::Label(M("LENSFUN_LENS"))), Gtk::PACK_SHRINK, 4); + hb->pack_start(*lensfunLenses); + pack_start(*hb); + + fillLensfunLenses(); + + corrLcpFile = Gtk::manage(new Gtk::RadioButton(corrGroup)); + hbLCPFile = Gtk::manage(new Gtk::HBox()); + hbLCPFile->pack_start(*corrLcpFile, Gtk::PACK_SHRINK); + + lLCPFileHead = Gtk::manage(new Gtk::Label(M("LENSPROFILE_CORRECTION_LCPFILE"))); hbLCPFile->pack_start(*lLCPFileHead, Gtk::PACK_SHRINK, 4); fcbLCPFile = Gtk::manage(new MyFileChooserButton(M("TP_LENSPROFILE_LABEL"), Gtk::FILE_CHOOSER_ACTION_OPEN)); @@ -74,6 +125,13 @@ LensProfilePanel::LensProfilePanel () : FoldableToolPanel(this, "lensprof", M("T ckbUseVign->signal_toggled().connect( sigc::mem_fun(*this, &LensProfilePanel::onUseVignChanged) ); ckbUseCA->signal_toggled().connect( sigc::mem_fun(*this, &LensProfilePanel::onUseCAChanged) ); + lensfunCameras->signal_changed().connect(sigc::mem_fun(*this, &LensProfilePanel::onLensfunCameraChanged)); + lensfunLenses->signal_changed().connect(sigc::mem_fun(*this, &LensProfilePanel::onLensfunLensChanged)); + corrOff->signal_toggled().connect(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged)); + corrLensfunAuto->signal_toggled().connect(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged)); + corrLensfunManual->signal_toggled().connect(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged)); + corrLcpFile->signal_toggled().connect(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged)); + allowFocusDep = true; } @@ -82,7 +140,15 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa disableListener (); conUseDist.block(true); - if (!pp->lensProf.lcpFile.empty() && lcpStore->isValidLCPFileName(pp->lensProf.lcpFile)) { + corrOff->set_active(true); + if (pp->lensProf.useLensfun) { + if (pp->lensProf.lfAutoMatch) { + corrLensfunAuto->set_active(true); + } else { + corrLensfunManual->set_active(true); + } + } else if (!pp->lensProf.lcpFile.empty() && lcpStore->isValidLCPFileName(pp->lensProf.lcpFile)) { + corrLcpFile->set_active(true); fcbLCPFile->set_filename (pp->lensProf.lcpFile); updateDisabled(true); } else { @@ -104,7 +170,48 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa ckbUseVign->set_active (pp->lensProf.useVign && isRaw); ckbUseCA->set_active (pp->lensProf.useCA && isRaw); + if (!pp->lensProf.lfCameraMake.empty() && !pp->lensProf.lfCameraModel.empty()) { + // search for the active row + for (auto row : lensfunCameraModel->children()) { + if (row[lensfunModelCam.make] == pp->lensProf.lfCameraMake) { + auto &c = row.children(); + for (auto it = c.begin(), end = c.end(); it != end; ++it) { + auto &childrow = *it; + if (childrow[lensfunModelCam.model] == pp->lensProf.lfCameraModel) { + lensfunCameras->set_active(it); + break; + } + } + break; + } + } + } + + if (!pp->lensProf.lfLens.empty()) { + // search for the active row + auto pos = pp->lensProf.lfLens.find_first_of(' '); + Glib::ustring make = "(Unknown)"; + if (pos != Glib::ustring::npos) { + make = pp->lensProf.lfLens.substr(0, pos); + } + + for (auto row : lensfunCameraModel->children()) { + if (row[lensfunModelLens.lens] == make) { + auto &c = row.children(); + for (auto it = c.begin(), end = c.end(); it != end; ++it) { + auto &childrow = *it; + if (childrow[lensfunModelLens.lens] == pp->lensProf.lfLens) { + lensfunLenses->set_active(it); + break; + } + } + break; + } + } + } + lcpFileChanged = useDistChanged = useVignChanged = useCAChanged = false; + useLensfunChanged = lensfunAutoChanged = lensfunCameraChanged = lensfunLensChanged = false; enableListener (); conUseDist.block(false); @@ -128,7 +235,7 @@ void LensProfilePanel::setRawMeta(bool raw, const rtengine::ImageMetaData* pMeta void LensProfilePanel::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) { - if (lcpStore->isValidLCPFileName(fcbLCPFile->get_filename())) { + if (corrLcpFile->get_active() && lcpStore->isValidLCPFileName(fcbLCPFile->get_filename())) { pp->lensProf.lcpFile = fcbLCPFile->get_filename(); } else { pp->lensProf.lcpFile = ""; @@ -138,11 +245,33 @@ void LensProfilePanel::write( rtengine::procparams::ProcParams* pp, ParamsEdited pp->lensProf.useVign = ckbUseVign->get_active(); pp->lensProf.useCA = ckbUseCA->get_active(); + pp->lensProf.useLensfun = corrLensfunAuto->get_active() || corrLensfunManual->get_active(); + pp->lensProf.lfAutoMatch = corrLensfunAuto->get_active(); + auto itc = lensfunCameras->get_active(); + if (itc) { + pp->lensProf.lfCameraMake = (*itc)[lensfunModelCam.make]; + pp->lensProf.lfCameraModel = (*itc)[lensfunModelCam.model]; + } else { + pp->lensProf.lfCameraMake = ""; + pp->lensProf.lfCameraModel = ""; + } + auto itl = lensfunLenses->get_active(); + if (itl) { + pp->lensProf.lfLens = (*itl)[lensfunModelLens.lens]; + } else { + pp->lensProf.lfLens = ""; + } + if (pedited) { pedited->lensProf.lcpFile = lcpFileChanged; pedited->lensProf.useDist = useDistChanged; pedited->lensProf.useVign = useVignChanged; pedited->lensProf.useCA = useCAChanged; + pedited->lensProf.useLensfun = useLensfunChanged; + pedited->lensProf.lfAutoMatch = lensfunAutoChanged; + pedited->lensProf.lfCameraMake = lensfunCameraChanged; + pedited->lensProf.lfCameraModel = lensfunCameraChanged; + pedited->lensProf.lfLens = lensfunLensChanged; } } @@ -199,3 +328,146 @@ void LensProfilePanel::updateDisabled(bool enable) ckbUseVign->set_sensitive(enable && isRaw); ckbUseCA->set_sensitive(enable && allowFocusDep); } + +void LensProfilePanel::setBatchMode(bool yes) +{ + FoldableToolPanel::setBatchMode(yes); +} + + +void LensProfilePanel::fillLensfunCameras() +{ + std::map> camnames; + auto camlist = LFDatabase::getInstance()->getCameras(); + for (auto &c : camlist) { + camnames[c.getMake()].insert(c.getModel()); + } + for (auto &p : camnames) { + Gtk::TreeModel::Row row = *(lensfunCameraModel->append()); + row[lensfunModelCam.make] = p.first; + row[lensfunModelCam.model] = ""; + for (auto &c : p.second) { + Gtk::TreeModel::Row child = *(lensfunCameraModel->append(row.children())); + child[lensfunModelCam.make] = p.first; + child[lensfunModelCam.model] = c; + } + } +} + + +void LensProfilePanel::fillLensfunLenses() +{ + std::map> lenses; + auto lenslist = LFDatabase::getInstance()->getLenses(); + for (auto &l : lenslist) { + auto name = l.getDisplayString(); + auto pos = name.find_first_of(' '); + Glib::ustring make = "(Unknown)"; + if (pos != Glib::ustring::npos) { + make = name.substr(0, pos); + } + lenses[make].insert(name); + } + for (auto &p : lenses) { + Gtk::TreeModel::Row row = *(lensfunLensModel->append()); + row[lensfunModelLens.lens] = p.first; + for (auto &c : p.second) { + Gtk::TreeModel::Row child = *(lensfunLensModel->append(row.children())); + child[lensfunModelLens.lens] = c; + } + } +} + + +void LensProfilePanel::onLensfunCameraChanged() +{ + auto iter = lensfunCameras->get_active(); + + if (iter) { + lensfunCameraChanged = true; + + if (listener) { + Glib::ustring name = (*iter)[lensfunModelCam.model]; + listener->panelChanged(EvLensCorrLensfunCamera, name); + } + } +} + + +void LensProfilePanel::onLensfunLensChanged() +{ + auto iter = lensfunLenses->get_active(); + + if (iter) { + lensfunLensChanged = true; + + if (listener) { + Glib::ustring name = (*iter)[lensfunModelLens.lens]; + listener->panelChanged(EvLensCorrLensfunLens, name); + } + } +} + + +void LensProfilePanel::onCorrModeChanged() +{ + Glib::ustring mode; + + if (corrOff->get_active()) { + useLensfunChanged = true; + lcpFileChanged = true; + + lensfunCameras->set_sensitive(false); + lensfunLenses->set_sensitive(false); + ckbUseDist->set_sensitive(false); + ckbUseVign->set_sensitive(false); + ckbUseCA->set_sensitive(false); + + mode = M("LENSPROFILE_CORRECTION_OFF"); + } else if (corrLensfunAuto->get_active()) { + useLensfunChanged = true; + lcpFileChanged = true; + useDistChanged = true; + useVignChanged = true; + + lensfunCameras->set_sensitive(false); + lensfunLenses->set_sensitive(false); + + ckbUseDist->set_sensitive(true); + ckbUseVign->set_sensitive(true); + ckbUseCA->set_sensitive(false); + + mode = M("LENSPROFILE_CORRECTION_AUTOMATCH"); + } else if (corrLensfunManual->get_active()) { + useLensfunChanged = true; + lcpFileChanged = true; + lcpFileChanged = true; + useDistChanged = true; + useVignChanged = true; + + lensfunCameras->set_sensitive(true); + lensfunLenses->set_sensitive(true); + + ckbUseDist->set_sensitive(true); + ckbUseVign->set_sensitive(true); + ckbUseCA->set_sensitive(false); + + mode = M("LENSPROFILE_CORRECTION_MANUAL"); + } else if (corrLcpFile->get_active()) { + useLensfunChanged = true; + lcpFileChanged = true; + lcpFileChanged = true; + useDistChanged = true; + useVignChanged = true; + + lensfunCameras->set_sensitive(false); + lensfunLenses->set_sensitive(false); + updateDisabled(true); + + mode = M("LENSPROFILE_CORRECTION_LCPFILE"); + } + + if (listener) { + listener->panelChanged(EvLensCorrMode, mode); + } +} diff --git a/rtgui/lensprofile.h b/rtgui/lensprofile.h index 9543721a0..34222932e 100644 --- a/rtgui/lensprofile.h +++ b/rtgui/lensprofile.h @@ -41,6 +41,42 @@ protected: bool isRaw; LensGeometry *lensgeomLcpFill; + Gtk::RadioButton::Group corrGroup; + Gtk::RadioButton *corrOff; + Gtk::RadioButton *corrLensfunAuto; + Gtk::RadioButton *corrLensfunManual; + Gtk::RadioButton *corrLcpFile; + Gtk::RadioButton *corrUnchanged; + MyComboBox *lensfunCameras; + MyComboBox *lensfunLenses; + + class LFModelCam: public Gtk::TreeModel::ColumnRecord { + public: + LFModelCam() { add(make); add(model); } + Gtk::TreeModelColumn make; + Gtk::TreeModelColumn model; + }; + + class LFModelLens: public Gtk::TreeModel::ColumnRecord { + public: + LFModelLens() { add(lens); } + Gtk::TreeModelColumn lens; + }; + + LFModelCam lensfunModelCam; + LFModelLens lensfunModelLens; + + Glib::RefPtr lensfunCameraModel; + Glib::RefPtr lensfunLensModel; + + bool useLensfunChanged; + bool lensfunAutoChanged; + bool lensfunCameraChanged; + bool lensfunLensChanged; + + void fillLensfunCameras(); + void fillLensfunLenses(); + public: LensProfilePanel (); @@ -58,6 +94,12 @@ public: { lensgeomLcpFill = foo ; }; + + void setBatchMode(bool yes); + + void onLensfunCameraChanged(); + void onLensfunLensChanged(); + void onCorrModeChanged(); }; #endif From f3ae370ea7be2fb25ae74a03301fbc93f2e8f6aa Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 7 Sep 2017 16:53:53 +0200 Subject: [PATCH 11/52] fixed bug in passing image dimensions to the lensfun correction engine --- rtengine/iptransform.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index d36e2b5b9..2226c493b 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -322,7 +322,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, LensCorrection *pLCPMap = nullptr; if (needsLensfun()) { - pLCPMap = LFDatabase::findModifier(params->lensProf, metadata, fW, fH, params->coarse, rawRotationDeg); + pLCPMap = LFDatabase::findModifier(params->lensProf, metadata, oW, oH, params->coarse, rawRotationDeg); } else if (needsLCP()) { // don't check focal length to allow distortion correction for lenses without chip LCPProfile *pLCPProf = lcpStore->getProfile (params->lensProf.lcpFile); From 9bfd2d60d3a24064f6c3161410bf42f195351c55 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 7 Sep 2017 17:50:33 +0200 Subject: [PATCH 12/52] fixed update of lensfun-related procparams after editing --- rtgui/lensprofile.cc | 9 ++++++--- rtgui/paramsedited.cc | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 82e156199..7ac67e106 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -140,7 +140,6 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa disableListener (); conUseDist.block(true); - corrOff->set_active(true); if (pp->lensProf.useLensfun) { if (pp->lensProf.lfAutoMatch) { corrLensfunAuto->set_active(true); @@ -164,6 +163,8 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa } updateDisabled(false); + + corrOff->set_active(true); } ckbUseDist->set_active (pp->lensProf.useDist); @@ -415,6 +416,7 @@ void LensProfilePanel::onCorrModeChanged() if (corrOff->get_active()) { useLensfunChanged = true; + lensfunAutoChanged = true; lcpFileChanged = true; lensfunCameras->set_sensitive(false); @@ -426,6 +428,7 @@ void LensProfilePanel::onCorrModeChanged() mode = M("LENSPROFILE_CORRECTION_OFF"); } else if (corrLensfunAuto->get_active()) { useLensfunChanged = true; + lensfunAutoChanged = true; lcpFileChanged = true; useDistChanged = true; useVignChanged = true; @@ -440,7 +443,7 @@ void LensProfilePanel::onCorrModeChanged() mode = M("LENSPROFILE_CORRECTION_AUTOMATCH"); } else if (corrLensfunManual->get_active()) { useLensfunChanged = true; - lcpFileChanged = true; + lensfunAutoChanged = true; lcpFileChanged = true; useDistChanged = true; useVignChanged = true; @@ -455,7 +458,7 @@ void LensProfilePanel::onCorrModeChanged() mode = M("LENSPROFILE_CORRECTION_MANUAL"); } else if (corrLcpFile->get_active()) { useLensfunChanged = true; - lcpFileChanged = true; + lensfunAutoChanged = true; lcpFileChanged = true; useDistChanged = true; useVignChanged = true; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 588006eca..e5c995f90 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -2078,6 +2078,26 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.lensProf.useCA = mods.lensProf.useCA; } + if (lensProf.useLensfun) { + toEdit.lensProf.useLensfun = mods.lensProf.useLensfun; + } + + if (lensProf.lfAutoMatch) { + toEdit.lensProf.lfAutoMatch = mods.lensProf.lfAutoMatch; + } + + if (lensProf.lfCameraMake) { + toEdit.lensProf.lfCameraMake = mods.lensProf.lfCameraMake; + } + + if (lensProf.lfCameraModel) { + toEdit.lensProf.lfCameraModel = mods.lensProf.lfCameraModel; + } + + if (lensProf.lfLens) { + toEdit.lensProf.lfLens = mods.lensProf.lfLens; + } + if (perspective.horizontal) { toEdit.perspective.horizontal = dontforceSet && options.baBehav[ADDSET_PERSPECTIVE] ? toEdit.perspective.horizontal + mods.perspective.horizontal : mods.perspective.horizontal; } From c8d9573606527f7eb10721a796899e1599a578b0 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 7 Sep 2017 18:04:55 +0200 Subject: [PATCH 13/52] disable the selection of lensfun auto mode if no match is found in the db --- rtdata/languages/default | 6 +++++- rtgui/lensprofile.cc | 18 ++++++++++++++++-- rtgui/lensprofile.h | 1 + 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 91eb5e2bf..083afc4df 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1654,7 +1654,7 @@ TP_LABCURVE_RSTPRO_TOOLTIP;Works on the Chromaticity slider and the CC curve. TP_LENSGEOM_AUTOCROP;Auto-Crop TP_LENSGEOM_FILL;Auto-fill TP_LENSGEOM_LABEL;Lens / Geometry -TP_LENSPROFILE_LABEL;Lens Correction Profile +TP_LENSPROFILE_LABEL;Profiled Lens Correction TP_LENSPROFILE_USECA;Chromatic aberration correction TP_LENSPROFILE_USEDIST;Distortion correction TP_LENSPROFILE_USEVIGN;Vignetting correction @@ -2152,3 +2152,7 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f ZOOMPANEL_ZOOMFITSCREEN;Fit whole image to screen\nShortcut: f ZOOMPANEL_ZOOMIN;Zoom In\nShortcut: + ZOOMPANEL_ZOOMOUT;Zoom Out\nShortcut: - +LENSPROFILE_CORRECTION_OFF;None +LENSPROFILE_CORRECTION_AUTOMATCH;Auto-matched correction parameters +LENSPROFILE_CORRECTION_MANUAL;Manual correction parameters +LENSPROFILE_CORRECTION_LCPFILE;LCP File diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 7ac67e106..bd518ffe0 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -36,6 +36,7 @@ LensProfilePanel::LensProfilePanel () : useVignChanged(false), useCAChanged(false), isRaw(true), + metadata(nullptr), lensgeomLcpFill(nullptr), useLensfunChanged(false), lensfunAutoChanged(false), @@ -64,14 +65,14 @@ LensProfilePanel::LensProfilePanel () : lensfunLenses->pack_start(lensfunModelLens.lens); Gtk::HBox *hb = Gtk::manage(new Gtk::HBox()); - hb->pack_start(*Gtk::manage(new Gtk::Label(M("LENSFUN_CAMERA"))), Gtk::PACK_SHRINK, 4); + hb->pack_start(*Gtk::manage(new Gtk::Label(M("EXIFFILTER_CAMERA"))), Gtk::PACK_SHRINK, 4); hb->pack_start(*lensfunCameras); pack_start(*hb); fillLensfunCameras(); hb = Gtk::manage(new Gtk::HBox()); - hb->pack_start(*Gtk::manage(new Gtk::Label(M("LENSFUN_LENS"))), Gtk::PACK_SHRINK, 4); + hb->pack_start(*Gtk::manage(new Gtk::Label(M("EXIFFILTER_LENS"))), Gtk::PACK_SHRINK, 4); hb->pack_start(*lensfunLenses); pack_start(*hb); @@ -140,6 +141,8 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa disableListener (); conUseDist.block(true); + corrLensfunAuto->set_sensitive(true); + if (pp->lensProf.useLensfun) { if (pp->lensProf.lfAutoMatch) { corrLensfunAuto->set_active(true); @@ -214,6 +217,16 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa lcpFileChanged = useDistChanged = useVignChanged = useCAChanged = false; useLensfunChanged = lensfunAutoChanged = lensfunCameraChanged = lensfunLensChanged = false; + if (!batchMode && metadata && pp->lensProf.useLensfun) { + std::unique_ptr mod(LFDatabase::findModifier(pp->lensProf, metadata, 100, 100, pp->coarse, -1)); + if (!mod) { + corrOff->set_active(true); + if (pp->lensProf.lfAutoMatch) { + corrLensfunAuto->set_sensitive(false); + } + } + } + enableListener (); conUseDist.block(false); } @@ -232,6 +245,7 @@ void LensProfilePanel::setRawMeta(bool raw, const rtengine::ImageMetaData* pMeta } isRaw = raw; + metadata = pMeta; } void LensProfilePanel::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) diff --git a/rtgui/lensprofile.h b/rtgui/lensprofile.h index 34222932e..11e3bb219 100644 --- a/rtgui/lensprofile.h +++ b/rtgui/lensprofile.h @@ -39,6 +39,7 @@ protected: void updateDisabled(bool enable); bool allowFocusDep; bool isRaw; + const rtengine::ImageMetaData* metadata; LensGeometry *lensgeomLcpFill; Gtk::RadioButton::Group corrGroup; From bece5d19ed288c37efb8e0bc2ca47b5eb3ecadbb Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 8 Sep 2017 09:35:49 +0200 Subject: [PATCH 14/52] fixed bug in showing the list of cameras in the lensfun db --- rtgui/lensprofile.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index bd518ffe0..6f901946e 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -360,7 +360,7 @@ void LensProfilePanel::fillLensfunCameras() for (auto &p : camnames) { Gtk::TreeModel::Row row = *(lensfunCameraModel->append()); row[lensfunModelCam.make] = p.first; - row[lensfunModelCam.model] = ""; + row[lensfunModelCam.model] = p.first; for (auto &c : p.second) { Gtk::TreeModel::Row child = *(lensfunCameraModel->append(row.children())); child[lensfunModelCam.make] = p.first; From 266efa40aed70fef6d64427bdbb7f26eff71cd65 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 8 Sep 2017 11:24:10 +0200 Subject: [PATCH 15/52] fixed lensfun-based distortion correction for pictures in portrait orientation --- rtengine/rtlensfun.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 31392698d..3900d4ee9 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -63,11 +63,13 @@ void LFModifier::correctDistortion(double &x, double &y, int cx, int cy, double std::swap(xx, yy); } if (data_->ApplyGeometryDistortion(xx, yy, 1, 1, pos)) { - x = pos[0] - cx; - y = pos[1] - cy; + x = pos[0]; + y = pos[1]; if (swap_xy_) { std::swap(x, y); } + x -= cx; + y -= cy; } } From 5a37c38bfba2736ff408c7f6be7f5022fff852ce Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 8 Sep 2017 17:45:18 +0200 Subject: [PATCH 16/52] update the lens correction when selecting a camera/lens combination manually --- rtengine/procevents.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 4623b600b..5f30374ee 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -513,8 +513,8 @@ enum ProcEvent { EvCATAutoyb = 483, // profiled lens correction new events EvLensCorrMode = 484, - EvLensCorrLensfunCamera = 488, - EvLensCorrLensfunLens = 489, + EvLensCorrLensfunCamera = 485, + EvLensCorrLensfunLens = 486, NUMOFEVENTS From b2232f3843da25e706d64ebd14fb4b2ccf138f0b Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 8 Sep 2017 18:02:34 +0200 Subject: [PATCH 17/52] improved logic for auto-matching lensfun correction (esp. with fixed-lens cameras) --- rtengine/rtlensfun.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 3900d4ee9..1122fdd89 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -250,7 +250,13 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c { LFLens ret; if (data_) { - auto found = data_->FindLenses(camera.data_, NULL, name.c_str(), LF_SEARCH_LOOSE); + const char *lname = name.c_str(); + const lfCamera *cam = nullptr; + if (name.empty() || name.find("Unknown ") == 0) { + lname = "Standard"; + cam = camera.data_; + } + auto found = data_->FindLenses(cam, nullptr, lname, LF_SEARCH_LOOSE); if (!found) { // try to split the maker from the model of the lens Glib::ustring make, model; @@ -258,7 +264,7 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c if (i != Glib::ustring::npos) { make = name.substr(0, i); model = name.substr(i+1); - found = data_->FindLenses(camera.data_, make.c_str(), model.c_str(), LF_SEARCH_LOOSE); + found = data_->FindLenses(cam, make.c_str(), model.c_str(), LF_SEARCH_LOOSE); } } if (found) { From cbae5e07189d630536fe29f45f8ed9d121f573df Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 8 Sep 2017 23:52:16 +0200 Subject: [PATCH 18/52] further tweaks to the lensfun automatching logic --- rtengine/rtlensfun.cc | 52 +++++++++++++++++++++++++++++++++---------- rtengine/rtlensfun.h | 9 +++++--- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 1122fdd89..4e2216fc0 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -30,9 +30,10 @@ extern const Settings *settings; // LFModifier //----------------------------------------------------------------------------- -LFModifier::LFModifier(lfModifier *m, bool swap_xy): +LFModifier::LFModifier(lfModifier *m, bool swap_xy, int flags): data_(m), - swap_xy_(swap_xy) + swap_xy_(swap_xy), + flags_(flags) { } @@ -86,6 +87,31 @@ void LFModifier::processVignetteLine3Channels(int width, int y, float *line) con } +Glib::ustring LFModifier::getDisplayString() const +{ + if (!data_) { + return "NONE"; + } else { + Glib::ustring ret; + Glib::ustring sep = ""; + if (flags_ & LF_MODIFY_DISTORTION) { + ret += "distortion"; + sep = ", "; + } + if (flags_ & LF_MODIFY_VIGNETTING) { + ret += sep; + ret += "vignetting"; + sep = ", "; + } + if (flags_ & LF_MODIFY_SCALE) { + ret += sep; + ret += "autoscaling"; + } + return ret; + } +} + + //----------------------------------------------------------------------------- // LFCamera //----------------------------------------------------------------------------- @@ -158,7 +184,7 @@ bool LFLens::ok() const } -Glib::ustring LFLens::getDisplayString() const +Glib::ustring LFLens::getLens() const { if (data_) { return Glib::ustring::compose("%1 %2", data_->Maker, data_->Model); @@ -251,12 +277,11 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c LFLens ret; if (data_) { const char *lname = name.c_str(); - const lfCamera *cam = nullptr; - if (name.empty() || name.find("Unknown ") == 0) { + bool stdlens = camera.ok() && (name.empty() || name.find("Unknown ") == 0); + if (stdlens) { lname = "Standard"; - cam = camera.data_; } - auto found = data_->FindLenses(cam, nullptr, lname, LF_SEARCH_LOOSE); + auto found = data_->FindLenses(camera.data_, nullptr, lname, LF_SEARCH_LOOSE); if (!found) { // try to split the maker from the model of the lens Glib::ustring make, model; @@ -264,12 +289,14 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c if (i != Glib::ustring::npos) { make = name.substr(0, i); model = name.substr(i+1); - found = data_->FindLenses(cam, make.c_str(), model.c_str(), LF_SEARCH_LOOSE); + found = data_->FindLenses(camera.data_, make.c_str(), model.c_str(), LF_SEARCH_LOOSE); } } if (found) { ret.data_ = found[0]; lf_free(found); + } else if (camera.ok() && !stdlens) { + ret = findLens(LFCamera(), name); } } return ret; @@ -281,11 +308,11 @@ LFModifier *LFDatabase::getModifier(const LFCamera &camera, const LFLens &lens, int width, int height, bool swap_xy) const { LFModifier *ret = nullptr; - if (data_) { + if (data_ && focalLen > 0) { if (camera.ok() && lens.ok()) { lfModifier *mod = lfModifier::Create(lens.data_, camera.getCropFactor(), width, height); - mod->Initialize(lens.data_, LF_PF_F32, focalLen, aperture, focusDist > 0 ? focusDist : 1000, 0.0, LF_RECTILINEAR, LF_MODIFY_VIGNETTING | LF_MODIFY_DISTORTION, false); - ret = new LFModifier(mod, swap_xy); + int flags = mod->Initialize(lens.data_, LF_PF_F32, focalLen, aperture, focusDist > 0 ? focusDist : 1000, 0.0, LF_RECTILINEAR, LF_MODIFY_VIGNETTING | LF_MODIFY_DISTORTION | LF_MODIFY_SCALE, false); + ret = new LFModifier(mod, swap_xy, flags); } } return ret; @@ -323,7 +350,8 @@ LFModifier *LFDatabase::findModifier(const LensProfParams &lensProf, const Image std::cout << "LENSFUN:\n" << " camera: " << c.getDisplayString() << "\n" << " lens: " << l.getDisplayString() << "\n" - << " correction? " << (ret ? "yes" : "no") << std::endl; + << " correction: " + << (ret ? ret->getDisplayString() : "NONE") << std::endl; } return ret; diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index d02535623..4e2064332 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -39,15 +39,18 @@ public: void correctCA(double &x, double &y, int channel) const {} void processVignetteLine(int width, int y, float *line) const; void processVignetteLine3Channels(int width, int y, float *line) const; + + Glib::ustring getDisplayString() const; private: - explicit LFModifier(lfModifier *m, bool rotateXY); + explicit LFModifier(lfModifier *m, bool swap_xy, int flags); LFModifier(const LFModifier &); LFModifier &operator=(const LFModifier &); friend class LFDatabase; lfModifier *data_; bool swap_xy_; + int flags_; }; class LFCamera { @@ -70,8 +73,8 @@ class LFLens { public: LFLens(); bool ok() const; - - Glib::ustring getDisplayString() const; + Glib::ustring getLens() const; + Glib::ustring getDisplayString() const { return getLens(); } private: friend class LFDatabase; const lfLens *data_; From 58ec1e316ba330417b54b3f5c84a0e5ee3758c96 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 8 Sep 2017 23:52:43 +0200 Subject: [PATCH 19/52] show the auto-matched lensfun camera/lens pair in the gui --- rtgui/lensprofile.cc | 98 ++++++++++++++++++++++++++------------------ rtgui/lensprofile.h | 2 + 2 files changed, 61 insertions(+), 39 deletions(-) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 6f901946e..1386469a9 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -174,45 +174,8 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa ckbUseVign->set_active (pp->lensProf.useVign && isRaw); ckbUseCA->set_active (pp->lensProf.useCA && isRaw); - if (!pp->lensProf.lfCameraMake.empty() && !pp->lensProf.lfCameraModel.empty()) { - // search for the active row - for (auto row : lensfunCameraModel->children()) { - if (row[lensfunModelCam.make] == pp->lensProf.lfCameraMake) { - auto &c = row.children(); - for (auto it = c.begin(), end = c.end(); it != end; ++it) { - auto &childrow = *it; - if (childrow[lensfunModelCam.model] == pp->lensProf.lfCameraModel) { - lensfunCameras->set_active(it); - break; - } - } - break; - } - } - } - - if (!pp->lensProf.lfLens.empty()) { - // search for the active row - auto pos = pp->lensProf.lfLens.find_first_of(' '); - Glib::ustring make = "(Unknown)"; - if (pos != Glib::ustring::npos) { - make = pp->lensProf.lfLens.substr(0, pos); - } - - for (auto row : lensfunCameraModel->children()) { - if (row[lensfunModelLens.lens] == make) { - auto &c = row.children(); - for (auto it = c.begin(), end = c.end(); it != end; ++it) { - auto &childrow = *it; - if (childrow[lensfunModelLens.lens] == pp->lensProf.lfLens) { - lensfunLenses->set_active(it); - break; - } - } - break; - } - } - } + setLensfunCamera(pp->lensProf.lfCameraMake, pp->lensProf.lfCameraModel); + setLensfunLens(pp->lensProf.lfLens); lcpFileChanged = useDistChanged = useVignChanged = useCAChanged = false; useLensfunChanged = lensfunAutoChanged = lensfunCameraChanged = lensfunLensChanged = false; @@ -224,6 +187,12 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa if (pp->lensProf.lfAutoMatch) { corrLensfunAuto->set_sensitive(false); } + } else if (pp->lensProf.lfAutoMatch) { + const LFDatabase *db = LFDatabase::getInstance(); + LFCamera c = db->findCamera(metadata->getMake(), metadata->getModel()); + LFLens l = db->findLens(c, metadata->getLens()); + setLensfunCamera(c.getMake(), c.getModel()); + setLensfunLens(l.getLens()); } } @@ -394,6 +363,57 @@ void LensProfilePanel::fillLensfunLenses() } +bool LensProfilePanel::setLensfunCamera(const Glib::ustring &make, const Glib::ustring &model) +{ + if (!make.empty() && !model.empty()) { + // search for the active row + for (auto row : lensfunCameraModel->children()) { + if (row[lensfunModelCam.make] == make) { + auto &c = row.children(); + for (auto it = c.begin(), end = c.end(); it != end; ++it) { + auto &childrow = *it; + if (childrow[lensfunModelCam.model] == model) { + lensfunCameras->set_active(it); + return true; + } + } + break; + } + } + } + return false; +} + + +bool LensProfilePanel::setLensfunLens(const Glib::ustring &lens) +{ + if (!lens.empty()) { + // search for the active row + auto pos = lens.find_first_of(' '); + Glib::ustring make = "(Unknown)"; + if (pos != Glib::ustring::npos) { + make = lens.substr(0, pos); + } + + for (auto row : lensfunLensModel->children()) { + if (row[lensfunModelLens.lens] == make) { + auto &c = row.children(); + for (auto it = c.begin(), end = c.end(); it != end; ++it) { + auto &childrow = *it; + if (childrow[lensfunModelLens.lens] == lens) { + lensfunLenses->set_active(it); + return true; + } + } + break; + } + } + } + return false; +} + + + void LensProfilePanel::onLensfunCameraChanged() { auto iter = lensfunCameras->get_active(); diff --git a/rtgui/lensprofile.h b/rtgui/lensprofile.h index 11e3bb219..e1e9b0e5f 100644 --- a/rtgui/lensprofile.h +++ b/rtgui/lensprofile.h @@ -77,6 +77,8 @@ protected: void fillLensfunCameras(); void fillLensfunLenses(); + bool setLensfunCamera(const Glib::ustring &make, const Glib::ustring &model); + bool setLensfunLens(const Glib::ustring &lens); public: From 66979d290a6fc66adc556ca19048adcb9ba27915 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 9 Sep 2017 00:54:28 +0200 Subject: [PATCH 20/52] lensfun: further tweaks on the matching logic and the UI --- rtengine/rtlensfun.cc | 10 +++++----- rtgui/lensprofile.cc | 42 ++++++++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 4e2216fc0..7a2e94bb3 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -262,7 +262,7 @@ LFCamera LFDatabase::findCamera(const Glib::ustring &make, const Glib::ustring & { LFCamera ret; if (data_) { - auto found = data_->FindCamerasExt(make.c_str(), model.c_str(), LF_SEARCH_LOOSE); + auto found = data_->FindCamerasExt(make.c_str(), model.c_str()); if (found) { ret.data_ = found[0]; lf_free(found); @@ -276,12 +276,12 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c { LFLens ret; if (data_) { - const char *lname = name.c_str(); + Glib::ustring lname = name; bool stdlens = camera.ok() && (name.empty() || name.find("Unknown ") == 0); if (stdlens) { - lname = "Standard"; + lname = camera.getModel(); // "Standard" } - auto found = data_->FindLenses(camera.data_, nullptr, lname, LF_SEARCH_LOOSE); + auto found = data_->FindLenses(camera.data_, nullptr, lname.c_str()); if (!found) { // try to split the maker from the model of the lens Glib::ustring make, model; @@ -289,7 +289,7 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c if (i != Glib::ustring::npos) { make = name.substr(0, i); model = name.substr(i+1); - found = data_->FindLenses(camera.data_, make.c_str(), model.c_str(), LF_SEARCH_LOOSE); + found = data_->FindLenses(camera.data_, make.c_str(), model.c_str()); } } if (found) { diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 1386469a9..5f98ef4af 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -174,25 +174,31 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa ckbUseVign->set_active (pp->lensProf.useVign && isRaw); ckbUseCA->set_active (pp->lensProf.useCA && isRaw); - setLensfunCamera(pp->lensProf.lfCameraMake, pp->lensProf.lfCameraModel); - setLensfunLens(pp->lensProf.lfLens); + const LFDatabase *db = LFDatabase::getInstance(); + LFCamera c; + LFLens l; + if (metadata) { + c = db->findCamera(metadata->getMake(), metadata->getModel()); + l = db->findLens(c, metadata->getLens()); + } + + if (!setLensfunCamera(pp->lensProf.lfCameraMake, pp->lensProf.lfCameraModel) && pp->lensProf.lfAutoMatch) { + setLensfunCamera(c.getMake(), c.getModel()); + } + if (!setLensfunLens(pp->lensProf.lfLens) && pp->lensProf.lfAutoMatch) { + setLensfunLens(l.getLens()); + } lcpFileChanged = useDistChanged = useVignChanged = useCAChanged = false; useLensfunChanged = lensfunAutoChanged = lensfunCameraChanged = lensfunLensChanged = false; - if (!batchMode && metadata && pp->lensProf.useLensfun) { + if (metadata) { std::unique_ptr mod(LFDatabase::findModifier(pp->lensProf, metadata, 100, 100, pp->coarse, -1)); if (!mod) { - corrOff->set_active(true); - if (pp->lensProf.lfAutoMatch) { - corrLensfunAuto->set_sensitive(false); + if (pp->lensProf.useLensfun) { + corrOff->set_active(true); } - } else if (pp->lensProf.lfAutoMatch) { - const LFDatabase *db = LFDatabase::getInstance(); - LFCamera c = db->findCamera(metadata->getMake(), metadata->getModel()); - LFLens l = db->findLens(c, metadata->getLens()); - setLensfunCamera(c.getMake(), c.getModel()); - setLensfunLens(l.getLens()); + corrLensfunAuto->set_sensitive(false); } } @@ -366,6 +372,11 @@ void LensProfilePanel::fillLensfunLenses() bool LensProfilePanel::setLensfunCamera(const Glib::ustring &make, const Glib::ustring &model) { if (!make.empty() && !model.empty()) { + auto it = lensfunCameras->get_active(); + if (it && (*it)[lensfunModelCam.make] == make && (*it)[lensfunModelCam.model] == model) { + return true; + } + // search for the active row for (auto row : lensfunCameraModel->children()) { if (row[lensfunModelCam.make] == make) { @@ -381,6 +392,7 @@ bool LensProfilePanel::setLensfunCamera(const Glib::ustring &make, const Glib::u } } } + lensfunCameras->set_active(-1); return false; } @@ -388,6 +400,11 @@ bool LensProfilePanel::setLensfunCamera(const Glib::ustring &make, const Glib::u bool LensProfilePanel::setLensfunLens(const Glib::ustring &lens) { if (!lens.empty()) { + auto it = lensfunLenses->get_active(); + if (it && (*it)[lensfunModelLens.lens] == lens) { + return true; + } + // search for the active row auto pos = lens.find_first_of(' '); Glib::ustring make = "(Unknown)"; @@ -409,6 +426,7 @@ bool LensProfilePanel::setLensfunLens(const Glib::ustring &lens) } } } + lensfunLenses->set_active(-1); return false; } From 13c4b0c2849537df7cd399f0bada25d9a3d1811b Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 9 Sep 2017 09:53:57 +0200 Subject: [PATCH 21/52] one more refinement to the lensfun automatching logic hopefully this is the good one :-) --- rtengine/rtlensfun.cc | 4 +--- rtengine/rtlensfun.h | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 7a2e94bb3..78e07d2c9 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -295,8 +295,6 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c if (found) { ret.data_ = found[0]; lf_free(found); - } else if (camera.ok() && !stdlens) { - ret = findLens(LFCamera(), name); } } return ret; @@ -333,7 +331,7 @@ LFModifier *LFDatabase::findModifier(const LensProfParams &lensProf, const Image lens = lensProf.lfLens; } LFCamera c = db->findCamera(make, model); - LFLens l = db->findLens(c, lens); + LFLens l = db->findLens(lensProf.lfAutoMatch ? c : LFCamera(), lens); bool swap_xy = false; if (rawRotationDeg >= 0) { int rot = (coarse.rotate + rawRotationDeg) % 360; diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index 4e2064332..cb8748eb3 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -91,13 +91,13 @@ public: std::vector getLenses() const; LFCamera findCamera(const Glib::ustring &make, const Glib::ustring &model) const; LFLens findLens(const LFCamera &camera, const Glib::ustring &name) const; - LFModifier *getModifier(const LFCamera &camera, const LFLens &lens, - float focalLen, float aperture, float focusDist, - int width, int height, bool swap_xy) const; static LFModifier *findModifier(const LensProfParams &lensProf, const ImageMetaData *idata, int width, int height, const CoarseTransformParams &coarse, int rawRotationDeg); private: + LFModifier *getModifier(const LFCamera &camera, const LFLens &lens, + float focalLen, float aperture, float focusDist, + int width, int height, bool swap_xy) const; LFDatabase(); LFDatabase(const LFDatabase &); LFDatabase &operator=(const LFDatabase &); From afb503c50f59fcb08d2d250a92cb982c4a324491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Sat, 9 Sep 2017 17:43:33 +0200 Subject: [PATCH 22/52] Prevent /0 in dcraw.cc (fixes #4061) Final solution by @heckflosse. Thanks! --- rtengine/dcraw.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index a70c2d2dc..5bda02f86 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -2562,6 +2562,12 @@ void CLASS kodak_radc_load_raw() ((short *)buf)[i] = 2048; for (row=0; row < height; row+=4) { FORC3 mul[c] = getbits(6); + FORC3 { + if (!mul[c]) { + mul[c] = 1; + derror(); + } + } FORC3 { val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c]; s = val > 65564 ? 10:12; @@ -9936,4 +9942,4 @@ struct tiff_hdr { /*RT*/#undef CLIP #ifdef __GNUC__ #pragma GCC diagnostic pop -#endif \ No newline at end of file +#endif From 0c0f3d146d00a9bb26d0f385203bc198d5257e0d Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 9 Sep 2017 18:41:47 +0200 Subject: [PATCH 23/52] lensfun: take the focal length from the lens (if a prime) when there is no exif info about it --- rtengine/rtlensfun.cc | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 78e07d2c9..c90de08d9 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -306,10 +306,14 @@ LFModifier *LFDatabase::getModifier(const LFCamera &camera, const LFLens &lens, int width, int height, bool swap_xy) const { LFModifier *ret = nullptr; - if (data_ && focalLen > 0) { + if (data_) { if (camera.ok() && lens.ok()) { lfModifier *mod = lfModifier::Create(lens.data_, camera.getCropFactor(), width, height); - int flags = mod->Initialize(lens.data_, LF_PF_F32, focalLen, aperture, focusDist > 0 ? focusDist : 1000, 0.0, LF_RECTILINEAR, LF_MODIFY_VIGNETTING | LF_MODIFY_DISTORTION | LF_MODIFY_SCALE, false); + int flags = LF_MODIFY_DISTORTION | LF_MODIFY_SCALE; + if (aperture > 0) { + flags |= LF_MODIFY_VIGNETTING; + } + flags = mod->Initialize(lens.data_, LF_PF_F32, focalLen, aperture, focusDist > 0 ? focusDist : 1000, 0.0, LF_RECTILINEAR, flags, false); ret = new LFModifier(mod, swap_xy, flags); } } @@ -321,7 +325,11 @@ LFModifier *LFDatabase::findModifier(const LensProfParams &lensProf, const Image { const LFDatabase *db = getInstance(); Glib::ustring make, model, lens; + float focallen = idata->getFocalLen(); if (lensProf.lfAutoMatch) { + if (focallen <= 0) { + return nullptr; + } make = idata->getMake(); model = idata->getModel(); lens = idata->getLens(); @@ -332,6 +340,12 @@ LFModifier *LFDatabase::findModifier(const LensProfParams &lensProf, const Image } LFCamera c = db->findCamera(make, model); LFLens l = db->findLens(lensProf.lfAutoMatch ? c : LFCamera(), lens); + if (focallen <= 0 && l.data_ && l.data_->MinFocal == l.data_->MaxFocal) { + focallen = l.data_->MinFocal; + } + if (focallen <= 0) { + return nullptr; + } bool swap_xy = false; if (rawRotationDeg >= 0) { int rot = (coarse.rotate + rawRotationDeg) % 360; @@ -343,7 +357,6 @@ LFModifier *LFDatabase::findModifier(const LensProfParams &lensProf, const Image LFModifier *ret = db->getModifier(c, l, idata->getFocalLen(), idata->getFNumber(), idata->getFocusDist(), width, height, swap_xy); - if (settings->verbose) { std::cout << "LENSFUN:\n" << " camera: " << c.getDisplayString() << "\n" From 03ec1abca8886a788613e0f0ca1029031aad9cc1 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 9 Sep 2017 18:42:16 +0200 Subject: [PATCH 24/52] properly disable auto-matched lens correction when not available --- rtgui/lensprofile.cc | 39 +++++++++++++++++++++++++++++++-------- rtgui/lensprofile.h | 1 + 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 5f98ef4af..b6af0d8d0 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -183,7 +183,7 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa } if (!setLensfunCamera(pp->lensProf.lfCameraMake, pp->lensProf.lfCameraModel) && pp->lensProf.lfAutoMatch) { - setLensfunCamera(c.getMake(), c.getModel()); + setLensfunCamera(c.getMake(), c.getModel()); } if (!setLensfunLens(pp->lensProf.lfLens) && pp->lensProf.lfAutoMatch) { setLensfunLens(l.getLens()); @@ -192,16 +192,26 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa lcpFileChanged = useDistChanged = useVignChanged = useCAChanged = false; useLensfunChanged = lensfunAutoChanged = lensfunCameraChanged = lensfunLensChanged = false; - if (metadata) { - std::unique_ptr mod(LFDatabase::findModifier(pp->lensProf, metadata, 100, 100, pp->coarse, -1)); - if (!mod) { - if (pp->lensProf.useLensfun) { - corrOff->set_active(true); - } - corrLensfunAuto->set_sensitive(false); + if (!checkLensfunCanCorrect(true)) { + if (corrLensfunAuto->get_active()) { + corrOff->set_active(true); } + corrLensfunAuto->set_sensitive(false); } + if (corrLensfunManual->get_active() && !checkLensfunCanCorrect(false)) { + corrOff->set_active(true); + } + // if (metadata) { + // std::unique_ptr mod(LFDatabase::findModifier(pp->lensProf, metadata, 100, 100, pp->coarse, -1)); + // if (!mod) { + // if (pp->lensProf.useLensfun) { + // corrOff->set_active(true); + // } + // corrLensfunAuto->set_sensitive(false); + // } + // } + enableListener (); conUseDist.block(false); } @@ -526,3 +536,16 @@ void LensProfilePanel::onCorrModeChanged() listener->panelChanged(EvLensCorrMode, mode); } } + + +bool LensProfilePanel::checkLensfunCanCorrect(bool automatch) +{ + if (!metadata) { + return false; + } + rtengine::procparams::ProcParams lpp; + write(&lpp); + lpp.lensProf.lfAutoMatch = automatch; + std::unique_ptr mod(LFDatabase::findModifier(lpp.lensProf, metadata, 100, 100, lpp.coarse, -1)); + return mod.get() != nullptr; +} diff --git a/rtgui/lensprofile.h b/rtgui/lensprofile.h index e1e9b0e5f..0894d7a98 100644 --- a/rtgui/lensprofile.h +++ b/rtgui/lensprofile.h @@ -79,6 +79,7 @@ protected: void fillLensfunLenses(); bool setLensfunCamera(const Glib::ustring &make, const Glib::ustring &model); bool setLensfunLens(const Glib::ustring &lens); + bool checkLensfunCanCorrect(bool automatch); public: From d442f7a85b09805b4264cb9db214ead873bf9153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Sat, 9 Sep 2017 20:19:11 +0200 Subject: [PATCH 25/52] LCP cleanup (#4062) - Removed `using namespace` - Use real `Cache` - Use `std::shared_ptr` - Moved `LCPPersModel` to .cc More could be done... --- rtengine/clutstore.cc | 2 +- rtengine/clutstore.h | 4 +- rtengine/improccoordinator.cc | 2 +- rtengine/iptransform.cc | 4 +- rtengine/lcp.cc | 1618 ++++++++++++++++++--------------- rtengine/lcp.h | 169 ++-- rtengine/rawimagesource.cc | 2 +- rtgui/lensprofile.cc | 8 +- 8 files changed, 1013 insertions(+), 796 deletions(-) diff --git a/rtengine/clutstore.cc b/rtengine/clutstore.cc index ba117a2fd..5731773a4 100644 --- a/rtengine/clutstore.cc +++ b/rtengine/clutstore.cc @@ -305,7 +305,7 @@ rtengine::CLUTStore& rtengine::CLUTStore::getInstance() return instance; } -std::shared_ptr rtengine::CLUTStore::getClut(const Glib::ustring& filename) +std::shared_ptr rtengine::CLUTStore::getClut(const Glib::ustring& filename) const { std::shared_ptr result; diff --git a/rtengine/clutstore.h b/rtengine/clutstore.h index 5e4930fa1..a43526f78 100644 --- a/rtengine/clutstore.h +++ b/rtengine/clutstore.h @@ -57,14 +57,14 @@ class CLUTStore final : public: static CLUTStore& getInstance(); - std::shared_ptr getClut(const Glib::ustring& filename); + std::shared_ptr getClut(const Glib::ustring& filename) const; void clearCache(); private: CLUTStore(); - Cache> cache; + mutable Cache> cache; }; } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index d5032cd46..9bbd9b48b 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -1121,7 +1121,7 @@ void ImProcCoordinator::getAutoCrop (double ratio, int &x, int &y, int &w, int & LCPMapper *pLCPMap = nullptr; if (params.lensProf.lcpFile.length() && imgsrc->getMetaData()->getFocalLen() > 0) { - LCPProfile *pLCPProf = lcpStore->getProfile (params.lensProf.lcpFile); + const std::shared_ptr pLCPProf = LCPStore::getInstance()->getProfile (params.lensProf.lcpFile); if (pLCPProf) pLCPMap = new LCPMapper (pLCPProf, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(), 0, false, params.lensProf.useDist, fullw, fullh, params.coarse, imgsrc->getRotateDegree()); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index ee14c80c2..c46176365 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -313,7 +313,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, LCPMapper *pLCPMap = nullptr; if (needsLCP()) { // don't check focal length to allow distortion correction for lenses without chip - LCPProfile *pLCPProf = lcpStore->getProfile (params->lensProf.lcpFile); + const std::shared_ptr pLCPProf = LCPStore::getInstance()->getProfile (params->lensProf.lcpFile); if (pLCPProf) { pLCPMap = new LCPMapper (pLCPProf, focalLen, focalLen35mm, @@ -784,7 +784,7 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, true /*fullImage*/ ? pLCPMap : nullptr) : 1.0; // smaller crop images are a problem, so only when processing fully - bool enableLCPCA = pLCPMap && params->lensProf.useCA && fullImage && pLCPMap->enableCA; + bool enableLCPCA = pLCPMap && params->lensProf.useCA && fullImage && pLCPMap->isCACorrectionAvailable(); bool enableLCPDist = pLCPMap && params->lensProf.useDist; // && fullImage; if (enableLCPCA) { diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index 048ba9dcf..d95d3a32e 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -20,27 +20,43 @@ #include #include -#include "lcp.h" #include #ifdef WIN32 -#include #include +#include #endif +#include "lcp.h" + #include "settings.h" -using namespace std; -using namespace rtengine; - - -namespace rtengine { +namespace rtengine +{ extern const Settings* settings; } -LCPModelCommon::LCPModelCommon() : +class rtengine::LCPProfile::LCPPersModel +{ +public: + LCPPersModel(); + bool hasModeData(LCPCorrectionMode mode) const; + void print() const; + + float focLen; + float focDist; + float aperture; // this is what it refers to + + LCPModelCommon base; // base perspective correction + LCPModelCommon chromRG; + LCPModelCommon chromG; + LCPModelCommon chromBG; // red/green, green, blue/green (may be empty) + LCPModelCommon vignette; // vignette (may be empty) +}; + +rtengine::LCPModelCommon::LCPModelCommon() : foc_len_x(-1.0f), foc_len_y(-1.0f), img_center_x(0.5f), @@ -59,20 +75,23 @@ LCPModelCommon::LCPModelCommon() : { } -bool LCPModelCommon::empty() const +bool rtengine::LCPModelCommon::empty() const { - return param[0] == 0.0f && param[1] == 0.0f && param[2] == 0.0f; + return + param[0] == 0.0f + && param[1] == 0.0f + && param[2] == 0.0f; } -void LCPModelCommon::print() const +void rtengine::LCPModelCommon::print() const { - printf("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n", foc_len_x, foc_len_y, img_center_x, img_center_y, scale_factor, mean_error); - printf("xy0 %g/%g fxy %g/%g\n", x0, y0, fx, fy); - printf("param: %g/%g/%g/%g/%g\n", param[0], param[1], param[2], param[3], param[4]); + std::printf("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n", foc_len_x, foc_len_y, img_center_x, img_center_y, scale_factor, mean_error); + std::printf("xy0 %g/%g fxy %g/%g\n", x0, y0, fx, fy); + std::printf("param: %g/%g/%g/%g/%g\n", param[0], param[1], param[2], param[3], param[4]); } // weighted merge two parameters -void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA) +void rtengine::LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA) { const float facB = 1.0f - facA; @@ -83,7 +102,7 @@ void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, flo scale_factor = facA * a.scale_factor + facB * b.scale_factor; mean_error = facA * a.mean_error + facB * b.mean_error; - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { param[i] = facA * a.param[i] + facB * b.param[i]; } @@ -96,7 +115,16 @@ void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, flo } -void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY) +void rtengine::LCPModelCommon::prepareParams( + int fullWidth, + int fullHeight, + float focalLength, + float focalLength35mm, + float sensorFormatFactor, + bool swapXY, + bool mirrorX, + bool mirrorY +) { // Mention that the Adobe technical paper has a bug here, the DMAX is handled differently for focLen and imgCenter const int Dmax = std::max(fullWidth, fullHeight); @@ -125,69 +153,842 @@ void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLen rfx = 1.0f / fx; rfy = 1.0f / fy; - //printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter); + //std::printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter); } -LCPPersModel::LCPPersModel() +rtengine::LCPProfile::LCPPersModel::LCPPersModel() : + focLen(0.f), + focDist(0.f), + aperture(0.f) { - focLen = focDist = aperture = 0; } -// mode: 0=distortion, 1=vignette, 2=CA -bool LCPPersModel::hasModeData(LCPCorrectionMode mode) const +bool rtengine::LCPProfile::LCPPersModel::hasModeData(LCPCorrectionMode mode) const { switch (mode) { - case LCP_MODE_VIGNETTE: - return !vignette.empty() && !vignette.bad_error; - case LCP_MODE_DISTORTION: - return !base.empty() && !base.bad_error; - case LCP_MODE_CA: - return !chromRG.empty() && !chromG.empty() && !chromBG.empty() && - !chromRG.bad_error && !chromG.bad_error && !chromBG.bad_error; - default: - assert(false); - return false; + case LCPCorrectionMode::VIGNETTE: { + return !vignette.empty() && !vignette.bad_error; + } + + case LCPCorrectionMode::DISTORTION: { + return !base.empty() && !base.bad_error; + } + + case LCPCorrectionMode::CA: { + return + !chromRG.empty() + && !chromG.empty() + && !chromBG.empty() + && !chromRG.bad_error + && !chromG.bad_error + && !chromBG.bad_error; + } } + + assert(false); + return false; } -void LCPPersModel::print() const +void rtengine::LCPProfile::LCPPersModel::print() const { - printf("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture); - printf("Base:\n"); + std::printf("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture); + std::printf("Base:\n"); base.print(); if (!chromRG.empty()) { - printf("ChromRG:\n"); + std::printf("ChromRG:\n"); chromRG.print(); } if (!chromG.empty()) { - printf("ChromG:\n"); + std::printf("ChromG:\n"); chromG.print(); } if (!chromBG.empty()) { - printf("ChromBG:\n"); + std::printf("ChromBG:\n"); chromBG.print(); } if (!vignette.empty()) { - printf("Vignette:\n"); + std::printf("Vignette:\n"); vignette.print(); } - printf("\n"); + std::printf("\n"); } -// if !vignette then geometric and CA -LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, - int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg) :useCADist(false), swapXY(false), isFisheye(false), enableCA(false) +rtengine::LCPProfile::LCPProfile(const Glib::ustring& fname) : + isFisheye(false), + sensorFormatFactor(1.f), + persModelCount(0), + inCamProfiles(false), + firstLIDone(false), + inPerspect(false), + inAlternateLensID(false), + inAlternateLensNames(false), + lastTag{}, + inInvalidTag{}, + pCurPersModel(nullptr), + pCurCommon(nullptr), + aPersModel{} { - if (pProf == nullptr) { + const int BufferSize = 8192; + char buf[BufferSize]; + + XML_Parser parser = XML_ParserCreate(nullptr); + + if (!parser) { + throw "Couldn't allocate memory for XML parser"; + } + + XML_SetElementHandler(parser, XmlStartHandler, XmlEndHandler); + XML_SetCharacterDataHandler(parser, XmlTextHandler); + XML_SetUserData(parser, static_cast(this)); + + FILE* const pFile = g_fopen(fname.c_str (), "rb"); + + if (pFile) { + bool done; + + do { + int bytesRead = fread(buf, 1, BufferSize, pFile); + done = feof(pFile); + + if (XML_Parse(parser, buf, bytesRead, done) == XML_STATUS_ERROR) { + XML_ParserFree(parser); + throw "Invalid XML in LCP file"; + } + } while (!done); + + fclose(pFile); + } + + XML_ParserFree(parser); + + if (settings->verbose) { + std::printf("Parsing %s\n", fname.c_str()); + } + // Two phase filter: first filter out the very rough ones, that distord the average a lot + // force it, even if there are few frames (community profiles) + filterBadFrames(LCPCorrectionMode::VIGNETTE, 2.0, 0); + filterBadFrames(LCPCorrectionMode::CA, 2.0, 0); + // from the non-distorded, filter again on new average basis, but only if there are enough frames left + filterBadFrames(LCPCorrectionMode::VIGNETTE, 1.5, 50); + filterBadFrames(LCPCorrectionMode::CA, 1.5, 50); +} + +rtengine::LCPProfile::~LCPProfile() +{ + delete pCurPersModel; + + for (int i = 0; i < MaxPersModelCount; ++i) { + delete aPersModel[i]; + } +} + +void rtengine::LCPProfile::calcParams( + LCPCorrectionMode mode, + float focalLength, + float focusDist, + float aperture, + LCPModelCommon* pCorr1, + LCPModelCommon* pCorr2, + LCPModelCommon* pCorr3 +) const +{ + const float euler = std::exp(1.0); + + // find the frames with the least distance, focal length wise + LCPPersModel* pLow = nullptr; + LCPPersModel* pHigh = nullptr; + + const float focalLengthLog = std::log(focalLength); //, apertureLog=aperture>0 ? std::log(aperture) : 0; + const float focusDistLog = focusDist > 0 ? std::log(focusDist) + euler : 0; + + // Pass 1: determining best focal length, if possible different focusDistances (for the focDist is not given case) + for (int pm = 0; pm < persModelCount; ++pm) { + const float f = aPersModel[pm]->focLen; + + if (aPersModel[pm]->hasModeData(mode)) { + if ( + f <= focalLength + && ( + pLow == nullptr + || f > pLow->focLen + || ( + focusDist == 0 + && f == pLow->focLen + && pLow->focDist > aPersModel[pm]->focDist + ) + ) + ) { + pLow = aPersModel[pm]; + } + + if ( + f >= focalLength + && ( + pHigh == nullptr + || f < pHigh->focLen + || ( + focusDist == 0 + && f == pHigh->focLen + && pHigh->focDist < aPersModel[pm]->focDist + ) + ) + ) { + pHigh = aPersModel[pm]; + } + } + } + + if (!pLow) { + pLow = pHigh; + } + else if (!pHigh) { + pHigh = pLow; + } + else { + // Pass 2: We have some, so take the best aperture for vignette and best focus for CA and distortion + // there are usually several frame per focal length. In the end pLow will have both flen and apterure/focdis below the target, + // and vice versa pHigh + const float bestFocLenLow = pLow->focLen; + const float bestFocLenHigh = pHigh->focLen; + + for (int pm = 0; pm < persModelCount; ++pm) { + const float aper = aPersModel[pm]->aperture; // float aperLog=std::log(aper); + const float focDist = aPersModel[pm]->focDist; + const float focDistLog = std::log(focDist) + euler; + + double meanErr; + + if (aPersModel[pm]->hasModeData(mode)) { + double lowMeanErr = 0.0; + double highMeanErr = 0.0; + + switch (mode) { + case LCPCorrectionMode::VIGNETTE: { + meanErr = aPersModel[pm]->vignette.mean_error; + lowMeanErr = pLow->vignette.mean_error; + highMeanErr = pHigh->vignette.mean_error; + break; + } + + case LCPCorrectionMode::DISTORTION: { + meanErr = aPersModel[pm]->base.mean_error; + lowMeanErr = pLow->base.mean_error; + highMeanErr = pHigh->base.mean_error; + break; + } + + case LCPCorrectionMode::CA: { + meanErr = aPersModel[pm]->chromG.mean_error; + lowMeanErr = pLow->chromG.mean_error; + highMeanErr = pHigh->chromG.mean_error; + break; + } + } + + if (aperture > 0 && mode != LCPCorrectionMode::CA) { + if ( + aPersModel[pm]->focLen == bestFocLenLow + && ( + ( + aper == aperture + && lowMeanErr > meanErr + ) + || ( + aper >= aperture + && aper < pLow->aperture + && pLow->aperture > aperture + ) + || ( + aper <= aperture + && ( + pLow->aperture > aperture + || fabs(aperture - aper) < fabs(aperture - pLow->aperture) + ) + ) + ) + ) { + pLow = aPersModel[pm]; + } + + if ( + aPersModel[pm]->focLen == bestFocLenHigh + && ( + ( + aper == aperture + && highMeanErr > meanErr + ) + || ( + aper <= aperture + && aper > pHigh->aperture + && pHigh->aperture < aperture + ) + || ( + aper >= aperture + && ( + pHigh->aperture < aperture + || fabs(aperture - aper) < fabs(aperture - pHigh->aperture) + ) + ) + ) + ) { + pHigh = aPersModel[pm]; + } + } + else if (focusDist > 0 && mode != LCPCorrectionMode::VIGNETTE) { + // by focus distance + if ( + aPersModel[pm]->focLen == bestFocLenLow + && ( + ( + focDist == focusDist + && lowMeanErr > meanErr + ) + || ( + focDist >= focusDist + && focDist < pLow->focDist + && pLow->focDist > focusDist + ) + || ( + focDist <= focusDist + && ( + pLow->focDist > focusDist + || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (std::log(pLow->focDist) + euler)) + ) + ) + ) + ) { + pLow = aPersModel[pm]; + } + + if ( + aPersModel[pm]->focLen == bestFocLenHigh + && ( + ( + focDist == focusDist + && highMeanErr > meanErr + ) + || ( + focDist <= focusDist + && focDist > pHigh->focDist + && pHigh->focDist < focusDist + ) + || ( + focDist >= focusDist + && ( + pHigh->focDist < focusDist + || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (std::log(pHigh->focDist) + euler)) + ) + ) + ) + ) { + pHigh = aPersModel[pm]; + } + } + else { + // no focus distance available, just error + if (aPersModel[pm]->focLen == bestFocLenLow && lowMeanErr > meanErr) { + pLow = aPersModel[pm]; + } + + if (aPersModel[pm]->focLen == bestFocLenHigh && highMeanErr > meanErr) { + pHigh = aPersModel[pm]; + } + } + + } + } + } + + if (pLow != nullptr && pHigh != nullptr) { + // average out the factors, linear interpolation in logarithmic scale + float facLow = 0.5f; + bool focLenOnSpot = false; // pretty often, since max/min are often as frames in LCP + + // There is as foclen range, take that as basis + if (pLow->focLen < pHigh->focLen) { + facLow = (std::log(pHigh->focLen) - focalLengthLog) / (std::log(pHigh->focLen) - std::log(pLow->focLen)); + } else { + focLenOnSpot = pLow->focLen == pHigh->focLen && pLow->focLen == focalLength; + } + + // and average the other factor if available + if ( + mode == LCPCorrectionMode::VIGNETTE + && pLow->aperture < aperture + && pHigh->aperture > aperture + ) { + // Mix in aperture + const float facAperLow = (pHigh->aperture - aperture) / (pHigh->aperture - pLow->aperture); + facLow = focLenOnSpot ? facAperLow : (0.5 * facLow + 0.5 * facAperLow); + } + else if ( + mode != LCPCorrectionMode::VIGNETTE + && focusDist > 0 + && pLow->focDist < focusDist + && pHigh->focDist > focusDist + ) { + // focus distance for all else (if focus distance is given) + const float facDistLow = (std::log(pHigh->focDist) + euler - focusDistLog) / (std::log(pHigh->focDist) - std::log(pLow->focDist)); + facLow = focLenOnSpot ? facDistLow : (0.8 * facLow + 0.2 * facDistLow); + } + + switch (mode) { + case LCPCorrectionMode::VIGNETTE: { + pCorr1->merge(pLow->vignette, pHigh->vignette, facLow); + break; + } + + case LCPCorrectionMode::DISTORTION: { + pCorr1->merge(pLow->base, pHigh->base, facLow); + break; + } + + case LCPCorrectionMode::CA: { + pCorr1->merge(pLow->chromRG, pHigh->chromRG, facLow); + pCorr2->merge(pLow->chromG, pHigh->chromG, facLow); + pCorr3->merge(pLow->chromBG, pHigh->chromBG, facLow); + break; + } + } + + if (settings->verbose) { + std::printf("LCP mode=%i, dist: %g found frames: Fno %g-%g; FocLen %g-%g; Dist %g-%g with weight %g\n", toUnderlying(mode), focusDist, pLow->aperture, pHigh->aperture, pLow->focLen, pHigh->focLen, pLow->focDist, pHigh->focDist, facLow); + } + } else { + if (settings->verbose) { + std::printf("Error: LCP file contained no %s parameters\n", mode == LCPCorrectionMode::VIGNETTE ? "vignette" : mode == LCPCorrectionMode::DISTORTION ? "distortion" : "CA" ); + } + } +} + +void rtengine::LCPProfile::print() const +{ + std::printf("=== Profile %s\n", profileName.c_str()); + std::printf("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n", persModelCount, isRaw, isFisheye, sensorFormatFactor); + + for (int pm = 0; pm < persModelCount; ++pm) { + aPersModel[pm]->print(); + } +} + +// from all frames not marked as bad already, take average and filter out frames with higher deviation than this if there are enough values +int rtengine::LCPProfile::filterBadFrames(LCPCorrectionMode mode, double maxAvgDevFac, int minFramesLeft) +{ + // take average error, then calculated the maximum deviation allowed + double err = 0.0; + int count = 0; + + for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; ++pm) { + if (aPersModel[pm]->hasModeData(mode)) { + ++count; + switch (mode) { + case LCPCorrectionMode::VIGNETTE: { + err += aPersModel[pm]->vignette.mean_error; + break; + } + + case LCPCorrectionMode::DISTORTION: { + err += aPersModel[pm]->base.mean_error; + break; + } + + case LCPCorrectionMode::CA: { + err += rtengine::max(aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error, aPersModel[pm]->chromBG.mean_error); + break; + } + } + } + } + + // Only if we have enough frames, filter out errors + int filtered = 0; + + if (count >= minFramesLeft) { + if (count > 0) { + err /= count; + } + + // Now mark all the bad ones as bad, and hasModeData will return false; + for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; ++pm) { + if (aPersModel[pm]->hasModeData(mode)) { + switch (mode) { + case LCPCorrectionMode::VIGNETTE: { + if (aPersModel[pm]->vignette.mean_error > maxAvgDevFac * err) { + aPersModel[pm]->vignette.bad_error = true; + filtered++; + } + break; + } + + case LCPCorrectionMode::DISTORTION: { + if (aPersModel[pm]->base.mean_error > maxAvgDevFac * err) { + aPersModel[pm]->base.bad_error = true; + filtered++; + } + break; + } + + case LCPCorrectionMode::CA: { + if ( + aPersModel[pm]->chromRG.mean_error > maxAvgDevFac * err + || aPersModel[pm]->chromG.mean_error > maxAvgDevFac * err + || aPersModel[pm]->chromBG.mean_error > maxAvgDevFac * err + ) { + aPersModel[pm]->chromRG.bad_error = true; + aPersModel[pm]->chromG.bad_error = true; + aPersModel[pm]->chromBG.bad_error = true; + ++filtered; + } + break; + } + } + } + } + + if (settings->verbose) { + std::printf("Filtered %.1f%% frames for maxAvgDevFac %g leaving %i\n", filtered *100.f / count, maxAvgDevFac, count - filtered); + } + } + + return filtered; +} + +void rtengine::LCPProfile::handle_text(const std::string& text) +{ + // Check if it contains non-whitespaces (there are several calls to this for one tag unfortunately) + bool onlyWhiteSpace = true; + for (auto c : text) { + if (!std::isspace(c)) { + onlyWhiteSpace = false; + break; + } + } + + if (onlyWhiteSpace) { return; } - useCADist = useCADistP; + LCPProfile* const pProf = this; + + // convert to null terminated + const std::string tag = pProf->lastTag; + + // Common data section + if (!pProf->firstLIDone) { + // Generic tags are the same for all + if (tag == "ProfileName") { + pProf->profileName = text; + } else if (tag == "Model") { + pProf->camera = text; + } else if (tag == "Lens") { + pProf->lens = text; + } else if (tag == "CameraPrettyName") { + pProf->cameraPrettyName = text; + } else if (tag == "LensPrettyName") { + pProf->lensPrettyName = text; + } else if (tag == "CameraRawProfile") { + pProf->isRaw = text == "True"; + } + } + + // Locale should be already set + assert(std::atof("1.2345") == 1.2345); + + if (!pProf->firstLIDone) { + if (tag == "SensorFormatFactor") { + pProf->sensorFormatFactor = std::atof(text.c_str()); + } + } + + // Perspective model base data + if (tag == "FocalLength") { + pProf->pCurPersModel->focLen = std::atof(text.c_str()); + } else if (tag == "FocusDistance") { + double focDist = std::atof(text.c_str()); + pProf->pCurPersModel->focDist = focDist < 10000 ? focDist : 10000; + } else if (tag == "ApertureValue") { + pProf->pCurPersModel->aperture = std::atof(text.c_str()); + } + + // Section depended + if (tag == "FocalLengthX") { + pProf->pCurCommon->foc_len_x = std::atof(text.c_str()); + } else if (tag == "FocalLengthY") { + pProf->pCurCommon->foc_len_y = std::atof(text.c_str()); + } else if (tag == "ImageXCenter") { + pProf->pCurCommon->img_center_x = std::atof(text.c_str()); + } else if (tag == "ImageYCenter") { + pProf->pCurCommon->img_center_y = std::atof(text.c_str()); + } else if (tag == "ScaleFactor") { + pProf->pCurCommon->scale_factor = std::atof(text.c_str()); + } else if (tag == "ResidualMeanError") { + pProf->pCurCommon->mean_error = std::atof(text.c_str()); + } else if (tag == "RadialDistortParam1" || tag == "VignetteModelParam1") { + pProf->pCurCommon->param[0] = std::atof(text.c_str()); + } else if (tag == "RadialDistortParam2" || tag == "VignetteModelParam2") { + pProf->pCurCommon->param[1] = std::atof(text.c_str()); + } else if (tag == "RadialDistortParam3" || tag == "VignetteModelParam3") { + pProf->pCurCommon->param[2] = std::atof(text.c_str()); + } else if (tag == "RadialDistortParam4" || tag == "TangentialDistortParam1") { + pProf->pCurCommon->param[3] = std::atof(text.c_str()); + } else if (tag == "RadialDistortParam5" || tag == "TangentialDistortParam2") { + pProf->pCurCommon->param[4] = std::atof(text.c_str()); + } +} + +void XMLCALL rtengine::LCPProfile::XmlStartHandler(void* pLCPProfile, const char* el, const char** attr) +{ + LCPProfile* const pProf = static_cast(pLCPProfile); + + bool parseAttr = false; + + if (*pProf->inInvalidTag) { + return; // We ignore everything in dirty tag till it's gone + } + + // clean up tagname + const char* src = strrchr(el, ':'); + + if (src == nullptr) { + src = el; + } else { + ++src; + } + + strcpy(pProf->lastTag, src); + + const std::string src_str = src; + + if (src_str == "VignetteModelPiecewiseParam") { + strcpy(pProf->inInvalidTag, src); + } + + if (src_str == "CameraProfiles") { + pProf->inCamProfiles = true; + } + + if (src_str == "AlternateLensIDs") { + pProf->inAlternateLensID = true; + } + + if (src_str == "AlternateLensNames") { + pProf->inAlternateLensNames = true; + } + + if ( + !pProf->inCamProfiles + || pProf->inAlternateLensID + || pProf->inAlternateLensNames + ) { + return; + } + + if (src_str == "li") { + pProf->pCurPersModel = new LCPPersModel(); + pProf->pCurCommon = &pProf->pCurPersModel->base; // iterated to next tags within persModel + return; + } + + if (src_str == "PerspectiveModel") { + pProf->firstLIDone = true; + pProf->inPerspect = true; + return; + } else if (src_str == "FisheyeModel") { + pProf->firstLIDone = true; + pProf->inPerspect = true; + pProf->isFisheye = true; // just misses third param, and different path, rest is the same + return; + } else if (src_str == "Description") { + parseAttr = true; + } + + // Move pointer to general section + if (pProf->inPerspect) { + if (src_str == "ChromaticRedGreenModel") { + pProf->pCurCommon = &pProf->pCurPersModel->chromRG; + parseAttr = true; + } else if (src_str == "ChromaticGreenModel") { + pProf->pCurCommon = &pProf->pCurPersModel->chromG; + parseAttr = true; + } else if (src_str == "ChromaticBlueGreenModel") { + pProf->pCurCommon = &pProf->pCurPersModel->chromBG; + parseAttr = true; + } else if (src_str == "VignetteModel") { + pProf->pCurCommon = &pProf->pCurPersModel->vignette; + parseAttr = true; + } + } + + // some profiles (espc. Pentax) have a different structure that is attributes based + // simulate tags by feeding them in + if (parseAttr && attr != nullptr) { + for (int i = 0; attr[i]; i += 2) { + const char* nameStart = strrchr(attr[i], ':'); + + if (nameStart == nullptr) { + nameStart = attr[i]; + } else { + ++nameStart; + } + + strncpy(pProf->lastTag, nameStart, 255); + + pProf->handle_text(attr[i + 1]); + } + } +} + +void XMLCALL rtengine::LCPProfile::XmlTextHandler(void* pLCPProfile, const XML_Char* s, int len) +{ + LCPProfile* const pProf = static_cast(pLCPProfile); + + if ( + !pProf->inCamProfiles + || pProf->inAlternateLensID + || pProf->inAlternateLensNames + || *pProf->inInvalidTag + ) { + return; + } + + for (int i = 0; i < len; ++i) { + pProf->textbuf << s[i]; + } +} + +void XMLCALL rtengine::LCPProfile::XmlEndHandler(void* pLCPProfile, const char* el) +{ + LCPProfile* const pProf = static_cast(pLCPProfile); + + pProf->handle_text(pProf->textbuf.str()); + pProf->textbuf.str(""); + + // We ignore everything in dirty tag till it's gone + if (*pProf->inInvalidTag) { + if (std::strstr(el, pProf->inInvalidTag)) { + *pProf->inInvalidTag = 0; + } + + return; + } + + if (std::strstr(el, ":CameraProfiles")) { + pProf->inCamProfiles = false; + } + + if (std::strstr(el, ":AlternateLensIDs")) { + pProf->inAlternateLensID = false; + } + + if (std::strstr(el, ":AlternateLensNames")) { + pProf->inAlternateLensNames = false; + } + + if ( + !pProf->inCamProfiles + || pProf->inAlternateLensID + || pProf->inAlternateLensNames + ) { + return; + } + + if (std::strstr(el, ":PerspectiveModel") || std::strstr(el, ":FisheyeModel")) { + pProf->inPerspect = false; + } else if (std::strstr(el, ":li")) { + pProf->aPersModel[pProf->persModelCount] = pProf->pCurPersModel; + pProf->pCurPersModel = nullptr; + ++pProf->persModelCount; + } +} + +// Generates as singleton +rtengine::LCPStore* rtengine::LCPStore::getInstance() +{ + static LCPStore instance_; + return &instance_; +} + +bool rtengine::LCPStore::isValidLCPFileName(const Glib::ustring& filename) const +{ + if (!Glib::file_test(filename, Glib::FILE_TEST_EXISTS) || Glib::file_test (filename, Glib::FILE_TEST_IS_DIR)) { + return false; + } + + const size_t pos = filename.find_last_of ('.'); + return pos > 0 && !filename.casefold().compare(pos, 4, ".lcp"); +} + +std::shared_ptr rtengine::LCPStore::getProfile(const Glib::ustring& filename) const +{ + if (filename.length() == 0 || !isValidLCPFileName(filename)) { + return nullptr; + } + + std::shared_ptr res; + if (!cache.get(filename, res)) { + res.reset(new LCPProfile(filename)); + cache.set(filename, res); + } + + return res; +} + +Glib::ustring rtengine::LCPStore::getDefaultCommonDirectory() const +{ + Glib::ustring dir; + +#ifdef WIN32 + WCHAR pathW[MAX_PATH] = {0}; + + if (SHGetSpecialFolderPathW(NULL, pathW, CSIDL_COMMON_APPDATA, false)) { + char pathA[MAX_PATH]; + WideCharToMultiByte(CP_UTF8, 0, pathW, -1, pathA, MAX_PATH, 0, 0); + Glib::ustring fullDir = Glib::ustring(pathA) + Glib::ustring("\\Adobe\\CameraRaw\\LensProfiles\\1.0"); + + if (Glib::file_test (fullDir, Glib::FILE_TEST_IS_DIR)) { + dir = fullDir; + } + } + +#endif + + // TODO: Add Mac paths here + + return dir; +} + +rtengine::LCPStore::LCPStore(unsigned int _cache_size) : + cache(_cache_size) +{ +} + +// if !vignette then geometric and CA +rtengine::LCPMapper::LCPMapper( + const std::shared_ptr& pProf, + float focalLength, + float focalLength35mm, + float focusDist, + float aperture, + bool vignette, + bool useCADistP, + int fullWidth, + int fullHeight, + const CoarseTransformParams& coarse, + int rawRotationDeg +) : + enableCA(false), + useCADist(useCADistP), + swapXY(false), + isFisheye(false) +{ + if (!pProf) { + return; + } // determine in what the image with the RAW landscape in comparison (calibration target) // in vignetting, the rotation has not taken place yet @@ -197,84 +998,91 @@ LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm rot = (coarse.rotate + rawRotationDeg) % 360; } - swapXY = (rot == 90 || rot == 270); - bool mirrorX = (rot == 90 || rot == 180); - bool mirrorY = (rot == 180 || rot == 270); + swapXY = (rot == 90 || rot == 270); + + const bool mirrorX = (rot == 90 || rot == 180); + const bool mirrorY = (rot == 180 || rot == 270); if (settings->verbose) { - printf("Vign: %i, fullWidth: %i/%i, focLen %g SwapXY: %i / MirX/Y %i / %i on rot:%i from %i\n",vignette, fullWidth, fullHeight, focalLength, swapXY, mirrorX, mirrorY, rot, rawRotationDeg); + std::printf("Vign: %i, fullWidth: %i/%i, focLen %g SwapXY: %i / MirX/Y %i / %i on rot:%i from %i\n",vignette, fullWidth, fullHeight, focalLength, swapXY, mirrorX, mirrorY, rot, rawRotationDeg); } - pProf->calcParams(vignette ? LCP_MODE_VIGNETTE : LCP_MODE_DISTORTION, focalLength, focusDist, aperture, &mc, nullptr, nullptr); + pProf->calcParams(vignette ? LCPCorrectionMode::VIGNETTE : LCPCorrectionMode::DISTORTION, focalLength, focusDist, aperture, &mc, nullptr, nullptr); mc.prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); if (!vignette) { - pProf->calcParams(LCP_MODE_CA, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]); + pProf->calcParams(LCPCorrectionMode::CA, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 3; ++i) { chrom[i].prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); } } - enableCA = !vignette && focusDist > 0; + enableCA = !vignette && focusDist > 0.f; isFisheye = pProf->isFisheye; } -void LCPMapper::correctDistortion(double& x, double& y, double scale) const +bool rtengine::LCPMapper::isCACorrectionAvailable() const +{ + return enableCA; +} + +void rtengine::LCPMapper::correctDistortion(double& x, double& y, double scale) const { if (isFisheye) { - double u = x * scale; - double v = y * scale; - double u0 = mc.x0 * scale; - double v0 = mc.y0 * scale; - double du = (u - u0); - double dv = (v - v0); - double fx = mc.fx; - double fy = mc.fy; - double k1 = mc.param[0]; - double k2 = mc.param[1]; - double r = sqrt(du * du + dv * dv); - double f = sqrt(fx*fy / (scale * scale)); - double th = atan2(r, f); - double th2 = th * th; - double cfact = (((k2 * th2 + k1) * th2 + 1) * th) / r; - double ud = cfact * fx * du + u0; - double vd = cfact * fy * dv + v0; + const double u = x * scale; + const double v = y * scale; + const double u0 = mc.x0 * scale; + const double v0 = mc.y0 * scale; + const double du = (u - u0); + const double dv = (v - v0); + const double fx = mc.fx; + const double fy = mc.fy; + const double k1 = mc.param[0]; + const double k2 = mc.param[1]; + const double r = sqrt(du * du + dv * dv); + const double f = sqrt(fx*fy / (scale * scale)); + const double th = atan2(r, f); + const double th2 = th * th; + const double cfact = (((k2 * th2 + k1) * th2 + 1) * th) / r; + const double ud = cfact * fx * du + u0; + const double vd = cfact * fy * dv + v0; x = ud; y = vd; } else { x *= scale; y *= scale; - double x0 = mc.x0 * scale; - double y0 = mc.y0 * scale; - double xd = (x - x0) / mc.fx, yd = (y - y0) / mc.fy; + const double x0 = mc.x0 * scale; + const double y0 = mc.y0 * scale; + const double xd = (x - x0) / mc.fx, yd = (y - y0) / mc.fy; const LCPModelCommon::Param aDist = mc.param; - double rsqr = xd * xd + yd * yd; - double xfac = aDist[swapXY ? 3 : 4], yfac = aDist[swapXY ? 4 : 3]; + const double rsqr = xd * xd + yd * yd; + const double xfac = aDist[swapXY ? 3 : 4], yfac = aDist[swapXY ? 4 : 3]; - double commonFac = (((aDist[2] * rsqr + aDist[1]) * rsqr + aDist[0]) * rsqr + 1.) + const double commonFac = (((aDist[2] * rsqr + aDist[1]) * rsqr + aDist[0]) * rsqr + 1.) + 2. * (yfac * yd + xfac * xd); - double xnew = xd * commonFac + xfac * rsqr; - double ynew = yd * commonFac + yfac * rsqr; + const double xnew = xd * commonFac + xfac * rsqr; + const double ynew = yd * commonFac + yfac * rsqr; x = xnew * mc.fx + x0; y = ynew * mc.fy + y0; } } -void LCPMapper::correctCA(double& x, double& y, int channel) const +void rtengine::LCPMapper::correctCA(double& x, double& y, int channel) const { if (!enableCA) { return; } - double rsqr, xgreen, ygreen; + double xgreen, ygreen; // First calc the green channel like normal distortion // the other are just deviations from it - double xd = (x - chrom[1].x0) / chrom[1].fx, yd = (y - chrom[1].y0) / chrom[1].fy; + double xd = (x - chrom[1].x0) / chrom[1].fx; + double yd = (y - chrom[1].y0) / chrom[1].fy; // Green contains main distortion, just like base if (useCADist) { @@ -300,18 +1108,18 @@ void LCPMapper::correctCA(double& x, double& y, int channel) const // others are diffs from green xd = xgreen; yd = ygreen; - rsqr = xd * xd + yd * yd; + const double rsqr = xd * xd + yd * yd; const LCPModelCommon::Param aCA = chrom[channel].param; - double xfac = aCA[swapXY ? 3 : 4], yfac = aCA[swapXY ? 4 : 3]; - double commonSum = 1. + rsqr * (aCA[0] + rsqr * (aCA[1] + aCA[2] * rsqr)) + 2. * (yfac * yd + xfac * xd); + const double xfac = aCA[swapXY ? 3 : 4], yfac = aCA[swapXY ? 4 : 3]; + const double commonSum = 1. + rsqr * (aCA[0] + rsqr * (aCA[1] + aCA[2] * rsqr)) + 2. * (yfac * yd + xfac * xd); x = (chrom[channel].scale_factor * ( xd * commonSum + xfac * rsqr )) * chrom[channel].fx + chrom[channel].x0; y = (chrom[channel].scale_factor * ( yd * commonSum + yfac * rsqr )) * chrom[channel].fy + chrom[channel].y0; } } -SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) const +SSEFUNCTION void rtengine::LCPMapper::processVignetteLine(int width, int y, float* line) const { // No need for swapXY, since vignette is in RAW and always before rotation float yd = ((float)y - mc.y0) * mc.rfy; @@ -330,9 +1138,9 @@ SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) c vfloat xv = _mm_setr_ps(0.f, 1.f, 2.f, 3.f); for (; x < width-3; x+=4) { - vfloat xdv = (xv - x0v) * rfxv; - vfloat rsqr = xdv * xdv + ydv; - vfloat vignFactorv = rsqr * (p0 + rsqr * (p1 - p2 * rsqr + p3 * rsqr * rsqr)); + const vfloat xdv = (xv - x0v) * rfxv; + const vfloat rsqr = xdv * xdv + ydv; + const vfloat vignFactorv = rsqr * (p0 + rsqr * (p1 - p2 * rsqr + p3 * rsqr * rsqr)); vfloat valv = LVFU(line[x]); valv += valv * vselfzero(vmaskf_gt(valv, zerov), vignFactorv); STVFU(line[x], valv); @@ -341,24 +1149,24 @@ SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) c #endif // __SSE2__ for (; x < width; x++) { if (line[x] > 0) { - float xd = ((float)x - mc.x0) * mc.rfx; + const float xd = ((float)x - mc.x0) * mc.rfx; const LCPModelCommon::VignParam vignParam = mc.vign_param; - float rsqr = xd * xd + yd; + const float rsqr = xd * xd + yd; line[x] += line[x] * rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); } } } -SSEFUNCTION void LCPMapper::processVignetteLine3Channels(int width, int y, float *line) const +SSEFUNCTION void rtengine::LCPMapper::processVignetteLine3Channels(int width, int y, float* line) const { // No need for swapXY, since vignette is in RAW and always before rotation float yd = ((float)y - mc.y0) * mc.rfy; yd *= yd; const LCPModelCommon::VignParam vignParam = mc.vign_param; for (int x = 0; x < width; x++) { - float xd = ((float)x - mc.x0) * mc.rfx; - float rsqr = xd * xd + yd; - float vignetteFactor = rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); + const float xd = ((float)x - mc.x0) * mc.rfx; + const float rsqr = xd * xd + yd; + const float vignetteFactor = rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); for(int c = 0;c < 3; ++c) { if (line[3*x+c] > 0) { line[3*x+c] += line[3*x+c] * vignetteFactor; @@ -366,623 +1174,3 @@ SSEFUNCTION void LCPMapper::processVignetteLine3Channels(int width, int y, float } } } - - -LCPProfile::LCPProfile(const Glib::ustring &fname) -{ - for (int i = 0; i < MaxPersModelCount; i++) { - aPersModel[i] = nullptr; - } - pCurPersModel = nullptr; - - const int BufferSize = 8192; - char buf[BufferSize]; - - XML_Parser parser = XML_ParserCreate(nullptr); - - if (!parser) { - throw "Couldn't allocate memory for XML parser"; - } - - XML_SetElementHandler(parser, XmlStartHandler, XmlEndHandler); - XML_SetCharacterDataHandler(parser, XmlTextHandler); - XML_SetUserData(parser, (void *)this); - - - isFisheye = inCamProfiles = firstLIDone = inPerspect = inAlternateLensID = inAlternateLensNames = false; - sensorFormatFactor = 1; - - persModelCount = 0; - *inInvalidTag = 0; - - FILE *pFile = g_fopen(fname.c_str (), "rb"); - - if(pFile) { - bool done; - - do { - int bytesRead = (int)fread(buf, 1, BufferSize, pFile); - done = feof(pFile); - - if (XML_Parse(parser, buf, bytesRead, done) == XML_STATUS_ERROR) { - throw "Invalid XML in LCP file"; - } - } while (!done); - - fclose(pFile); - } - - XML_ParserFree(parser); - - if (settings->verbose) { - printf("Parsing %s\n", fname.c_str()); - } - // Two phase filter: first filter out the very rough ones, that distord the average a lot - // force it, even if there are few frames (community profiles) - filterBadFrames(LCP_MODE_VIGNETTE, 2.0, 0); - filterBadFrames(LCP_MODE_CA, 2.0, 0); - // from the non-distorded, filter again on new average basis, but only if there are enough frames left - filterBadFrames(LCP_MODE_VIGNETTE, 1.5, 50); - filterBadFrames(LCP_MODE_CA, 1.5, 50); -} - - -LCPProfile::~LCPProfile() -{ - if (pCurPersModel) { - delete pCurPersModel; - } - for (int i = 0; i < MaxPersModelCount; i++) { - if (aPersModel[i]) { - delete aPersModel[i]; - } - } -} - -// from all frames not marked as bad already, take average and filter out frames with higher deviation than this if there are enough values -int LCPProfile::filterBadFrames(LCPCorrectionMode mode, double maxAvgDevFac, int minFramesLeft) -{ - // take average error, then calculated the maximum deviation allowed - double err = 0; - int count = 0; - - for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) { - if (aPersModel[pm]->hasModeData(mode)) { - count++; - switch (mode) { - case LCP_MODE_VIGNETTE: - err += aPersModel[pm]->vignette.mean_error; - break; - case LCP_MODE_DISTORTION: - err += aPersModel[pm]->base.mean_error; - break; - case LCP_MODE_CA: - err += rtengine::max(aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error, aPersModel[pm]->chromBG.mean_error); - break; - } - } - } - - // Only if we have enough frames, filter out errors - int filtered = 0; - - if (count >= minFramesLeft) { - if (count > 0) { - err /= (double)count; - } - - // Now mark all the bad ones as bad, and hasModeData will return false; - for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) { - if (aPersModel[pm]->hasModeData(mode)) { - switch (mode) { - case LCP_MODE_VIGNETTE: - if (aPersModel[pm]->vignette.mean_error > maxAvgDevFac * err) { - aPersModel[pm]->vignette.bad_error = true; - filtered++; - } - break; - case LCP_MODE_DISTORTION: - if (aPersModel[pm]->base.mean_error > maxAvgDevFac * err) { - aPersModel[pm]->base.bad_error = true; - filtered++; - } - break; - case LCP_MODE_CA: - if ((aPersModel[pm]->chromRG.mean_error > maxAvgDevFac * err || aPersModel[pm]->chromG.mean_error > maxAvgDevFac * err - || aPersModel[pm]->chromBG.mean_error > maxAvgDevFac * err)) { - aPersModel[pm]->chromRG.bad_error = aPersModel[pm]->chromG.bad_error = aPersModel[pm]->chromBG.bad_error = true; - filtered++; - } - break; - } - } - } - - if (settings->verbose) { - printf("Filtered %.1f%% frames for maxAvgDevFac %g leaving %i\n", filtered*100./count, maxAvgDevFac, count-filtered); - } - } - - return filtered; -} - - -void LCPProfile::calcParams(LCPCorrectionMode mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const -{ - float euler = exp(1.0); - - // find the frames with the least distance, focal length wise - LCPPersModel *pLow = nullptr, *pHigh = nullptr; - - float focalLengthLog = log(focalLength); //, apertureLog=aperture>0 ? log(aperture) : 0; - float focusDistLog = focusDist > 0 ? log(focusDist) + euler : 0; - - // Pass 1: determining best focal length, if possible different focusDistances (for the focDist is not given case) - for (int pm = 0; pm < persModelCount; pm++) { - float f = aPersModel[pm]->focLen; - - if (aPersModel[pm]->hasModeData(mode)) { - if (f <= focalLength && (pLow == nullptr || f > pLow->focLen || (focusDist == 0 && f == pLow->focLen && pLow->focDist > aPersModel[pm]->focDist))) { - pLow = aPersModel[pm]; - } - - if (f >= focalLength && (pHigh == nullptr || f < pHigh->focLen || (focusDist == 0 && f == pHigh->focLen && pHigh->focDist < aPersModel[pm]->focDist))) { - pHigh = aPersModel[pm]; - } - } - } - - if (!pLow) { - pLow = pHigh; - } else if (!pHigh) { - pHigh = pLow; - } else { - // Pass 2: We have some, so take the best aperture for vignette and best focus for CA and distortion - // there are usually several frame per focal length. In the end pLow will have both flen and apterure/focdis below the target, - // and vice versa pHigh - float bestFocLenLow = pLow->focLen, bestFocLenHigh = pHigh->focLen; - - for (int pm = 0; pm < persModelCount; pm++) { - float aper = aPersModel[pm]->aperture; // float aperLog=log(aper); - float focDist = aPersModel[pm]->focDist; - float focDistLog = log(focDist) + euler; - double meanErr; - if (aPersModel[pm]->hasModeData(mode)) { - double lowMeanErr, highMeanErr; - switch (mode) { - case LCP_MODE_VIGNETTE: - meanErr = aPersModel[pm]->vignette.mean_error; - lowMeanErr = pLow->vignette.mean_error; - highMeanErr = pHigh->vignette.mean_error; - break; - case LCP_MODE_DISTORTION: - meanErr = aPersModel[pm]->base.mean_error; - lowMeanErr = pLow->base.mean_error; - highMeanErr = pHigh->base.mean_error; - break; - default: // LCP_MODE_CA - meanErr = aPersModel[pm]->chromG.mean_error; - lowMeanErr = pLow->chromG.mean_error; - highMeanErr = pHigh->chromG.mean_error; - break; - } - - if (aperture > 0 && mode != LCP_MODE_CA) { - if (aPersModel[pm]->focLen == bestFocLenLow && ( - (aper == aperture && lowMeanErr > meanErr) - || (aper >= aperture && aper < pLow->aperture && pLow->aperture > aperture) - || (aper <= aperture && (pLow->aperture > aperture || fabs(aperture - aper) < fabs(aperture - pLow->aperture))))) { - pLow = aPersModel[pm]; - } - - if (aPersModel[pm]->focLen == bestFocLenHigh && ( - (aper == aperture && highMeanErr > meanErr) - || (aper <= aperture && aper > pHigh->aperture && pHigh->aperture < aperture) - || (aper >= aperture && (pHigh->aperture < aperture || fabs(aperture - aper) < fabs(aperture - pHigh->aperture))))) { - pHigh = aPersModel[pm]; - } - } else if (focusDist > 0 && mode != LCP_MODE_VIGNETTE) { - // by focus distance - if (aPersModel[pm]->focLen == bestFocLenLow && ( - (focDist == focusDist && lowMeanErr > meanErr) - || (focDist >= focusDist && focDist < pLow->focDist && pLow->focDist > focusDist) - || (focDist <= focusDist && (pLow->focDist > focusDist || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (log(pLow->focDist) + euler)))))) { - pLow = aPersModel[pm]; - } - - if (aPersModel[pm]->focLen == bestFocLenHigh && ( - (focDist == focusDist && highMeanErr > meanErr) - || (focDist <= focusDist && focDist > pHigh->focDist && pHigh->focDist < focusDist) - || (focDist >= focusDist && (pHigh->focDist < focusDist || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (log(pHigh->focDist) + euler)))))) { - pHigh = aPersModel[pm]; - } - } else { - // no focus distance available, just error - if (aPersModel[pm]->focLen == bestFocLenLow && lowMeanErr > meanErr) { - pLow = aPersModel[pm]; - } - - if (aPersModel[pm]->focLen == bestFocLenHigh && highMeanErr > meanErr) { - pHigh = aPersModel[pm]; - } - } - - } - } - } - - if (pLow != nullptr && pHigh != nullptr) { - // average out the factors, linear interpolation in logarithmic scale - float facLow = 0.5; - bool focLenOnSpot = false; // pretty often, since max/min are often as frames in LCP - - // There is as foclen range, take that as basis - if (pLow->focLen < pHigh->focLen) { - facLow = (log(pHigh->focLen) - focalLengthLog) / (log(pHigh->focLen) - log(pLow->focLen)); - } else { - focLenOnSpot = pLow->focLen == pHigh->focLen && pLow->focLen == focalLength; - } - - // and average the other factor if available - if (mode == LCP_MODE_VIGNETTE && 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 != LCP_MODE_VIGNETTE && focusDist > 0 && pLow->focDist < focusDist && pHigh->focDist > focusDist) { - // focus distance for all else (if focus distance is given) - float facDistLow = (log(pHigh->focDist) + euler - focusDistLog) / (log(pHigh->focDist) - log(pLow->focDist)); - facLow = focLenOnSpot ? facDistLow : (0.8 * facLow + 0.2 * facDistLow); - } - - switch (mode) { - case LCP_MODE_VIGNETTE: - pCorr1->merge(pLow->vignette, pHigh->vignette, facLow); - break; - - case LCP_MODE_DISTORTION: - pCorr1->merge(pLow->base, pHigh->base, facLow); - break; - - case LCP_MODE_CA: - pCorr1->merge(pLow->chromRG, pHigh->chromRG, facLow); - pCorr2->merge(pLow->chromG, pHigh->chromG, facLow); - pCorr3->merge(pLow->chromBG, pHigh->chromBG, facLow); - break; - } - - if (settings->verbose) { - printf("LCP mode=%i, dist: %g found frames: Fno %g-%g; FocLen %g-%g; Dist %g-%g with weight %g\n", mode, focusDist, pLow->aperture, pHigh->aperture, pLow->focLen, pHigh->focLen, pLow->focDist, pHigh->focDist, facLow); - } - } else { - if (settings->verbose) { - printf("Error: LCP file contained no %s parameters\n", mode == LCP_MODE_VIGNETTE ? "vignette" : mode == LCP_MODE_DISTORTION ? "distortion" : "CA" ); - } - } -} - -void LCPProfile::print() const -{ - printf("=== Profile %s\n", profileName.c_str()); - printf("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n", persModelCount, isRaw, isFisheye, sensorFormatFactor); - - for (int pm = 0; pm < persModelCount; pm++) { - aPersModel[pm]->print(); - } -} - -void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, const char **attr) -{ - LCPProfile *pProf = static_cast(pLCPProfile); - bool parseAttr = false; - - if (*pProf->inInvalidTag) { - return; // We ignore everything in dirty tag till it's gone - } - - // clean up tagname - const char* src = strrchr(el, ':'); - - if (src == nullptr) { - src = const_cast(el); - } else { - src++; - } - - strcpy(pProf->lastTag, src); - - if (!strcmp("VignetteModelPiecewiseParam", src)) { - strcpy(pProf->inInvalidTag, src); - } - - if (!strcmp("CameraProfiles", src)) { - pProf->inCamProfiles = true; - } - - if (!strcmp("AlternateLensIDs", src)) { - pProf->inAlternateLensID = true; - } - - if (!strcmp("AlternateLensNames", src)) { - pProf->inAlternateLensNames = true; - } - - if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames) { - return; - } - - if (!strcmp("li", src)) { - pProf->pCurPersModel = new LCPPersModel(); - pProf->pCurCommon = &pProf->pCurPersModel->base; // iterated to next tags within persModel - return; - } - - if (!strcmp("PerspectiveModel", src)) { - pProf->firstLIDone = true; - pProf->inPerspect = true; - return; - } else if (!strcmp("FisheyeModel", src)) { - pProf->firstLIDone = true; - pProf->inPerspect = true; - pProf->isFisheye = true; // just misses third param, and different path, rest is the same - return; - } else if (!strcmp("Description", src)) { - parseAttr = true; - } - - // Move pointer to general section - if (pProf->inPerspect) { - if (!strcmp("ChromaticRedGreenModel", src)) { - pProf->pCurCommon = &pProf->pCurPersModel->chromRG; - parseAttr = true; - } else if (!strcmp("ChromaticGreenModel", src)) { - pProf->pCurCommon = &pProf->pCurPersModel->chromG; - parseAttr = true; - } else if (!strcmp("ChromaticBlueGreenModel", src)) { - pProf->pCurCommon = &pProf->pCurPersModel->chromBG; - parseAttr = true; - } else if (!strcmp("VignetteModel", src)) { - pProf->pCurCommon = &pProf->pCurPersModel->vignette; - parseAttr = true; - } - } - - // some profiles (espc. Pentax) have a different structure that is attributes based - // simulate tags by feeding them in - if (parseAttr && attr != nullptr) { - for (int i = 0; attr[i]; i += 2) { - const char* nameStart = strrchr(attr[i], ':'); - - if (nameStart == nullptr) { - nameStart = const_cast(attr[i]); - } else { - nameStart++; - } - - strncpy(pProf->lastTag, nameStart, 255); - - pProf->handle_text(attr[i+1]); - //XmlTextHandler(pLCPProfile, attr[i + 1], strlen(attr[i + 1])); - } - } -} - -void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, int len) -{ - LCPProfile *pProf = static_cast(pLCPProfile); - - if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames || *pProf->inInvalidTag) { - return; - } - - for (int i = 0; i < len; ++i) { - pProf->textbuf << s[i]; - } -} - - -void LCPProfile::handle_text(std::string text) -{ - // Check if it contains non-whitespaces (there are several calls to this for one tag unfortunately) - bool onlyWhiteSpace = true; - for (size_t i = 0; i < text.size(); ++i) { - if (!isspace(text[i])) { - onlyWhiteSpace = false; - break; - } - } - - if (onlyWhiteSpace) { - return; - } - - LCPProfile *pProf = this; - - // convert to null terminated - char* tag = pProf->lastTag; - - const char* raw = text.c_str(); - - // Common data section - if (!pProf->firstLIDone) { - // Generic tags are the same for all - if (!strcmp("ProfileName", tag)) { - pProf->profileName = Glib::ustring(raw); - } else if (!strcmp("Model", tag)) { - pProf->camera = Glib::ustring(raw); - } else if (!strcmp("Lens", tag)) { - pProf->lens = Glib::ustring(raw); - } else if (!strcmp("CameraPrettyName", tag)) { - pProf->cameraPrettyName = Glib::ustring(raw); - } else if (!strcmp("LensPrettyName", tag)) { - pProf->lensPrettyName = Glib::ustring(raw); - } else if (!strcmp("CameraRawProfile", tag)) { - pProf->isRaw = !strcmp("True", raw); - } - } - - // --- Now all floating points. Must replace local dot characters - // WARNING: called by different threads, that may run on different local settings, - // so don't use system params - if (atof("1,2345") == 1.2345) { - for (size_t i = 0; i < text.size(); ++i) { - if (text[i] == '.') { - text[i] = ','; - } - } - raw = text.c_str(); - } - - if (!pProf->firstLIDone) { - if (!strcmp("SensorFormatFactor", tag)) { - pProf->sensorFormatFactor = atof(raw); - } - } - - // Perspective model base data - if (!strcmp("FocalLength", tag)) { - pProf->pCurPersModel->focLen = atof(raw); - } else if (!strcmp("FocusDistance", tag)) { - double focDist = atof(raw); - pProf->pCurPersModel->focDist = focDist < 10000 ? focDist : 10000; - } else if (!strcmp("ApertureValue", tag)) { - pProf->pCurPersModel->aperture = atof(raw); - } - - // Section depended - if (!strcmp("FocalLengthX", tag)) { - pProf->pCurCommon->foc_len_x = atof(raw); - } else if (!strcmp("FocalLengthY", tag)) { - pProf->pCurCommon->foc_len_y = atof(raw); - } else if (!strcmp("ImageXCenter", tag)) { - pProf->pCurCommon->img_center_x = atof(raw); - } else if (!strcmp("ImageYCenter", tag)) { - pProf->pCurCommon->img_center_y = atof(raw); - } else if (!strcmp("ScaleFactor", tag)) { - pProf->pCurCommon->scale_factor = atof(raw); - } else if (!strcmp("ResidualMeanError", tag)) { - pProf->pCurCommon->mean_error = atof(raw); - } else if (!strcmp("RadialDistortParam1", tag) || !strcmp("VignetteModelParam1", tag)) { - pProf->pCurCommon->param[0] = atof(raw); - } else if (!strcmp("RadialDistortParam2", tag) || !strcmp("VignetteModelParam2", tag)) { - pProf->pCurCommon->param[1] = atof(raw); - } else if (!strcmp("RadialDistortParam3", tag) || !strcmp("VignetteModelParam3", tag)) { - pProf->pCurCommon->param[2] = atof(raw); - } else if (!strcmp("RadialDistortParam4", tag) || !strcmp("TangentialDistortParam1", tag)) { - pProf->pCurCommon->param[3] = atof(raw); - } else if (!strcmp("RadialDistortParam5", tag) || !strcmp("TangentialDistortParam2", tag)) { - pProf->pCurCommon->param[4] = atof(raw); - } -} - -void XMLCALL LCPProfile::XmlEndHandler(void *pLCPProfile, const char *el) -{ - LCPProfile *pProf = static_cast(pLCPProfile); - - pProf->handle_text(pProf->textbuf.str()); - pProf->textbuf.str(""); - - // We ignore everything in dirty tag till it's gone - if (*pProf->inInvalidTag) { - if (strstr(el, pProf->inInvalidTag)) { - *pProf->inInvalidTag = 0; - } - - return; - } - - if (strstr(el, ":CameraProfiles")) { - pProf->inCamProfiles = false; - } - - if (strstr(el, ":AlternateLensIDs")) { - pProf->inAlternateLensID = false; - } - - if (strstr(el, ":AlternateLensNames")) { - pProf->inAlternateLensNames = false; - } - - if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames) { - return; - } - - if (strstr(el, ":PerspectiveModel") || strstr(el, ":FisheyeModel")) { - pProf->inPerspect = false; - } else if (strstr(el, ":li")) { - pProf->aPersModel[pProf->persModelCount] = pProf->pCurPersModel; - pProf->pCurPersModel = nullptr; - pProf->persModelCount++; - } -} - -// Generates as singleton -LCPStore* LCPStore::getInstance() -{ - static LCPStore instance_; - return &instance_; -} - - -LCPStore::~LCPStore() -{ - for (auto &p : profileCache) { - delete p.second; - } -} - - -LCPProfile* LCPStore::getProfile (Glib::ustring filename) -{ - if (filename.length() == 0 || !isValidLCPFileName(filename)) { - return nullptr; - } - - MyMutex::MyLock lock(mtx); - - std::map::iterator r = profileCache.find (filename); - - if (r != profileCache.end()) { - return r->second; - } - - // Add profile (if exists) - profileCache[filename] = new LCPProfile(filename); - if (settings->verbose) { - profileCache[filename]->print(); - } - return profileCache[filename]; -} - -bool LCPStore::isValidLCPFileName(Glib::ustring filename) const -{ - if (!Glib::file_test (filename, Glib::FILE_TEST_EXISTS) || Glib::file_test (filename, Glib::FILE_TEST_IS_DIR)) { - return false; - } - - size_t pos = filename.find_last_of ('.'); - return pos > 0 && !filename.casefold().compare (pos, 4, ".lcp"); -} - -Glib::ustring LCPStore::getDefaultCommonDirectory() const -{ - Glib::ustring dir; - -#ifdef WIN32 - WCHAR pathW[MAX_PATH] = {0}; - - if (SHGetSpecialFolderPathW(NULL, pathW, CSIDL_COMMON_APPDATA, false)) { - char pathA[MAX_PATH]; - WideCharToMultiByte(CP_UTF8, 0, pathW, -1, pathA, MAX_PATH, 0, 0); - Glib::ustring fullDir = Glib::ustring(pathA) + Glib::ustring("\\Adobe\\CameraRaw\\LensProfiles\\1.0"); - - if (Glib::file_test (fullDir, Glib::FILE_TEST_IS_DIR)) { - dir = fullDir; - } - } - -#endif - - // TODO: Add Mac paths here - - return dir; -} diff --git a/rtengine/lcp.h b/rtengine/lcp.h index 291710c5f..cbeff3259 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -21,22 +21,24 @@ #include #include +#include #include #include #include #include +#include "cache.h" #include "imagefloat.h" #include "opthelper.h" namespace rtengine { -enum LCPCorrectionMode { - LCP_MODE_VIGNETTE = 0, - LCP_MODE_DISTORTION = 1, - LCP_MODE_CA = 2 +enum class LCPCorrectionMode { + VIGNETTE, + DISTORTION, + CA }; // Perspective model common data, also used for Vignette and Fisheye @@ -44,10 +46,20 @@ class LCPModelCommon final { public: LCPModelCommon(); + 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); + void prepareParams( + int fullWidth, + int fullHeight, + float focalLength, + float focalLength35mm, + float sensorFormatFactor, + bool swapXY, + bool mirrorX, + bool mirrorY + ); //private: using Param = std::array; @@ -72,97 +84,114 @@ public: VignParam vign_param; }; -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(); - bool hasModeData(LCPCorrectionMode mode) const; - void print() const; -}; - - class LCPProfile { - // Temporary data for parsing - bool inCamProfiles, firstLIDone, inPerspect, inAlternateLensID, inAlternateLensNames; - char lastTag[256], inInvalidTag[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); - - int filterBadFrames(LCPCorrectionMode mode, double maxAvgDevFac, int minFramesLeft); - - void handle_text(std::string text); - std::ostringstream textbuf; - public: + explicit LCPProfile(const Glib::ustring& fname); + ~LCPProfile(); + + void calcParams( + LCPCorrectionMode mode, + float focalLength, + float focusDist, + float aperture, + LCPModelCommon* pCorr1, + LCPModelCommon* pCorr2, + LCPModelCommon *pCorr3 + ) const; // Interpolates between the persModels frames + + void print() const; + +//private: // Common data - Glib::ustring profileName, lensPrettyName, cameraPrettyName, lens, camera; // lens/camera(=model) can be auto-matched with DNG - bool isRaw, isFisheye; + Glib::ustring profileName; + Glib::ustring lensPrettyName; + Glib::ustring cameraPrettyName; + Glib::ustring lens; + Glib::ustring camera; // lens/camera(=model) can be auto-matched with DNG + bool isRaw; + bool isFisheye; float sensorFormatFactor; int persModelCount; +private: + class LCPPersModel; + + int filterBadFrames(LCPCorrectionMode mode, double maxAvgDevFac, int minFramesLeft); + + void handle_text(const std::string& text); + + 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); + + // Temporary data for parsing + bool inCamProfiles; + bool firstLIDone; + bool inPerspect; + bool inAlternateLensID; + bool inAlternateLensNames; + char lastTag[256]; + char inInvalidTag[256]; + LCPPersModel* pCurPersModel; + LCPModelCommon* pCurCommon; + + std::ostringstream textbuf; + // The correction frames - static const int MaxPersModelCount = 3000; + static constexpr int MaxPersModelCount = 3000; LCPPersModel* aPersModel[MaxPersModelCount]; // Do NOT use std::list or something, it's buggy in GCC! - - explicit LCPProfile(const Glib::ustring &fname); - ~LCPProfile(); - - void calcParams(LCPCorrectionMode mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const; // Interpolates between the persModels frames - - void print() const; }; class LCPStore { - MyMutex mtx; +public: + static LCPStore* getInstance(); + + bool isValidLCPFileName(const Glib::ustring& filename) const; + std::shared_ptr getProfile(const Glib::ustring& filename) const; + Glib::ustring getDefaultCommonDirectory() const; + +private: + LCPStore(unsigned int _cache_size = 32); // Maps file name to profile as cache - std::map profileCache; - -public: - ~LCPStore(); - Glib::ustring getDefaultCommonDirectory() const; - bool isValidLCPFileName(Glib::ustring filename) const; - LCPProfile* getProfile(Glib::ustring filename); - - static LCPStore* getInstance(); + mutable Cache> cache; }; -#define lcpStore LCPStore::getInstance() - - // Once precalculated class to correct a point class LCPMapper { +public: + // Precalculates the mapper + LCPMapper( + const std::shared_ptr& pProf, + float focalLength, + float focalLength35mm, + float focusDist, + float aperture, + bool vignette, + bool useCADistP, + int fullWidth, + int fullHeight, + const CoarseTransformParams& coarse, + int rawRotationDeg + ); + bool isCACorrectionAvailable() const; + + void correctDistortion(double& x, double& y, double scale) const; // MUST be the first stage + void correctCA(double& x, double& y, int channel) const; + void processVignetteLine(int width, int y, float* line) const; + void processVignetteLine3Channels(int width, int y, float* line) const; + +private: + bool enableCA; // is the mapper capable if CA correction? bool useCADist; // should the distortion in the CA info be used? bool swapXY; LCPModelCommon mc; LCPModelCommon chrom[3]; // in order RedGreen/Green/BlueGreen bool isFisheye; - -public: - bool enableCA; // is the mapper capable if CA correction? - - // precalculates the mapper. - 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, double scale) const; // MUST be the first stage - void correctCA(double& x, double& y, int channel) const; - void processVignetteLine(int width, int y, float *line) const; - void processVignetteLine3Channels(int width, int y, float *line) const; }; } diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 148d17ee9..718b518ae 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1855,7 +1855,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le // Correct vignetting of lens profile if (!hasFlatField && lensProf.useVign) { - LCPProfile *pLCPProf = lcpStore->getProfile(lensProf.lcpFile); + const std::shared_ptr pLCPProf = LCPStore::getInstance()->getProfile(lensProf.lcpFile); if (pLCPProf) { // don't check focal length to allow distortion correction for lenses without chip, also pass dummy focal length 1 in case of 0 LCPMapper map(pLCPProf, max(idata->getFocalLen(), 1.0), idata->getFocalLen35mm(), idata->getFocusDist(), idata->getFNumber(), true, false, W, H, coarse, -1); diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 0855ef03f..91fc55687 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -41,7 +41,7 @@ LensProfilePanel::LensProfilePanel () : FoldableToolPanel(this, "lensprof", M("T filterLCP->add_pattern("*.LCP"); fcbLCPFile->add_filter(filterLCP); - Glib::ustring defDir = lcpStore->getDefaultCommonDirectory(); + Glib::ustring defDir = LCPStore::getInstance()->getDefaultCommonDirectory(); if (!defDir.empty()) { #ifdef WIN32 @@ -82,7 +82,7 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa disableListener (); conUseDist.block(true); - if (!pp->lensProf.lcpFile.empty() && lcpStore->isValidLCPFileName(pp->lensProf.lcpFile)) { + if (!pp->lensProf.lcpFile.empty() && LCPStore::getInstance()->isValidLCPFileName(pp->lensProf.lcpFile)) { fcbLCPFile->set_filename (pp->lensProf.lcpFile); updateDisabled(true); } else { @@ -128,7 +128,7 @@ void LensProfilePanel::setRawMeta(bool raw, const rtengine::ImageMetaData* pMeta void LensProfilePanel::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) { - if (lcpStore->isValidLCPFileName(fcbLCPFile->get_filename())) { + if (LCPStore::getInstance()->isValidLCPFileName(fcbLCPFile->get_filename())) { pp->lensProf.lcpFile = fcbLCPFile->get_filename(); } else { pp->lensProf.lcpFile = ""; @@ -149,7 +149,7 @@ void LensProfilePanel::write( rtengine::procparams::ProcParams* pp, ParamsEdited void LensProfilePanel::onLCPFileChanged() { lcpFileChanged = true; - updateDisabled(lcpStore->isValidLCPFileName(fcbLCPFile->get_filename())); + updateDisabled(LCPStore::getInstance()->isValidLCPFileName(fcbLCPFile->get_filename())); if (listener) { listener->panelChanged (EvLCPFile, Glib::path_get_basename(fcbLCPFile->get_filename())); From 099e6e9f678c46b3fd047a3b833f25e12b156445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Sat, 9 Sep 2017 20:30:02 +0200 Subject: [PATCH 26/52] Don't use `` for trivial cases (#4056) --- rtgui/rtwindow.cc | 41 +++++++++++++++-------------------------- rtgui/xtransprocess.cc | 28 +++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/rtgui/rtwindow.cc b/rtgui/rtwindow.cc index 9af387567..80e481315 100644 --- a/rtgui/rtwindow.cc +++ b/rtgui/rtwindow.cc @@ -18,7 +18,6 @@ */ #include -#include #include "rtwindow.h" #include "options.h" #include "preferences.h" @@ -315,37 +314,27 @@ void RTWindow::on_realize () // Display release notes only if new major version. // Pattern matches "5.1" from "5.1-23-g12345678" - std::string vs[] = {versionString, options.version}; - std::regex pat ("(^[0-9.]+).*"); - std::smatch sm; + const std::string vs[] = {versionString, options.version}; std::vector vMajor; - for (const auto &v : vs) { - if (std::regex_match (v, sm, pat)) { - if (sm.size() == 2) { - std::ssub_match smsub = sm[1]; - vMajor.push_back (smsub.str()); - } - } + for (const auto& v : vs) { + vMajor.emplace_back(v, 0, v.find_first_not_of("0123456789.")); } - if (vMajor.size() == 2) { - if (vMajor[0] != vMajor[1]) { + if (vMajor.size() == 2 && vMajor[0] != vMajor[1]) { + // Update the version parameter with the right value + options.version = versionString; - // Update the version parameter with the right value - options.version = versionString; + splash = new Splash (*this); + splash->set_transient_for (*this); + splash->signal_delete_event().connect ( sigc::mem_fun (*this, &RTWindow::splashClosed) ); - splash = new Splash (*this); - splash->set_transient_for (*this); - splash->signal_delete_event().connect ( sigc::mem_fun (*this, &RTWindow::splashClosed) ); - - if (splash->hasReleaseNotes()) { - splash->showReleaseNotes(); - splash->show (); - } else { - delete splash; - splash = nullptr; - } + if (splash->hasReleaseNotes()) { + splash->showReleaseNotes(); + splash->show (); + } else { + delete splash; + splash = nullptr; } } } diff --git a/rtgui/xtransprocess.cc b/rtgui/xtransprocess.cc index a663ac7c5..453f0a53d 100644 --- a/rtgui/xtransprocess.cc +++ b/rtgui/xtransprocess.cc @@ -19,7 +19,7 @@ #include "xtransprocess.h" #include "options.h" #include "guiutils.h" -#include + using namespace rtengine; using namespace rtengine::procparams; @@ -30,8 +30,30 @@ XTransProcess::XTransProcess () : FoldableToolPanel(this, "xtransprocess", M("TP method = Gtk::manage (new MyComboBoxText ()); for( size_t i = 0; i < procparams::RAWParams::XTransSensor::numMethods; i++) { - static const std::regex what ("[() -]"); - const std::string langKey = std::regex_replace (procparams::RAWParams::XTransSensor::methodstring[i], what, ""); + const std::string langKey = + [i]() -> std::string + { + const std::string str(procparams::RAWParams::XTransSensor::methodstring[i]); + + std::string res; + for (const auto& c : str) { + switch (c) { + case '(': + case ')': + case ' ': + case '-': { + continue; + } + + default: { + res += c; + break; + } + } + } + + return res; + }(); method->append(M("TP_RAW_" + Glib::ustring(langKey).uppercase())); } From 0f197bf1a4b1e9660e3f5264cca3d869cf6902ab Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 9 Sep 2017 22:31:59 +0200 Subject: [PATCH 27/52] lensfun: properly group lenses by maker in the UI --- rtengine/rtlensfun.cc | 10 ++++++++++ rtengine/rtlensfun.h | 1 + rtgui/lensprofile.cc | 27 +++++++++++++-------------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index c90de08d9..9b35d038f 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -184,6 +184,16 @@ bool LFLens::ok() const } +Glib::ustring LFLens::getMake() const +{ + if (data_) { + return data_->Maker; + } else { + return ""; + } +} + + Glib::ustring LFLens::getLens() const { if (data_) { diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index cb8748eb3..640b6a18c 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -73,6 +73,7 @@ class LFLens { public: LFLens(); bool ok() const; + Glib::ustring getMake() const; Glib::ustring getLens() const; Glib::ustring getDisplayString() const { return getLens(); } private: diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index b6af0d8d0..1280a3524 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -360,12 +360,8 @@ void LensProfilePanel::fillLensfunLenses() std::map> lenses; auto lenslist = LFDatabase::getInstance()->getLenses(); for (auto &l : lenslist) { - auto name = l.getDisplayString(); - auto pos = name.find_first_of(' '); - Glib::ustring make = "(Unknown)"; - if (pos != Glib::ustring::npos) { - make = name.substr(0, pos); - } + auto name = l.getLens(); + auto make = l.getMake(); lenses[make].insert(name); } for (auto &p : lenses) { @@ -415,15 +411,8 @@ bool LensProfilePanel::setLensfunLens(const Glib::ustring &lens) return true; } - // search for the active row - auto pos = lens.find_first_of(' '); - Glib::ustring make = "(Unknown)"; - if (pos != Glib::ustring::npos) { - make = lens.substr(0, pos); - } - for (auto row : lensfunLensModel->children()) { - if (row[lensfunModelLens.lens] == make) { + if (lens.find(row[lensfunModelLens.lens]) == 0) { auto &c = row.children(); for (auto it = c.begin(), end = c.end(); it != end; ++it) { auto &childrow = *it; @@ -502,6 +491,16 @@ void LensProfilePanel::onCorrModeChanged() ckbUseVign->set_sensitive(true); ckbUseCA->set_sensitive(false); + if (metadata) { + disableListener(); + const LFDatabase *db = LFDatabase::getInstance(); + LFCamera c = db->findCamera(metadata->getMake(), metadata->getModel()); + LFLens l = db->findLens(c, metadata->getLens()); + setLensfunCamera(c.getMake(), c.getModel()); + setLensfunLens(l.getLens()); + enableListener(); + } + mode = M("LENSPROFILE_CORRECTION_AUTOMATCH"); } else if (corrLensfunManual->get_active()) { useLensfunChanged = true; From 07dfda5d73600b7a27ff918e5de3f337142ba262 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 9 Sep 2017 23:15:56 +0200 Subject: [PATCH 28/52] fixed UI deadlock introduced by 0f197bf1a4b1e9660e3f5264cca3d869cf6902ab --- rtgui/lensprofile.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 1280a3524..9688bf013 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -492,13 +492,15 @@ void LensProfilePanel::onCorrModeChanged() ckbUseCA->set_sensitive(false); if (metadata) { - disableListener(); + bool b = disableListener(); const LFDatabase *db = LFDatabase::getInstance(); LFCamera c = db->findCamera(metadata->getMake(), metadata->getModel()); LFLens l = db->findLens(c, metadata->getLens()); setLensfunCamera(c.getMake(), c.getModel()); setLensfunLens(l.getLens()); - enableListener(); + if (b) { + enableListener(); + } } mode = M("LENSPROFILE_CORRECTION_AUTOMATCH"); From 376cb09f067b95c23ef8bce3802f8c01987e823c Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 10 Sep 2017 00:49:06 +0200 Subject: [PATCH 29/52] use a static initialization of the list of lensfun cameras and lenses --- rtgui/lensprofile.cc | 146 ++++++++++++++++++++++++------------------- rtgui/lensprofile.h | 39 +++++++----- 2 files changed, 105 insertions(+), 80 deletions(-) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 9688bf013..28fef16cf 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -29,6 +29,8 @@ using namespace rtengine; using namespace rtengine::procparams; +LensProfilePanel::LFDbHelper *LensProfilePanel::lf(nullptr); + LensProfilePanel::LensProfilePanel () : FoldableToolPanel(this, "lensprof", M("TP_LENSPROFILE_LABEL")), lcpFileChanged(false), @@ -43,6 +45,10 @@ LensProfilePanel::LensProfilePanel () : lensfunCameraChanged(false), lensfunLensChanged(false) { + if (!lf) { + lf = new LFDbHelper(); + } + corrOff = Gtk::manage(new Gtk::RadioButton(M("LENSPROFILE_CORRECTION_OFF"))); pack_start(*corrOff); @@ -54,30 +60,23 @@ LensProfilePanel::LensProfilePanel () : corrLensfunManual = Gtk::manage(new Gtk::RadioButton(corrGroup, M("LENSPROFILE_CORRECTION_MANUAL"))); pack_start(*corrLensfunManual); - lensfunCameraModel = Gtk::TreeStore::create(lensfunModelCam); - lensfunLensModel = Gtk::TreeStore::create(lensfunModelLens); - lensfunCameras = Gtk::manage(new MyComboBox()); - lensfunCameras->set_model(lensfunCameraModel); - lensfunCameras->pack_start(lensfunModelCam.model); + lensfunCameras->set_model(lf->lensfunCameraModel); + lensfunCameras->pack_start(lf->lensfunModelCam.model); lensfunLenses = Gtk::manage(new MyComboBox()); - lensfunLenses->set_model(lensfunLensModel); - lensfunLenses->pack_start(lensfunModelLens.lens); + lensfunLenses->set_model(lf->lensfunLensModel); + lensfunLenses->pack_start(lf->lensfunModelLens.prettylens); Gtk::HBox *hb = Gtk::manage(new Gtk::HBox()); hb->pack_start(*Gtk::manage(new Gtk::Label(M("EXIFFILTER_CAMERA"))), Gtk::PACK_SHRINK, 4); hb->pack_start(*lensfunCameras); pack_start(*hb); - fillLensfunCameras(); - hb = Gtk::manage(new Gtk::HBox()); hb->pack_start(*Gtk::manage(new Gtk::Label(M("EXIFFILTER_LENS"))), Gtk::PACK_SHRINK, 4); hb->pack_start(*lensfunLenses); pack_start(*hb); - fillLensfunLenses(); - corrLcpFile = Gtk::manage(new Gtk::RadioButton(corrGroup)); hbLCPFile = Gtk::manage(new Gtk::HBox()); hbLCPFile->pack_start(*corrLcpFile, Gtk::PACK_SHRINK); @@ -249,15 +248,15 @@ void LensProfilePanel::write( rtengine::procparams::ProcParams* pp, ParamsEdited pp->lensProf.lfAutoMatch = corrLensfunAuto->get_active(); auto itc = lensfunCameras->get_active(); if (itc) { - pp->lensProf.lfCameraMake = (*itc)[lensfunModelCam.make]; - pp->lensProf.lfCameraModel = (*itc)[lensfunModelCam.model]; + pp->lensProf.lfCameraMake = (*itc)[lf->lensfunModelCam.make]; + pp->lensProf.lfCameraModel = (*itc)[lf->lensfunModelCam.model]; } else { pp->lensProf.lfCameraMake = ""; pp->lensProf.lfCameraModel = ""; } auto itl = lensfunLenses->get_active(); if (itl) { - pp->lensProf.lfLens = (*itl)[lensfunModelLens.lens]; + pp->lensProf.lfLens = (*itl)[lf->lensfunModelLens.lens]; } else { pp->lensProf.lfLens = ""; } @@ -335,61 +334,21 @@ void LensProfilePanel::setBatchMode(bool yes) } -void LensProfilePanel::fillLensfunCameras() -{ - std::map> camnames; - auto camlist = LFDatabase::getInstance()->getCameras(); - for (auto &c : camlist) { - camnames[c.getMake()].insert(c.getModel()); - } - for (auto &p : camnames) { - Gtk::TreeModel::Row row = *(lensfunCameraModel->append()); - row[lensfunModelCam.make] = p.first; - row[lensfunModelCam.model] = p.first; - for (auto &c : p.second) { - Gtk::TreeModel::Row child = *(lensfunCameraModel->append(row.children())); - child[lensfunModelCam.make] = p.first; - child[lensfunModelCam.model] = c; - } - } -} - - -void LensProfilePanel::fillLensfunLenses() -{ - std::map> lenses; - auto lenslist = LFDatabase::getInstance()->getLenses(); - for (auto &l : lenslist) { - auto name = l.getLens(); - auto make = l.getMake(); - lenses[make].insert(name); - } - for (auto &p : lenses) { - Gtk::TreeModel::Row row = *(lensfunLensModel->append()); - row[lensfunModelLens.lens] = p.first; - for (auto &c : p.second) { - Gtk::TreeModel::Row child = *(lensfunLensModel->append(row.children())); - child[lensfunModelLens.lens] = c; - } - } -} - - bool LensProfilePanel::setLensfunCamera(const Glib::ustring &make, const Glib::ustring &model) { if (!make.empty() && !model.empty()) { auto it = lensfunCameras->get_active(); - if (it && (*it)[lensfunModelCam.make] == make && (*it)[lensfunModelCam.model] == model) { + if (it && (*it)[lf->lensfunModelCam.make] == make && (*it)[lf->lensfunModelCam.model] == model) { return true; } // search for the active row - for (auto row : lensfunCameraModel->children()) { - if (row[lensfunModelCam.make] == make) { + for (auto row : lf->lensfunCameraModel->children()) { + if (row[lf->lensfunModelCam.make] == make) { auto &c = row.children(); for (auto it = c.begin(), end = c.end(); it != end; ++it) { auto &childrow = *it; - if (childrow[lensfunModelCam.model] == model) { + if (childrow[lf->lensfunModelCam.model] == model) { lensfunCameras->set_active(it); return true; } @@ -407,16 +366,16 @@ bool LensProfilePanel::setLensfunLens(const Glib::ustring &lens) { if (!lens.empty()) { auto it = lensfunLenses->get_active(); - if (it && (*it)[lensfunModelLens.lens] == lens) { + if (it && (*it)[lf->lensfunModelLens.lens] == lens) { return true; } - for (auto row : lensfunLensModel->children()) { - if (lens.find(row[lensfunModelLens.lens]) == 0) { + for (auto row : lf->lensfunLensModel->children()) { + if (lens.find(row[lf->lensfunModelLens.lens]) == 0) { auto &c = row.children(); for (auto it = c.begin(), end = c.end(); it != end; ++it) { auto &childrow = *it; - if (childrow[lensfunModelLens.lens] == lens) { + if (childrow[lf->lensfunModelLens.lens] == lens) { lensfunLenses->set_active(it); return true; } @@ -439,7 +398,7 @@ void LensProfilePanel::onLensfunCameraChanged() lensfunCameraChanged = true; if (listener) { - Glib::ustring name = (*iter)[lensfunModelCam.model]; + Glib::ustring name = (*iter)[lf->lensfunModelCam.model]; listener->panelChanged(EvLensCorrLensfunCamera, name); } } @@ -454,7 +413,7 @@ void LensProfilePanel::onLensfunLensChanged() lensfunLensChanged = true; if (listener) { - Glib::ustring name = (*iter)[lensfunModelLens.lens]; + Glib::ustring name = (*iter)[lf->lensfunModelLens.lens]; listener->panelChanged(EvLensCorrLensfunLens, name); } } @@ -550,3 +509,62 @@ bool LensProfilePanel::checkLensfunCanCorrect(bool automatch) std::unique_ptr mod(LFDatabase::findModifier(lpp.lensProf, metadata, 100, 100, lpp.coarse, -1)); return mod.get() != nullptr; } + + +//----------------------------------------------------------------------------- +// LFDbHelper +//----------------------------------------------------------------------------- + +LensProfilePanel::LFDbHelper::LFDbHelper() +{ + lensfunCameraModel = Gtk::TreeStore::create(lensfunModelCam); + lensfunLensModel = Gtk::TreeStore::create(lensfunModelLens); + + fillLensfunCameras(); + fillLensfunLenses(); +} + +void LensProfilePanel::LFDbHelper::fillLensfunCameras() +{ + std::map> camnames; + auto camlist = LFDatabase::getInstance()->getCameras(); + for (auto &c : camlist) { + camnames[c.getMake()].insert(c.getModel()); + } + for (auto &p : camnames) { + Gtk::TreeModel::Row row = *(lensfunCameraModel->append()); + row[lensfunModelCam.make] = p.first; + row[lensfunModelCam.model] = p.first; + for (auto &c : p.second) { + Gtk::TreeModel::Row child = *(lensfunCameraModel->append(row.children())); + child[lensfunModelCam.make] = p.first; + child[lensfunModelCam.model] = c; + } + } +} + + +void LensProfilePanel::LFDbHelper::fillLensfunLenses() +{ + std::map> lenses; + auto lenslist = LFDatabase::getInstance()->getLenses(); + for (auto &l : lenslist) { + auto name = l.getLens(); + auto make = l.getMake(); + lenses[make].insert(name); + } + for (auto &p : lenses) { + Gtk::TreeModel::Row row = *(lensfunLensModel->append()); + row[lensfunModelLens.lens] = p.first; + row[lensfunModelLens.prettylens] = p.first; + for (auto &c : p.second) { + Gtk::TreeModel::Row child = *(lensfunLensModel->append(row.children())); + child[lensfunModelLens.lens] = c; + if (c.find(p.first, p.first.size()+1) == p.first.size()+1) { + child[lensfunModelLens.prettylens] = c.substr(p.first.size()+1); + } else { + child[lensfunModelLens.prettylens] = c; + } + } + } +} diff --git a/rtgui/lensprofile.h b/rtgui/lensprofile.h index 0894d7a98..feda2a259 100644 --- a/rtgui/lensprofile.h +++ b/rtgui/lensprofile.h @@ -51,32 +51,39 @@ protected: MyComboBox *lensfunCameras; MyComboBox *lensfunLenses; - class LFModelCam: public Gtk::TreeModel::ColumnRecord { + class LFDbHelper { public: - LFModelCam() { add(make); add(model); } - Gtk::TreeModelColumn make; - Gtk::TreeModelColumn model; - }; + class LFModelCam: public Gtk::TreeModel::ColumnRecord { + public: + LFModelCam() { add(make); add(model); } + Gtk::TreeModelColumn make; + Gtk::TreeModelColumn model; + }; - class LFModelLens: public Gtk::TreeModel::ColumnRecord { - public: - LFModelLens() { add(lens); } - Gtk::TreeModelColumn lens; - }; + class LFModelLens: public Gtk::TreeModel::ColumnRecord { + public: + LFModelLens() { add(lens); add(prettylens); } + Gtk::TreeModelColumn lens; + Gtk::TreeModelColumn prettylens; + }; - LFModelCam lensfunModelCam; - LFModelLens lensfunModelLens; + LFModelCam lensfunModelCam; + LFModelLens lensfunModelLens; - Glib::RefPtr lensfunCameraModel; - Glib::RefPtr lensfunLensModel; + Glib::RefPtr lensfunCameraModel; + Glib::RefPtr lensfunLensModel; + + LFDbHelper(); + void fillLensfunCameras(); + void fillLensfunLenses(); + }; + static LFDbHelper *lf; bool useLensfunChanged; bool lensfunAutoChanged; bool lensfunCameraChanged; bool lensfunLensChanged; - void fillLensfunCameras(); - void fillLensfunLenses(); bool setLensfunCamera(const Glib::ustring &make, const Glib::ustring &model); bool setLensfunLens(const Glib::ustring &lens); bool checkLensfunCanCorrect(bool automatch); From ff798cdf240ffb48ee3f88e63682559de73841fa Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 10 Sep 2017 00:54:07 +0200 Subject: [PATCH 30/52] print the list of found lensfun cameras and lenses when in verbose mode --- rtgui/lensprofile.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 28fef16cf..31f021819 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -526,10 +526,17 @@ LensProfilePanel::LFDbHelper::LFDbHelper() void LensProfilePanel::LFDbHelper::fillLensfunCameras() { + if (options.rtSettings.verbose) { + std::cout << "LENSFUN, scanning cameras:" << std::endl; + } std::map> camnames; auto camlist = LFDatabase::getInstance()->getCameras(); for (auto &c : camlist) { camnames[c.getMake()].insert(c.getModel()); + + if (options.rtSettings.verbose) { + std::cout << " found: " << c.getDisplayString() << std::endl; + } } for (auto &p : camnames) { Gtk::TreeModel::Row row = *(lensfunCameraModel->append()); @@ -546,12 +553,19 @@ void LensProfilePanel::LFDbHelper::fillLensfunCameras() void LensProfilePanel::LFDbHelper::fillLensfunLenses() { + if (options.rtSettings.verbose) { + std::cout << "LENSFUN, scanning lenses:" << std::endl; + } std::map> lenses; auto lenslist = LFDatabase::getInstance()->getLenses(); for (auto &l : lenslist) { auto name = l.getLens(); auto make = l.getMake(); lenses[make].insert(name); + + if (options.rtSettings.verbose) { + std::cout << " found: " << l.getDisplayString() << std::endl; + } } for (auto &p : lenses) { Gtk::TreeModel::Row row = *(lensfunLensModel->append()); From 6855dd2111a22db033cca3d526611d98ceac45a8 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 10 Sep 2017 12:13:18 +0200 Subject: [PATCH 31/52] show an "(Unchanged)" entry for profiled lens correction in batch mode --- rtdata/languages/default | 1 - rtgui/lensprofile.cc | 31 +++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 083afc4df..329048d3a 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2152,7 +2152,6 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f ZOOMPANEL_ZOOMFITSCREEN;Fit whole image to screen\nShortcut: f ZOOMPANEL_ZOOMIN;Zoom In\nShortcut: + ZOOMPANEL_ZOOMOUT;Zoom Out\nShortcut: - -LENSPROFILE_CORRECTION_OFF;None LENSPROFILE_CORRECTION_AUTOMATCH;Auto-matched correction parameters LENSPROFILE_CORRECTION_MANUAL;Manual correction parameters LENSPROFILE_CORRECTION_LCPFILE;LCP File diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 31f021819..4fffbe7df 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -48,11 +48,14 @@ LensProfilePanel::LensProfilePanel () : if (!lf) { lf = new LFDbHelper(); } - - corrOff = Gtk::manage(new Gtk::RadioButton(M("LENSPROFILE_CORRECTION_OFF"))); - pack_start(*corrOff); - corrGroup = corrOff->get_group(); + corrUnchanged = Gtk::manage(new Gtk::RadioButton(M("GENERAL_UNCHANGED"))); + pack_start(*corrUnchanged); + + corrGroup = corrUnchanged->get_group(); + + corrOff = Gtk::manage(new Gtk::RadioButton(corrGroup, M("GENERAL_NONE"))); + pack_start(*corrOff); corrLensfunAuto = Gtk::manage(new Gtk::RadioButton(corrGroup, M("LENSPROFILE_CORRECTION_AUTOMATCH"))); pack_start(*corrLensfunAuto); @@ -132,6 +135,8 @@ LensProfilePanel::LensProfilePanel () : corrLensfunManual->signal_toggled().connect(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged)); corrLcpFile->signal_toggled().connect(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged)); + corrUnchanged->hide(); + allowFocusDep = true; } @@ -140,6 +145,10 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa disableListener (); conUseDist.block(true); + if (!batchMode) { + corrUnchanged->hide(); + } + corrLensfunAuto->set_sensitive(true); if (pp->lensProf.useLensfun) { @@ -331,6 +340,12 @@ void LensProfilePanel::updateDisabled(bool enable) void LensProfilePanel::setBatchMode(bool yes) { FoldableToolPanel::setBatchMode(yes); + if (yes) { + corrUnchanged->show(); + corrUnchanged->set_active(true); + } else { + corrUnchanged->hide(); + } } @@ -490,6 +505,14 @@ void LensProfilePanel::onCorrModeChanged() updateDisabled(true); mode = M("LENSPROFILE_CORRECTION_LCPFILE"); + } else if (corrUnchanged->get_active()) { + useLensfunChanged = false; + lensfunAutoChanged = false; + lcpFileChanged = false; + lensfunCameraChanged = false; + lensfunLensChanged = false; + + mode = M("GENERAL_UNCHANGED"); } if (listener) { From 076149955770491df7847a55ce6009f17c2fe396 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 10 Sep 2017 12:22:36 +0200 Subject: [PATCH 32/52] lensfun: fixed bug in auto-matching of some fixed-lens cameras (e.g. nikon coolpix) --- rtengine/rtlensfun.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 9b35d038f..a758c1872 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -287,7 +287,7 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c LFLens ret; if (data_) { Glib::ustring lname = name; - bool stdlens = camera.ok() && (name.empty() || name.find("Unknown ") == 0); + bool stdlens = camera.ok() && (name.empty() || name.find("Unknown") == 0); if (stdlens) { lname = camera.getModel(); // "Standard" } From 626f8cace385ad608316440dc666cbd171267de8 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 10 Sep 2017 14:02:22 +0200 Subject: [PATCH 33/52] disable 'autofill' setting when using lensfun --- rtgui/lensgeom.h | 2 ++ rtgui/lensprofile.cc | 29 ++++++++++++++++++++--------- rtgui/lensprofile.h | 1 + 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/rtgui/lensgeom.h b/rtgui/lensgeom.h index 29b0c7f20..aa473cb91 100644 --- a/rtgui/lensgeom.h +++ b/rtgui/lensgeom.h @@ -44,6 +44,8 @@ public: return packBox; } + Gtk::CheckButton *getFill() { return fill; } + void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr); void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr); void setBatchMode (bool batchMode); diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 4fffbe7df..5ddfdfa56 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -210,16 +210,8 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa if (corrLensfunManual->get_active() && !checkLensfunCanCorrect(false)) { corrOff->set_active(true); } - // if (metadata) { - // std::unique_ptr mod(LFDatabase::findModifier(pp->lensProf, metadata, 100, 100, pp->coarse, -1)); - // if (!mod) { - // if (pp->lensProf.useLensfun) { - // corrOff->set_active(true); - // } - // corrLensfunAuto->set_sensitive(false); - // } - // } + setAutoFill(); enableListener (); conUseDist.block(false); } @@ -514,6 +506,8 @@ void LensProfilePanel::onCorrModeChanged() mode = M("GENERAL_UNCHANGED"); } + + setAutoFill(); if (listener) { listener->panelChanged(EvLensCorrMode, mode); @@ -534,6 +528,23 @@ bool LensProfilePanel::checkLensfunCanCorrect(bool automatch) } +void LensProfilePanel::setAutoFill() +{ + if (lensgeomLcpFill) { + bool b = lensgeomLcpFill->disableListener(); + if (corrLensfunAuto->get_active() || corrLensfunManual->get_active()) { + lensgeomLcpFill->getFill()->set_active(true); + lensgeomLcpFill->getFill()->set_sensitive(false); + } else { + lensgeomLcpFill->getFill()->set_sensitive(true); + } + if (b) { + lensgeomLcpFill->enableListener(); + } + } +} + + //----------------------------------------------------------------------------- // LFDbHelper //----------------------------------------------------------------------------- diff --git a/rtgui/lensprofile.h b/rtgui/lensprofile.h index feda2a259..9475792ae 100644 --- a/rtgui/lensprofile.h +++ b/rtgui/lensprofile.h @@ -87,6 +87,7 @@ protected: bool setLensfunCamera(const Glib::ustring &make, const Glib::ustring &model); bool setLensfunLens(const Glib::ustring &lens); bool checkLensfunCanCorrect(bool automatch); + void setAutoFill(); public: From 7b6343bf5d7e1bee5d1d4543a7d181224609d369 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 10 Sep 2017 14:08:40 +0200 Subject: [PATCH 34/52] changed PARTIALPASTE_LENSPROFILE from "Lens correction profile" to "Profiled lens correction" --- rtdata/languages/default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 329048d3a..3cadd09db 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -887,7 +887,7 @@ PARTIALPASTE_IMPULSEDENOISE;Impulse noise reduction PARTIALPASTE_IPTCINFO;IPTC PARTIALPASTE_LABCURVE;L*a*b* adjustments PARTIALPASTE_LENSGROUP;Lens Related Settings -PARTIALPASTE_LENSPROFILE;Lens correction profile +PARTIALPASTE_LENSPROFILE;Profiled lens correction PARTIALPASTE_METAGROUP;Metadata PARTIALPASTE_PCVIGNETTE;Vignette filter PARTIALPASTE_PERSPECTIVE;Perspective From 489b641c8b8e0ddeff0b4a4ecd1ccea32e5b9519 Mon Sep 17 00:00:00 2001 From: Hombre57 Date: Sun, 10 Sep 2017 15:13:21 +0200 Subject: [PATCH 35/52] Width of LensProfilePanel's lens and camera combobox fixed (see #4070) --- rtgui/lensprofile.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 5ddfdfa56..12d220271 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -66,9 +66,18 @@ LensProfilePanel::LensProfilePanel () : lensfunCameras = Gtk::manage(new MyComboBox()); lensfunCameras->set_model(lf->lensfunCameraModel); lensfunCameras->pack_start(lf->lensfunModelCam.model); + Gtk::CellRendererText* cellRenderer = dynamic_cast(lensfunCameras->get_first_cell()); + cellRenderer->property_ellipsize() = Pango::ELLIPSIZE_MIDDLE; + cellRenderer->property_ellipsize_set() = true; + lensfunCameras->setPreferredWidth(50, 120); + lensfunLenses = Gtk::manage(new MyComboBox()); lensfunLenses->set_model(lf->lensfunLensModel); lensfunLenses->pack_start(lf->lensfunModelLens.prettylens); + cellRenderer = dynamic_cast(lensfunLenses->get_first_cell()); + cellRenderer->property_ellipsize() = Pango::ELLIPSIZE_MIDDLE; + cellRenderer->property_ellipsize_set() = true; + lensfunLenses->setPreferredWidth(50, 120); Gtk::HBox *hb = Gtk::manage(new Gtk::HBox()); hb->pack_start(*Gtk::manage(new Gtk::Label(M("EXIFFILTER_CAMERA"))), Gtk::PACK_SHRINK, 4); From e4ba4d19f5e425b461e4031d1ff200a0f976d70e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 10 Sep 2017 22:02:43 +0200 Subject: [PATCH 36/52] made lensfun-based correction compatible with auto fill mode --- rtengine/iptransform.cc | 4 ---- rtengine/lcp.h | 2 -- rtengine/rtlensfun.cc | 2 ++ rtengine/rtlensfun.h | 1 - rtgui/lensprofile.cc | 21 --------------------- rtgui/lensprofile.h | 6 ------ rtgui/toolpanelcoord.cc | 1 - 7 files changed, 2 insertions(+), 35 deletions(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 2226c493b..77da87e4c 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -969,10 +969,6 @@ double ImProcFunctions::getTransformAutoFill (int oW, int oH, const LensCorrecti return 1; } - if (pLCPMap && !pLCPMap->supportsAutoFill()) { - return 1; - } - double scaleU = 2, scaleL = 0.001; // upper and lower border, iterate inbetween do { diff --git a/rtengine/lcp.h b/rtengine/lcp.h index 1a4b677d3..f3aca09cd 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -140,7 +140,6 @@ class LensCorrection { public: virtual ~LensCorrection() {} virtual void correctDistortion(double &x, double &y, int cx, int cy, double scale) const = 0; - virtual bool supportsAutoFill() const = 0; virtual bool supportsCA() const = 0; virtual void correctCA(double &x, double &y, int channel) const = 0; virtual void processVignetteLine(int width, int y, float *line) const = 0; @@ -167,7 +166,6 @@ public: void correctDistortion(double &x, double &y, int cx, int cy, double scale) const; // MUST be the first stage bool supportsCA() const { return enableCA; } - bool supportsAutoFill() const { return true; } void correctCA(double& x, double& y, int channel) const; void processVignetteLine(int width, int y, float *line) const; void processVignetteLine3Channels(int width, int y, float *line) const; diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index a758c1872..15ec001eb 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -72,6 +72,8 @@ void LFModifier::correctDistortion(double &x, double &y, int cx, int cy, double x -= cx; y -= cy; } + x *= scale; + y *= scale; } diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index 640b6a18c..fce7d3a18 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -34,7 +34,6 @@ public: bool ok() const; void correctDistortion(double &x, double &y, int cx, int cy, double scale) const; - bool supportsAutoFill() const { return false; } bool supportsCA() const { return false; } void correctCA(double &x, double &y, int channel) const {} void processVignetteLine(int width, int y, float *line) const; diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 12d220271..9eb41aa25 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -39,7 +39,6 @@ LensProfilePanel::LensProfilePanel () : useCAChanged(false), isRaw(true), metadata(nullptr), - lensgeomLcpFill(nullptr), useLensfunChanged(false), lensfunAutoChanged(false), lensfunCameraChanged(false), @@ -220,7 +219,6 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa corrOff->set_active(true); } - setAutoFill(); enableListener (); conUseDist.block(false); } @@ -516,8 +514,6 @@ void LensProfilePanel::onCorrModeChanged() mode = M("GENERAL_UNCHANGED"); } - setAutoFill(); - if (listener) { listener->panelChanged(EvLensCorrMode, mode); } @@ -537,23 +533,6 @@ bool LensProfilePanel::checkLensfunCanCorrect(bool automatch) } -void LensProfilePanel::setAutoFill() -{ - if (lensgeomLcpFill) { - bool b = lensgeomLcpFill->disableListener(); - if (corrLensfunAuto->get_active() || corrLensfunManual->get_active()) { - lensgeomLcpFill->getFill()->set_active(true); - lensgeomLcpFill->getFill()->set_sensitive(false); - } else { - lensgeomLcpFill->getFill()->set_sensitive(true); - } - if (b) { - lensgeomLcpFill->enableListener(); - } - } -} - - //----------------------------------------------------------------------------- // LFDbHelper //----------------------------------------------------------------------------- diff --git a/rtgui/lensprofile.h b/rtgui/lensprofile.h index 9475792ae..5e2a5b484 100644 --- a/rtgui/lensprofile.h +++ b/rtgui/lensprofile.h @@ -40,7 +40,6 @@ protected: bool allowFocusDep; bool isRaw; const rtengine::ImageMetaData* metadata; - LensGeometry *lensgeomLcpFill; Gtk::RadioButton::Group corrGroup; Gtk::RadioButton *corrOff; @@ -87,7 +86,6 @@ protected: bool setLensfunCamera(const Glib::ustring &make, const Glib::ustring &model); bool setLensfunLens(const Glib::ustring &lens); bool checkLensfunCanCorrect(bool automatch); - void setAutoFill(); public: @@ -102,10 +100,6 @@ public: void onUseDistChanged(); void onUseVignChanged(); void onUseCAChanged(); - void setLensGeomRef( LensGeometry *foo) - { - lensgeomLcpFill = foo ; - }; void setBatchMode(bool yes); diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index e30309fdf..0052e35ca 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -53,7 +53,6 @@ ToolPanelCoordinator::ToolPanelCoordinator () : ipc (nullptr), hasChanged (false colortoning = Gtk::manage (new ColorToning ()); lensgeom = Gtk::manage (new LensGeometry ()); lensProf = Gtk::manage (new LensProfilePanel ()); - lensProf->setLensGeomRef (lensgeom); distortion = Gtk::manage (new Distortion ()); rotate = Gtk::manage (new Rotate ()); vibrance = Gtk::manage (new Vibrance ()); From 2611adf4e4bdfe4a0bd5831711b549d01dab4e03 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 10 Sep 2017 22:26:35 +0200 Subject: [PATCH 37/52] lens correction: do not disable stuff in batch mode --- rtgui/lensprofile.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 9eb41aa25..e56863948 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -208,7 +208,7 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa lcpFileChanged = useDistChanged = useVignChanged = useCAChanged = false; useLensfunChanged = lensfunAutoChanged = lensfunCameraChanged = lensfunLensChanged = false; - if (!checkLensfunCanCorrect(true)) { + if (!batchMode && !checkLensfunCanCorrect(true)) { if (corrLensfunAuto->get_active()) { corrOff->set_active(true); } @@ -510,6 +510,12 @@ void LensProfilePanel::onCorrModeChanged() lcpFileChanged = false; lensfunCameraChanged = false; lensfunLensChanged = false; + + lensfunCameras->set_sensitive(true); + lensfunLenses->set_sensitive(true); + ckbUseDist->set_sensitive(true); + ckbUseVign->set_sensitive(true); + ckbUseCA->set_sensitive(true); mode = M("GENERAL_UNCHANGED"); } From 736d0e8612f05ceee375648055aa282966a17dfc Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 11 Sep 2017 17:37:11 +0200 Subject: [PATCH 38/52] added history messages for lens correction modes --- rtdata/languages/default | 11 +++++++---- rtgui/lensprofile.cc | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 3cadd09db..49ec00878 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -281,9 +281,9 @@ HISTORY_MSG_30;RLD - Radius HISTORY_MSG_31;RLD - Amount HISTORY_MSG_32;RLD - Damping HISTORY_MSG_33;RLD - Iterations -HISTORY_MSG_34;LCP distortion correction -HISTORY_MSG_35;LCP vignetting correction -HISTORY_MSG_36;LCP CA correction +HISTORY_MSG_34;Lens Correction - Distortion +HISTORY_MSG_35;Lens Correction - Vignetting +HISTORY_MSG_36;Lens Correction - CA HISTORY_MSG_37;Exposure - Auto levels HISTORY_MSG_38;White Balance - Method HISTORY_MSG_39;WB - Temperature @@ -332,7 +332,7 @@ HISTORY_MSG_81;Resize HISTORY_MSG_82;Profile changed HISTORY_MSG_83;S/H - Sharp mask HISTORY_MSG_84;Perspective correction -HISTORY_MSG_85;LCP +HISTORY_MSG_85;Lens Correction - LCP file HISTORY_MSG_86;RGB Curves - Luminosity mode HISTORY_MSG_87;Impulse Noise Reduction HISTORY_MSG_88;Impulse NR threshold @@ -715,6 +715,9 @@ HISTORY_MSG_481;CAM02 - Temp scene HISTORY_MSG_482;CAM02 - Green scene HISTORY_MSG_483;CAM02 - Yb scene HISTORY_MSG_484;CAM02 - Auto Yb scene +HISTORY_MSG_485;Lens Correction +HISTORY_MSG_486;Lens Correction - Camera +HISTORY_MSG_487;Lens Correction - Lens HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 0fbb08c85..d3b5490d6 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -449,7 +449,7 @@ void LensProfilePanel::onCorrModeChanged() ckbUseVign->set_sensitive(false); ckbUseCA->set_sensitive(false); - mode = M("LENSPROFILE_CORRECTION_OFF"); + mode = M("GENERAL_NONE"); } else if (corrLensfunAuto->get_active()) { useLensfunChanged = true; lensfunAutoChanged = true; From 84984422c757bf3e965c6bb4ad3c74e074315284 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 12 Sep 2017 08:19:20 +0200 Subject: [PATCH 39/52] improved responsiveness of UI for profiled lens correction --- rtgui/lensprofile.cc | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index d3b5490d6..b402cdfe3 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -285,9 +285,15 @@ void LensProfilePanel::write( rtengine::procparams::ProcParams* pp, ParamsEdited void LensProfilePanel::onLCPFileChanged() { lcpFileChanged = true; - updateDisabled(LCPStore::getInstance()->isValidLCPFileName(fcbLCPFile->get_filename())); + bool valid = LCPStore::getInstance()->isValidLCPFileName(fcbLCPFile->get_filename()); + updateDisabled(valid); if (listener) { + if (valid) { + disableListener(); + corrLcpFile->set_active(true); + enableListener(); + } listener->panelChanged (EvLCPFile, Glib::path_get_basename(fcbLCPFile->get_filename())); } } @@ -299,7 +305,11 @@ void LensProfilePanel::onLCPFileReset() fcbLCPFile->unselect_filename(fcbLCPFile->get_filename()); updateDisabled(false); + if (listener) { + disableListener(); + corrOff->set_active(true); + enableListener(); listener->panelChanged (EvLCPFile, M("GENERAL_NONE")); } } @@ -412,6 +422,10 @@ void LensProfilePanel::onLensfunCameraChanged() lensfunCameraChanged = true; if (listener) { + disableListener(); + corrLensfunManual->set_active(true); + enableListener(); + Glib::ustring name = (*iter)[lf->lensfunModelCam.model]; listener->panelChanged(EvLensCorrLensfunCamera, name); } @@ -427,7 +441,11 @@ void LensProfilePanel::onLensfunLensChanged() lensfunLensChanged = true; if (listener) { - Glib::ustring name = (*iter)[lf->lensfunModelLens.lens]; + disableListener(); + corrLensfunManual->set_active(true); + enableListener(); + + Glib::ustring name = (*iter)[lf->lensfunModelLens.prettylens]; listener->panelChanged(EvLensCorrLensfunLens, name); } } @@ -443,8 +461,6 @@ void LensProfilePanel::onCorrModeChanged() lensfunAutoChanged = true; lcpFileChanged = true; - lensfunCameras->set_sensitive(false); - lensfunLenses->set_sensitive(false); ckbUseDist->set_sensitive(false); ckbUseVign->set_sensitive(false); ckbUseCA->set_sensitive(false); @@ -457,9 +473,6 @@ void LensProfilePanel::onCorrModeChanged() useDistChanged = true; useVignChanged = true; - lensfunCameras->set_sensitive(false); - lensfunLenses->set_sensitive(false); - ckbUseDist->set_sensitive(true); ckbUseVign->set_sensitive(true); ckbUseCA->set_sensitive(false); @@ -484,9 +497,6 @@ void LensProfilePanel::onCorrModeChanged() useDistChanged = true; useVignChanged = true; - lensfunCameras->set_sensitive(true); - lensfunLenses->set_sensitive(true); - ckbUseDist->set_sensitive(true); ckbUseVign->set_sensitive(true); ckbUseCA->set_sensitive(false); @@ -499,8 +509,6 @@ void LensProfilePanel::onCorrModeChanged() useDistChanged = true; useVignChanged = true; - lensfunCameras->set_sensitive(false); - lensfunLenses->set_sensitive(false); updateDisabled(true); mode = M("LENSPROFILE_CORRECTION_LCPFILE"); @@ -511,8 +519,6 @@ void LensProfilePanel::onCorrModeChanged() lensfunCameraChanged = false; lensfunLensChanged = false; - lensfunCameras->set_sensitive(true); - lensfunLenses->set_sensitive(true); ckbUseDist->set_sensitive(true); ckbUseVign->set_sensitive(true); ckbUseCA->set_sensitive(true); From ea5f8c3bebba933c15b70246e0a119307501fb1f Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 12 Sep 2017 08:25:45 +0200 Subject: [PATCH 40/52] profile lens correction: show a warning if the lens profile has a crop factor greater than the selected camera --- rtdata/languages/default | 1 + rtengine/rtlensfun.cc | 10 ++++++++++ rtengine/rtlensfun.h | 1 + rtgui/lensprofile.cc | 36 ++++++++++++++++++++++++++++++++++++ rtgui/lensprofile.h | 2 ++ 5 files changed, 50 insertions(+) diff --git a/rtdata/languages/default b/rtdata/languages/default index 49ec00878..412aad101 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2158,3 +2158,4 @@ ZOOMPANEL_ZOOMOUT;Zoom Out\nShortcut: - LENSPROFILE_CORRECTION_AUTOMATCH;Auto-matched correction parameters LENSPROFILE_CORRECTION_MANUAL;Manual correction parameters LENSPROFILE_CORRECTION_LCPFILE;LCP File +LENSPROFILE_LENS_WARNING;Warning: the crop factor used for lens profiling is larger than the crop factor of the camera, the results might be wrong. \ No newline at end of file diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 15ec001eb..2f70aabac 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -206,6 +206,16 @@ Glib::ustring LFLens::getLens() const } +float LFLens::getCropFactor() const +{ + if (data_) { + return data_->CropFactor; + } else { + return 0; + } +} + + //----------------------------------------------------------------------------- // LFDatabase //----------------------------------------------------------------------------- diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index 2ea893302..a862c7b2a 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -75,6 +75,7 @@ public: Glib::ustring getMake() const; Glib::ustring getLens() const; Glib::ustring getDisplayString() const { return getLens(); } + float getCropFactor() const; private: friend class LFDatabase; const lfLens *data_; diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index b402cdfe3..3927ef225 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -86,6 +86,11 @@ LensProfilePanel::LensProfilePanel () : hb = Gtk::manage(new Gtk::HBox()); hb->pack_start(*Gtk::manage(new Gtk::Label(M("EXIFFILTER_LENS"))), Gtk::PACK_SHRINK, 4); hb->pack_start(*lensfunLenses); + warning = Gtk::manage(new Gtk::Image()); + warning->set_from_icon_name("dialog-warning", Gtk::ICON_SIZE_LARGE_TOOLBAR); + warning->set_tooltip_text(M("LENSPROFILE_LENS_WARNING")); + warning->hide(); + hb->pack_start(*warning, Gtk::PACK_SHRINK, 4); pack_start(*hb); corrLcpFile = Gtk::manage(new Gtk::RadioButton(corrGroup)); @@ -219,10 +224,35 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa corrOff->set_active(true); } + updateLensfunWarning(); + enableListener (); conUseDist.block(false); } + +void LensProfilePanel::updateLensfunWarning() +{ + warning->hide(); + if (corrLensfunManual->get_active()) { + const LFDatabase *db = LFDatabase::getInstance(); + + auto itc = lensfunCameras->get_active(); + if (!itc) { + return; + } + LFCamera c = db->findCamera((*itc)[lf->lensfunModelCam.make], (*itc)[lf->lensfunModelCam.model]); + auto itl = lensfunLenses->get_active(); + if (!itl) { + return; + } + LFLens l = db->findLens(LFCamera(), (*itl)[lf->lensfunModelLens.lens]); + if (l.getCropFactor() - c.getCropFactor() >= 0.01) { + warning->show(); + } + } +} + void LensProfilePanel::setRawMeta(bool raw, const rtengine::ImageMetaData* pMeta) { if (!raw || pMeta->getFocusDist() <= 0) { @@ -430,6 +460,8 @@ void LensProfilePanel::onLensfunCameraChanged() listener->panelChanged(EvLensCorrLensfunCamera, name); } } + + updateLensfunWarning(); } @@ -449,6 +481,8 @@ void LensProfilePanel::onLensfunLensChanged() listener->panelChanged(EvLensCorrLensfunLens, name); } } + + updateLensfunWarning(); } @@ -526,6 +560,8 @@ void LensProfilePanel::onCorrModeChanged() mode = M("GENERAL_UNCHANGED"); } + updateLensfunWarning(); + if (listener) { listener->panelChanged(EvLensCorrMode, mode); } diff --git a/rtgui/lensprofile.h b/rtgui/lensprofile.h index 5e2a5b484..aca8f16ef 100644 --- a/rtgui/lensprofile.h +++ b/rtgui/lensprofile.h @@ -49,6 +49,7 @@ protected: Gtk::RadioButton *corrUnchanged; MyComboBox *lensfunCameras; MyComboBox *lensfunLenses; + Gtk::Image *warning; class LFDbHelper { public: @@ -86,6 +87,7 @@ protected: bool setLensfunCamera(const Glib::ustring &make, const Glib::ustring &model); bool setLensfunLens(const Glib::ustring &lens); bool checkLensfunCanCorrect(bool automatch); + void updateLensfunWarning(); public: From 88b343d1b39cb93d81703f155c2b11d1c02cc952 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 12 Sep 2017 15:29:41 +0200 Subject: [PATCH 41/52] update AboutThisBuild.txt with lensfun info --- AboutThisBuild.txt.in | 1 + CMakeLists.txt | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AboutThisBuild.txt.in b/AboutThisBuild.txt.in index ea3269c09..f48d39b50 100644 --- a/AboutThisBuild.txt.in +++ b/AboutThisBuild.txt.in @@ -7,6 +7,7 @@ Processor: ${PROC_LABEL} System: ${SYSTEM} Bit depth: ${PROC_BIT_DEPTH} Gtkmm: V${GTKMM_VERSION} +Lensfun: V${LENSFUN_VERSION} Build type: ${BUILD_TYPE} Build flags: ${CXX_FLAGS} Link flags: ${LFLAGS} diff --git a/CMakeLists.txt b/CMakeLists.txt index e506499b9..333e292f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -390,7 +390,8 @@ set(ABOUT_COMMAND_WITH_ARGS ${CMAKE_COMMAND} -DBUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DGTKMM_VERSION:STRING=${GTKMM_VERSION} -DOPTION_OMP:STRING=${OPTION_OMP} - -DWITH_MYFILE_MMAP:STRING=${WITH_MYFILE_MMAP}) + -DWITH_MYFILE_MMAP:STRING=${WITH_MYFILE_MMAP} + -DLENSFUN_VERSION:STRING=${LENSFUN_VERSION}) if(WIN32) list(APPEND ABOUT_COMMAND_WITH_ARGS -DSYSTEM:STRING=Windows From fea0fbe776dc7c7e94b9755fa81942f52fe73b49 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 12 Sep 2017 16:38:01 +0200 Subject: [PATCH 42/52] show lensfun version info when invoked with '-v' (patch by heckflosse) --- rtgui/main.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rtgui/main.cc b/rtgui/main.cc index 3ebd894df..f5e134b5a 100644 --- a/rtgui/main.cc +++ b/rtgui/main.cc @@ -33,6 +33,7 @@ #include #include #include +#include #include "options.h" #include "soundman.h" #include "rtimage.h" @@ -138,6 +139,7 @@ int processLineParams ( int argc, char **argv ) #endif case 'v': + std::cout << "Using lensfun " << LF_VERSION_MAJOR << "." << LF_VERSION_MINOR << "." << LF_VERSION_MICRO << "." << LF_VERSION_BUGFIX << std::endl; return 0; #ifndef __APPLE__ // TODO agriggio - there seems to be already some "single instance app" support for OSX in rtwindow. Disabling it here until I understand how to merge the two From 2d1cca8cbb50085a3731e10ebe9f03e0da5bd9ef Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 12 Sep 2017 16:41:25 +0200 Subject: [PATCH 43/52] disable vignetting or distortion correction checkboxes if the selected lensfun profile doesn't support them Patch by heckflosse --- rtengine/rtlensfun.cc | 18 ++++++++++++++++++ rtengine/rtlensfun.h | 2 ++ rtgui/lensprofile.cc | 2 ++ 3 files changed, 22 insertions(+) diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 2f70aabac..626d333aa 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -215,6 +215,24 @@ float LFLens::getCropFactor() const } } +bool LFLens::hasVignettingCorrection() const +{ + if (data_) { + return data_->CalibVignetting; + } else { + return false; + } +} + +bool LFLens::hasDistortionCorrection() const +{ + if (data_) { + return data_->CalibDistortion; + } else { + return false; + } +} + //----------------------------------------------------------------------------- // LFDatabase diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index a862c7b2a..1d54d9444 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -76,6 +76,8 @@ public: Glib::ustring getLens() const; Glib::ustring getDisplayString() const { return getLens(); } float getCropFactor() const; + bool hasVignettingCorrection() const; + bool hasDistortionCorrection() const; private: friend class LFDatabase; const lfLens *data_; diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 3927ef225..c2dde09b5 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -250,6 +250,8 @@ void LensProfilePanel::updateLensfunWarning() if (l.getCropFactor() - c.getCropFactor() >= 0.01) { warning->show(); } + ckbUseVign->set_sensitive(l.hasVignettingCorrection()); + ckbUseDist->set_sensitive(l.hasDistortionCorrection()); } } From f2853d8742f9b36e60f064f3d6d8baac8d348757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Tue, 12 Sep 2017 20:58:20 +0200 Subject: [PATCH 44/52] Some little cleanups to `rtlensfun.*` - Sanitize `std::unique_ptr<>` handling - Use `NonCopyable` - Employ `explicit operator bool()` - Correct use of `std::vector::emplace_back()` - Cleanup includes - Streamline implementation order --- rtengine/iptransform.cc | 21 ++++++------ rtengine/rawimagesource.cc | 2 +- rtengine/rtlensfun.cc | 62 ++++++++++++++++++++-------------- rtengine/rtlensfun.h | 69 ++++++++++++++++++++++---------------- 4 files changed, 89 insertions(+), 65 deletions(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 84464af97..e5f3a7af1 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -319,18 +319,21 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, float focusDist = metadata->getFocusDist(); double fNumber = metadata->getFNumber(); - LensCorrection *pLCPMap = nullptr; + std::unique_ptr pLCPMap; if (needsLensfun()) { - pLCPMap = LFDatabase::findModifier(params->lensProf, metadata, oW, oH, params->coarse, rawRotationDeg); + pLCPMap = std::move(LFDatabase::findModifier(params->lensProf, metadata, oW, oH, params->coarse, rawRotationDeg)); } else if (needsLCP()) { // don't check focal length to allow distortion correction for lenses without chip const std::shared_ptr pLCPProf = LCPStore::getInstance()->getProfile (params->lensProf.lcpFile); if (pLCPProf) { - pLCPMap = new LCPMapper (pLCPProf, focalLen, focalLen35mm, - focusDist, fNumber, false, - params->lensProf.useDist, - oW, oH, params->coarse, rawRotationDeg); + pLCPMap.reset( + new LCPMapper (pLCPProf, focalLen, focalLen35mm, + focusDist, fNumber, false, + params->lensProf.useDist, + oW, oH, params->coarse, rawRotationDeg + ) + ); } } @@ -345,11 +348,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, } else { mode = TRANSFORM_HIGH_QUALITY_FULLIMAGE; } - transformGeneral(mode, original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap); - } - - if (pLCPMap) { - delete pLCPMap; + transformGeneral(mode, original, transformed, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap.get()); } } diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 131fc354c..1dd63339c 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1858,7 +1858,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le if (!hasFlatField && lensProf.useVign) { std::unique_ptr pmap; if (lensProf.useLensfun) { - pmap.reset(LFDatabase::findModifier(lensProf, idata, W, H, coarse, -1)); + pmap = std::move(LFDatabase::findModifier(lensProf, idata, W, H, coarse, -1)); } else { const std::shared_ptr pLCPProf = LCPStore::getInstance()->getProfile(lensProf.lcpFile); diff --git a/rtengine/rtlensfun.cc b/rtengine/rtlensfun.cc index 626d333aa..87a7272e9 100644 --- a/rtengine/rtlensfun.cc +++ b/rtengine/rtlensfun.cc @@ -1,5 +1,5 @@ /* -*- C++ -*- - * + * * This file is part of RawTherapee. * * Copyright (c) 2017 Alberto Griggio @@ -30,14 +30,6 @@ extern const Settings *settings; // LFModifier //----------------------------------------------------------------------------- -LFModifier::LFModifier(lfModifier *m, bool swap_xy, int flags): - data_(m), - swap_xy_(swap_xy), - flags_(flags) -{ -} - - LFModifier::~LFModifier() { if (data_) { @@ -45,7 +37,8 @@ LFModifier::~LFModifier() } } -bool LFModifier::ok() const + +LFModifier::operator bool() const { return data_; } @@ -53,7 +46,7 @@ bool LFModifier::ok() const void LFModifier::correctDistortion(double &x, double &y, int cx, int cy, double scale) const { - if (!ok()) { + if (!data_) { return; } @@ -77,6 +70,17 @@ void LFModifier::correctDistortion(double &x, double &y, int cx, int cy, double } +bool LFModifier::isCACorrectionAvailable() const +{ + return false; +} + + +void LFModifier::correctCA(double &x, double &y, int channel) const +{ +} + + void LFModifier::processVignetteLine(int width, int y, float *line) const { data_->ApplyColorModification(line, 0, y, width, 1, LF_CR_1(INTENSITY), 0); @@ -114,6 +118,14 @@ Glib::ustring LFModifier::getDisplayString() const } +LFModifier::LFModifier(lfModifier *m, bool swap_xy, int flags): + data_(m), + swap_xy_(swap_xy), + flags_(flags) +{ +} + + //----------------------------------------------------------------------------- // LFCamera //----------------------------------------------------------------------------- @@ -124,7 +136,7 @@ LFCamera::LFCamera(): } -bool LFCamera::ok() const +LFCamera::operator bool() const { return data_; } @@ -180,7 +192,7 @@ LFLens::LFLens(): } -bool LFLens::ok() const +LFLens::operator bool() const { return data_; } @@ -274,11 +286,11 @@ std::vector LFDatabase::getCameras() const if (data_) { auto cams = data_->GetCameras(); while (*cams) { - ret.emplace_back(LFCamera()); + ret.emplace_back(); ret.back().data_ = *cams; ++cams; } - } + } return ret; } @@ -289,7 +301,7 @@ std::vector LFDatabase::getLenses() const if (data_) { auto lenses = data_->GetLenses(); while (*lenses) { - ret.emplace_back(LFLens()); + ret.emplace_back(); ret.back().data_ = *lenses; ++lenses; } @@ -317,7 +329,7 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c LFLens ret; if (data_) { Glib::ustring lname = name; - bool stdlens = camera.ok() && (name.empty() || name.find("Unknown") == 0); + bool stdlens = camera && (name.empty() || name.find("Unknown") == 0); if (stdlens) { lname = camera.getModel(); // "Standard" } @@ -341,27 +353,27 @@ LFLens LFDatabase::findLens(const LFCamera &camera, const Glib::ustring &name) c } -LFModifier *LFDatabase::getModifier(const LFCamera &camera, const LFLens &lens, +std::unique_ptr LFDatabase::getModifier(const LFCamera &camera, const LFLens &lens, float focalLen, float aperture, float focusDist, int width, int height, bool swap_xy) const { - LFModifier *ret = nullptr; + std::unique_ptr ret; if (data_) { - if (camera.ok() && lens.ok()) { + if (camera && lens) { lfModifier *mod = lfModifier::Create(lens.data_, camera.getCropFactor(), width, height); int flags = LF_MODIFY_DISTORTION | LF_MODIFY_SCALE; if (aperture > 0) { flags |= LF_MODIFY_VIGNETTING; } flags = mod->Initialize(lens.data_, LF_PF_F32, focalLen, aperture, focusDist > 0 ? focusDist : 1000, 0.0, LF_RECTILINEAR, flags, false); - ret = new LFModifier(mod, swap_xy, flags); + ret.reset(new LFModifier(mod, swap_xy, flags)); } } return ret; } -LFModifier *LFDatabase::findModifier(const LensProfParams &lensProf, const ImageMetaData *idata, int width, int height, const CoarseTransformParams &coarse, int rawRotationDeg) +std::unique_ptr LFDatabase::findModifier(const LensProfParams &lensProf, const ImageMetaData *idata, int width, int height, const CoarseTransformParams &coarse, int rawRotationDeg) { const LFDatabase *db = getInstance(); Glib::ustring make, model, lens; @@ -394,8 +406,8 @@ LFModifier *LFDatabase::findModifier(const LensProfParams &lensProf, const Image std::swap(width, height); } } - - LFModifier *ret = db->getModifier(c, l, idata->getFocalLen(), idata->getFNumber(), idata->getFocusDist(), width, height, swap_xy); + + std::unique_ptr ret = db->getModifier(c, l, idata->getFocalLen(), idata->getFNumber(), idata->getFocusDist(), width, height, swap_xy); if (settings->verbose) { std::cout << "LENSFUN:\n" @@ -407,6 +419,6 @@ LFModifier *LFDatabase::findModifier(const LensProfParams &lensProf, const Image return ret; } - + } // namespace rtengine diff --git a/rtengine/rtlensfun.h b/rtengine/rtlensfun.h index 1d54d9444..5774968ee 100644 --- a/rtengine/rtlensfun.h +++ b/rtengine/rtlensfun.h @@ -1,5 +1,5 @@ /* -*- C++ -*- - * + * * This file is part of RawTherapee. * * Copyright (c) 2017 Alberto Griggio @@ -20,43 +20,52 @@ #pragma once -#include -#include #include +#include + +#include + +#include + #include "lcp.h" +#include "noncopyable.h" #include "procparams.h" namespace rtengine { -class LFModifier: public LensCorrection { +class LFModifier final : + public LensCorrection, + public NonCopyable +{ public: ~LFModifier(); - bool ok() const; - - void correctDistortion(double &x, double &y, int cx, int cy, double scale) const; - bool isCACorrectionAvailable() const { return false; } - void correctCA(double &x, double &y, int channel) const {} - void processVignetteLine(int width, int y, float *line) const; + + explicit operator bool() const; + + void correctDistortion(double &x, double &y, int cx, int cy, double scale) const override; + bool isCACorrectionAvailable() const override; + void correctCA(double &x, double &y, int channel) const override; + void processVignetteLine(int width, int y, float *line) const override; void processVignetteLine3Channels(int width, int y, float *line) const; Glib::ustring getDisplayString() const; - + private: - explicit LFModifier(lfModifier *m, bool swap_xy, int flags); - LFModifier(const LFModifier &); - LFModifier &operator=(const LFModifier &); - + LFModifier(lfModifier *m, bool swap_xy, int flags); + friend class LFDatabase; lfModifier *data_; bool swap_xy_; int flags_; }; -class LFCamera { +class LFCamera final +{ public: LFCamera(); - bool ok() const; - + + explicit operator bool() const; + Glib::ustring getMake() const; Glib::ustring getModel() const; float getCropFactor() const; @@ -68,42 +77,46 @@ private: const lfCamera *data_; }; -class LFLens { +class LFLens final +{ public: LFLens(); - bool ok() const; + + explicit operator bool() const; + Glib::ustring getMake() const; Glib::ustring getLens() const; Glib::ustring getDisplayString() const { return getLens(); } float getCropFactor() const; bool hasVignettingCorrection() const; bool hasDistortionCorrection() const; + private: friend class LFDatabase; const lfLens *data_; }; -class LFDatabase { +class LFDatabase final : + public NonCopyable +{ public: static bool init(); static const LFDatabase *getInstance(); ~LFDatabase(); - + std::vector getCameras() const; std::vector getLenses() const; LFCamera findCamera(const Glib::ustring &make, const Glib::ustring &model) const; LFLens findLens(const LFCamera &camera, const Glib::ustring &name) const; - static LFModifier *findModifier(const LensProfParams &lensProf, const ImageMetaData *idata, int width, int height, const CoarseTransformParams &coarse, int rawRotationDeg); + static std::unique_ptr findModifier(const LensProfParams &lensProf, const ImageMetaData *idata, int width, int height, const CoarseTransformParams &coarse, int rawRotationDeg); private: - LFModifier *getModifier(const LFCamera &camera, const LFLens &lens, - float focalLen, float aperture, float focusDist, - int width, int height, bool swap_xy) const; + std::unique_ptr getModifier(const LFCamera &camera, const LFLens &lens, + float focalLen, float aperture, float focusDist, + int width, int height, bool swap_xy) const; LFDatabase(); - LFDatabase(const LFDatabase &); - LFDatabase &operator=(const LFDatabase &); static LFDatabase instance_; lfDatabase *data_; }; From c9c02c137bc6d542882ae4dd895b4dd29f6d864b Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 13 Sep 2017 08:49:56 +0200 Subject: [PATCH 45/52] change calculation in curve brightness CIECAM --- rtengine/improcfun.cc | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 6d6fccc88..c8faa4ce0 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -1681,10 +1681,11 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int } } - if (alg >= 2 && la < 200.f) { - la = 200.f; - } - + /* + if (alg >= 2 && la < 200.f) { + la = 200.f; + } + */ const float la2 = float (params->colorappearance.adaplum); // level of adaptation @@ -1775,6 +1776,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int } float sum = 0.f; + float sumQ = 0.f; #ifdef _OPENMP const int numThreads = min (max (width * height / 65536, 1), omp_get_max_threads()); @@ -1794,7 +1796,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int hist16Qthr.clear(); } - #pragma omp for reduction(+:sum) + #pragma omp for reduction(+:sum,sumQ) for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) { //rough correspondence between L and J @@ -1839,11 +1841,26 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int hist16Jthr[ (int) ((koef * lab->L[i][j]))]++; //evaluate histogram luminance L # J } + //estimation of wh only with La + float whestim = 500.f; + + if (la < 200.f) { + whestim = 200.f; + } else if (la < 2500.f) { + whestim = 350.f; + } else { + whestim = 500.f; + } + if (needQ) { - hist16Qthr[ (int) (sqrtf ((koef * (lab->L[i][j])) * 32768.f))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L + hist16Qthr[CLIP ((int) (32768.f * sqrt ((koef * (lab->L[i][j])) / 32768.f)))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L + //perhaps needs to introduce whestim ?? + // hist16Qthr[ (int) (sqrtf ((koef * (lab->L[i][j])) * 32768.f))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L } sum += koef * lab->L[i][j]; //evaluate mean J to calculate Yb + sumQ += whestim * sqrt ((koef * (lab->L[i][j])) / 32768.f); + //can be used in case of... } #pragma omp critical @@ -1857,14 +1874,16 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int } } + float meanQ; if (std::isnan (mean)) { mean = (sum / ((height) * width)) / 327.68f; //for Yb for all image...if one day "pipette" we can adapt Yb for each zone + meanQ = (sumQ / ((height) * width));//in case of + } } - //evaluate lightness, contrast } @@ -1937,6 +1956,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int float cz, wh, pfl; Ciecam02::initcam1float (gamu, yb, pilot, f, la, xw, yw, zw, n, d, nbb, ncb, cz, aw, wh, pfl, fl, c); + //printf ("wh=%f \n", wh); + const float pow1 = pow_F ( 1.64f - pow_F ( 0.29f, n ), 0.73f ); float nj, nbbj, ncbj, czj, awj, flj; Ciecam02::initcam2float (gamu, yb2, pilotout, f2, la2, xw2, yw2, zw2, nj, dj, nbbj, ncbj, czj, awj, flj); @@ -1954,7 +1975,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int const bool LabPassOne = ! ((params->colorappearance.tonecie && (epdEnabled)) || (params->sharpening.enabled && settings->autocielab && execsharp) || (params->dirpyrequalizer.enabled && settings->autocielab) || (params->defringe.enabled && settings->autocielab) || (params->sharpenMicro.enabled && settings->autocielab) || (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl > 0 && settings->autocielab)); - + //printf("coQ=%f\n", coefQ); if (needJ) { if (!CAMBrightCurveJ) { From 5bd82a5c88b46bea55598497be9a8f100430d8a3 Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 13 Sep 2017 09:23:06 +0200 Subject: [PATCH 46/52] add la < 200 --- rtengine/improcfun.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index c8faa4ce0..236805f6c 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -1681,11 +1681,11 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int } } - /* + if (alg >= 2 && la < 200.f) { la = 200.f; } - */ + const float la2 = float (params->colorappearance.adaplum); // level of adaptation From de9c742d27404828e90a458365c13d6e87d6c460 Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 13 Sep 2017 13:48:24 +0200 Subject: [PATCH 47/52] Fixed another mistake in curve brigthness ciecam --- rtengine/ciecam02.cc | 4 ++++ rtengine/improcfun.cc | 29 ++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/rtengine/ciecam02.cc b/rtengine/ciecam02.cc index 488093060..625b77f05 100644 --- a/rtengine/ciecam02.cc +++ b/rtengine/ciecam02.cc @@ -178,6 +178,8 @@ void Ciecam02::curveJ (double br, double contr, int db, LUTf & outCurve, LUTu & for (int i = 0; i < (db * 32768); i++) { outCurve[i] = db * 32768.0 * dcurve[i]; } +// printf("double out500=%f out15000=%f\n", outCurve[500], outCurve[15000]); + } void Ciecam02::curveJfloat (float br, float contr, const LUTu & histogram, LUTf & outCurve) @@ -268,6 +270,8 @@ void Ciecam02::curveJfloat (float br, float contr, const LUTu & histogram, LUTf } outCurve *= 32767.f; + //printf("out500=%f out15000=%f\n", outCurve[500], outCurve[15000]); + //outCurve.dump("brig"); } /** diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 236805f6c..625f93bb1 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -1681,11 +1681,11 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int } } - + /* if (alg >= 2 && la < 200.f) { la = 200.f; } - + */ const float la2 = float (params->colorappearance.adaplum); // level of adaptation @@ -1996,7 +1996,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int if (CAMBrightCurveQ.dirty) { Ciecam02::curveJfloat (params->colorappearance.qbright, params->colorappearance.qcontrast, hist16Q, CAMBrightCurveQ);//brightness and contrast Q - CAMBrightCurveQ /= coefQ; + // CAMBrightCurveQ /= coefQ; CAMBrightCurveQ.dirty = false; } } @@ -2146,7 +2146,11 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int Qpro = QproFactor * sqrtf (Jpro); Cpro = (spro * spro * Qpro) / (10000.0f); } else if (alg == 2) { - Qpro = CAMBrightCurveQ[ (float) (Qpro * coefQ)]; //brightness and contrast + //printf("Qp0=%f ", Qpro); + + Qpro = CAMBrightCurveQ[ (float) (Qpro * coefQ)] / coefQ; //brightness and contrast + //printf("Qpaf=%f ", Qpro); + float Mp, sres; Mp = Mpro / 100.0f; Ciecam02::curvecolorfloat (mchr, Mp, sres, 2.5f); @@ -2160,7 +2164,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int Qpro = (Qpro == 0.f ? epsil : Qpro); // avoid division by zero spro = 100.0f * sqrtf ( Mpro / Qpro ); } else { /*if(alg == 3) */ - Qpro = CAMBrightCurveQ[ (float) (Qpro * coefQ)]; //brightness and contrast + Qpro = CAMBrightCurveQ[ (float) (Qpro * coefQ)] / coefQ; //brightness and contrast float Mp, sres; Mp = Mpro / 100.0f; @@ -2237,6 +2241,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int } else if (curveMode == ColorAppearanceParams::TC_MODE_BRIGHT) { //attention! Brightness curves are open - unlike Lightness or Lab or RGB==> rendering and algoritms will be different float coef = ((aw + 4.f) * (4.f / c)) / 100.f; + float Qanc = Qpro; float Qq = (float) Qpro * 327.68f * (1.f / coef); float Qold100 = (float) Qpro / coef; @@ -2262,8 +2267,9 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int Qq = 0.7f * (Qq - Qold) + Qold; // not zero ==>artifacts } - Qpro = (float) (Qq * (coef) / 327.68f); - Jpro = 100.f * (Qpro * Qpro) / ((4.0f / c) * (4.0f / c) * (aw + 4.0f) * (aw + 4.0f)); + Qpro = Qanc * (Qq / Qold); + // Jpro = 100.f * (Qpro * Qpro) / ((4.0f / c) * (4.0f / c) * (aw + 4.0f) * (aw + 4.0f)); + Jpro = Jpro * SQR (Qq / Qold); if (Jpro < 1.f) { Jpro = 1.f; @@ -2311,6 +2317,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int } } else if (curveMode2 == ColorAppearanceParams::TC_MODE_BRIGHT) { // + float Qanc = Qpro; + float coef = ((aw + 4.f) * (4.f / c)) / 100.f; float Qq = (float) Qpro * 327.68f * (1.f / coef); float Qold100 = (float) Qpro / coef; @@ -2337,8 +2345,11 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int Qq = 0.7f * (Qq - Qold) + Qold; // not zero ==>artifacts } - Qpro = (float) (Qq * (coef) / 327.68f); - Jpro = 100.f * (Qpro * Qpro) / ((4.0f / c) * (4.0f / c) * (aw + 4.0f) * (aw + 4.0f)); + // Qpro = (float) (Qq * (coef) / 327.68f); + Qpro = Qanc * (Qq / Qold); + Jpro = Jpro * SQR (Qq / Qold); + + // Jpro = 100.f * (Qpro * Qpro) / ((4.0f / c) * (4.0f / c) * (aw + 4.0f) * (aw + 4.0f)); if (t1L) { //to workaround the problem if we modify curve1-lightnees after curve2 brightness(the cat that bites its own tail!) in fact it's another type of curve only for this case coef = 2.f; //adapt Q to J approximation From b5c1293bf3b5ad77c0f2a8f3a3cbbcd556ab8b85 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 13 Sep 2017 14:51:16 +0200 Subject: [PATCH 48/52] updated logic for displaying lens correction crop factor warning --- rtgui/lensprofile.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index c2dde09b5..bb9138461 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -247,7 +247,9 @@ void LensProfilePanel::updateLensfunWarning() return; } LFLens l = db->findLens(LFCamera(), (*itl)[lf->lensfunModelLens.lens]); - if (l.getCropFactor() - c.getCropFactor() >= 0.01) { + float lenscrop = l.getCropFactor(); + float camcrop = c.getCropFactor(); + if (lenscrop <= 0 || camcrop <= 0 || lenscrop / camcrop >= 1.01f) { warning->show(); } ckbUseVign->set_sensitive(l.hasVignettingCorrection()); From c2960caab52804eaf7eb691bdf62502f73987f4c Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 13 Sep 2017 15:06:47 +0200 Subject: [PATCH 49/52] some (minor) code cleanup --- rtengine/dcrop.cc | 3 --- rtengine/improccoordinator.cc | 9 ++------- rtengine/improcfun.h | 2 -- rtengine/iptransform.cc | 19 ++----------------- rtengine/rtthumbnail.cc | 8 -------- rtengine/rtthumbnail.h | 2 -- rtengine/simpleprocess.cc | 6 +----- rtgui/cacheimagedata.h | 5 ----- rtgui/lensgeom.h | 2 -- 9 files changed, 5 insertions(+), 51 deletions(-) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 92416917d..25710205f 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -699,9 +699,6 @@ void Crop::update (int todo) if (needstransform) parent->ipf.transform (baseCrop, transCrop, cropx / skip, cropy / skip, trafx / skip, trafy / skip, skips (parent->fw, skip), skips (parent->fh, skip), parent->getFullWidth(), parent->getFullHeight(), parent->imgsrc->getMetaData(), - // parent->imgsrc->getMetaData()->getFocalLen(), parent->imgsrc->getMetaData()->getFocalLen35mm(), - // parent->imgsrc->getMetaData()->getFocusDist(), - // parent->imgsrc->getMetaData()->getFNumber(), parent->imgsrc->getRotateDegree(), false); else { baseCrop->copyData (transCrop); diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 8a3b979ae..2b9edc3d6 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -401,10 +401,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) if (needstransform) ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, fw, fh, - imgsrc->getMetaData(), - // imgsrc->getMetaData()->getFocalLen(), - // imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(), imgsrc->getMetaData()->getFNumber(), - imgsrc->getRotateDegree(), false); + imgsrc->getMetaData(), imgsrc->getRotateDegree(), false); else { orig_prev->copyData (oprevi); } @@ -1223,9 +1220,7 @@ void ImProcCoordinator::saveInputICCReference (const Glib::ustring& fname, bool if (ipf.needsTransform()) { Imagefloat* trImg = new Imagefloat (fW, fH); ipf.transform (im, trImg, 0, 0, 0, 0, fW, fH, fW, fH, - imgsrc->getMetaData(), - // imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getMetaData()->getFocusDist(), imgsrc->getMetaData()->getFNumber(), - imgsrc->getRotateDegree(), true); + imgsrc->getMetaData(), imgsrc->getRotateDegree(), true); delete im; im = trImg; } diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 1bcca4d37..e48ec3c7f 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -240,8 +240,6 @@ public: void colorCurve (LabImage* lold, LabImage* lnew); void sharpening (LabImage* lab, float** buffer, SharpeningParams &sharpenParam); void sharpeningcam (CieImage* ncie, float** buffer); - /* void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, */ - /* double focalLen, double focalLen35mm, float focusDist, double fNumber, int rawRotationDeg, bool fullImage); */ void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const ImageMetaData *metadata, int rawRotationDeg, bool fullImage); float resizeScale (const ProcParams* params, int fw, int fh, int &imw, int &imh); void lab2monitorRgb (LabImage* lab, Image8* image); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index e5f3a7af1..3f7472f6f 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -87,20 +87,6 @@ float normn (float a, float b, int n) } -inline void correct_distortion(const rtengine::LensCorrection *lcp, double &x, double &y, - int cx, int cy, double scale) -{ - assert (lcp); - - // x += cx; - // y += cy; - // std::cout << "DIST: x=" << x << ", y=" << y; - lcp->correctDistortion(x, y, cx, cy, scale); - // std::cout << " --> pos[0]=" << x << ", pos[1]=" << y << std::endl; - // x -= (cx * scale); - // y -= (cy * scale); -} - } namespace rtengine @@ -160,7 +146,7 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, double x_d = src[i].x, y_d = src[i].y; if (pLCPMap && params->lensProf.useDist) { - correct_distortion (pLCPMap, x_d, y_d, 0, 0, ascale); + pLCPMap->correctDistortion(x_d, y_d, 0, 0, ascale); } else { x_d *= ascale; y_d *= ascale; @@ -311,7 +297,6 @@ bool ImProcFunctions::transCoord (int W, int H, int x, int y, int w, int h, int& void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const ImageMetaData *metadata, - //double focalLen, double focalLen35mm, float focusDist, double fNumber, int rawRotationDeg, bool fullImage) { double focalLen = metadata->getFocalLen(); @@ -831,7 +816,7 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag double x_d = x, y_d = y; if (enableLCPDist) { - correct_distortion(pLCPMap, x_d, y_d, cx, cy, ascale); // must be first transform + pLCPMap->correctDistortion(x_d, y_d, cx, cy, ascale); // must be first transform } else { x_d *= ascale; y_d *= ascale; diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index a5d951de2..065d4640c 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -954,8 +954,6 @@ 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, float focusDist, float shutter, float fnumber, float iso, std::string expcomp_, double& myscale) IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rheight, TypeInterpolation interp, const ImageMetaData *metadata, double& myscale) { std::string camName = metadata->getCamera(); @@ -1086,7 +1084,6 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei int origFH; double tscale = 0.0; getDimensions (origFW, origFH, tscale); - // ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, origFW * tscale + 0.5, origFH * tscale + 0.5, focalLen, focalLen35mm, focusDist, fnumber, 0, true); // Raw rotate degree not detectable here ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, origFW * tscale + 0.5, origFH * tscale + 0.5, metadata, 0, true); // Raw rotate degree not detectable here delete baseImg; baseImg = trImg; @@ -1286,11 +1283,6 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei float fnum = fnumber;// F number float fiso = iso;// ISO float fspeed = shutter;//speed - // char * writ = new char[expcomp_.size() + 1];//convert expcomp_ to char - // std::copy (expcomp_.begin(), expcomp_.end(), writ); - // writ[expcomp_.size()] = '\0'; - // float fcomp = atof (writ); //compensation + - - // delete[] writ; float adap; if (fnum < 0.3f || fiso < 5.f || fspeed < 0.00001f) diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h index 4fc3466c8..3610a530f 100644 --- a/rtengine/rtthumbnail.h +++ b/rtengine/rtthumbnail.h @@ -71,8 +71,6 @@ public: void init (); - // IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, std::string camName, - // double focalLen, double focalLen35mm, float focusDist, float shutter, float fnumber, float iso, std::string expcomp_, double& scale); IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, const ImageMetaData *metadata, double& scale); IImage8* quickProcessImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, double& scale); int getImageWidth (const procparams::ProcParams& pparams, int rheight, float &ratio); diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index fabcf2158..fd0a67411 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -814,11 +814,7 @@ private: if (ipf.needsTransform()) { Imagefloat* trImg = new Imagefloat (fw, fh); ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, fw, fh, - imgsrc->getMetaData(), - // imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), - // imgsrc->getMetaData()->getFocusDist(), - // imgsrc->getMetaData()->getFNumber(), - imgsrc->getRotateDegree(), true); + imgsrc->getMetaData(), imgsrc->getRotateDegree(), true); delete baseImg; baseImg = trImg; } diff --git a/rtgui/cacheimagedata.h b/rtgui/cacheimagedata.h index 0af6d4fcf..e1c317508 100644 --- a/rtgui/cacheimagedata.h +++ b/rtgui/cacheimagedata.h @@ -77,11 +77,6 @@ public: int load (const Glib::ustring& fname); int save (const Glib::ustring& fname); - // Glib::ustring getCamera() const - // { - // return Glib::ustring(camMake + " " + camModel); - // } - //------------------------------------------------------------------------- // ImageMetaData interface //------------------------------------------------------------------------- diff --git a/rtgui/lensgeom.h b/rtgui/lensgeom.h index aa473cb91..29b0c7f20 100644 --- a/rtgui/lensgeom.h +++ b/rtgui/lensgeom.h @@ -44,8 +44,6 @@ public: return packBox; } - Gtk::CheckButton *getFill() { return fill; } - void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr); void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr); void setBatchMode (bool batchMode); From c465655cb471966117b5098396c407605e1044ff Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 13 Sep 2017 17:28:11 +0200 Subject: [PATCH 50/52] Fix two issues detected by coverity --- rtengine/green_equil_RT.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rtengine/green_equil_RT.cc b/rtengine/green_equil_RT.cc index 91093550d..90c412871 100644 --- a/rtengine/green_equil_RT.cc +++ b/rtengine/green_equil_RT.cc @@ -62,6 +62,16 @@ void RawImageSource::green_equilibrate_global(array2D &rawData) } } + // Avoid division by zero + if(ng1 == 0 || avgg1 == 0.0) { + ng1 = 1; + avgg1 = 1.0; + } + if(ng2 == 0 || avgg2 == 0.0) { + ng2 = 1; + avgg2 = 1.0; + } + double corrg1 = (avgg1 / ng1 + avgg2 / ng2) / 2.0 / (avgg1 / ng1); double corrg2 = (avgg1 / ng1 + avgg2 / ng2) / 2.0 / (avgg2 / ng2); From 71e6a063269b79c14b2e1d2822adccc87649293d Mon Sep 17 00:00:00 2001 From: Desmis Date: Wed, 13 Sep 2017 19:34:25 +0200 Subject: [PATCH 51/52] same modification for double --- rtengine/improcfun.cc | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 625f93bb1..3bc154446 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -846,6 +846,7 @@ void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh } else if (curveMode == ColorAppearanceParams::TC_MODE_BRIGHT) { //attention! Brightness curves are open - unlike Lightness or Lab or RGB==> rendering and algoritms will be different float coef = ((aw + 4.f) * (4.f / c)) / 100.f; + float Qanc = Qpro; float Qq = (float) Qpro * 327.68f * (1.f / coef); float Qold100 = (float) Qpro / coef; @@ -871,8 +872,15 @@ void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh Qq = 0.7f * (Qq - Qold) + Qold; // not zero ==>artifacts } - Qpro = (double) (Qq * (coef) / 327.68f); - Jpro = 100.* (Qpro * Qpro) / ((4.0 / c) * (4.0 / c) * (aw + 4.0) * (aw + 4.0)); + if (Qold == 0.f) { + Qold = 0.001f; + } + + Qpro = Qanc * (Qq / Qold); + Jpro = Jpro * SQR (Qq / Qold); + +// Qpro = (double) (Qq * (coef) / 327.68f); +// Jpro = 100.* (Qpro * Qpro) / ((4.0 / c) * (4.0 / c) * (aw + 4.0) * (aw + 4.0)); t1B = true; if (Jpro < 1.) { @@ -927,6 +935,7 @@ void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh } } else if (curveMode2 == ColorAppearanceParams::TC_MODE_BRIGHT) { // + float Qanc = Qpro; float coef = ((aw + 4.f) * (4.f / c)) / 100.f; float Qq = (float) Qpro * 327.68f * (1.f / coef); float Qold100 = (float) Qpro / coef; @@ -953,8 +962,16 @@ void ImProcFunctions::ciecam_02 (CieImage* ncie, double adap, int begh, int endh Qq = 0.7f * (Qq - Qold) + Qold; // not zero ==>artifacts } - Qpro = (double) (Qq * (coef) / 327.68f); - Jpro = 100.* (Qpro * Qpro) / ((4.0 / c) * (4.0 / c) * (aw + 4.0) * (aw + 4.0)); + if (Qold == 0.f) { + Qold = 0.001f; + } + + // Qpro = (float) (Qq * (coef) / 327.68f); + Qpro = Qanc * (Qq / Qold); + Jpro = Jpro * SQR (Qq / Qold); + + // Qpro = (double) (Qq * (coef) / 327.68f); + // Jpro = 100.* (Qpro * Qpro) / ((4.0 / c) * (4.0 / c) * (aw + 4.0) * (aw + 4.0)); t2B = true; if (t1L) { //to workaround the problem if we modify curve1-lightnees after curve2 brightness(the cat that bites its own tail!) in fact it's another type of curve only for this case @@ -2267,6 +2284,10 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int Qq = 0.7f * (Qq - Qold) + Qold; // not zero ==>artifacts } + if (Qold == 0.f) { + Qold = 0.001f; + } + Qpro = Qanc * (Qq / Qold); // Jpro = 100.f * (Qpro * Qpro) / ((4.0f / c) * (4.0f / c) * (aw + 4.0f) * (aw + 4.0f)); Jpro = Jpro * SQR (Qq / Qold); @@ -2345,6 +2366,10 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int begh, int Qq = 0.7f * (Qq - Qold) + Qold; // not zero ==>artifacts } + if (Qold == 0.f) { + Qold = 0.001f; + } + // Qpro = (float) (Qq * (coef) / 327.68f); Qpro = Qanc * (Qq / Qold); Jpro = Jpro * SQR (Qq / Qold); From 05b97f5c5086add6293437d892311f6e06015eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Wed, 13 Sep 2017 19:58:48 +0200 Subject: [PATCH 52/52] Fix some new Coverity warnings --- rtengine/iptransform.cc | 29 ++++++++++++++++++----------- rtengine/lcp.cc | 6 +++--- rtgui/cacheimagedata.h | 2 +- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 3f7472f6f..bfd01b301 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -789,18 +789,25 @@ void ImProcFunctions::transformGeneral(ImProcFunctions::TransformMode mode, Imag bool enableCA = false; switch (mode) { - case ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE: - enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable(); - // no break on purpose - case ImProcFunctions::TRANSFORM_HIGH_QUALITY: - enableLCPDist = pLCPMap && params->lensProf.useDist; - if (enableLCPCA) { - enableLCPDist = false; + case ImProcFunctions::TRANSFORM_HIGH_QUALITY_FULLIMAGE: { + enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable(); + } + //no break on purpose + + case ImProcFunctions::TRANSFORM_HIGH_QUALITY: { + enableLCPDist = pLCPMap && params->lensProf.useDist; + if (enableLCPCA) { + enableLCPDist = false; + } + enableCA = enableLCPCA || needsCA(); + } + //no break on purpose + + default: + case ImProcFunctions::TRANSFORM_PREVIEW: { + enableLCPDist = pLCPMap && params->lensProf.useDist; + break; } - enableCA = enableLCPCA || needsCA(); - default: // ImProcFunctions::TRANSFORM_PREVIEW - enableLCPDist = pLCPMap && params->lensProf.useDist; - break; } if (!enableCA) { diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index 3a7c44000..3824d2b2f 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -650,8 +650,8 @@ int rtengine::LCPProfile::filterBadFrames(LCPCorrectionMode mode, double maxAvgD } } - if (settings->verbose) { - std::printf("Filtered %.1f%% frames for maxAvgDevFac %g leaving %i\n", filtered *100.f / count, maxAvgDevFac, count - filtered); + if (settings->verbose && count) { + std::printf("Filtered %.1f%% frames for maxAvgDevFac %g leaving %i\n", filtered * 100.f / count, maxAvgDevFac, count - filtered); } } @@ -1030,7 +1030,7 @@ void rtengine::LCPMapper::correctDistortion(double &x, double &y, int cx, int cy { x += cx; y += cy; - + if (isFisheye) { const double u = x * scale; const double v = y * scale; diff --git a/rtgui/cacheimagedata.h b/rtgui/cacheimagedata.h index e1c317508..63eebf25c 100644 --- a/rtgui/cacheimagedata.h +++ b/rtgui/cacheimagedata.h @@ -85,7 +85,7 @@ public: const rtexif::TagDirectory *getExifData() const { return NULL; } bool hasIPTC() const { return false; } const rtengine::procparams::IPTCPairs getIPTCData () const { return rtengine::procparams::IPTCPairs(); } - struct tm getDateTime () const { struct tm ret; return ret; } + tm getDateTime () const { return tm{}; } time_t getDateTimeAsTS() const { return time_t(-1); } int getISOSpeed() const { return iso; } double getFNumber() const { return fnumber; }