Add Sony metadata lens correction

This commit is contained in:
Simone Gotti 2024-06-13 20:26:22 +02:00
parent f64ad13363
commit 27dc084e81

View File

@ -24,6 +24,31 @@
namespace rtengine
{
namespace
{
/* interpolateLinearSpline does a simple linear spline interpolation. Values
* outside the external knots will return the value of the nearest knot without
* any additional interpolation. */
double interpolateLinearSpline(const std::vector<double> &xi, const std::vector<double> &yi, double x)
{
if (x < xi[0]) {
return yi[0];
}
for (size_t i = 1; i < xi.size(); i++) {
if (x >= xi[i - 1] && x <= xi[i]) {
double dydx = (yi[i] - yi[i - 1]) / (xi[i] - xi[i - 1]);
return yi[i - 1] + (x - xi[i - 1]) * dydx;
}
}
return yi[yi.size() - 1];
}
} // namespace
CenterRadiusMetadataLensCorrection::CenterRadiusMetadataLensCorrection(const FramesMetaData *meta) :
swap_xy(false)
{
@ -132,9 +157,133 @@ void CenterRadiusMetadataLensCorrection::processVignette3Channels(int width, int
return processVignetteNChannels(width, height, rawData, 3);
}
/* Fuji, Sony, Olympus metadata handling and algorithms adapted from
* - src/iop/lens.cc
* - src/common/exif.cc
* in darktable 4.6 */
/*
This file is part of darktable,
Copyright (C) 2019-2024 darktable developers.
darktable 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.
darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
*/
class SonyMetadataLensCorrection : public CenterRadiusMetadataLensCorrection
{
public:
SonyMetadataLensCorrection(const FramesMetaData *meta) :
CenterRadiusMetadataLensCorrection(meta)
{
parse();
setup();
}
private:
int nc;
std::array<short, 16> distortion;
std::array<short, 16> ca_r;
std::array<short, 16> ca_b;
std::array<short, 16> vignetting;
std::vector<double> knots;
std::vector<double> dist;
std::array<std::vector<double>, 3> ca;
std::vector<double> vig;
void parse()
{
if (Exiv2::versionNumber() < EXIV2_MAKE_VERSION(0, 27, 4)) {
throw std::runtime_error("cannot get Sony correction data, too old exiv2 version " + Exiv2::versionString());
}
auto &exif = metadata.exifData();
auto posd = exif.findKey(Exiv2::ExifKey("Exif.SubImage1.DistortionCorrParams"));
auto posc = exif.findKey(Exiv2::ExifKey("Exif.SubImage1.ChromaticAberrationCorrParams"));
auto posv = exif.findKey(Exiv2::ExifKey("Exif.SubImage1.VignettingCorrParams"));
/* Sony metadata corrections parameters define some splines with N knots */
if (posd == exif.end() || posc == exif.end() || posv == exif.end()) {
throw std::runtime_error("cannot get Sony correction data");
}
const int nc = to_long(posd);
if (nc <= 16 && 2 * nc == to_long(posc) && nc == to_long(posv)) {
this->nc = nc;
for (int i = 0; i < nc; i++) {
distortion[i] = to_long(posd, i + 1);
ca_r[i] = to_long(posc, i + 1);
ca_b[i] = to_long(posc, nc + i + 1);
vignetting[i] = to_long(posv, i + 1);
}
} else {
throw std::runtime_error("cannot get Sony correction data");
}
}
void setup()
{
knots.resize(nc);
dist.resize(nc);
vig.resize(nc);
for (int i = 0; i < 3; ++i) {
ca[i].resize(nc);
}
for (int i = 0; i < this->nc; i++) {
knots[i] = (i + 0.5) / (nc - 1);
dist[i] = distortion[i] * powf(2, -14) + 1;
ca[0][i] = ca[1][i] = ca[2][i] = 1.f;
ca[0][i] *= ca_r[i] * powf(2, -21) + 1;
ca[2][i] *= ca_b[i] * powf(2, -21) + 1;
vig[i] = 1 / powf(2, 0.5f - powf(2, vignetting[i] * powf(2, -13) - 1));
}
}
double distortionCorrectionFactor(double rout) const override
{
return interpolateLinearSpline(knots, dist, rout);
}
double caCorrectionFactor(double rout, int channel) const override
{
return interpolateLinearSpline(knots, ca[channel], rout);
}
double distortionAndCACorrectionFactor(double rout, int channel) const override
{
return distortionCorrectionFactor(rout) * caCorrectionFactor(rout, channel);
}
double vignettingCorrectionFactor(double r) const override
{
return interpolateLinearSpline(knots, vig, r);
}
bool hasDistortionCorrection() const override { return true; }
bool hasVignettingCorrection() const override { return true; }
bool hasCACorrection() const override { return true; }
};
std::unique_ptr<MetadataLensCorrection> MetadataLensCorrectionFinder::findCorrection(const FramesMetaData *meta)
{
static const std::unordered_set<std::string> makers = {};
static const std::unordered_set<std::string> makers = {
"SONY",
};
std::string make = Glib::ustring(meta->getMake()).uppercase();
@ -144,6 +293,18 @@ std::unique_ptr<MetadataLensCorrection> MetadataLensCorrectionFinder::findCorrec
std::unique_ptr<MetadataLensCorrection> correction;
try {
if (make == "SONY") {
correction.reset(new SonyMetadataLensCorrection(meta));
}
} catch (std::exception &exc) {
if (settings->verbose) {
std::cerr << "error parsing lens metadata: " << exc.what() << std::endl;
}
correction.reset(nullptr);
}
return correction;
}