diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 78a4e2d3d..0a80db39e 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -305,7 +305,7 @@ HISTORY_MSG_81;Größe ändern HISTORY_MSG_82;Profil geändert HISTORY_MSG_83;Schatten/Lichter\nHohe Qualität HISTORY_MSG_84;Perspektive - Korrektur -HISTORY_MSG_85;Wavelet Koeffizienten +HISTORY_MSG_85;Linsenkorrektur-Profil HISTORY_MSG_86;Wavelet-Mixer HISTORY_MSG_87;Impulsrauschminderung HISTORY_MSG_88;Impulsrauschminderung\nSchwellenwert diff --git a/rtdata/languages/default b/rtdata/languages/default index 4ec201921..99a1e9929 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -305,7 +305,7 @@ HISTORY_MSG_81;Resize Enabled HISTORY_MSG_82;Profile Changed HISTORY_MSG_83;High Quality Shadows/Highlights HISTORY_MSG_84;Perspective Correction -HISTORY_MSG_85;Wavelet Coefficients +HISTORY_MSG_85;Lens Correction Profile HISTORY_MSG_86;Wavelet Equalizer HISTORY_MSG_87;Impulse Noise Reduction HISTORY_MSG_88;Impulse NR Threshold diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 12132db5b..eedd8fdc1 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -112,7 +112,7 @@ void Crop::update (int todo) { transCrop = new Imagefloat (cropw, croph); if ((todo & M_TRANSFORM) && needstransform) parent->ipf.transform (baseCrop, transCrop, cropx/skip, cropy/skip, trafx/skip, trafy/skip, SKIPS(parent->fw,skip), SKIPS(parent->fh,skip), - parent->imgsrc->getMetaData()->getFocalLen(), parent->imgsrc->getRotateDegree()); + parent->imgsrc->getMetaData()->getFocalLen(), parent->imgsrc->getMetaData()->getFocalLen35mm(), parent->imgsrc->getRotateDegree()); if (transCrop) baseCrop = transCrop; diff --git a/rtengine/imagedata.cc b/rtengine/imagedata.cc index 869fd0775..b79c227c9 100644 --- a/rtengine/imagedata.cc +++ b/rtengine/imagedata.cc @@ -108,7 +108,7 @@ void ImageData::extractInfo () { expcomp = 0; shutter = 0; aperture = 0; - focal_len = 0; + focal_len = focal_len35mm = 0; iso_speed = 0; memset (&time, 0, sizeof(time)); timeStamp = 0; @@ -169,6 +169,8 @@ void ImageData::extractInfo () { expcomp = exif->getTag ("ExposureBiasValue")->toDouble (); if (exif->getTag ("FocalLength")) focal_len = exif->getTag ("FocalLength")->toDouble (); + if (exif->getTag ("FocalLengthIn35mmFilm")) + focal_len35mm = exif->getTag ("FocalLengthIn35mmFilm")->toDouble (); if (exif->getTag ("ISOSpeedRatings")) iso_speed = exif->getTag ("ISOSpeedRatings")->toDouble (); if (exif->getTag ("DateTimeOriginal")) { diff --git a/rtengine/imagedata.h b/rtengine/imagedata.h index b414864cb..02dc63cee 100644 --- a/rtengine/imagedata.h +++ b/rtengine/imagedata.h @@ -40,7 +40,7 @@ class ImageData : public ImageMetaData { time_t timeStamp; int iso_speed; double aperture; - double focal_len; + double focal_len, focal_len35mm; double shutter; double expcomp; std::string make, model, serial; @@ -65,6 +65,7 @@ class ImageData : public ImageMetaData { int getISOSpeed () const { return iso_speed; } double getFNumber () const { return aperture; } double getFocalLen () const { return focal_len; } + double getFocalLen35mm () const { return focal_len35mm; } double getShutterSpeed () const { return shutter; } double getExpComp () const { return expcomp; } std::string getMake () const { return make; } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index d53361f7e..05735b497 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -193,7 +193,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) { if (needstransform && orig_prev==oprevi) oprevi = new Imagefloat (pW, pH); if ((todo & M_TRANSFORM) && needstransform) - ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, imgsrc->getMetaData()->getFocalLen(), imgsrc->getRotateDegree()); + ipf.transform (orig_prev, oprevi, 0, 0, 0, 0, pW, pH, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getRotateDegree()); readyphase++; diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 07278834a..77a37de66 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -123,7 +123,7 @@ class ImProcFunctions { void colorCurve (LabImage* lold, LabImage* lnew); void sharpening (LabImage* lab, float** buffer); void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, - double focalLen, int rawRotationDeg); + double focalLen, double focalLen35mm, int rawRotationDeg); void lab2monitorRgb (LabImage* lab, Image8* image); void resize (Image16* src, Image16* dst, float dScale); void deconvsharpening (LabImage* lab, float** buffer); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index b0c5ba6d8..4556222e0 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -177,11 +177,11 @@ bool ImProcFunctions::transCoord (int W, int H, int x, int y, int w, int h, int& } void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, - double focalLen, int rawRotationDeg) { + double focalLen, double focalLen35mm, int rawRotationDeg) { LCPMapper *pLCPMap=NULL; if (needsLCP() && focalLen>0) { LCPProfile *pLCPProf=lcpStore->getProfile(params->lensProf.lcpFile); - if (pLCPProf) pLCPMap=new LCPMapper(pLCPProf, focalLen, false, original->width, original->height, params->coarse, rawRotationDeg); + if (pLCPProf) pLCPMap=new LCPMapper(pLCPProf, focalLen, focalLen35mm, 0, false, original->width, original->height, params->coarse, rawRotationDeg); } if (!(needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP()) && needsVignetting()) @@ -194,6 +194,8 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, } else transformSep (original, transformed, cx, cy, sx, sy, oW, oH, pLCPMap); + + if (pLCPMap) delete pLCPMap; } void calcVignettingParams(int oW, int oH, const VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul) diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index e03d8e8d6..b7665775a 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -48,7 +48,7 @@ LCPModelCommon::LCPModelCommon() { } bool LCPModelCommon::empty() const { - return focLenX<0 && focLenY<0; + return param[0]==0 && param[1]==0 && param[2]==0; } void LCPModelCommon::print() const { @@ -71,43 +71,53 @@ void LCPPersModel::print() const { } // if !vignette then geometric -LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, bool vignette, int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg) +LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float aperture, bool vignette, int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg) { if (pProf==NULL) return; - pProf-> calcBasePerspectiveParams(focalLength, vignette, mc); - + pProf-> calcParams(focalLength, aperture, vignette, mc); + // determine in what the image with the RAW landscape in comparison (calibration target) int rot = (coarse.rotate+rawRotationDeg) % 360; - swapXY = (rot==90 || rot==270); - mirrorX = (rot==90 || rot==180); - mirrorY = (rot==180 || rot==270); + bool swapXY = (rot==90 || rot==270); + bool mirrorX = (rot==90 || rot==180); + bool mirrorY = (rot==180 || rot==270); // Mention that the Adobe technical paper has a bug here, the DMAX is handled differently for focLen and imgCenter int Dmax=fullWidth; if (fullHeight>fullWidth) Dmax=fullHeight; - + + // correct focLens + float focLenX=mc.focLenX; float focLenY=mc.focLenY; + if (focLenX<0) { // they may not be given + // and 35mm may not be given either + if (focalLength35mm<1) focalLength35mm = focalLength*pProf->sensorFormatFactor; + + focLenX=focLenY=focalLength / ( 35*focalLength/focalLength35mm); // focLen must be calculated in pixels + } + if (swapXY) { x0 = (mirrorX ? 1-mc.imgYCenter : mc.imgYCenter) * fullWidth; y0 = (mirrorY ? 1-mc.imgXCenter : mc.imgXCenter) * fullHeight; - fx = mc.focLenY * Dmax; - fy = mc.focLenX * Dmax; + fx = focLenY * Dmax; + fy = focLenX * Dmax; } else { x0 = (mirrorX ? 1-mc.imgXCenter : mc.imgXCenter) * fullWidth; y0 = (mirrorY ? 1-mc.imgYCenter : mc.imgYCenter) * fullHeight; - fx = mc.focLenX * Dmax; - fy = mc.focLenY * Dmax; + fx = focLenX * Dmax; + fy = focLenY * Dmax; } -} + //printf("\nMapping Dmax=%i f=%g/f35=%g vignette=%i using\n",Dmax, focalLength, focalLength35mm, vignette); + //mc.print(); +} void LCPMapper::correctDistortion(double& x, double& y) const { double xd=(x-x0)/fx, yd=(y-y0)/fy; double rsqr = xd*xd+yd*yd; - double rsqrrsqr = rsqr*rsqr; // speed - double commonFac = (mc.param[2]*rsqrrsqr*rsqr + mc.param[1]*rsqrrsqr + mc.param[0]*rsqr + 1.) - + 2 * (mc.param[3] * yd + mc.param[4] * xd); + double commonFac = (((mc.param[2]*rsqr + mc.param[1])*rsqr + mc.param[0])*rsqr + 1.) + + 2. * (mc.param[3] * yd + mc.param[4] * xd); double xnew = xd * commonFac + mc.param[4] * rsqr; double ynew = yd * commonFac + mc.param[3] * rsqr; @@ -116,20 +126,18 @@ void LCPMapper::correctDistortion(double& x, double& y) const { y = ynew * fy + y0; } -float LCPMapper::correctVignette(double x, double y) const { - double xd=(x-x0)/fx, yd=(y-y0)/fy; +float LCPMapper::correctVignette(int x, int y) const { + double xd=((double)x-x0)/fx, yd=((double)y-y0)/fy; double rsqr = xd*xd+yd*yd; - double rsqrrsqr = rsqr*rsqr; // speed double param0Sqr = mc.param[0]*mc.param[0]; - return 1. - mc.param[0]*rsqr + (param0Sqr - mc.param[1]) *rsqrrsqr - - (param0Sqr*mc.param[0] - 2*mc.param[0]*mc.param[1] + mc.param[2]) *rsqrrsqr*rsqr + return 1. + rsqr * (-mc.param[0] + rsqr * ((param0Sqr - mc.param[1]) + - (param0Sqr*mc.param[0] - 2.*mc.param[0]*mc.param[1] + mc.param[2]) *rsqr + (param0Sqr*param0Sqr + mc.param[1]*mc.param[1] - + 2*mc.param[0]*mc.param[2] - 3*mc.param[0]*mc.param[0]*mc.param[1]) *rsqrrsqr*rsqrrsqr; + + 2.*mc.param[0]*mc.param[2] - 3.*param0Sqr*mc.param[1]) *rsqr*rsqr)); } - LCPProfile::LCPProfile(Glib::ustring fname) { const int BufferSize=8192; char buf[BufferSize]; @@ -142,7 +150,10 @@ LCPProfile::LCPProfile(Glib::ustring fname) { XML_SetUserData(parser, (void *)this); - isFisheye=inCamProfiles=firstLIDone=inPerspect=false; + isFisheye=inCamProfiles=firstLIDone=inPerspect=inAlternateLensID=false; + sensorFormatFactor=1; + for (int i=0;i<2000;i++) aPersModel[i]=NULL; + persModelCount=0; FILE *pFile = safe_g_fopen(fname, "rb"); @@ -157,53 +168,92 @@ LCPProfile::LCPProfile(Glib::ustring fname) { XML_ParserFree(parser); } - -void LCPProfile::calcBasePerspectiveParams(float focalLength, bool vignette, LCPModelCommon& corr) { +void LCPProfile::calcParams(float focalLength, float aperture, bool vignette, LCPModelCommon& corr) const { // find the frames with the least distance, focal length wise LCPPersModel *pLow=NULL, *pHigh=NULL; - std::list::const_iterator it; - for (it=persModels.begin(); it!=persModels.end(); ++it) { - float f=(*it)->focLen; + float focalLengthLog=log(focalLength), apertureLog=aperture>0 ? log(aperture) : 0; - if ((!vignette && !(*it)->base.empty()) || (vignette && !(*it)->vignette.empty())) { - if (f <= focalLength && (pLow ==NULL || f > pLow->focLen)) pLow= (*it); - if (f >= focalLength && (pHigh==NULL || f < pHigh->focLen)) pHigh=(*it); + // Pass 1: determining best focal length, if possible different apertures + for (int pm=0;pmfocLen; + + if ((!vignette && !aPersModel[pm]->base.empty()) || (vignette && !aPersModel[pm]->vignette.empty())) { + if (f <= focalLength && (pLow==NULL || f > pLow->focLen || (f==pLow->focLen && pLow->aperture>aPersModel[pm]->aperture))) { + pLow=aPersModel[pm]; + } + if (f >= focalLength && (pHigh==NULL || f < pHigh->focLen || (f==pHigh->focLen && pHigh->apertureaperture))) { + pHigh=aPersModel[pm]; + } } } - if (!pLow) pLow=pHigh; if (!pHigh) pHigh=pLow; + if (!pLow) + pLow=pHigh; + else if (!pHigh) + pHigh=pLow; + else if (vignette) { + // We have some, so take the best aperture for vignette (unimportant for distortion) + float bestFocLenLow=pLow->focLen, bestFocLenHigh=pHigh->focLen; - // average out the factors, linear interpolation - float facLow = 0, facHigh=0; - if (pLow && pHigh && pLow->focLen < pHigh->focLen) { - facLow = (pHigh->focLen-focalLength) / (pHigh->focLen-pLow->focLen); - facHigh = (focalLength-pLow->focLen) / (pHigh->focLen-pLow->focLen); - } else { - facLow=pHigh?0:1; facHigh=pHigh?1:0; + for (int pm=0;pmaperture); + + if ((!vignette && !aPersModel[pm]->base.empty()) || (vignette && !aPersModel[pm]->vignette.empty())) { + if (fabs(aPersModel[pm]->focLen-bestFocLenLow)<0.01 && ((aperLog<=apertureLog && pLow->aperture>aperture) + || (aperLog<=apertureLog && fabs(apertureLog-aperLog)aperture))))) { + pLow=aPersModel[pm]; + } + if (fabs(aPersModel[pm]->focLen-bestFocLenHigh)<0.01 && ((aperLog>=apertureLog && pHigh->aperture=apertureLog && fabs(apertureLog-aperLog)aperture))))) { + pHigh=aPersModel[pm]; + } + } + } } - LCPModelCommon& mLow =(vignette?pLow->vignette :pLow->base); - LCPModelCommon& mHigh=(vignette?pHigh->vignette:pHigh->base); + if (pLow!=NULL && pHigh!=NULL) { + // average out the factors, linear interpolation in logarithmic scale + float facLow=0, facHigh=0; - corr.focLenX = facLow * mLow.focLenX + facHigh * mHigh.focLenX; - corr.focLenY = facLow * mLow.focLenY + facHigh * mHigh.focLenY; - corr.imgXCenter = facLow * mLow.imgXCenter + facHigh * mHigh.imgXCenter; - corr.imgYCenter = facLow * mLow.imgYCenter + facHigh * mHigh.imgYCenter; - corr.scaleFac = facLow * mLow.scaleFac + facHigh * mHigh.scaleFac; + // There is as foclen range, take that as basis + if (pLow->focLen < pHigh->focLen) { + float diff = log(pHigh->focLen) - log(pLow->focLen); + facLow = (log(pHigh->focLen)-focalLengthLog) / diff; + facHigh = (focalLengthLog-log(pLow->focLen)) / diff; + } else if (pLow->aperture < aperture && pHigh->aperture > aperture) { + // FocLen is the same, take aperture (espc. used for vignetting) + float diff = log(pHigh->aperture) - log(pLow->aperture); + facLow = (log(pHigh->aperture)-apertureLog) / diff; + facHigh = (apertureLog-log(pLow->aperture)) / diff; + } else { + facLow=facHigh=0.5; + } + + LCPModelCommon& mLow =(vignette?pLow->vignette :pLow->base); + LCPModelCommon& mHigh=(vignette?pHigh->vignette:pHigh->base); - for (int i=0;i<5;i++) corr.param[i]= facLow * mLow.param[i] + facHigh * mHigh.param[i]; + corr.focLenX = facLow * mLow.focLenX + facHigh * mHigh.focLenX; + corr.focLenY = facLow * mLow.focLenY + facHigh * mHigh.focLenY; + corr.imgXCenter = facLow * mLow.imgXCenter + facHigh * mHigh.imgXCenter; + corr.imgYCenter = facLow * mLow.imgYCenter + facHigh * mHigh.imgYCenter; + corr.scaleFac = facLow * mLow.scaleFac + facHigh * mHigh.scaleFac; + + for (int i=0;i<5;i++) corr.param[i]= facLow * mLow.param[i] + facHigh * mHigh.param[i]; + + //printf("LCP ( %i frames) vignette=%i for Fno %g - %g; FocLen %g - %g with fac %g - %g:\n", persModelCount, vignette, pLow->aperture, pHigh->aperture, pLow->focLen, pHigh->focLen, facLow, facHigh); + } else printf("Error: LCP file contained no parameters\n"); } void LCPProfile::print() const { printf("=== Profile %s\n", profileName.c_str()); - printf("RAW: %i; Fisheye: %i\n",isRaw,isFisheye); - std::list::const_iterator it; - for (it=persModels.begin(); it!=persModels.end(); ++it) (*it)->print(); + printf("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n",persModelCount,isRaw,isFisheye,sensorFormatFactor); + for (int pm=0;pmprint(); } void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, const char **attr) { LCPProfile *pProf=static_cast(pLCPProfile); + bool parseAttr=false; // clean up tagname const char* src=strrchr(el,':'); @@ -212,7 +262,8 @@ void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, cons strcpy(pProf->lastTag,src); if (!strcmp("CameraProfiles",src)) pProf->inCamProfiles=true; - if (!pProf->inCamProfiles) return; + if (!strcmp("AlternateLensIDs",src)) pProf->inAlternateLensID=true; + if (!pProf->inCamProfiles || pProf->inAlternateLensID) return; if (!strcmp("li",src)) { pProf->pCurPersModel=new LCPPersModel(); @@ -227,25 +278,42 @@ void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, cons 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)) + if (!strcmp("ChromaticRedGreenModel",src)) { pProf->pCurCommon=&pProf->pCurPersModel->chromRG; - else if (!strcmp("ChromaticGreenModel",src)) + parseAttr=true; + } else if (!strcmp("ChromaticGreenModel",src)) { pProf->pCurCommon=&pProf->pCurPersModel->chromG; - else if (!strcmp("ChromaticBlueGreenModel",src)) + parseAttr=true; + } else if (!strcmp("ChromaticBlueGreenModel",src)) { pProf->pCurCommon=&pProf->pCurPersModel->chromBG; - else if (!strcmp("VignetteModel",src)) + 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) return; + if (!pProf->inCamProfiles || pProf->inAlternateLensID) return; // Check if it contains non-whitespaces (there are several calls to this for one tag unfortunately) bool onlyWhiteSpace=true; @@ -277,12 +345,19 @@ void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, in } // --- Now all floating points. Must replace local dot characters - struct lconv * lc=localeconv(); + // 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++; + } + } - char* p=raw; - while (*p) { - if (*p=='.') *p=lc->decimal_point[0]; - p++; + if (!pProf->firstLIDone) { + if (!strcmp("SensorFormatFactor",tag)) + pProf->sensorFormatFactor=atof(raw); } // Perspective model base data @@ -320,14 +395,16 @@ void XMLCALL LCPProfile::XmlEndHandler(void *pLCPProfile, const char *el) { LCPProfile *pProf=static_cast(pLCPProfile); if (strstr(el,":CameraProfiles")) pProf->inCamProfiles=false; + if (strstr(el,":AlternateLensIDs")) pProf->inAlternateLensID=false; - if (!pProf->inCamProfiles) return; + if (!pProf->inCamProfiles || pProf->inAlternateLensID) return; if (strstr(el,":PerspectiveModel") || strstr(el,":FisheyeModel")) pProf->inPerspect=false; else if (strstr(el, ":li")) { - pProf->persModels.push_back(pProf->pCurPersModel); + pProf->aPersModel[pProf->persModelCount]=pProf->pCurPersModel; pProf->pCurPersModel=NULL; + pProf->persModelCount++; } } @@ -348,27 +425,25 @@ LCPStore* LCPStore::getInstance() } LCPProfile* LCPStore::getProfile (Glib::ustring filename) { - if (filename.length()==0) return NULL; + if (filename.length()==0 || !isValidLCPFileName(filename)) return NULL; Glib::Mutex::Lock lock(mtx); std::map::iterator r = profileCache.find (filename); if (r!=profileCache.end()) return r->second; - // Add profile + // 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; @@ -381,9 +456,9 @@ Glib::ustring LCPStore::getDefaultCommonDirectory() const { Glib::ustring fullDir=Glib::ustring(pathA)+Glib::ustring("\\Adobe\\CameraRaw\\LensProfiles\\1.0"); if (safe_file_test(fullDir, Glib::FILE_TEST_IS_DIR)) dir=fullDir; } -#else - printf("Sorry, default LCP directory are currently only configured on Windows\n"); #endif + // TODO: Add Mac paths here + return dir; } \ No newline at end of file diff --git a/rtengine/lcp.h b/rtengine/lcp.h index abd8a3483..b50f71082 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -55,7 +55,7 @@ namespace rtengine { class LCPProfile { // Temporary data for parsing - bool inCamProfiles,firstLIDone,inPerspect; + bool inCamProfiles,firstLIDone,inPerspect,inAlternateLensID; char lastTag[256]; LCPPersModel* pCurPersModel; LCPModelCommon* pCurCommon; @@ -68,13 +68,15 @@ namespace rtengine { // Common data Glib::ustring profileName, lensPrettyName, cameraPrettyName, lens, camera; // lens/camera(=model) can be auto-matched with DNG bool isRaw,isFisheye; + float sensorFormatFactor; + int persModelCount; // The correction frames - std::list persModels; + LCPPersModel* aPersModel[2000]; // Do NOT use std::list or something, it's buggy in GCC! LCPProfile(Glib::ustring fname); - void calcBasePerspectiveParams(float focalLength, bool vignette, LCPModelCommon& corr); // Interpolates between the persModels frames + void calcParams(float focalLength, float aperture, bool vignette, LCPModelCommon& corr) const; // Interpolates between the persModels frames void print() const; }; @@ -82,7 +84,6 @@ namespace rtengine { class LCPStore { Glib::Mutex mtx; - // Maps file name to profile as cache std::map profileCache; @@ -101,16 +102,14 @@ namespace rtengine { class LCPMapper { double x0,y0,fx,fy; LCPModelCommon mc; - bool swapXY; - bool mirrorX, mirrorY; public: // precalculates the mapper. - LCPMapper(LCPProfile* pProf, float focalLength, bool vignette, int fullWidth, int fullHeight, + LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float aperture, bool vignette, int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg); void correctDistortion(double& x, double& y) const; // MUST be the first stage - float correctVignette (double x, double y) const; // MUST be in RAW + float correctVignette (int x, int y) const; // MUST be in RAW }; } #endif diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 7843eae23..253f74ebe 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1014,24 +1014,24 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } } + scaleColors( 0,0, W, H, raw);//+ + raw parameters for black level(raw.blackxx) + // Correct vignetting of lens profile if (!hasFlatField) { LCPProfile *pLCPProf=lcpStore->getProfile(lensProf.lcpFile); if (pLCPProf) { - LCPMapper *pLCPMap=new LCPMapper(pLCPProf, idata->getFocalLen(), true, W, H, coarse, ri->get_rotateDegree()); + LCPMapper map(pLCPProf, idata->getFocalLen(), idata->getFocalLen35mm(), idata->getFNumber(), true, W, H, coarse, ri->get_rotateDegree()); - #pragma omp parallel + #pragma omp parallel for for (int y=0; ycorrectVignette(x,y); + if (rawData[y][x]>0) rawData[y][x]*=map.correctVignette(x,y); } } } } - scaleColors( 0,0, W, H, raw);//+ + raw parameters for black level(raw.blackxx) - defGain = 0.0;//log(initialGain) / log(2.0); if ( raw.hotdeadpix_filt>0 ) { diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index aeb46f598..50f7a0038 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -69,6 +69,8 @@ namespace rtengine { virtual double getFNumber () const =0; /** @return the focal length used at the exposure */ virtual double getFocalLen () const =0; + /** @return the focal length in 35mm used at the exposure */ + virtual double getFocalLen35mm () const =0; /** @return the shutter speed */ virtual double getShutterSpeed () const =0; /** @return the exposure compensation */ diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 4dd5cc1f3..45547d403 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -576,7 +576,8 @@ 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& myscale) { +IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rheight, TypeInterpolation interp, std::string camName, + double focalLen, double focalLen35mm, double& myscale) { // compute WB multipliers ColorTemp currWB = ColorTemp (params.wb.temperature, params.wb.green, params.wb.method); @@ -698,7 +699,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei // perform transform if (ipf.needsTransform()) { Imagefloat* trImg = new Imagefloat (fw, fh); - ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, focalLen, 0); // Raw rotate degree not detectable here + ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, focalLen, focalLen35mm, 0); // Raw rotate degree not detectable here delete baseImg; baseImg = trImg; } diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h index fc6d080fa..a705c008d 100644 --- a/rtengine/rtthumbnail.h +++ b/rtengine/rtthumbnail.h @@ -70,7 +70,7 @@ namespace rtengine { static void cleanupGamma (); void init (); - IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, std::string camName, double focalLen, double& scale); + IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, std::string camName, double focalLen, double focalLen35mm, 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 0ea6a7f63..969194580 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -115,7 +115,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p // perform transform (excepted resizing) if (ipf.needsTransform()) { Imagefloat* trImg = new Imagefloat (fw, fh); - ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, imgsrc->getMetaData()->getFocalLen(), imgsrc->getRotateDegree()); + ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, imgsrc->getMetaData()->getFocalLen(), imgsrc->getMetaData()->getFocalLen35mm(), imgsrc->getRotateDegree()); delete baseImg; baseImg = trImg; } diff --git a/rtgui/cacheimagedata.cc b/rtgui/cacheimagedata.cc index d8118785b..6fa0b5828 100644 --- a/rtgui/cacheimagedata.cc +++ b/rtgui/cacheimagedata.cc @@ -68,6 +68,8 @@ int CacheImageData::load (const Glib::ustring& fname) { if (keyFile.has_key ("ExifInfo", "FNumber")) fnumber = keyFile.get_double ("ExifInfo", "FNumber"); if (keyFile.has_key ("ExifInfo", "Shutter")) shutter = keyFile.get_double ("ExifInfo", "Shutter"); if (keyFile.has_key ("ExifInfo", "FocalLen")) focalLen = keyFile.get_double ("ExifInfo", "FocalLen"); + if (keyFile.has_key ("ExifInfo", "FocalLen35mm")) focalLen35mm = keyFile.get_double ("ExifInfo", "FocalLen35mm"); + else focalLen35mm=focalLen; // prevent crashes on old files if (keyFile.has_key ("ExifInfo", "ISO")) iso = keyFile.get_integer ("ExifInfo", "ISO"); if (keyFile.has_key ("ExifInfo", "ExpComp")) expcomp = keyFile.get_string ("ExifInfo", "ExpComp"); } @@ -126,6 +128,7 @@ int CacheImageData::save (const Glib::ustring& fname) { keyFile.set_double ("ExifInfo", "FNumber", fnumber); keyFile.set_double ("ExifInfo", "Shutter", shutter); keyFile.set_double ("ExifInfo", "FocalLen", focalLen); + keyFile.set_double ("ExifInfo", "FocalLen35mm", focalLen35mm); keyFile.set_integer ("ExifInfo", "ISO", iso); keyFile.set_string ("ExifInfo", "ExpComp", expcomp); } diff --git a/rtgui/cacheimagedata.h b/rtgui/cacheimagedata.h index 5cf375cf7..5feeca1ea 100644 --- a/rtgui/cacheimagedata.h +++ b/rtgui/cacheimagedata.h @@ -49,7 +49,7 @@ class CacheImageData { bool exifValid; double fnumber; double shutter; - double focalLen; + double focalLen,focalLen35mm; unsigned iso; Glib::ustring lens; Glib::ustring camera; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 49478aafc..0620170f6 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -610,3 +610,7 @@ bool RAWParamsEdited::isUnchanged() const { && hotDeadPixelFilter && hotDeadPixelThresh && linenoise && darkFrame && dfAuto && ff_file && ff_AutoSelect && ff_BlurRadius && ff_BlurType && exPos && exPreser && exBlackzero && exBlackone && exBlacktwo && exBlackthree && exTwoGreen; } + +bool LensProfParamsEdited::isUnchanged() const { + return lcpFile; +} diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 7385284ce..f9d840820 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -249,6 +249,8 @@ class DistortionParamsEdited { class LensProfParamsEdited { public: bool lcpFile; + + bool isUnchanged() const; }; class PerspectiveParamsEdited { diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index ca3b7ed26..8f94fd392 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -468,7 +468,7 @@ rtengine::IImage8* Thumbnail::processThumbImage (const rtengine::procparams::Pro else { // Full thumbnail: apply profile - image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, cfs.focalLen, scale ); + image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, cfs.focalLen, cfs.focalLen35mm, scale ); } tpp->getDimensions(lastW,lastH,lastScale); @@ -493,7 +493,7 @@ rtengine::IImage8* Thumbnail::upgradeThumbImage (const rtengine::procparams::Pro return 0; } - rtengine::IImage8* image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, cfs.focalLen, scale ); + rtengine::IImage8* image = tpp->processImage (pparams, h, rtengine::TI_Bilinear, cfs.camera, cfs.focalLen, cfs.focalLen35mm, scale ); tpp->getDimensions(lastW,lastH,lastScale); delete tpp; @@ -572,6 +572,7 @@ int Thumbnail::infoFromImage (const Glib::ustring& fname, rtengine::RawMetaDataL cfs.shutter = idata->getShutterSpeed (); cfs.fnumber = idata->getFNumber (); cfs.focalLen = idata->getFocalLen (); + cfs.focalLen35mm = idata->getFocalLen35mm (); cfs.iso = idata->getISOSpeed (); cfs.expcomp = idata->expcompToString (idata->getExpComp(), false); // do not mask Zero expcomp cfs.year = 1900 + idata->getDateTime().tm_year; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 8d4364580..3822a4275 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -294,7 +294,7 @@ void ToolPanelCoordinator::profileChange (const PartialProfile *nparams, rtengi pe.set(true); pe.initFrom (lParams); - filterRawRefresh=pe.raw.isUnchanged(); + filterRawRefresh=pe.raw.isUnchanged() && pe.lensProf.isUnchanged(); } *params = *mergedParams;