From 27dc084e81b3145eae103919a2395d3d25b0c036 Mon Sep 17 00:00:00 2001 From: Simone Gotti Date: Thu, 13 Jun 2024 20:26:22 +0200 Subject: [PATCH] Add Sony metadata lens correction --- rtengine/lensmetadata.cc | 163 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/rtengine/lensmetadata.cc b/rtengine/lensmetadata.cc index c531de65b..0e8156f39 100644 --- a/rtengine/lensmetadata.cc +++ b/rtengine/lensmetadata.cc @@ -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 &xi, const std::vector &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 . +*/ + +class SonyMetadataLensCorrection : public CenterRadiusMetadataLensCorrection +{ +public: + SonyMetadataLensCorrection(const FramesMetaData *meta) : + CenterRadiusMetadataLensCorrection(meta) + { + parse(); + setup(); + } + +private: + int nc; + std::array distortion; + std::array ca_r; + std::array ca_b; + std::array vignetting; + + std::vector knots; + std::vector dist; + std::array, 3> ca; + std::vector 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 MetadataLensCorrectionFinder::findCorrection(const FramesMetaData *meta) { - static const std::unordered_set makers = {}; + static const std::unordered_set makers = { + "SONY", + }; std::string make = Glib::ustring(meta->getMake()).uppercase(); @@ -144,6 +293,18 @@ std::unique_ptr MetadataLensCorrectionFinder::findCorrec std::unique_ptr 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; }