Merge pull request #7100 from sgotti/lens_metadata_correction
Lens correction based on metadata (Sony, Fuji, Olympus, Panasonic, DNG)
This commit is contained in:
commit
11bd068394
@ -2837,6 +2837,7 @@ TP_LENSGEOM_FILL;Auto-fill
|
|||||||
TP_LENSGEOM_LABEL;Lens / Geometry
|
TP_LENSGEOM_LABEL;Lens / Geometry
|
||||||
TP_LENSGEOM_LIN;Linear
|
TP_LENSGEOM_LIN;Linear
|
||||||
TP_LENSGEOM_LOG;Logarithmic
|
TP_LENSGEOM_LOG;Logarithmic
|
||||||
|
TP_LENSPROFILE_CORRECTION_METADATA;From file metadata
|
||||||
TP_LENSPROFILE_CORRECTION_AUTOMATCH;Automatically selected
|
TP_LENSPROFILE_CORRECTION_AUTOMATCH;Automatically selected
|
||||||
TP_LENSPROFILE_CORRECTION_LCPFILE;LCP file
|
TP_LENSPROFILE_CORRECTION_LCPFILE;LCP file
|
||||||
TP_LENSPROFILE_CORRECTION_MANUAL;Manually selected
|
TP_LENSPROFILE_CORRECTION_MANUAL;Manually selected
|
||||||
|
@ -148,6 +148,7 @@ set(RTENGINESOURCEFILES
|
|||||||
jpeg_ijg/jpeg_memsrc.cc
|
jpeg_ijg/jpeg_memsrc.cc
|
||||||
labimage.cc
|
labimage.cc
|
||||||
lcp.cc
|
lcp.cc
|
||||||
|
lensmetadata.cc
|
||||||
lmmse_demosaic.cc
|
lmmse_demosaic.cc
|
||||||
loadinitial.cc
|
loadinitial.cc
|
||||||
metadata.cc
|
metadata.cc
|
||||||
|
@ -1850,7 +1850,7 @@ bool check_need_larger_crop_for_lcp_distortion(int fw, int fh, int x, int y, int
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (params.lensProf.useDist && (params.lensProf.useLensfun() || params.lensProf.useLcp()));
|
return (params.lensProf.useDist && (params.lensProf.useLensfun() || params.lensProf.useLcp() || params.lensProf.useMetadata()));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -565,6 +565,8 @@ FramesData::FramesData(const Glib::ustring &fname, time_t ts) :
|
|||||||
|
|
||||||
meta.getDimensions(w_, h_);
|
meta.getDimensions(w_, h_);
|
||||||
|
|
||||||
|
isDNG = find_exif_tag("Exif.Image.DNGVersion");
|
||||||
|
|
||||||
// -----------------------
|
// -----------------------
|
||||||
// Special file type detection (HDR, PixelShift)
|
// Special file type detection (HDR, PixelShift)
|
||||||
// ------------------------
|
// ------------------------
|
||||||
@ -778,6 +780,11 @@ bool FramesData::getHDR() const
|
|||||||
return isHDR;
|
return isHDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FramesData::getDNG() const
|
||||||
|
{
|
||||||
|
return isDNG;
|
||||||
|
}
|
||||||
|
|
||||||
std::string FramesData::getImageType() const
|
std::string FramesData::getImageType() const
|
||||||
{
|
{
|
||||||
return isPixelShift ? "PS" : isHDR ? "HDR" : "STD";
|
return isPixelShift ? "PS" : isHDR ? "HDR" : "STD";
|
||||||
|
@ -59,6 +59,7 @@ private:
|
|||||||
time_t modTimeStamp;
|
time_t modTimeStamp;
|
||||||
bool isPixelShift;
|
bool isPixelShift;
|
||||||
bool isHDR;
|
bool isHDR;
|
||||||
|
bool isDNG;
|
||||||
int w_;
|
int w_;
|
||||||
int h_;
|
int h_;
|
||||||
|
|
||||||
@ -69,6 +70,7 @@ public:
|
|||||||
unsigned int getFrameCount() const override;
|
unsigned int getFrameCount() const override;
|
||||||
bool getPixelShift() const override;
|
bool getPixelShift() const override;
|
||||||
bool getHDR() const override;
|
bool getHDR() const override;
|
||||||
|
bool getDNG() const override;
|
||||||
std::string getImageType() const override;
|
std::string getImageType() const override;
|
||||||
IIOSampleFormat getSampleFormat() const override;
|
IIOSampleFormat getSampleFormat() const override;
|
||||||
bool hasExif() const override;
|
bool hasExif() const override;
|
||||||
|
@ -130,6 +130,7 @@ class ImProcFunctions
|
|||||||
bool needsGradient() const;
|
bool needsGradient() const;
|
||||||
bool needsVignetting() const;
|
bool needsVignetting() const;
|
||||||
bool needsLCP() const;
|
bool needsLCP() const;
|
||||||
|
bool needsMetadata() const;
|
||||||
bool needsLensfun() const;
|
bool needsLensfun() const;
|
||||||
// static cmsUInt8Number* Mempro = NULL;
|
// static cmsUInt8Number* Mempro = NULL;
|
||||||
|
|
||||||
@ -155,7 +156,7 @@ enum class BlurType {
|
|||||||
~ImProcFunctions();
|
~ImProcFunctions();
|
||||||
bool needsLuminanceOnly() const
|
bool needsLuminanceOnly() const
|
||||||
{
|
{
|
||||||
return !(needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsLensfun()) && (needsVignetting() || needsPCVignetting() || needsGradient());
|
return !(needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsLensfun() || needsMetadata()) && (needsVignetting() || needsPCVignetting() || needsGradient());
|
||||||
}
|
}
|
||||||
void setScale(double iscale);
|
void setScale(double iscale);
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "rt_math.h"
|
#include "rt_math.h"
|
||||||
#include "rtengine.h"
|
#include "rtengine.h"
|
||||||
#include "rtlensfun.h"
|
#include "rtlensfun.h"
|
||||||
|
#include "lensmetadata.h"
|
||||||
#include "sleef.h"
|
#include "sleef.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -420,7 +421,6 @@ homogeneous::Matrix<double> perspectiveMatrix(double camera_focal_length, double
|
|||||||
bool ImProcFunctions::transCoord (int W, int H, const std::vector<Coord2D> &src, std::vector<Coord2D> &red, std::vector<Coord2D> &green, std::vector<Coord2D> &blue, double ascaleDef,
|
bool ImProcFunctions::transCoord (int W, int H, const std::vector<Coord2D> &src, std::vector<Coord2D> &red, std::vector<Coord2D> &green, std::vector<Coord2D> &blue, double ascaleDef,
|
||||||
const LensCorrection *pLCPMap) const
|
const LensCorrection *pLCPMap) const
|
||||||
{
|
{
|
||||||
|
|
||||||
enum PerspType { NONE, SIMPLE, CAMERA_BASED };
|
enum PerspType { NONE, SIMPLE, CAMERA_BASED };
|
||||||
const PerspType perspectiveType = needsPerspective() ? (
|
const PerspType perspectiveType = needsPerspective() ? (
|
||||||
(params->perspective.method == "camera_based") ?
|
(params->perspective.method == "camera_based") ?
|
||||||
@ -515,14 +515,14 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector<Coord2D> &src,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pLCPMap && params->lensProf.useDist) {
|
|
||||||
pLCPMap->correctDistortion(x_d, y_d, w2, h2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// rotate
|
// rotate
|
||||||
double Dx = x_d * cost - y_d * sint;
|
double Dx = x_d * cost - y_d * sint;
|
||||||
double Dy = x_d * sint + y_d * cost;
|
double Dy = x_d * sint + y_d * cost;
|
||||||
|
|
||||||
|
if (pLCPMap && params->lensProf.useDist && pLCPMap->hasDistortionCorrection()) {
|
||||||
|
pLCPMap->correctDistortion(Dx, Dy, w2, h2);
|
||||||
|
}
|
||||||
|
|
||||||
// distortion correction
|
// distortion correction
|
||||||
double s = 1;
|
double s = 1;
|
||||||
|
|
||||||
@ -660,7 +660,13 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed,
|
|||||||
|
|
||||||
std::unique_ptr<const LensCorrection> pLCPMap;
|
std::unique_ptr<const LensCorrection> pLCPMap;
|
||||||
|
|
||||||
if (needsLensfun()) {
|
if (needsMetadata()) {
|
||||||
|
auto corr = MetadataLensCorrectionFinder::findCorrection(metadata);
|
||||||
|
if (corr) {
|
||||||
|
corr->initCorrections(oW, oH, params->coarse, rawRotationDeg);
|
||||||
|
pLCPMap = std::move(corr);
|
||||||
|
}
|
||||||
|
} else if (needsLensfun()) {
|
||||||
pLCPMap = LFDatabase::getInstance()->findModifier(params->lensProf, metadata, oW, oH, params->coarse, rawRotationDeg);
|
pLCPMap = LFDatabase::getInstance()->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
|
} else if (needsLCP()) { // don't check focal length to allow distortion correction for lenses without chip
|
||||||
const std::shared_ptr<LCPProfile> pLCPProf = LCPStore::getInstance()->getProfile (params->lensProf.lcpFile);
|
const std::shared_ptr<LCPProfile> pLCPProf = LCPStore::getInstance()->getProfile (params->lensProf.lcpFile);
|
||||||
@ -676,7 +682,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! (needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsLensfun()) && (needsVignetting() || needsPCVignetting() || needsGradient())) {
|
if (! (needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsMetadata() || needsLensfun()) && (needsVignetting() || needsPCVignetting() || needsGradient())) {
|
||||||
transformLuminanceOnly (original, transformed, cx, cy, oW, oH, fW, fH);
|
transformLuminanceOnly (original, transformed, cx, cy, oW, oH, fW, fH);
|
||||||
} else {
|
} else {
|
||||||
bool highQuality;
|
bool highQuality;
|
||||||
@ -686,24 +692,8 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed,
|
|||||||
highQuality = false;
|
highQuality = false;
|
||||||
} else {
|
} else {
|
||||||
highQuality = true;
|
highQuality = true;
|
||||||
// agriggio: CA correction via the lens profile has to be
|
|
||||||
// performed before separately from the the other transformations
|
|
||||||
// (except for the coarse rotation/flipping). In order to not
|
|
||||||
// change the code too much, I simply introduced a new mode
|
|
||||||
// TRANSFORM_HIGH_QUALITY_CA, which applies *only* profile-based
|
|
||||||
// CA correction. So, the correction in this case occurs in two
|
|
||||||
// steps, using an intermediate temporary image. There's room for
|
|
||||||
// optimization of course...
|
|
||||||
if (pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable()) {
|
|
||||||
tmpimg.reset(new Imagefloat(transformed->getWidth(), transformed->getHeight()));
|
|
||||||
dest = tmpimg.get();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
transformGeneral(highQuality, original, dest, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap.get(), useOriginalBuffer);
|
transformGeneral(highQuality, original, dest, cx, cy, sx, sy, oW, oH, fW, fH, pLCPMap.get(), useOriginalBuffer);
|
||||||
|
|
||||||
if (highQuality && dest != transformed) {
|
|
||||||
transformLCPCAOnly(dest, transformed, cx, cy, pLCPMap.get(), useOriginalBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1094,8 +1084,10 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I
|
|||||||
|
|
||||||
// set up stuff, depending on the mode we are
|
// set up stuff, depending on the mode we are
|
||||||
enum PerspType { NONE, SIMPLE, CAMERA_BASED };
|
enum PerspType { NONE, SIMPLE, CAMERA_BASED };
|
||||||
const bool enableLCPDist = pLCPMap && params->lensProf.useDist;
|
const bool enableLCPDist = pLCPMap && params->lensProf.useDist && pLCPMap->hasDistortionCorrection();
|
||||||
|
const bool enableLCPCA = pLCPMap && params->lensProf.useCA && pLCPMap->hasCACorrection();
|
||||||
const bool enableCA = highQuality && needsCA();
|
const bool enableCA = highQuality && needsCA();
|
||||||
|
const bool doCACorrection = enableCA || enableLCPCA;
|
||||||
const bool enableGradient = needsGradient();
|
const bool enableGradient = needsGradient();
|
||||||
const bool enablePCVignetting = needsPCVignetting();
|
const bool enablePCVignetting = needsPCVignetting();
|
||||||
const bool enableVignetting = needsVignetting();
|
const bool enableVignetting = needsVignetting();
|
||||||
@ -1240,25 +1232,33 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableLCPDist) {
|
|
||||||
pLCPMap->correctDistortion(x_d, y_d, w2, h2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// rotate
|
// rotate
|
||||||
const double Dxc = x_d * cost - y_d * sint;
|
const double Dxr = x_d * cost - y_d * sint;
|
||||||
const double Dyc = x_d * sint + y_d * cost;
|
const double Dyr = x_d * sint + y_d * cost;
|
||||||
|
|
||||||
// distortion correction
|
for (int c = 0; c < (doCACorrection ? 3 : 1); ++c) {
|
||||||
double s = 1.0;
|
double Dx = Dxr;
|
||||||
|
double Dy = Dyr;
|
||||||
|
|
||||||
if (enableDistortion) {
|
if (enableLCPDist && enableLCPCA) {
|
||||||
const double r = sqrt(Dxc * Dxc + Dyc * Dyc) / maxRadius;
|
pLCPMap->correctDistortionAndCA(Dx, Dy, w2, h2, c);
|
||||||
s = 1.0 - distAmount + distAmount * r;
|
} else if (enableLCPDist) {
|
||||||
}
|
pLCPMap->correctDistortion(Dx, Dy, w2, h2);
|
||||||
|
} else if (enableLCPCA) {
|
||||||
|
pLCPMap->correctCA(Dx, Dy, w2, h2, c);
|
||||||
|
}
|
||||||
|
|
||||||
for (int c = 0; c < (enableCA ? 3 : 1); ++c) {
|
// distortion correction
|
||||||
double Dx = Dxc * (s + chDist[c]);
|
double s = 1.0;
|
||||||
double Dy = Dyc * (s + chDist[c]);
|
|
||||||
|
if (enableDistortion) {
|
||||||
|
const double r = sqrt(Dx * Dx + Dy * Dy) / maxRadius;
|
||||||
|
s = 1.0 - distAmount + distAmount * r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CA correction
|
||||||
|
Dx *= s + chDist[c];
|
||||||
|
Dy *= s + chDist[c];
|
||||||
|
|
||||||
// de-center
|
// de-center
|
||||||
Dx += w2;
|
Dx += w2;
|
||||||
@ -1305,13 +1305,13 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I
|
|||||||
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->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);
|
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 if (!useLog) {
|
} else if (!useLog) {
|
||||||
if (enableCA) {
|
if (doCACorrection) {
|
||||||
interpolateTransformChannelsCubic(chOrig[c], xc - 1, yc - 1, Dx, Dy, chTrans[c][y][x], vignmul);
|
interpolateTransformChannelsCubic(chOrig[c], xc - 1, yc - 1, Dx, Dy, chTrans[c][y][x], vignmul);
|
||||||
} else {
|
} else {
|
||||||
interpolateTransformCubic(original, xc - 1, yc - 1, Dx, Dy, transformed->r(y, x), transformed->g(y, x), transformed->b(y, x), vignmul);
|
interpolateTransformCubic(original, xc - 1, yc - 1, Dx, Dy, transformed->r(y, x), transformed->g(y, x), transformed->b(y, x), vignmul);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (enableCA) {
|
if (doCACorrection) {
|
||||||
interpolateTransformChannelsCubicLog(chOrig[c], xc - 1, yc - 1, Dx, Dy, chTrans[c][y][x], vignmul);
|
interpolateTransformChannelsCubicLog(chOrig[c], xc - 1, yc - 1, Dx, Dy, chTrans[c][y][x], vignmul);
|
||||||
} else {
|
} else {
|
||||||
interpolateTransformCubicLog(original, xc - 1, yc - 1, Dx, Dy, transformed->r(y, x), transformed->g(y, x), transformed->b(y, x), vignmul);
|
interpolateTransformCubicLog(original, xc - 1, yc - 1, Dx, Dy, transformed->r(y, x), transformed->g(y, x), transformed->b(y, x), vignmul);
|
||||||
@ -1325,7 +1325,7 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I
|
|||||||
const int x2 = LIM(xc + 1, 0, original->getWidth() - 1);
|
const int x2 = LIM(xc + 1, 0, original->getWidth() - 1);
|
||||||
|
|
||||||
if (useLog) {
|
if (useLog) {
|
||||||
if (enableCA) {
|
if (doCACorrection) {
|
||||||
chTrans[c][y][x] = vignmul * xexpf(chOrig[c][y1][x1] * (1.0 - Dx) * (1.0 - Dy) + chOrig[c][y1][x2] * Dx * (1.0 - Dy) + chOrig[c][y2][x1] * (1.0 - Dx) * Dy + chOrig[c][y2][x2] * Dx * Dy);
|
chTrans[c][y][x] = vignmul * xexpf(chOrig[c][y1][x1] * (1.0 - Dx) * (1.0 - Dy) + chOrig[c][y1][x2] * Dx * (1.0 - Dy) + chOrig[c][y2][x1] * (1.0 - Dx) * Dy + chOrig[c][y2][x2] * Dx * Dy);
|
||||||
} else {
|
} else {
|
||||||
transformed->r(y, x) = vignmul * xexpf(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->r(y, x) = vignmul * xexpf(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);
|
||||||
@ -1333,7 +1333,7 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I
|
|||||||
transformed->b(y, x) = vignmul * xexpf(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);
|
transformed->b(y, x) = vignmul * xexpf(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 {
|
} else {
|
||||||
if (enableCA) {
|
if (doCACorrection) {
|
||||||
chTrans[c][y][x] = vignmul * (chOrig[c][y1][x1] * (1.0 - Dx) * (1.0 - Dy) + chOrig[c][y1][x2] * Dx * (1.0 - Dy) + chOrig[c][y2][x1] * (1.0 - Dx) * Dy + chOrig[c][y2][x2] * Dx * Dy);
|
chTrans[c][y][x] = vignmul * (chOrig[c][y1][x1] * (1.0 - Dx) * (1.0 - Dy) + chOrig[c][y1][x2] * Dx * (1.0 - Dy) + chOrig[c][y2][x1] * (1.0 - Dx) * Dy + chOrig[c][y2][x2] * Dx * Dy);
|
||||||
} else {
|
} else {
|
||||||
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->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);
|
||||||
@ -1343,7 +1343,7 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (enableCA) {
|
if (doCACorrection) {
|
||||||
// not valid (source pixel x,y not inside source image, etc.)
|
// not valid (source pixel x,y not inside source image, etc.)
|
||||||
chTrans[c][y][x] = 0;
|
chTrans[c][y][x] = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -1357,77 +1357,6 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ImProcFunctions::transformLCPCAOnly(Imagefloat *original, Imagefloat *transformed, int cx, int cy, const LensCorrection *pLCPMap, bool useOriginalBuffer)
|
|
||||||
{
|
|
||||||
assert(pLCPMap && params->lensProf.useCA && pLCPMap->isCACorrectionAvailable());
|
|
||||||
const bool useLog = params->commonTrans.method == "log";
|
|
||||||
|
|
||||||
float** chTrans[3] = {transformed->r.ptrs, transformed->g.ptrs, transformed->b.ptrs};
|
|
||||||
|
|
||||||
std::unique_ptr<Imagefloat> tempLog;
|
|
||||||
if (useLog) {
|
|
||||||
if (!useOriginalBuffer) {
|
|
||||||
tempLog.reset(new Imagefloat(original->getWidth(), original->getHeight()));
|
|
||||||
logEncode(original, tempLog.get(), multiThread);
|
|
||||||
original = tempLog.get();
|
|
||||||
} else {
|
|
||||||
logEncode(original, original, multiThread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
float** chOrig[3] = {original->r.ptrs, original->g.ptrs, original->b.ptrs};
|
|
||||||
|
|
||||||
#ifdef _OPENMP
|
|
||||||
#pragma omp parallel for if (multiThread)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int y = 0; y < transformed->getHeight(); y++) {
|
|
||||||
for (int x = 0; x < transformed->getWidth(); x++) {
|
|
||||||
for (int c = 0; c < 3; c++) {
|
|
||||||
double Dx = x;
|
|
||||||
double Dy = y;
|
|
||||||
|
|
||||||
pLCPMap->correctCA(Dx, Dy, cx, cy, c);
|
|
||||||
|
|
||||||
// Extract integer and fractions of coordinates
|
|
||||||
int xc = (int)Dx;
|
|
||||||
Dx -= (double)xc;
|
|
||||||
int yc = (int)Dy;
|
|
||||||
Dy -= (double)yc;
|
|
||||||
|
|
||||||
// Convert only valid pixels
|
|
||||||
if (yc >= 0 && yc < original->getHeight() && xc >= 0 && xc < original->getWidth()) {
|
|
||||||
|
|
||||||
// multiplier for vignetting correction
|
|
||||||
if (yc > 0 && yc < original->getHeight() - 2 && xc > 0 && xc < original->getWidth() - 2) {
|
|
||||||
// all interpolation pixels inside image
|
|
||||||
if (!useLog) {
|
|
||||||
interpolateTransformChannelsCubic(chOrig[c], xc - 1, yc - 1, Dx, Dy, chTrans[c][y][x], 1.0);
|
|
||||||
} else {
|
|
||||||
interpolateTransformChannelsCubicLog(chOrig[c], xc - 1, yc - 1, Dx, Dy, chTrans[c][y][x], 1.0);
|
|
||||||
}
|
|
||||||
} 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);
|
|
||||||
if (!useLog) {
|
|
||||||
chTrans[c][y][x] = (chOrig[c][y1][x1] * (1.0 - Dx) * (1.0 - Dy) + chOrig[c][y1][x2] * Dx * (1.0 - Dy) + chOrig[c][y2][x1] * (1.0 - Dx) * Dy + chOrig[c][y2][x2] * Dx * Dy);
|
|
||||||
} else {
|
|
||||||
chTrans[c][y][x] = xexpf(chOrig[c][y1][x1] * (1.0 - Dx) * (1.0 - Dy) + chOrig[c][y1][x2] * Dx * (1.0 - Dy) + chOrig[c][y2][x1] * (1.0 - Dx) * Dy + chOrig[c][y2][x2] * Dx * Dy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// not valid (source pixel x,y not inside source image, etc.)
|
|
||||||
chTrans[c][y][x] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
double ImProcFunctions::getTransformAutoFill (int oW, int oH, const LensCorrection *pLCPMap) const
|
double ImProcFunctions::getTransformAutoFill (int oW, int oH, const LensCorrection *pLCPMap) const
|
||||||
{
|
{
|
||||||
if (!needsCA() && !needsDistortion() && !needsRotation() && !needsPerspective() && (!params->lensProf.useDist || pLCPMap == nullptr)) {
|
if (!needsCA() && !needsDistortion() && !needsRotation() && !needsPerspective() && (!params->lensProf.useDist || pLCPMap == nullptr)) {
|
||||||
@ -1500,6 +1429,11 @@ bool ImProcFunctions::needsVignetting () const
|
|||||||
return params->vignetting.amount;
|
return params->vignetting.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ImProcFunctions::needsMetadata () const
|
||||||
|
{
|
||||||
|
return params->lensProf.useMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
bool ImProcFunctions::needsLCP () const
|
bool ImProcFunctions::needsLCP () const
|
||||||
{
|
{
|
||||||
return params->lensProf.useLcp();
|
return params->lensProf.useLcp();
|
||||||
@ -1517,9 +1451,8 @@ bool ImProcFunctions::needsTransform (int oW, int oH, int rawRotationDeg, const
|
|||||||
std::unique_ptr<const LensCorrection> pLCPMap = LFDatabase::getInstance()->findModifier(params->lensProf, metadata, oW, oH, params->coarse, rawRotationDeg);
|
std::unique_ptr<const LensCorrection> pLCPMap = LFDatabase::getInstance()->findModifier(params->lensProf, metadata, oW, oH, params->coarse, rawRotationDeg);
|
||||||
needsLf = pLCPMap.get();
|
needsLf = pLCPMap.get();
|
||||||
}
|
}
|
||||||
return needsCA () || needsDistortion () || needsRotation () || needsPerspective () || needsGradient () || needsPCVignetting () || needsVignetting () || needsLCP() || needsLf;
|
return needsCA () || needsDistortion () || needsRotation () || needsPerspective () || needsGradient () || needsPCVignetting () || needsVignetting () || needsLCP() || needsMetadata() || needsLf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -985,11 +985,29 @@ rtengine::LCPMapper::LCPMapper(
|
|||||||
isFisheye = pProf->isFisheye;
|
isFisheye = pProf->isFisheye;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rtengine::LCPMapper::isCACorrectionAvailable() const
|
bool rtengine::LCPMapper::hasDistortionCorrection() const
|
||||||
|
{
|
||||||
|
// assume lcp always provides distortion correction
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rtengine::LCPMapper::hasCACorrection() const
|
||||||
{
|
{
|
||||||
return enableCA;
|
return enableCA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool rtengine::LCPMapper::hasVignettingCorrection() const
|
||||||
|
{
|
||||||
|
// assume lcp always provides vignetting correction
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rtengine::LCPMapper::correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const
|
||||||
|
{
|
||||||
|
correctDistortion(x, y, cx, cy);
|
||||||
|
correctCA(x, y, cx, cy, channel);
|
||||||
|
}
|
||||||
|
|
||||||
void rtengine::LCPMapper::correctDistortion(double &x, double &y, int cx, int cy) const
|
void rtengine::LCPMapper::correctDistortion(double &x, double &y, int cx, int cy) const
|
||||||
{
|
{
|
||||||
x += cx;
|
x += cx;
|
||||||
|
@ -166,8 +166,13 @@ private:
|
|||||||
class LensCorrection {
|
class LensCorrection {
|
||||||
public:
|
public:
|
||||||
virtual ~LensCorrection() {}
|
virtual ~LensCorrection() {}
|
||||||
|
|
||||||
|
virtual bool hasDistortionCorrection() const = 0;
|
||||||
|
virtual bool hasCACorrection() const = 0;
|
||||||
|
virtual bool hasVignettingCorrection() const = 0;
|
||||||
|
|
||||||
|
virtual void correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const = 0;
|
||||||
virtual void correctDistortion(double &x, double &y, int cx, int cy) const = 0;
|
virtual void correctDistortion(double &x, double &y, int cx, int cy) const = 0;
|
||||||
virtual bool isCACorrectionAvailable() const = 0;
|
|
||||||
virtual void correctCA(double &x, double &y, int cx, int cy, int channel) const = 0;
|
virtual void correctCA(double &x, double &y, int cx, int cy, int channel) const = 0;
|
||||||
virtual void processVignette(int width, int height, float** rawData) const = 0;
|
virtual void processVignette(int width, int height, float** rawData) const = 0;
|
||||||
virtual void processVignette3Channels(int width, int height, float** rawData) const = 0;
|
virtual void processVignette3Channels(int width, int height, float** rawData) const = 0;
|
||||||
@ -194,8 +199,12 @@ public:
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
bool hasDistortionCorrection() const override;
|
||||||
|
bool hasCACorrection() const override;
|
||||||
|
bool hasVignettingCorrection() const override;
|
||||||
|
|
||||||
|
void correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const override;
|
||||||
void correctDistortion(double &x, double &y, int cx, int cy) const override;
|
void correctDistortion(double &x, double &y, int cx, int cy) const override;
|
||||||
bool isCACorrectionAvailable() const override;
|
|
||||||
void correctCA(double& x, double& y, int cx, int cy, int channel) const override;
|
void correctCA(double& x, double& y, int cx, int cy, int channel) const override;
|
||||||
void processVignette(int width, int height, float** rawData) const override;
|
void processVignette(int width, int height, float** rawData) const override;
|
||||||
void processVignette3Channels(int width, int height, float** rawData) const override;
|
void processVignette3Channels(int width, int height, float** rawData) const override;
|
||||||
|
1043
rtengine/lensmetadata.cc
Normal file
1043
rtengine/lensmetadata.cc
Normal file
File diff suppressed because it is too large
Load Diff
89
rtengine/lensmetadata.h
Normal file
89
rtengine/lensmetadata.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RawTherapee.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Rawtherapee developers
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "lcp.h"
|
||||||
|
#include "metadata.h"
|
||||||
|
#include "procparams.h"
|
||||||
|
#include "rtengine.h"
|
||||||
|
|
||||||
|
namespace rtengine
|
||||||
|
{
|
||||||
|
|
||||||
|
/* MetadataLensCorrection is an abstract class for various lens correction based on raw file metadata
|
||||||
|
this metadata is vendor dependent */
|
||||||
|
class MetadataLensCorrection : public LensCorrection,
|
||||||
|
public NonCopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void initCorrections(int width, int height, const procparams::CoarseTransformParams &coarse, int rawRotationDeg) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* CenterRadiusMetadataLensCorrection is an abstract class the extends MetadataLensCorrection to easily handle center radius based corrections */
|
||||||
|
class CenterRadiusMetadataLensCorrection : public MetadataLensCorrection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CenterRadiusMetadataLensCorrection(const FramesMetaData *meta);
|
||||||
|
|
||||||
|
void process(double &x, double &y, int cx, int cy, int channel, bool dist, bool ca) const;
|
||||||
|
|
||||||
|
void correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const override;
|
||||||
|
void correctDistortion(double &x, double &y, int cx, int cy) const override;
|
||||||
|
void correctCA(double &x, double &y, int cx, int cy, int channel) const override;
|
||||||
|
void processVignette(int width, int height, float **rawData) const override;
|
||||||
|
void processVignette3Channels(int width, int height, float **rawData) const override;
|
||||||
|
|
||||||
|
void processVignetteNChannels(int width, int height, float **rawData, int channels) const;
|
||||||
|
void initCorrections(int width, int height, const procparams::CoarseTransformParams &coarse, int rawRotationDeg) override;
|
||||||
|
|
||||||
|
/* Implementers should implement the below methods */
|
||||||
|
virtual bool hasDistortionCorrection() const override = 0;
|
||||||
|
virtual bool hasCACorrection() const override = 0;
|
||||||
|
virtual bool hasVignettingCorrection() const override = 0;
|
||||||
|
|
||||||
|
/* These methods should return the distortion correction factor (cf) for the
|
||||||
|
* provided radius rout (radius of the output image (corrected)).
|
||||||
|
* So rin = rout * cf
|
||||||
|
* */
|
||||||
|
virtual double distortionCorrectionFactor(double rout) const = 0;
|
||||||
|
virtual double caCorrectionFactor(double rout, int channel) const = 0;
|
||||||
|
virtual double distortionAndCACorrectionFactor(double rout, int channel) const = 0;
|
||||||
|
|
||||||
|
/* This methods should return the vignetting correction factor (cf) for the
|
||||||
|
* provided radius */
|
||||||
|
virtual double vignettingCorrectionFactor(double r) const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool swap_xy;
|
||||||
|
double w2;
|
||||||
|
double h2;
|
||||||
|
double rf;
|
||||||
|
Exiv2Metadata metadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* MetadataLensCorrectionFinder tries to find and return MetadataLensCorrection for the provided metadata */
|
||||||
|
class MetadataLensCorrectionFinder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::unique_ptr<MetadataLensCorrection> findCorrection(const FramesMetaData *meta);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rtengine
|
@ -2064,13 +2064,19 @@ bool LensProfParams::lfManual() const
|
|||||||
return lcMode == LcMode::LENSFUNMANUAL;
|
return lcMode == LcMode::LENSFUNMANUAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LensProfParams::useMetadata() const
|
||||||
|
{
|
||||||
|
return lcMode == LcMode::METADATA;
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<const char*>& LensProfParams::getMethodStrings() const
|
const std::vector<const char*>& LensProfParams::getMethodStrings() const
|
||||||
{
|
{
|
||||||
static const std::vector<const char*> method_strings = {
|
static const std::vector<const char*> method_strings = {
|
||||||
"none",
|
"none",
|
||||||
"lfauto",
|
"lfauto",
|
||||||
"lfmanual",
|
"lfmanual",
|
||||||
"lcp"
|
"lcp",
|
||||||
|
"metadata"
|
||||||
};
|
};
|
||||||
return method_strings;
|
return method_strings;
|
||||||
}
|
}
|
||||||
|
@ -966,7 +966,8 @@ struct LensProfParams {
|
|||||||
NONE, // No lens correction
|
NONE, // No lens correction
|
||||||
LENSFUNAUTOMATCH, // Lens correction using auto matched lensfun database entry
|
LENSFUNAUTOMATCH, // Lens correction using auto matched lensfun database entry
|
||||||
LENSFUNMANUAL, // Lens correction using manually selected lensfun database entry
|
LENSFUNMANUAL, // Lens correction using manually selected lensfun database entry
|
||||||
LCP // Lens correction using lcp file
|
LCP, // Lens correction using lcp file
|
||||||
|
METADATA // Lens correction using embedded metadata
|
||||||
};
|
};
|
||||||
|
|
||||||
LcMode lcMode;
|
LcMode lcMode;
|
||||||
@ -985,6 +986,7 @@ struct LensProfParams {
|
|||||||
bool lfAutoMatch() const;
|
bool lfAutoMatch() const;
|
||||||
bool useLcp() const;
|
bool useLcp() const;
|
||||||
bool lfManual() const;
|
bool lfManual() const;
|
||||||
|
bool useMetadata() const;
|
||||||
|
|
||||||
const std::vector<const char*>& getMethodStrings() const;
|
const std::vector<const char*>& getMethodStrings() const;
|
||||||
Glib::ustring getMethodString(LcMode mode) const;
|
Glib::ustring getMethodString(LcMode mode) const;
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#include "rt_math.h"
|
#include "rt_math.h"
|
||||||
#include "rtengine.h"
|
#include "rtengine.h"
|
||||||
#include "rtlensfun.h"
|
#include "rtlensfun.h"
|
||||||
|
#include "lensmetadata.h"
|
||||||
#include "../rtgui/options.h"
|
#include "../rtgui/options.h"
|
||||||
|
|
||||||
#define BENCHMARK
|
#define BENCHMARK
|
||||||
@ -1583,7 +1584,13 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens
|
|||||||
if (!hasFlatField && lensProf.useVign && lensProf.lcMode != LensProfParams::LcMode::NONE) {
|
if (!hasFlatField && lensProf.useVign && lensProf.lcMode != LensProfParams::LcMode::NONE) {
|
||||||
std::unique_ptr<LensCorrection> pmap;
|
std::unique_ptr<LensCorrection> pmap;
|
||||||
|
|
||||||
if (lensProf.useLensfun()) {
|
if (lensProf.useMetadata()) {
|
||||||
|
auto corr = MetadataLensCorrectionFinder::findCorrection(idata);
|
||||||
|
if (corr) {
|
||||||
|
corr->initCorrections(W, H, coarse, -1);
|
||||||
|
pmap = std::move(corr);
|
||||||
|
}
|
||||||
|
} else if (lensProf.useLensfun()) {
|
||||||
pmap = LFDatabase::getInstance()->findModifier(lensProf, idata, W, H, coarse, -1);
|
pmap = LFDatabase::getInstance()->findModifier(lensProf, idata, W, H, coarse, -1);
|
||||||
} else {
|
} else {
|
||||||
const std::shared_ptr<LCPProfile> pLCPProf = LCPStore::getInstance()->getProfile(lensProf.lcpFile);
|
const std::shared_ptr<LCPProfile> pLCPProf = LCPStore::getInstance()->getProfile(lensProf.lcpFile);
|
||||||
|
@ -130,6 +130,8 @@ public:
|
|||||||
virtual bool getPixelShift () const = 0;
|
virtual bool getPixelShift () const = 0;
|
||||||
/** @return false: not an HDR file ; true: single or multi-frame HDR file (e.g. Pentax HDR raw file or 32 bit float DNG file or Log compressed) */
|
/** @return false: not an HDR file ; true: single or multi-frame HDR file (e.g. Pentax HDR raw file or 32 bit float DNG file or Log compressed) */
|
||||||
virtual bool getHDR() const = 0;
|
virtual bool getHDR() const = 0;
|
||||||
|
/** @return true if the file is a DNG file */
|
||||||
|
virtual bool getDNG() const = 0;
|
||||||
|
|
||||||
/** @return false: not an HDR file ; true: single or multi-frame HDR file (e.g. Pentax HDR raw file or 32 bit float DNG file or Log compressed) */
|
/** @return false: not an HDR file ; true: single or multi-frame HDR file (e.g. Pentax HDR raw file or 32 bit float DNG file or Log compressed) */
|
||||||
virtual std::string getImageType() const = 0;
|
virtual std::string getImageType() const = 0;
|
||||||
|
@ -102,6 +102,21 @@ LFModifier::operator bool() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool LFModifier::hasDistortionCorrection() const
|
||||||
|
{
|
||||||
|
return (flags_ & LF_MODIFY_DISTORTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LFModifier::hasCACorrection() const
|
||||||
|
{
|
||||||
|
return (flags_ & LF_MODIFY_TCA);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LFModifier::hasVignettingCorrection() const
|
||||||
|
{
|
||||||
|
return (flags_ & LF_MODIFY_VIGNETTING);
|
||||||
|
}
|
||||||
|
|
||||||
void LFModifier::correctDistortion(double &x, double &y, int cx, int cy) const
|
void LFModifier::correctDistortion(double &x, double &y, int cx, int cy) const
|
||||||
{
|
{
|
||||||
if (!data_) {
|
if (!data_) {
|
||||||
@ -125,12 +140,6 @@ void LFModifier::correctDistortion(double &x, double &y, int cx, int cy) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LFModifier::isCACorrectionAvailable() const
|
|
||||||
{
|
|
||||||
return (flags_ & LF_MODIFY_TCA);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LFModifier::correctCA(double &x, double &y, int cx, int cy, int channel) const
|
void LFModifier::correctCA(double &x, double &y, int cx, int cy, int channel) const
|
||||||
{
|
{
|
||||||
assert(channel >= 0 && channel <= 2);
|
assert(channel >= 0 && channel <= 2);
|
||||||
@ -156,6 +165,31 @@ void LFModifier::correctCA(double &x, double &y, int cx, int cy, int channel) co
|
|||||||
y -= cy;
|
y -= cy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LFModifier::correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const
|
||||||
|
{
|
||||||
|
assert(channel >= 0 && channel <= 2);
|
||||||
|
|
||||||
|
// RT currently applies the CA correction per channel, whereas
|
||||||
|
// lensfun applies it to all the three channels simultaneously. This means
|
||||||
|
// we do the work 3 times, because each time we discard 2 of the 3
|
||||||
|
// channels. We could consider caching the info to speed this up
|
||||||
|
x += cx;
|
||||||
|
y += cy;
|
||||||
|
|
||||||
|
float pos[6];
|
||||||
|
if (swap_xy_) {
|
||||||
|
std::swap(x, y);
|
||||||
|
}
|
||||||
|
data_->ApplySubpixelGeometryDistortion(x, y, 1, 1, pos); // This is thread-safe
|
||||||
|
x = pos[2*channel];
|
||||||
|
y = pos[2*channel+1];
|
||||||
|
if (swap_xy_) {
|
||||||
|
std::swap(x, y);
|
||||||
|
}
|
||||||
|
x -= cx;
|
||||||
|
y -= cy;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _OPENMP
|
#ifdef _OPENMP
|
||||||
void LFModifier::processVignette(int width, int height, float** rawData) const
|
void LFModifier::processVignette(int width, int height, float** rawData) const
|
||||||
{
|
{
|
||||||
|
@ -53,8 +53,12 @@ public:
|
|||||||
|
|
||||||
explicit operator bool() const;
|
explicit operator bool() const;
|
||||||
|
|
||||||
|
bool hasDistortionCorrection() const override;
|
||||||
|
bool hasCACorrection() const override;
|
||||||
|
bool hasVignettingCorrection() const override;
|
||||||
|
|
||||||
|
void correctDistortionAndCA(double &x, double &y, int cx, int cy, int channel) const override;
|
||||||
void correctDistortion(double &x, double &y, int cx, int cy) const override;
|
void correctDistortion(double &x, double &y, int cx, int cy) const override;
|
||||||
bool isCACorrectionAvailable() const override;
|
|
||||||
void correctCA(double &x, double &y, int cx, int cy, int channel) const override;
|
void correctCA(double &x, double &y, int cx, int cy, int channel) const override;
|
||||||
void processVignette(int width, int height, float** rawData) const override;
|
void processVignette(int width, int height, float** rawData) const override;
|
||||||
void processVignette3Channels(int width, int height, float** rawData) const override;
|
void processVignette3Channels(int width, int height, float** rawData) const override;
|
||||||
|
@ -59,6 +59,7 @@ CacheImageData::CacheImageData() :
|
|||||||
iso(0),
|
iso(0),
|
||||||
rating(0),
|
rating(0),
|
||||||
isHDR (false),
|
isHDR (false),
|
||||||
|
isDNG (false),
|
||||||
isPixelShift (false),
|
isPixelShift (false),
|
||||||
sensortype(rtengine::ST_NONE),
|
sensortype(rtengine::ST_NONE),
|
||||||
sampleFormat(rtengine::IIOSF_UNKNOWN),
|
sampleFormat(rtengine::IIOSF_UNKNOWN),
|
||||||
@ -194,6 +195,10 @@ int CacheImageData::load (const Glib::ustring& fname)
|
|||||||
isHDR = keyFile.get_boolean ("ExifInfo", "IsHDR");
|
isHDR = keyFile.get_boolean ("ExifInfo", "IsHDR");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keyFile.has_key ("ExifInfo", "IsDNG")) {
|
||||||
|
isDNG = keyFile.get_boolean ("ExifInfo", "IsDNG");
|
||||||
|
}
|
||||||
|
|
||||||
if (keyFile.has_key ("ExifInfo", "IsPixelShift")) {
|
if (keyFile.has_key ("ExifInfo", "IsPixelShift")) {
|
||||||
isPixelShift = keyFile.get_boolean ("ExifInfo", "IsPixelShift");
|
isPixelShift = keyFile.get_boolean ("ExifInfo", "IsPixelShift");
|
||||||
}
|
}
|
||||||
@ -316,6 +321,7 @@ int CacheImageData::save (const Glib::ustring& fname)
|
|||||||
keyFile.set_double ("ExifInfo", "FocusDist", focusDist);
|
keyFile.set_double ("ExifInfo", "FocusDist", focusDist);
|
||||||
keyFile.set_integer ("ExifInfo", "ISO", iso);
|
keyFile.set_integer ("ExifInfo", "ISO", iso);
|
||||||
keyFile.set_boolean ("ExifInfo", "IsHDR", isHDR);
|
keyFile.set_boolean ("ExifInfo", "IsHDR", isHDR);
|
||||||
|
keyFile.set_boolean ("ExifInfo", "IsDNG", isDNG);
|
||||||
keyFile.set_boolean ("ExifInfo", "IsPixelShift", isPixelShift);
|
keyFile.set_boolean ("ExifInfo", "IsPixelShift", isPixelShift);
|
||||||
keyFile.set_string ("ExifInfo", "ExpComp", expcomp);
|
keyFile.set_string ("ExifInfo", "ExpComp", expcomp);
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ public:
|
|||||||
unsigned iso;
|
unsigned iso;
|
||||||
int rating;
|
int rating;
|
||||||
bool isHDR;
|
bool isHDR;
|
||||||
|
bool isDNG;
|
||||||
bool isPixelShift;
|
bool isPixelShift;
|
||||||
int sensortype;
|
int sensortype;
|
||||||
rtengine::IIO_Sample_Format sampleFormat;
|
rtengine::IIO_Sample_Format sampleFormat;
|
||||||
@ -114,6 +115,7 @@ public:
|
|||||||
int getRating () const override { return rating; } // FIXME-piotr : missing rating
|
int getRating () const override { return rating; } // FIXME-piotr : missing rating
|
||||||
bool getPixelShift () const override { return isPixelShift; }
|
bool getPixelShift () const override { return isPixelShift; }
|
||||||
bool getHDR() const override { return isHDR; }
|
bool getHDR() const override { return isHDR; }
|
||||||
|
bool getDNG() const override { return isDNG; }
|
||||||
std::string getImageType() const override { return isPixelShift ? "PS" : isHDR ? "HDR" : "STD"; }
|
std::string getImageType() const override { return isPixelShift ? "PS" : isHDR ? "HDR" : "STD"; }
|
||||||
rtengine::IIOSampleFormat getSampleFormat() const override { return sampleFormat; }
|
rtengine::IIOSampleFormat getSampleFormat() const override { return sampleFormat; }
|
||||||
void getDimensions(int &w, int &h) const override
|
void getDimensions(int &w, int &h) const override
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "../rtengine/lcp.h"
|
#include "../rtengine/lcp.h"
|
||||||
#include "../rtengine/procparams.h"
|
#include "../rtengine/procparams.h"
|
||||||
#include "../rtengine/rtlensfun.h"
|
#include "../rtengine/rtlensfun.h"
|
||||||
|
#include "../rtengine/lensmetadata.h"
|
||||||
|
|
||||||
using namespace rtengine;
|
using namespace rtengine;
|
||||||
using namespace rtengine::procparams;
|
using namespace rtengine::procparams;
|
||||||
@ -56,6 +57,7 @@ LensProfilePanel::LensProfilePanel() :
|
|||||||
distGrid(Gtk::manage((new Gtk::Grid()))),
|
distGrid(Gtk::manage((new Gtk::Grid()))),
|
||||||
corrUnchangedRB(Gtk::manage((new Gtk::RadioButton(M("GENERAL_UNCHANGED"))))),
|
corrUnchangedRB(Gtk::manage((new Gtk::RadioButton(M("GENERAL_UNCHANGED"))))),
|
||||||
corrOffRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("GENERAL_NONE"))))),
|
corrOffRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("GENERAL_NONE"))))),
|
||||||
|
corrMetadata(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_METADATA"))))),
|
||||||
corrLensfunAutoRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_AUTOMATCH"))))),
|
corrLensfunAutoRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_AUTOMATCH"))))),
|
||||||
corrLensfunManualRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_MANUAL"))))),
|
corrLensfunManualRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_MANUAL"))))),
|
||||||
corrLcpFileRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_LCPFILE"))))),
|
corrLcpFileRB(Gtk::manage((new Gtk::RadioButton(corrGroup, M("TP_LENSPROFILE_CORRECTION_LCPFILE"))))),
|
||||||
@ -144,17 +146,18 @@ LensProfilePanel::LensProfilePanel() :
|
|||||||
// Populate modes grid:
|
// Populate modes grid:
|
||||||
|
|
||||||
modesGrid->attach(*corrOffRB, 0, 0, 3, 1);
|
modesGrid->attach(*corrOffRB, 0, 0, 3, 1);
|
||||||
modesGrid->attach(*corrLensfunAutoRB, 0, 1, 3, 1);
|
modesGrid->attach(*corrMetadata, 0, 1, 3, 1);
|
||||||
modesGrid->attach(*corrLensfunManualRB, 0, 2, 3, 1);
|
modesGrid->attach(*corrLensfunAutoRB, 0, 2, 3, 1);
|
||||||
|
modesGrid->attach(*corrLensfunManualRB, 0, 3, 3, 1);
|
||||||
|
|
||||||
modesGrid->attach(*lensfunCamerasLbl, 0, 3, 1, 1);
|
modesGrid->attach(*lensfunCamerasLbl, 0, 4, 1, 1);
|
||||||
modesGrid->attach(*lensfunCameras, 1, 3, 1, 1);
|
modesGrid->attach(*lensfunCameras, 1, 4, 1, 1);
|
||||||
modesGrid->attach(*lensfunLensesLbl, 0, 4, 1, 1);
|
modesGrid->attach(*lensfunLensesLbl, 0, 5, 1, 1);
|
||||||
modesGrid->attach(*lensfunLenses, 1, 4, 1, 1);
|
modesGrid->attach(*lensfunLenses, 1, 5, 1, 1);
|
||||||
modesGrid->attach(*warning, 2, 3, 1, 2);
|
modesGrid->attach(*warning, 2, 4, 1, 2);
|
||||||
|
|
||||||
modesGrid->attach(*corrLcpFileRB, 0, 5, 1, 1);
|
modesGrid->attach(*corrLcpFileRB, 0, 6, 1, 1);
|
||||||
modesGrid->attach(*corrLcpFileChooser, 1, 5, 1, 1);
|
modesGrid->attach(*corrLcpFileChooser, 1, 6, 1, 1);
|
||||||
|
|
||||||
// Populate distortions grid:
|
// Populate distortions grid:
|
||||||
|
|
||||||
@ -179,6 +182,7 @@ LensProfilePanel::LensProfilePanel() :
|
|||||||
lensfunCameras->signal_changed().connect(sigc::mem_fun(*this, &LensProfilePanel::onLensfunCameraChanged));
|
lensfunCameras->signal_changed().connect(sigc::mem_fun(*this, &LensProfilePanel::onLensfunCameraChanged));
|
||||||
lensfunLenses->signal_changed().connect(sigc::mem_fun(*this, &LensProfilePanel::onLensfunLensChanged));
|
lensfunLenses->signal_changed().connect(sigc::mem_fun(*this, &LensProfilePanel::onLensfunLensChanged));
|
||||||
corrOffRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrOffRB));
|
corrOffRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrOffRB));
|
||||||
|
corrMetadata->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrMetadata));
|
||||||
corrLensfunAutoRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrLensfunAutoRB));
|
corrLensfunAutoRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrLensfunAutoRB));
|
||||||
corrLensfunManualRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrLensfunManualRB));
|
corrLensfunManualRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrLensfunManualRB));
|
||||||
corrLcpFileRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrLcpFileRB));
|
corrLcpFileRB->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &LensProfilePanel::onCorrModeChanged), corrLcpFileRB));
|
||||||
@ -211,30 +215,52 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case procparams::LensProfParams::LcMode::METADATA: {
|
||||||
|
if (metadata) {
|
||||||
|
auto metadataCorrection= rtengine::MetadataLensCorrectionFinder::findCorrection(metadata);
|
||||||
|
if (metadataCorrection) {
|
||||||
|
corrMetadata->set_active(true);
|
||||||
|
corrMetadata->set_sensitive(true);
|
||||||
|
} else {
|
||||||
|
corrMetadata->set_sensitive(false);
|
||||||
|
corrOffRB->set_active(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
corrMetadata->set_sensitive(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case procparams::LensProfParams::LcMode::NONE: {
|
case procparams::LensProfParams::LcMode::NONE: {
|
||||||
corrOffRB->set_active(true);
|
corrOffRB->set_active(true);
|
||||||
setManualParamsVisibility(false);
|
setManualParamsVisibility(false);
|
||||||
|
|
||||||
|
ckbUseDist->set_sensitive(false);
|
||||||
|
ckbUseVign->set_sensitive(false);
|
||||||
|
ckbUseCA->set_sensitive(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pp->lensProf.lcpFile.empty()) {
|
if (pp->lensProf.lcMode == procparams::LensProfParams::LcMode::LCP) {
|
||||||
const Glib::ustring lastFolder = corrLcpFileChooser->get_current_folder();
|
if (pp->lensProf.lcpFile.empty()) {
|
||||||
corrLcpFileChooser->set_current_folder(lastFolder);
|
const Glib::ustring lastFolder = corrLcpFileChooser->get_current_folder();
|
||||||
corrLcpFileChooser->unselect_all();
|
corrLcpFileChooser->set_current_folder(lastFolder);
|
||||||
bindCurrentFolder(*corrLcpFileChooser, options.lastLensProfileDir);
|
corrLcpFileChooser->unselect_all();
|
||||||
updateDisabled(false);
|
bindCurrentFolder(*corrLcpFileChooser, options.lastLensProfileDir);
|
||||||
}
|
updateLCPDisabled(false);
|
||||||
else if (LCPStore::getInstance()->isValidLCPFileName(pp->lensProf.lcpFile)) {
|
}
|
||||||
corrLcpFileChooser->set_filename(pp->lensProf.lcpFile);
|
else if (LCPStore::getInstance()->isValidLCPFileName(pp->lensProf.lcpFile)) {
|
||||||
|
corrLcpFileChooser->set_filename(pp->lensProf.lcpFile);
|
||||||
if (corrLcpFileRB->get_active()) {
|
|
||||||
updateDisabled(true);
|
if (corrLcpFileRB->get_active()) {
|
||||||
|
updateLCPDisabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
corrLcpFileChooser->unselect_filename(corrLcpFileChooser->get_filename());
|
||||||
|
updateLCPDisabled(false);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
corrLcpFileChooser->unselect_filename(corrLcpFileChooser->get_filename());
|
|
||||||
updateDisabled(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const LFDatabase* const db = LFDatabase::getInstance();
|
const LFDatabase* const db = LFDatabase::getInstance();
|
||||||
@ -308,6 +334,9 @@ void LensProfilePanel::write(rtengine::procparams::ProcParams* pp, ParamsEdited*
|
|||||||
if (corrLcpFileRB->get_active()) {
|
if (corrLcpFileRB->get_active()) {
|
||||||
pp->lensProf.lcMode = procparams::LensProfParams::LcMode::LCP;
|
pp->lensProf.lcMode = procparams::LensProfParams::LcMode::LCP;
|
||||||
}
|
}
|
||||||
|
else if (corrMetadata->get_active()) {
|
||||||
|
pp->lensProf.lcMode = procparams::LensProfParams::LcMode::METADATA;
|
||||||
|
}
|
||||||
else if (corrLensfunManualRB->get_active()) {
|
else if (corrLensfunManualRB->get_active()) {
|
||||||
pp->lensProf.lcMode = procparams::LensProfParams::LcMode::LENSFUNMANUAL;
|
pp->lensProf.lcMode = procparams::LensProfParams::LcMode::LENSFUNMANUAL;
|
||||||
}
|
}
|
||||||
@ -367,12 +396,18 @@ void LensProfilePanel::setRawMeta(bool raw, const rtengine::FramesMetaData* pMet
|
|||||||
|
|
||||||
// CA is very focus layer dependent, otherwise it might even worsen things
|
// CA is very focus layer dependent, otherwise it might even worsen things
|
||||||
allowFocusDep = false;
|
allowFocusDep = false;
|
||||||
ckbUseCA->set_active(false);
|
|
||||||
ckbUseCA->set_sensitive(false);
|
|
||||||
|
|
||||||
enableListener();
|
enableListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
corrMetadata->set_sensitive(false);
|
||||||
|
if (pMeta) {
|
||||||
|
metadataCorrection = MetadataLensCorrectionFinder::findCorrection(pMeta);
|
||||||
|
if (metadataCorrection) {
|
||||||
|
corrMetadata->set_sensitive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isRaw = raw;
|
isRaw = raw;
|
||||||
metadata = pMeta;
|
metadata = pMeta;
|
||||||
}
|
}
|
||||||
@ -381,7 +416,7 @@ void LensProfilePanel::onLCPFileChanged()
|
|||||||
{
|
{
|
||||||
lcpFileChanged = true;
|
lcpFileChanged = true;
|
||||||
const bool valid = LCPStore::getInstance()->isValidLCPFileName(corrLcpFileChooser->get_filename());
|
const bool valid = LCPStore::getInstance()->isValidLCPFileName(corrLcpFileChooser->get_filename());
|
||||||
updateDisabled(valid);
|
updateLCPDisabled(valid);
|
||||||
|
|
||||||
if (listener) {
|
if (listener) {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
@ -552,10 +587,20 @@ void LensProfilePanel::onCorrModeChanged(const Gtk::RadioButton* rbChanged)
|
|||||||
lensfunAutoChanged = true;
|
lensfunAutoChanged = true;
|
||||||
lcpFileChanged = true;
|
lcpFileChanged = true;
|
||||||
|
|
||||||
updateDisabled(true);
|
updateLCPDisabled(true);
|
||||||
|
|
||||||
mode = M("TP_LENSPROFILE_CORRECTION_LCPFILE");
|
mode = M("TP_LENSPROFILE_CORRECTION_LCPFILE");
|
||||||
|
|
||||||
|
} else if (rbChanged == corrMetadata) {
|
||||||
|
lcModeChanged = true;
|
||||||
|
useLensfunChanged = true;
|
||||||
|
lensfunAutoChanged = true;
|
||||||
|
lcpFileChanged = true;
|
||||||
|
|
||||||
|
updateMetadataDisabled();
|
||||||
|
|
||||||
|
mode = M("TP_LENSPROFILE_CORRECTION_METADATA");
|
||||||
|
|
||||||
} else if (rbChanged == corrUnchangedRB) {
|
} else if (rbChanged == corrUnchangedRB) {
|
||||||
lcModeChanged = false;
|
lcModeChanged = false;
|
||||||
useLensfunChanged = false;
|
useLensfunChanged = false;
|
||||||
@ -680,7 +725,7 @@ void LensProfilePanel::LFDbHelper::fillLensfunLenses()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LensProfilePanel::updateDisabled(bool enable)
|
void LensProfilePanel::updateLCPDisabled(bool enable)
|
||||||
{
|
{
|
||||||
if (!batchMode) {
|
if (!batchMode) {
|
||||||
ckbUseDist->set_sensitive(enable);
|
ckbUseDist->set_sensitive(enable);
|
||||||
@ -689,6 +734,21 @@ void LensProfilePanel::updateDisabled(bool enable)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LensProfilePanel::updateMetadataDisabled()
|
||||||
|
{
|
||||||
|
if (!batchMode) {
|
||||||
|
if (metadataCorrection) {
|
||||||
|
ckbUseDist->set_sensitive(metadataCorrection->hasDistortionCorrection());
|
||||||
|
ckbUseVign->set_sensitive(metadataCorrection->hasVignettingCorrection());
|
||||||
|
ckbUseCA->set_sensitive(metadataCorrection->hasCACorrection());
|
||||||
|
} else {
|
||||||
|
ckbUseDist->set_sensitive(false);
|
||||||
|
ckbUseVign->set_sensitive(false);
|
||||||
|
ckbUseCA->set_sensitive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool LensProfilePanel::setLensfunCamera(const Glib::ustring& make, const Glib::ustring& model)
|
bool LensProfilePanel::setLensfunCamera(const Glib::ustring& make, const Glib::ustring& model)
|
||||||
{
|
{
|
||||||
if (!make.empty() && !model.empty()) {
|
if (!make.empty() && !model.empty()) {
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "guiutils.h"
|
#include "guiutils.h"
|
||||||
#include "toolpanel.h"
|
#include "toolpanel.h"
|
||||||
|
#include "../rtengine/lensmetadata.h"
|
||||||
|
|
||||||
class LensProfilePanel final :
|
class LensProfilePanel final :
|
||||||
public ToolParamBlock,
|
public ToolParamBlock,
|
||||||
@ -89,7 +90,8 @@ private:
|
|||||||
void fillLensfunLenses();
|
void fillLensfunLenses();
|
||||||
};
|
};
|
||||||
|
|
||||||
void updateDisabled(bool enable);
|
void updateLCPDisabled(bool enable);
|
||||||
|
void updateMetadataDisabled();
|
||||||
|
|
||||||
bool setLensfunCamera(const Glib::ustring& make, const Glib::ustring& model);
|
bool setLensfunCamera(const Glib::ustring& make, const Glib::ustring& model);
|
||||||
bool setLensfunLens(const Glib::ustring& lens);
|
bool setLensfunLens(const Glib::ustring& lens);
|
||||||
@ -113,12 +115,14 @@ private:
|
|||||||
bool allowFocusDep;
|
bool allowFocusDep;
|
||||||
bool isRaw;
|
bool isRaw;
|
||||||
const rtengine::FramesMetaData* metadata;
|
const rtengine::FramesMetaData* metadata;
|
||||||
|
std::unique_ptr<rtengine::MetadataLensCorrection> metadataCorrection;
|
||||||
|
|
||||||
Gtk::Grid* const modesGrid;
|
Gtk::Grid* const modesGrid;
|
||||||
Gtk::Grid* const distGrid;
|
Gtk::Grid* const distGrid;
|
||||||
Gtk::RadioButton* const corrUnchangedRB;
|
Gtk::RadioButton* const corrUnchangedRB;
|
||||||
Gtk::RadioButton::Group corrGroup;
|
Gtk::RadioButton::Group corrGroup;
|
||||||
Gtk::RadioButton* const corrOffRB;
|
Gtk::RadioButton* const corrOffRB;
|
||||||
|
Gtk::RadioButton* const corrMetadata;
|
||||||
Gtk::RadioButton* const corrLensfunAutoRB;
|
Gtk::RadioButton* const corrLensfunAutoRB;
|
||||||
Gtk::RadioButton* const corrLensfunManualRB;
|
Gtk::RadioButton* const corrLensfunManualRB;
|
||||||
Gtk::RadioButton* const corrLcpFileRB;
|
Gtk::RadioButton* const corrLcpFileRB;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user