/* * This file is part of RawTherapee. * * Copyright (c) 2012 Oliver Duis * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RawTherapee is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ #include #include "lcp.h" #include "safegtk.h" #include "iccmatrices.h" #include "iccstore.h" #include "rawimagesource.h" #include "improcfun.h" #include "rt_math.h" #ifdef WIN32 #include // for GCC32 #ifndef _WIN32_IE #define _WIN32_IE 0x0600 #endif #include #endif using namespace std; using namespace rtengine; using namespace rtexif; LCPModelCommon::LCPModelCommon() { focLenX = focLenY = -1; imgXCenter = imgYCenter = 0.5; 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(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(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(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(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(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_; return &instance_; } LCPProfile* LCPStore::getProfile (Glib::ustring filename) { if (filename.length() == 0 || !isValidLCPFileName(filename)) { return NULL; } 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); //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; }