Files
rawTherapee/rtengine/lcp.cc
Hombre 8b2eac9a3d Pipette and "On Preview Widgets" branch. See issue 227
The pipette part is already working quite nice but need to be finished. The widgets part needs more work...
2014-01-21 23:37:36 +01:00

682 lines
28 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2012 Oliver Duis <www.oliverduis.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include "lcp.h"
#include "safegtk.h"
#include "iccmatrices.h"
#include "iccstore.h"
#include "rawimagesource.h"
#include "improcfun.h"
#include "rt_math.h"
#ifdef WIN32
#include <windows.h>
// for GCC32
#ifndef _WIN32_IE
#define _WIN32_IE 0x0600
#endif
#include <shlobj.h>
#endif
using namespace std;
using namespace rtengine;
using namespace rtexif;
LCPModelCommon::LCPModelCommon() {
focLenX=focLenY=-1; imgXCenter=imgYCenter=0.5;
x0=y0=fx=fy=meanErr=0; badErr=false;
for (int i=0;i<5;i++) param[i]=0;
scaleFac=1;
}
bool LCPModelCommon::empty() const {
return param[0]==0 && param[1]==0 && param[2]==0;
}
void LCPModelCommon::print() const {
printf("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n",focLenX,focLenY,imgXCenter,imgYCenter,scaleFac,meanErr);
printf("xy0 %g/%g fxy %g/%g\n",x0,y0,fx,fy);
printf("param: %g/%g/%g/%g/%g\n",param[0],param[1],param[2],param[3],param[4]);
}
// weightened merge two parameters
void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA) {
float facB=1-facA;
focLenX = facA * a.focLenX + facB * b.focLenX;
focLenY = facA * a.focLenY + facB * b.focLenY;
imgXCenter = facA * a.imgXCenter + facB * b.imgXCenter;
imgYCenter = facA * a.imgYCenter + facB * b.imgYCenter;
scaleFac = facA * a.scaleFac + facB * b.scaleFac;
meanErr = facA * a.meanErr + facB * b.meanErr;
for (int i=0;i<5;i++) param[i]= facA * a.param[i] + facB * b.param[i];
}
void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY) {
// Mention that the Adobe technical paper has a bug here, the DMAX is handled differently for focLen and imgCenter
int Dmax=fullWidth; if (fullHeight>fullWidth) Dmax=fullHeight;
// correct focLens
if (focLenX<0) { // they may not be given
// and 35mm may not be given either
if (focalLength35mm<1) focalLength35mm = focalLength*sensorFormatFactor;
focLenX=focLenY=focalLength / ( 35*focalLength/focalLength35mm); // focLen must be calculated in pixels
}
if (swapXY) {
x0 = (mirrorX ? 1.-imgYCenter : imgYCenter) * fullWidth;
y0 = (mirrorY ? 1.-imgXCenter : imgXCenter) * fullHeight;
fx = focLenY * Dmax;
fy = focLenX * Dmax;
} else {
x0 = (mirrorX ? 1.-imgXCenter : imgXCenter) * fullWidth;
y0 = (mirrorY ? 1.-imgYCenter : imgYCenter) * fullHeight;
fx = focLenX * Dmax;
fy = focLenY * Dmax;
}
//printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter);
}
LCPPersModel::LCPPersModel() {
focLen=focDist=aperture=0;
}
// mode: 0=distortion, 1=vignette, 2=CA
bool LCPPersModel::hasModeData(int mode) const {
return (mode==0 && !vignette.empty() && !vignette.badErr) || (mode==1 && !base.empty() && !base.badErr)
|| (mode==2 && !chromRG.empty() && !chromG.empty() && !chromBG.empty() &&
!chromRG.badErr && !chromG.badErr && !chromBG.badErr);
}
void LCPPersModel::print() const {
printf("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture);
printf("Base:\n"); base.print();
if (!chromRG.empty()) { printf("ChromRG:\n"); chromRG.print(); }
if (!chromG.empty()) { printf("ChromG:\n"); chromG.print(); }
if (!chromBG.empty()) { printf("ChromBG:\n"); chromBG.print(); }
if (!vignette.empty()) { printf("Vignette:\n"); vignette.print(); }
printf("\n");
}
// if !vignette then geometric and CA
LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP,
int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg)
{
if (pProf==NULL) return;
useCADist=useCADistP;
// determine in what the image with the RAW landscape in comparison (calibration target)
// in vignetting, the rotation has not taken place yet
int rot = 0;
if (rawRotationDeg>=0) rot=(coarse.rotate+rawRotationDeg) % 360;
swapXY = (rot==90 || rot==270);
bool mirrorX = (rot==90 || rot==180);
bool mirrorY = (rot==180 || rot==270);
//printf("Vign: %i, fullWidth: %i/%i, focLen %g SwapXY: %i / MirX/Y %i / %i on rot:%i from %i\n",vignette, fullWidth, fullHeight, focalLength, swapXY, mirrorX, mirrorY, rot, rawRotationDeg);
pProf->calcParams(vignette?0:1, focalLength, focusDist, aperture, &mc, NULL, NULL);
mc.prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY);
if (!vignette) {
pProf->calcParams(2, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]);
for (int i=0;i<3;i++) chrom[i].prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY);
}
enableCA = !vignette && focusDist>0;
}
void LCPMapper::correctDistortion(double& x, double& y) const {
double xd=(x-mc.x0)/mc.fx, yd=(y-mc.y0)/mc.fy;
const float* aDist = mc.param;
double rsqr = xd*xd+yd*yd;
double xfac=aDist[swapXY?3:4], yfac=aDist[swapXY?4:3];
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;
x = xnew * mc.fx + mc.x0;
y = ynew * mc.fy + mc.y0;
}
void LCPMapper::correctCA(double& x, double& y, int channel) const {
if (!enableCA) return;
double rsqr, xgreen, ygreen;
// First calc the green channel like normal distortion
// the other are just deviations from it
double xd=(x-chrom[1].x0)/chrom[1].fx, yd=(y-chrom[1].y0)/chrom[1].fy;
// Green contains main distortion, just like base
if (useCADist) {
const float* aDist = chrom[1].param;
double rsqr = xd*xd+yd*yd;
double xfac=aDist[swapXY?3:4], yfac=aDist[swapXY?4:3];
double commonFac = (((aDist[2]*rsqr + aDist[1])*rsqr + aDist[0])*rsqr + 1.)
+ 2. * (yfac * yd + xfac * xd);
xgreen = xd * commonFac + aDist[4] * rsqr;
ygreen = yd * commonFac + aDist[3] * rsqr;
} else {
xgreen=xd; ygreen=yd;
}
if (channel==1) {
// green goes directly
x = xgreen * chrom[1].fx + chrom[1].x0;
y = ygreen * chrom[1].fy + chrom[1].y0;
} else {
// others are diffs from green
xd=xgreen; yd=ygreen;
rsqr=xd*xd+yd*yd;
const float* aCA =chrom[channel].param;
double xfac=aCA[swapXY?3:4], yfac=aCA[swapXY?4:3];
double commonSum = 1. + rsqr * (aCA[0] + rsqr * (aCA[1] + aCA[2]*rsqr)) + 2. * (yfac*yd + xfac*xd);
x = (chrom[channel].scaleFac * ( xd * commonSum + xfac*rsqr )) * chrom[channel].fx + chrom[channel].x0;
y = (chrom[channel].scaleFac * ( yd * commonSum + yfac*rsqr )) * chrom[channel].fy + chrom[channel].y0;
}
}
float LCPMapper::calcVignetteFac(int x, int y) const {
// No need for swapXY, since vignette is in RAW and always before rotation
double xd=((double)x-mc.x0)/mc.fx, yd=((double)y-mc.y0)/mc.fy;
const float* aVig= mc.param;
double rsqr = xd*xd+yd*yd;
double param0Sqr = aVig[0]*aVig[0];
return 1. + rsqr * (-aVig[0] + rsqr * ((param0Sqr - aVig[1])
- (param0Sqr*aVig[0] - 2.*aVig[0]*aVig[1] + aVig[2]) *rsqr
+ (param0Sqr*param0Sqr + aVig[1]*aVig[1]
+ 2.*aVig[0]*aVig[2] - 3.*param0Sqr*aVig[1]) *rsqr*rsqr));
}
LCPProfile::LCPProfile(Glib::ustring fname) {
const int BufferSize=8192;
char buf[BufferSize];
XML_Parser parser = XML_ParserCreate(NULL);
if (!parser) throw "Couldn't allocate memory for XML parser";
XML_SetElementHandler(parser, XmlStartHandler, XmlEndHandler);
XML_SetCharacterDataHandler(parser, XmlTextHandler);
XML_SetUserData(parser, (void *)this);
isFisheye=inCamProfiles=firstLIDone=inPerspect=inAlternateLensID=inAlternateLensNames=false;
sensorFormatFactor=1;
for (int i=0;i<MaxPersModelCount;i++) aPersModel[i]=NULL;
persModelCount=0; *inInvalidTag=0;
FILE *pFile = safe_g_fopen(fname, "rb");
bool done;
do {
int bytesRead = (int)fread(buf, 1, BufferSize, pFile);
done=feof(pFile);
if (XML_Parse(parser, buf, bytesRead, done) == XML_STATUS_ERROR)
throw "Invalid XML in LCP file";
} while (!done);
fclose(pFile);
XML_ParserFree(parser);
//printf("Parsing %s\n", fname.c_str());
// Two phase filter: first filter out the very rough ones, that distord the average a lot
// force it, even if there are few frames (community profiles)
filterBadFrames(2.0, 0);
// from the non-distorded, filter again on new average basis, but only if there are enough frames left
filterBadFrames(1.5, 100);
}
// from all frames not marked as bad already, take average and filter out frames with higher deviation than this if there are enough values
int LCPProfile::filterBadFrames(double maxAvgDevFac, int minFramesLeft) {
// take average error per type, then calculated the maximum deviation allowed
double errBase=0, errChrom=0, errVignette=0;
int baseCount=0, chromCount=0, vignetteCount=0;
for (int pm=0;pm<MaxPersModelCount && aPersModel[pm];pm++) {
if (aPersModel[pm]->hasModeData(0)) {
errVignette+=aPersModel[pm]->vignette.meanErr;
vignetteCount++;
}
if (aPersModel[pm]->hasModeData(1)) {
errBase+=aPersModel[pm]->base.meanErr;
baseCount++;
}
if (aPersModel[pm]->hasModeData(2)) {
errChrom+=std::max(std::max(aPersModel[pm]->chromRG.meanErr,aPersModel[pm]->chromG.meanErr),aPersModel[pm]->chromBG.meanErr);
chromCount++;
}
}
// Only if we have enough frames, filter out errors
int filtered=0;
if (baseCount+chromCount+vignetteCount>=minFramesLeft) {
if (baseCount>0) errBase/=(double)baseCount;
if (chromCount>0) errChrom/=(double)chromCount;
if (vignetteCount>0) errVignette/=(double)vignetteCount;
// Now mark all the bad ones as bad, and hasModeData will return false;
for (int pm=0;pm<MaxPersModelCount && aPersModel[pm];pm++) {
if (aPersModel[pm]->hasModeData(0) && aPersModel[pm]->vignette.meanErr > maxAvgDevFac * errVignette) {
aPersModel[pm]->vignette.badErr=true;
filtered++;
}
if (aPersModel[pm]->hasModeData(1) && aPersModel[pm]->base.meanErr > maxAvgDevFac * errBase) {
aPersModel[pm]->base.badErr=true;
filtered++;
}
if (aPersModel[pm]->hasModeData(2) &&
(aPersModel[pm]->chromRG.meanErr > maxAvgDevFac * errChrom || aPersModel[pm]->chromG.meanErr > maxAvgDevFac * errChrom
|| aPersModel[pm]->chromBG.meanErr > maxAvgDevFac * errChrom)) {
aPersModel[pm]->chromRG.badErr=aPersModel[pm]->chromG.badErr=aPersModel[pm]->chromBG.badErr=true;
filtered++;
}
}
//printf("Filtered %.1f%% frames for maxAvgDevFac %g leaving %i\n", filtered*100./(baseCount+chromCount+vignetteCount), maxAvgDevFac, baseCount+chromCount+vignetteCount-filtered);
}
return filtered;
}
// mode: 0=vignette, 1=distortion, 2=CA
void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const {
float euler=exp(1.0);
// find the frames with the least distance, focal length wise
LCPPersModel *pLow=NULL, *pHigh=NULL;
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==NULL || f > pLow->focLen || (focusDist==0 && f==pLow->focLen && pLow->focDist>aPersModel[pm]->focDist))) {
pLow=aPersModel[pm];
}
if (f >= focalLength && (pHigh==NULL || f < pHigh->focLen || (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)) {
if (mode==0) {
meanErr=aPersModel[pm]->vignette.meanErr;
// by aperture (vignette), and max out focus distance
// tests showed doing this by log(aperture) is not as advisable
if (aPersModel[pm]->focLen==bestFocLenLow && (
(aper==aperture && pLow->vignette.meanErr>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 && pHigh->vignette.meanErr>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 {
meanErr = (mode==1 ? aPersModel[pm]->base.meanErr : aPersModel[pm]->chromG.meanErr);
if (focusDist>0) {
// by focus distance
if (aPersModel[pm]->focLen==bestFocLenLow && (
(focDist==focusDist && (mode==1 ? pLow->base.meanErr : pLow->chromG.meanErr)>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 && (mode==1 ? pHigh->base.meanErr : pHigh->chromG.meanErr)>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 && (mode==1 ? pLow->base.meanErr : pLow->chromG.meanErr)>meanErr) {
pLow=aPersModel[pm];
}
if (aPersModel[pm]->focLen==bestFocLenHigh && (mode==1 ? pHigh->base.meanErr : pHigh->chromG.meanErr)>meanErr) {
pHigh=aPersModel[pm];
}
}
}
}
}
}
if (pLow!=NULL && pHigh!=NULL) {
// average out the factors, linear interpolation in logarithmic scale
float facLow=0.5;
bool focLenOnSpot=false; // pretty often, since max/min are often as frames in LCP
// There is as foclen range, take that as basis
if (pLow->focLen < pHigh->focLen) {
facLow = (log(pHigh->focLen)-focalLengthLog) / (log(pHigh->focLen) - log(pLow->focLen));
} else {
focLenOnSpot=pLow->focLen==pHigh->focLen && pLow->focLen==focalLength;
}
// and average the other factor if available
if (mode==0 && pLow->aperture < aperture && pHigh->aperture > aperture) {
// Mix in aperture
float facAperLow = (pHigh->aperture - aperture) / (pHigh->aperture - pLow->aperture);
facLow = focLenOnSpot ? facAperLow : (0.5*facLow + 0.5*facAperLow);
} else if (mode!=0 && focusDist>0 && pLow->focDist < focusDist && pHigh->focDist > focusDist) {
// focus distance for all else (if focus distance is given)
float facDistLow = (log(pHigh->focDist)+euler - focusDistLog) / (log(pHigh->focDist) - log(pLow->focDist));
facLow = focLenOnSpot ? facDistLow : (0.8*facLow + 0.2*facDistLow);
}
switch (mode) {
case 0: // vignette
pCorr1->merge(pLow->vignette, pHigh->vignette, facLow);
break;
case 1: // distortion
pCorr1->merge(pLow->base, pHigh->base, facLow);
break;
case 2: // CA
pCorr1->merge(pLow->chromRG, pHigh->chromRG, facLow);
pCorr2->merge(pLow->chromG, pHigh->chromG, facLow);
pCorr3->merge(pLow->chromBG, pHigh->chromBG, facLow);
break;
}
//printf("LCP mode=%i, dist: %g found frames: Fno %g-%g; FocLen %g-%g; Dist %g-%g with weight %g\n", mode, focusDist, pLow->aperture, pHigh->aperture, pLow->focLen, pHigh->focLen, pLow->focDist, pHigh->focDist, facLow);
} else printf("Error: LCP file contained no %s parameters\n",mode==0 ? "vignette" : mode == 1 ? "distortion" : "CA" );
}
void LCPProfile::print() const {
printf("=== Profile %s\n", profileName.c_str());
printf("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n",persModelCount,isRaw,isFisheye,sensorFormatFactor);
for (int pm=0;pm<persModelCount;pm++) aPersModel[pm]->print();
}
void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, const char **attr) {
LCPProfile *pProf=static_cast<LCPProfile*>(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==NULL) src=const_cast<char*>(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!=NULL) {
for (int i = 0; attr[i]; i += 2) {
const char* nameStart=strrchr(attr[i],':');
if (nameStart==NULL) nameStart=const_cast<char*>(attr[i]); else nameStart++;
strcpy(pProf->lastTag, nameStart);
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<LCPProfile*>(pLCPProfile);
if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames || *pProf->inInvalidTag) return;
// Check if it contains non-whitespaces (there are several calls to this for one tag unfortunately)
bool onlyWhiteSpace=true;
int i=0;
while (i<len && onlyWhiteSpace) { onlyWhiteSpace=isspace(s[i]); i++; }
if (onlyWhiteSpace) return;
// convert to null terminated
char raw[len+1]; memcpy(raw,s,len); raw[len]=0;
char* tag=pProf->lastTag;
// 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) {
char* p=raw;
while (*p) {
if (*p=='.') *p=',';
p++;
}
}
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->focLenX=atof(raw);
else if (!strcmp("FocalLengthY",tag))
pProf->pCurCommon->focLenY=atof(raw);
else if (!strcmp("ImageXCenter",tag))
pProf->pCurCommon->imgXCenter=atof(raw);
else if (!strcmp("ImageYCenter",tag))
pProf->pCurCommon->imgYCenter=atof(raw);
else if (!strcmp("ScaleFactor",tag))
pProf->pCurCommon->scaleFac=atof(raw);
else if (!strcmp("ResidualMeanError",tag))
pProf->pCurCommon->meanErr=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<LCPProfile*>(pLCPProfile);
// 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=NULL;
pProf->persModelCount++;
}
}
// Generates as singleton
LCPStore* LCPStore::getInstance()
{
static LCPStore* instance_ = 0;
if ( instance_ == 0 )
{
static MyMutex smutex_;
MyMutex::MyLock lock(smutex_);
if ( instance_ == 0 )
{
instance_ = new LCPStore();
}
}
return instance_;
}
LCPProfile* LCPStore::getProfile (Glib::ustring filename) {
if (filename.length()==0 || !isValidLCPFileName(filename)) return NULL;
MyMutex::MyLock lock(mtx);
std::map<Glib::ustring, LCPProfile*>::iterator r = profileCache.find (filename);
if (r!=profileCache.end()) return r->second;
// Add profile (if exists)
profileCache[filename]=new LCPProfile(filename);
//profileCache[filename]->print();
return profileCache[filename];
}
bool LCPStore::isValidLCPFileName(Glib::ustring filename) const {
if (!safe_file_test (filename, Glib::FILE_TEST_EXISTS) || safe_file_test (filename, Glib::FILE_TEST_IS_DIR)) return false;
size_t pos=filename.find_last_of ('.');
return pos>0 && !filename.casefold().compare (pos, 4, ".lcp");
}
Glib::ustring LCPStore::getDefaultCommonDirectory() const {
Glib::ustring dir;
#ifdef WIN32
WCHAR pathW[MAX_PATH]={0}; char pathA[MAX_PATH];
if (SHGetSpecialFolderPathW(NULL,pathW,CSIDL_COMMON_APPDATA,false)) {
char pathA[MAX_PATH];
WideCharToMultiByte(CP_UTF8,0,pathW,-1,pathA,MAX_PATH,0,0);
Glib::ustring fullDir=Glib::ustring(pathA)+Glib::ustring("\\Adobe\\CameraRaw\\LensProfiles\\1.0");
if (safe_file_test(fullDir, Glib::FILE_TEST_IS_DIR)) dir=fullDir;
}
#endif
// TODO: Add Mac paths here
return dir;
}