| Message ID | 20260821125958.95928-4-mzamazal@redhat.com |
|---|---|
| State | Superseded |
| Headers | show |
| Series |
|
| Related | show |
2026. 08. 21. 14:59 keltezéssel, Milan Zamazal írta: > From: Xander Pronk <xander.c.pronk@gmail.com> > > The algorithm is based on the common libipa lens shading correction > implementation. The grid values obtained from the libipa algorithm are > passed to the debayer algorithm as an array and used as an an RGB > texture. > > Notes on the implementation: > > - The overall idea is to keep things simple, to not make the LSC > computation unnecessarily expensive. > > - LscAlgorithm accepts only quantised types. UQ<2,6> is used, to be > converted to float in debayering. > > - The limit of 100 degrees to consider a temperature change noticeable > is arbitrary. > > Co-developed-by: Rick ten Wolde <rick_libcamera@wolde.info> > Signed-off-by: Rick ten Wolde <rick_libcamera@wolde.info> > Signed-off-by: Xander Pronk <xander.c.pronk@gmail.com> > Signed-off-by: Milan Zamazal <mzamazal@redhat.com> > --- > .../internal/software_isp/debayer_params.h | 12 ++- > src/ipa/softisp/algorithms/lsc.cpp | 97 +++++++++++++++++++ > src/ipa/softisp/algorithms/lsc.h | 51 ++++++++++ > src/ipa/softisp/algorithms/meson.build | 1 + > src/ipa/softisp/ipa_context.h | 5 + > src/ipa/softisp/softisp.cpp | 2 + > src/libcamera/software_isp/debayer.cpp | 8 ++ > 7 files changed, 175 insertions(+), 1 deletion(-) > create mode 100644 src/ipa/softisp/algorithms/lsc.cpp > create mode 100644 src/ipa/softisp/algorithms/lsc.h > > diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h > index 93dcf42e6..b7860c9dd 100644 > --- a/include/libcamera/internal/software_isp/debayer_params.h > +++ b/include/libcamera/internal/software_isp/debayer_params.h > @@ -35,7 +35,17 @@ struct DebayerParams { > static constexpr unsigned int kLscValuesPerCell = 4; > using LscLookupTable = > std::array<uint8_t, kLscGridSize * kLscGridSize * kLscValuesPerCell>; > - LscLookupTable lscLut{}; > + static constexpr auto identityLscLut = [] { > + LscLookupTable lut = {}; > + /* lut.fill(64) could be used, but it fails with older gcc versions */ > + for (size_t i = 0; kLscValuesPerCell * i < lut.size(); i++) { > + lut[i * kLscValuesPerCell + 0] = 64; /* == UQ<2, 6>(1.0f).quantized() */ > + lut[i * kLscValuesPerCell + 1] = 64; > + lut[i * kLscValuesPerCell + 2] = 64; > + } > + return lut; > + }(); > + LscLookupTable lscLut = identityLscLut; > uint64_t lscLutVersion = 0; > }; > > diff --git a/src/ipa/softisp/algorithms/lsc.cpp b/src/ipa/softisp/algorithms/lsc.cpp > new file mode 100644 > index 000000000..e05e1bb89 > --- /dev/null > +++ b/src/ipa/softisp/algorithms/lsc.cpp > @@ -0,0 +1,97 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Lens shading correction > + */ > + > +#include "lsc.h" > + > +#include <libcamera/base/log.h> > + > +namespace libcamera { > + > +namespace ipa::softisp::algorithms { > + > +LOG_DEFINE_CATEGORY(IPASoftLsc) `IPASoftIspLsc` to be consistent with the recent rename. > + > +int Lsc::init(IPAContext &context, const ValueNode &tuningData) > +{ > + static constexpr unsigned int kGridSize = DebayerParams::kLscGridSize; > + > + for (unsigned int i = 0; i < kGridSize; i++) > + gridPos_.push_back(static_cast<double>(i) / (kGridSize - 1)); > + > + return lscAlgo_.init(tuningData, context.ctrlMap, > + { .keys = { "r", "g", "b" }, > + .numHSamples = kGridSize, > + .numVSamples = kGridSize, > + .sensorSize = context.sensorInfo.activeAreaSize }); > +} > + > +int Lsc::configure(IPAContext &context, > + [[maybe_unused]] const IPAConfigInfo &configInfo) > +{ > + return lscAlgo_.configure(context.activeState.lsc, > + context.sensorInfo.analogCrop, > + gridPos_, gridPos_); > +} > + > +void Lsc::prepare([[maybe_unused]] IPAContext &context, > + [[maybe_unused]] const uint32_t frame, > + IPAFrameContext &frameContext, > + DebayerParams *params) > +{ > + unsigned int ct = frameContext.awb.colourTemperature; > + constexpr unsigned int minTemperatureChange = 100; > + > + if (!frameContext.lsc.enabled) { > + if (lastAppliedCt_ != 0) { > + params->lscLut = DebayerParams::identityLscLut; > + params->lscLutVersion++; > + lastAppliedCt_ = 0; > + } > + return; > + } > + > + if (utils::abs_diff(ct, lastAppliedCt_) < minTemperatureChange) > + return; > + > + const auto &set = lscAlgo_.interpolateComponents(ct); > + > + const auto &red = set.at("r"); > + const auto &green = set.at("g"); > + const auto &blue = set.at("b"); > + > + DebayerParams::LscLookupTable &lut = params->lscLut; > + constexpr unsigned int gridSize = DebayerParams::kLscGridSize; > + for (unsigned int i = 0, j = 0; i < gridSize * gridSize; i++) { > + lut[j++] = red[i]; > + lut[j++] = green[i]; > + lut[j++] = blue[i]; > + lut[j++] = 0; /* padding */ > + } > + params->lscLutVersion++; I think it would be nice to get a debug log message with `ct` and `lastAppliedCt_` and the new version here. Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> > + > + lastAppliedCt_ = ct; > +} > + > +void Lsc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, > + IPAFrameContext &frameContext, const ControlList &controls) > +{ > + lscAlgo_.queueRequest(context.activeState.lsc, frameContext.lsc, > + controls); > +} > + > +void Lsc::process([[maybe_unused]] IPAContext &context, > + [[maybe_unused]] const uint32_t frame, > + IPAFrameContext &frameContext, > + [[maybe_unused]] const SwIspStats *stats, > + ControlList &metadata) > +{ > + lscAlgo_.process(frameContext.lsc, metadata); > +} > + > +REGISTER_IPA_ALGORITHM(Lsc, "Lsc") > + > +} /* namespace ipa::softisp::algorithms */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/softisp/algorithms/lsc.h b/src/ipa/softisp/algorithms/lsc.h > new file mode 100644 > index 000000000..83ec5aec2 > --- /dev/null > +++ b/src/ipa/softisp/algorithms/lsc.h > @@ -0,0 +1,51 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Lens shading correction > + */ > + > +#pragma once > + > +#include <libipa/interpolator.h> > + > +#include "libipa/fixedpoint.h" > +#include "libipa/lsc.h" > + > +#include "algorithm.h" > +#include "ipa_context.h" > + > +namespace libcamera { > + > +namespace ipa::softisp::algorithms { > + > +class Lsc : public Algorithm > +{ > +public: > + Lsc() = default; > + ~Lsc() = default; > + > + int init(IPAContext &context, const ValueNode &tuningData) override; > + int configure(IPAContext &context, > + const IPAConfigInfo &configInfo) override; > + void queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, > + IPAFrameContext &frameContext, const ControlList &controls) override; > + void prepare(IPAContext &context, > + const uint32_t frame, > + IPAFrameContext &frameContext, > + DebayerParams *params) override; > + void process([[maybe_unused]] IPAContext &context, > + [[maybe_unused]] const uint32_t frame, > + IPAFrameContext &frameContext, > + [[maybe_unused]] const SwIspStats *stats, > + ControlList &metadata) override; > + > +private: > + LscAlgorithm<UQ<2, 6>> lscAlgo_; > + > + std::vector<double> gridPos_; > + > + unsigned int lastAppliedCt_ = 0; > +}; > + > +} /* namespace ipa::softisp::algorithms */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/softisp/algorithms/meson.build b/src/ipa/softisp/algorithms/meson.build > index d240409e5..d98f4b1f4 100644 > --- a/src/ipa/softisp/algorithms/meson.build > +++ b/src/ipa/softisp/algorithms/meson.build > @@ -6,4 +6,5 @@ softisp_ipa_algorithms = files([ > 'agc.cpp', > 'blc.cpp', > 'ccm.cpp', > + 'lsc.cpp', > ]) > diff --git a/src/ipa/softisp/ipa_context.h b/src/ipa/softisp/ipa_context.h > index 2eed32f88..e9c3eef0e 100644 > --- a/src/ipa/softisp/ipa_context.h > +++ b/src/ipa/softisp/ipa_context.h > @@ -19,6 +19,7 @@ > #include <libipa/awb.h> > #include <libipa/ccm.h> > #include <libipa/fc_queue.h> > +#include "libipa/lsc.h" > > #include "core_ipa_interface.h" > > @@ -61,6 +62,8 @@ struct IPAActiveState { > std::optional<float> contrast; > std::optional<float> saturation; > } knobs; > + > + ipa::lsc::ActiveState lsc; > }; > > struct IPAFrameContext : public FrameContext { > @@ -75,6 +78,7 @@ struct IPAFrameContext : public FrameContext { > float gamma; > std::optional<float> contrast; > std::optional<float> saturation; > + ipa::lsc::FrameContext lsc; > }; > > struct IPAContext { > @@ -89,6 +93,7 @@ struct IPAContext { > FCQueue<IPAFrameContext> frameContexts; > ControlInfoMap::Map ctrlMap; > bool ccmEnabled = false; > + ipa::lsc::ActiveState lsc; > }; > > } /* namespace ipa::softisp */ > diff --git a/src/ipa/softisp/softisp.cpp b/src/ipa/softisp/softisp.cpp > index aec04c528..41d3e23d8 100644 > --- a/src/ipa/softisp/softisp.cpp > +++ b/src/ipa/softisp/softisp.cpp > @@ -163,6 +163,8 @@ int IPASoftIsp::init(const IPASettings &settings, > params_->gamma = 1.0 / algorithms::kDefaultGamma; > params_->contrastExp = 1.0; > params_->gains = { { 1.0, 1.0, 1.0 } }; > + params_->lscLutVersion = 0; > + params_->lscLut = DebayerParams::identityLscLut; > /* combinedMatrix is reset for each frame. */ > } > > diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp > index 82cdf1dc2..6ebf7ed1a 100644 > --- a/src/libcamera/software_isp/debayer.cpp > +++ b/src/libcamera/software_isp/debayer.cpp > @@ -66,6 +66,14 @@ namespace libcamera { > * \brief Lens shading lookup table > */ > > +/** > + * \var DebayerParams::identityLscLut > + * \brief Lens shading lookup table of identity mapping > + * > + * This table can be used to initialise the lens shading lookup table or to > + * not apply any real lens shading correction. > + */ > + > /** > * \var DebayerParams::lscLutVersion > * \brief Incremented on each \a lscLut change
diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h index 93dcf42e6..b7860c9dd 100644 --- a/include/libcamera/internal/software_isp/debayer_params.h +++ b/include/libcamera/internal/software_isp/debayer_params.h @@ -35,7 +35,17 @@ struct DebayerParams { static constexpr unsigned int kLscValuesPerCell = 4; using LscLookupTable = std::array<uint8_t, kLscGridSize * kLscGridSize * kLscValuesPerCell>; - LscLookupTable lscLut{}; + static constexpr auto identityLscLut = [] { + LscLookupTable lut = {}; + /* lut.fill(64) could be used, but it fails with older gcc versions */ + for (size_t i = 0; kLscValuesPerCell * i < lut.size(); i++) { + lut[i * kLscValuesPerCell + 0] = 64; /* == UQ<2, 6>(1.0f).quantized() */ + lut[i * kLscValuesPerCell + 1] = 64; + lut[i * kLscValuesPerCell + 2] = 64; + } + return lut; + }(); + LscLookupTable lscLut = identityLscLut; uint64_t lscLutVersion = 0; }; diff --git a/src/ipa/softisp/algorithms/lsc.cpp b/src/ipa/softisp/algorithms/lsc.cpp new file mode 100644 index 000000000..e05e1bb89 --- /dev/null +++ b/src/ipa/softisp/algorithms/lsc.cpp @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Lens shading correction + */ + +#include "lsc.h" + +#include <libcamera/base/log.h> + +namespace libcamera { + +namespace ipa::softisp::algorithms { + +LOG_DEFINE_CATEGORY(IPASoftLsc) + +int Lsc::init(IPAContext &context, const ValueNode &tuningData) +{ + static constexpr unsigned int kGridSize = DebayerParams::kLscGridSize; + + for (unsigned int i = 0; i < kGridSize; i++) + gridPos_.push_back(static_cast<double>(i) / (kGridSize - 1)); + + return lscAlgo_.init(tuningData, context.ctrlMap, + { .keys = { "r", "g", "b" }, + .numHSamples = kGridSize, + .numVSamples = kGridSize, + .sensorSize = context.sensorInfo.activeAreaSize }); +} + +int Lsc::configure(IPAContext &context, + [[maybe_unused]] const IPAConfigInfo &configInfo) +{ + return lscAlgo_.configure(context.activeState.lsc, + context.sensorInfo.analogCrop, + gridPos_, gridPos_); +} + +void Lsc::prepare([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + DebayerParams *params) +{ + unsigned int ct = frameContext.awb.colourTemperature; + constexpr unsigned int minTemperatureChange = 100; + + if (!frameContext.lsc.enabled) { + if (lastAppliedCt_ != 0) { + params->lscLut = DebayerParams::identityLscLut; + params->lscLutVersion++; + lastAppliedCt_ = 0; + } + return; + } + + if (utils::abs_diff(ct, lastAppliedCt_) < minTemperatureChange) + return; + + const auto &set = lscAlgo_.interpolateComponents(ct); + + const auto &red = set.at("r"); + const auto &green = set.at("g"); + const auto &blue = set.at("b"); + + DebayerParams::LscLookupTable &lut = params->lscLut; + constexpr unsigned int gridSize = DebayerParams::kLscGridSize; + for (unsigned int i = 0, j = 0; i < gridSize * gridSize; i++) { + lut[j++] = red[i]; + lut[j++] = green[i]; + lut[j++] = blue[i]; + lut[j++] = 0; /* padding */ + } + params->lscLutVersion++; + + lastAppliedCt_ = ct; +} + +void Lsc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, const ControlList &controls) +{ + lscAlgo_.queueRequest(context.activeState.lsc, frameContext.lsc, + controls); +} + +void Lsc::process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const SwIspStats *stats, + ControlList &metadata) +{ + lscAlgo_.process(frameContext.lsc, metadata); +} + +REGISTER_IPA_ALGORITHM(Lsc, "Lsc") + +} /* namespace ipa::softisp::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/softisp/algorithms/lsc.h b/src/ipa/softisp/algorithms/lsc.h new file mode 100644 index 000000000..83ec5aec2 --- /dev/null +++ b/src/ipa/softisp/algorithms/lsc.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Lens shading correction + */ + +#pragma once + +#include <libipa/interpolator.h> + +#include "libipa/fixedpoint.h" +#include "libipa/lsc.h" + +#include "algorithm.h" +#include "ipa_context.h" + +namespace libcamera { + +namespace ipa::softisp::algorithms { + +class Lsc : public Algorithm +{ +public: + Lsc() = default; + ~Lsc() = default; + + int init(IPAContext &context, const ValueNode &tuningData) override; + int configure(IPAContext &context, + const IPAConfigInfo &configInfo) override; + void queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, const ControlList &controls) override; + void prepare(IPAContext &context, + const uint32_t frame, + IPAFrameContext &frameContext, + DebayerParams *params) override; + void process([[maybe_unused]] IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + [[maybe_unused]] const SwIspStats *stats, + ControlList &metadata) override; + +private: + LscAlgorithm<UQ<2, 6>> lscAlgo_; + + std::vector<double> gridPos_; + + unsigned int lastAppliedCt_ = 0; +}; + +} /* namespace ipa::softisp::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/softisp/algorithms/meson.build b/src/ipa/softisp/algorithms/meson.build index d240409e5..d98f4b1f4 100644 --- a/src/ipa/softisp/algorithms/meson.build +++ b/src/ipa/softisp/algorithms/meson.build @@ -6,4 +6,5 @@ softisp_ipa_algorithms = files([ 'agc.cpp', 'blc.cpp', 'ccm.cpp', + 'lsc.cpp', ]) diff --git a/src/ipa/softisp/ipa_context.h b/src/ipa/softisp/ipa_context.h index 2eed32f88..e9c3eef0e 100644 --- a/src/ipa/softisp/ipa_context.h +++ b/src/ipa/softisp/ipa_context.h @@ -19,6 +19,7 @@ #include <libipa/awb.h> #include <libipa/ccm.h> #include <libipa/fc_queue.h> +#include "libipa/lsc.h" #include "core_ipa_interface.h" @@ -61,6 +62,8 @@ struct IPAActiveState { std::optional<float> contrast; std::optional<float> saturation; } knobs; + + ipa::lsc::ActiveState lsc; }; struct IPAFrameContext : public FrameContext { @@ -75,6 +78,7 @@ struct IPAFrameContext : public FrameContext { float gamma; std::optional<float> contrast; std::optional<float> saturation; + ipa::lsc::FrameContext lsc; }; struct IPAContext { @@ -89,6 +93,7 @@ struct IPAContext { FCQueue<IPAFrameContext> frameContexts; ControlInfoMap::Map ctrlMap; bool ccmEnabled = false; + ipa::lsc::ActiveState lsc; }; } /* namespace ipa::softisp */ diff --git a/src/ipa/softisp/softisp.cpp b/src/ipa/softisp/softisp.cpp index aec04c528..41d3e23d8 100644 --- a/src/ipa/softisp/softisp.cpp +++ b/src/ipa/softisp/softisp.cpp @@ -163,6 +163,8 @@ int IPASoftIsp::init(const IPASettings &settings, params_->gamma = 1.0 / algorithms::kDefaultGamma; params_->contrastExp = 1.0; params_->gains = { { 1.0, 1.0, 1.0 } }; + params_->lscLutVersion = 0; + params_->lscLut = DebayerParams::identityLscLut; /* combinedMatrix is reset for each frame. */ } diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp index 82cdf1dc2..6ebf7ed1a 100644 --- a/src/libcamera/software_isp/debayer.cpp +++ b/src/libcamera/software_isp/debayer.cpp @@ -66,6 +66,14 @@ namespace libcamera { * \brief Lens shading lookup table */ +/** + * \var DebayerParams::identityLscLut + * \brief Lens shading lookup table of identity mapping + * + * This table can be used to initialise the lens shading lookup table or to + * not apply any real lens shading correction. + */ + /** * \var DebayerParams::lscLutVersion * \brief Incremented on each \a lscLut change