[{"id":40061,"web_url":"https://patchwork.libcamera.org/comment/40061/","msgid":"<178595030998.2300677.11408272210622088958@ping.linuxembedded.co.uk>","date":"2026-08-05T17:18:29","subject":"Re: [PATCH v7 28/32] ipa: libipa: lsc: Quantize gains in\n\tLscAlgorithm","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Jacopo Mondi (2026-08-05 17:13:10)\n> Template the LscAlgorithm class with the LSC engine register format and\n> make lsc::Components<> parameteric on the type used to store the\n> quantized LSC gains.\n> \n> This allows to quantize gains in the LscAlgorithm class and not in the\n> IPA modules.\n> \n> Move the Components<> and ComponentsMap<> type to lsc.h in order for IPA\n> modules to be able to access the quantized gains, and make the existing\n> float-based Components internal to LscImplementation to be used as the\n> exchange format between LscImplementation and LscAlgorithm.\n> \n> Split the LscAlgorithm class in two, one LscAlgorithmBase one for\n> non-templated function implementations and one for the templated\n> configure() implementation and for templated data members and accessor.\n> \n> Note that currently table-based LSC data do not require quantization as\n> they're already expressed in register format in tuning files. This will\n> change soon to make it easier to re-use LSC data between different\n> platforms by expressing gains in floating point format in tuning files\n> as well.\n> \n> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> ---\n>  src/ipa/libipa/lsc.cpp            | 134 +++++++++++++++++++++++++++++---------\n>  src/ipa/libipa/lsc.h              |  83 +++++++++++++++++++----\n>  src/ipa/libipa/lsc_base.cpp       |  75 +++++++++------------\n>  src/ipa/libipa/lsc_base.h         |  31 ++-------\n>  src/ipa/libipa/lsc_polynomial.cpp |   6 +-\n>  src/ipa/libipa/lsc_polynomial.h   |   2 +-\n>  src/ipa/libipa/lsc_table.cpp      |   2 +-\n>  src/ipa/libipa/lsc_table.h        |   4 +-\n>  src/ipa/rkisp1/algorithms/lsc.cpp |  28 +++-----\n>  src/ipa/rkisp1/algorithms/lsc.h   |   6 +-\n>  10 files changed, 231 insertions(+), 140 deletions(-)\n> \n> diff --git a/src/ipa/libipa/lsc.cpp b/src/ipa/libipa/lsc.cpp\n> index 8c5c28077cce..bb02290fc9ad 100644\n> --- a/src/ipa/libipa/lsc.cpp\n> +++ b/src/ipa/libipa/lsc.cpp\n> @@ -46,11 +46,91 @@ namespace lsc {\n>   * \\brief Boolean flag for the LscAlgorithm updated status\n>   */\n>  \n> +/**\n> + * \\typedef Components\n> + * \\brief Associate colour components with a list of gains in register format\n> + * \\tparam T The type used to store the gain values\n> + *\n> + * LSC tables are defined as a list of gain values associated to a colour\n> + * component.\n> + *\n> + * As different ISPs support different colour components (usually 'r', 'gr',\n> + * 'gb', 'b' or just 'r', 'g', 'b') this class associates a string\n> + * identifier for the colour component to a list of gains.\n> + *\n> + * Each key name shall match an entry in the tuning file.\n> + *\n> + * The list of keys is provided to the LscAlgorithm class using \\a\n> + * LscDescriptor::keys.\n> + *\n> + * The gain values are expressed in fixed-point register format stored in\n> + * variabled of type \\a T, as each platform potentially has a different register\n> + * width.\n> + */\n> +\n> +/**\n> + * \\typedef ComponentsMap\n> + * \\brief Associate a colour temperature to a LSC table\n> + * \\tparam T The type of the gain values\n> + *\n> + * An LSC table is generated during the tuning phase for a specific colour\n> + * temperature, and a tuning file usually contains LSC tables generated for\n> + * several different colour temperatures.\n> + */\n> +\n>  } /* namespace lsc */\n>  \n> +#ifndef __DOXYGEN__\n> +template<typename T>\n> +void interpolateVector(const std::vector<T> &a,\n> +                      const std::vector<T> &b,\n> +                      std::vector<T> &dest, double lambda)\n> +{\n> +       ASSERT(a.size() == b.size());\n> +       dest.resize(a.size());\n> +       for (size_t i = 0; i < a.size(); i++)\n> +               dest[i] = a[i] * (1.0 - lambda) + b[i] * lambda;\n> +}\n> +\n> +template<>\n> +void Interpolator<lsc::Components<uint16_t>>::\n> +       interpolate(const lsc::Components<uint16_t> &a,\n> +                   const lsc::Components<uint16_t> &b,\n> +                   lsc::Components<uint16_t> &dest,\n> +                   double lambda)\n> +{\n> +       for (auto const &[k, v] : a)\n> +               interpolateVector(v, b.at(k), dest[k], lambda);\n> +}\n> +#endif\n> +\n> +/**\n> + * \\class LscAlgorithmBase\n> + * \\brief Base class for LscAlgorithm\n> + *\n> + * Base class for LscAlgorithm for non-templated functions implementation\n> + */\n> +\n> +/**\n> + * \\var LscAlgorithmBase::impl_\n> + * \\brief The LSC algorithm implementation\n> + *\n> + * There are two classes derived from LscImplementation, the LscTable and\n> + * LscPolynomial ones. Which one to instantiate is decided by parsing the tuning\n> + * file.\n> + */\n> +\n> +/**\n> + * \\var LscAlgorithmBase::polynomial_\n> + * \\brief Boolean flag for polynomial LSC\n> + *\n> + * Set to true if polynomial LSC is in use.\n> + */\n> +\n>  /**\n>   * \\class LscAlgorithm\n>   * \\brief libIPA LSC algorithm implementation\n> + * \\tparam U The platform fixed-point register format representation\n>   *\n>   * Due to the optical characteristics of the lens, the light intensity received\n>   * by the sensor is not uniform. The Lens Shading Correction algorithm applies\n> @@ -192,6 +272,14 @@ namespace lsc {\n>   *\n>   * \\todo Implement grid based re-sampling\n>   *\n> + * After re-sampling, the LSC tables gain values are convereted from their\n\ns/convereted/converted/\n\n> + * floating point representation (LscImplementation::Components) to the\n> + * platform's register representation (lsc::Components<>). Grid-based LSC tables\n> + * currently already contain gains represented in register format, so no\n> + * quantization is necessary but only a simple cast is required.\n> + *\n> + * \\todo Express gains in floating point format for grid-based LSC tables\n> + *\n>   * When the IPA algorithms wants to get access to the (re-sampled) tables to\n>   * program its LSC engine, it uses LscAlgorithm::interpolateComponents() to get\n>   * an LSC table interpolated by the LscAlgorithm class for the specified colour\n> @@ -209,8 +297,8 @@ namespace lsc {\n>   *\n>   * \\return 0 on success, a negative error code otherwise\n>   */\n> -int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &controls,\n> -                      const LscDescriptor &descriptor)\n> +int LscAlgorithmBase::init(const ValueNode &tuningData, ControlInfoMap::Map &controls,\n> +                          const LscDescriptor &descriptor)\n>  {\n>         polynomial_ = false;\n>  \n> @@ -245,12 +333,15 @@ int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &control\n>  }\n>  \n>  /**\n> + * \\fn LscAlgorithm::configure()\n> + * \\brief Re-sample and quantize LSC data\n>   * \\param[in] state The LSC active state\n>   * \\param[in] analogCrop The current sensor analog crop rectangle\n>   * \\param[in] xPos List of horizontal positions of the LSC grid nodes\n>   * \\param[in] yPos List of vertical positions of the LSC grid nodes\n>   *\n> - * Re-sample the LSC data for an \\a analogCrop.\n> + * Re-sample the LSC data for an \\a analogCrop and convert gains to their\n> + * register representation using the class template paramter \\a U.\n>   *\n>   * LSC tables are generated at tuning time using a known sensor configuration.\n>   * When a new streaming session is started, it might use a different sensor\n> @@ -263,30 +354,12 @@ int LscAlgorithm::init(const ValueNode &tuningData, ControlInfoMap::Map &control\n>   *\n>   * \\sa LscImplementation::sampleForCrop\n>   *\n> + * Once tables have been re-sampled, they get quantized to the platform's\n> + * fixed-point register representation using the LscAlgorithm template parameter\n> + * \\a U.\n> + *\n>   * \\return 0 on success, a negative error code otherwise\n>   */\n> -int LscAlgorithm::configure(lsc::ActiveState &state, const Rectangle &analogCrop,\n> -                           const std::vector<double> &xPos,\n> -                           const std::vector<double> &yPos)\n> -{\n> -       LOG(Lsc, Debug) << \"Sample Lsc data for \" << analogCrop;\n> -       lsc::ComponentsMap lscData =\n> -               impl_->sampleForCrop(analogCrop, xPos, yPos);\n> -\n> -       /*\n> -        * Retain a copy of the components table.\n> -        *\n> -        * We could avoid a copy here if getComponents() could\n> -        * return sets_.data() but I wasn't able to work around the\n> -        * compiler refusing it.\n> -        */\n> -       lscData_ = lscData;\n> -\n> -       sets_.setData(std::move(lscData));\n> -       state.enabled = true;\n> -\n> -       return 0;\n> -}\n>  \n>  /**\n>   * \\brief Queue a request to the lsc algorithm\n> @@ -297,9 +370,9 @@ int LscAlgorithm::configure(lsc::ActiveState &state, const Rectangle &analogCrop\n>   * Queue a new list of \\a controls to the lsc algorithm.\n>   * The only supported control is controls::LensShadingCorrectionEnable.\n>   */\n> -void LscAlgorithm::queueRequest(lsc::ActiveState &state,\n> -                               lsc::FrameContext &context,\n> -                               const ControlList &controls)\n> +void LscAlgorithmBase::queueRequest(lsc::ActiveState &state,\n> +                                   lsc::FrameContext &context,\n> +                                   const ControlList &controls)\n>  {\n>         const auto &lscEnable = controls.get(controls::LensShadingCorrectionEnable);\n>         if (lscEnable && *lscEnable != state.enabled) {\n> @@ -322,7 +395,7 @@ void LscAlgorithm::queueRequest(lsc::ActiveState &state,\n>   * Populates the list of \\a metadata with controls handled by the LscAlgorithm\n>   * class. The only supported metadata is controls::LensShadingCorrectionEnable.\n>   */\n> -void LscAlgorithm::process(lsc::FrameContext &context, ControlList &metadata)\n> +void LscAlgorithmBase::process(lsc::FrameContext &context, ControlList &metadata)\n>  {\n>         metadata.set(controls::LensShadingCorrectionEnable, context.enabled);\n>  }\n> @@ -350,8 +423,7 @@ void LscAlgorithm::process(lsc::FrameContext &context, ControlList &metadata)\n>  \n>  /**\n>   * \\fn LscAlgorithm::getComponents\n> - *\n> - * Return the map of LSC data per-colour-temperature.\n> + * \\brief Return the map of LSC data per-colour-temperature\n>   *\n>   * Calling this function is only valid after LscAlgorithm::configure() has been\n>   * called. An empty components list is returned otherwise.\n> diff --git a/src/ipa/libipa/lsc.h b/src/ipa/libipa/lsc.h\n> index 9fe8ad67ea33..30330f284013 100644\n> --- a/src/ipa/libipa/lsc.h\n> +++ b/src/ipa/libipa/lsc.h\n> @@ -33,37 +33,96 @@ struct FrameContext {\n>         bool update;\n>  };\n>  \n> +template<typename T>\n> +using Components = std::map<std::string, std::vector<T>, std::less<>>;\n> +\n> +template<typename T>\n> +using ComponentsMap = std::map<unsigned int, Components<T>>;\n> +\n>  } /* namespace lsc */\n>  \n> -class LscAlgorithm\n> +#ifndef __DOXYGEN__\n> +template<>\n> +void Interpolator<lsc::Components<uint16_t>>::\n> +       interpolate(const lsc::Components<uint16_t> &a,\n> +                   const lsc::Components<uint16_t> &b,\n> +                   lsc::Components<uint16_t> &dest,\n> +                   double lambda);\n> +#endif /* __DOXYGEN__ */\n> +\n> +class LscAlgorithmBase\n>  {\n>  public:\n>         int init(const ValueNode &tuningData, ControlInfoMap::Map &controls,\n>                  const LscDescriptor &descriptor);\n>  \n> -       int configure(lsc::ActiveState &state, const Rectangle &analogCrop,\n> -                     const std::vector<double> &xPos,\n> -                     const std::vector<double> &yPos);\n> -\n>         void queueRequest(lsc::ActiveState &state, lsc::FrameContext &context,\n>                           const ControlList &controls);\n>         void process(lsc::FrameContext &context, ControlList &metadata);\n>  \n> -       const lsc::Components interpolateComponents(unsigned int ct)\n> +protected:\n> +       LscAlgorithmBase() = default;\n> +\n> +       std::unique_ptr<LscImplementation> impl_;\n> +       bool polynomial_;\n> +};\n> +\n> +template<typename U>\n> +class LscAlgorithm : public LscAlgorithmBase\n> +{\n> +private:\n> +       using Components = lsc::Components<typename U::QuantizedType>;\n> +       using ComponentsMap = lsc::ComponentsMap<typename U::QuantizedType>;\n> +\n> +public:\n> +       LscAlgorithm() = default;\n> +\n> +       int configure(lsc::ActiveState &state, const Rectangle &analogCrop,\n> +                     const std::vector<double> &xPos,\n> +                     const std::vector<double> &yPos)\n> +       {\n> +               LscImplementation::ComponentsMap data =\n> +                       impl_->sampleForCrop(analogCrop, xPos, yPos);\n> +\n> +               ComponentsMap lscData;\n> +               for (const auto &[t, c] : data) {\n> +                       Components &comp = lscData[t];\n> +\n> +                       for (const auto &[k, gains] : c) {\n> +                               auto &quantizedGains = comp[k];\n> +                               quantizedGains.reserve(gains.size());\n> +\n> +                               for (const float &gain : gains) {\n> +                                       /*\n> +                                        * Tabular LSC tables already store\n> +                                        * quantized values.\n> +                                        */\n> +                                       if (polynomial_)\n> +                                               quantizedGains.push_back(U(gain).quantized());\n> +                                       else\n> +                                               quantizedGains.push_back(gain);\n> +                               }\n> +                       }\n> +               }\n> +\n> +               sets_.setData(std::move(lscData));\n> +               state.enabled = true;\n> +\n> +               return 0;\n> +       }\n> +\n> +       const Components interpolateComponents(unsigned int ct)\n>         {\n>                 return sets_.getInterpolated(ct);\n>         }\n>  \n> -       const lsc::ComponentsMap getComponents()\n> +       const ComponentsMap &getComponents() const\n>         {\n> -               return lscData_;\n> +               return sets_.data();\n>         }\n>  \n>  private:\n> -       std::unique_ptr<LscImplementation> impl_;\n> -       Interpolator<lsc::Components> sets_;\n> -       lsc::ComponentsMap lscData_;\n> -       bool polynomial_;\n> +       Interpolator<Components> sets_;\n>  };\n>  \n>  } /* namespace ipa */\n> diff --git a/src/ipa/libipa/lsc_base.cpp b/src/ipa/libipa/lsc_base.cpp\n> index cb770f164d53..baf25df6b908 100644\n> --- a/src/ipa/libipa/lsc_base.cpp\n> +++ b/src/ipa/libipa/lsc_base.cpp\n> @@ -16,49 +16,6 @@ namespace libcamera {\n>  \n>  namespace ipa {\n>  \n> -namespace lsc {\n> -\n> -/**\n> - * \\typedef Components\n> - * \\brief Associate colour components with a list of gains\n> - *\n> - * LSC tables are defined as a list of gain values associated to a colour\n> - * component.\n> - *\n> - * As different ISPs support different colour components (usually 'r', 'gr',\n> - * 'gb', 'b' or just 'r', 'g', 'b') this class associates a string\n> - * identifier for the colour component to a list of gains.\n> - *\n> - * Each key name shall match an entry in the tuning file.\n> - *\n> - * The list of keys is provided to the LscAlgorithm class using \\a\n> - * LscDescriptor::keys.\n> - */\n> -\n> -/**\n> - * \\typedef ComponentsMap\n> - * \\brief Associate a colour temperature to a LSC table\n> - *\n> - * An LSC table is generated during the tuning phase for a specific colour\n> - * temperature, and a tuning file usually contains LSC tables generated for\n> - * several different colour temperatures.\n> - */\n> -\n> -} /* namespace lsc */\n> -\n> -#ifndef __DOXYGEN__\n> -template<>\n> -void Interpolator<lsc::Components>::\n> -       interpolate(const lsc::Components &a,\n> -                   const lsc::Components &b,\n> -                   lsc::Components &dest,\n> -                   double lambda)\n> -{\n> -       for (auto const &[k, v] : a)\n> -               interpolateVector(v, b.at(k), dest[k], lambda);\n> -}\n> -#endif\n> -\n>  /**\n>   * \\struct LscDescriptor\n>   * \\brief Describe the ISP LSC engine\n> @@ -90,6 +47,38 @@ void Interpolator<lsc::Components>::\n>   * Defines the interface for the LSC algorithm implementation.\n>   */\n>  \n> +/**\n> + * \\typedef LscImplementation::Components\n> + * \\brief Associate colour components with a list of gains in float format\n> + *\n> + * LSC tables are defined as a list of gain values associated to a colour\n> + * component.\n> + *\n> + * As different ISP support different colour components (usually 'r', 'gr',\n> + * 'gb', 'b' or just 'r', 'g', 'b') this class associates a string\n> + * identifier for the colour component to a list of gains.\n> + *\n> + * The gain values are represented as floats and are either loaded from tuning\n> + * file or are the result of a polynomial expansion, depending on the\n> + * LscImplementation derived class in use (LscTable or LscPolynomial).\n> + *\n> + * The gain values are converted to the platform's register format before being\n> + * returned to the IPA module by LscAlgorithm::configure().\n> + *\n> + * \\todo Tabular LSC data are already expressed in register format. Make the\n> + * tuning files express gains in float format as well and actually perform\n> + * quantization in LscAlgorithm::configure().\n> + */\n> +\n> +/**\n> + * \\typedef LscImplementation::ComponentsMap\n> + * \\brief Associate a colour temperature to a LSC table\n> + *\n> + * An LSC table is generated during the tuning phase for a specific colour\n> + * temperature, and a tuning file usually contains LSC tables generated for\n> + * several different colour temperatures.\n> + */\n> +\n>  /**\n>   * \\fn LscImplementation::~LscImplementation\n>   * \\brief Virtual class destructor\n> diff --git a/src/ipa/libipa/lsc_base.h b/src/ipa/libipa/lsc_base.h\n> index 2cff34b32a2b..d53736cb71d3 100644\n> --- a/src/ipa/libipa/lsc_base.h\n> +++ b/src/ipa/libipa/lsc_base.h\n> @@ -24,32 +24,6 @@ namespace libcamera {\n>  \n>  namespace ipa {\n>  \n> -namespace lsc {\n> -\n> -using Components = std::map<std::string, std::vector<float>, std::less<>>;\n> -using ComponentsMap = std::map<unsigned int, Components>;\n> -\n> -} /* namespace lsc */\n> -\n> -#ifndef __DOXYGEN__\n> -template<typename T>\n> -void interpolateVector(const std::vector<T> &a, const std::vector<T> &b,\n> -                      std::vector<T> &dest, double lambda)\n> -{\n> -       ASSERT(a.size() == b.size());\n> -       dest.resize(a.size());\n> -       for (size_t i = 0; i < a.size(); i++)\n> -               dest[i] = a[i] * (1.0 - lambda) + b[i] * lambda;\n> -}\n> -\n> -template<>\n> -void Interpolator<lsc::Components>::\n> -       interpolate(const lsc::Components &a,\n> -                   const lsc::Components &b,\n> -                   lsc::Components &dest,\n> -                   double lambda);\n> -#endif /* __DOXYGEN__ */\n> -\n>  struct LscDescriptor {\n>         std::vector<std::string> keys;\n>         unsigned int numHSamples;\n> @@ -60,12 +34,15 @@ struct LscDescriptor {\n>  class LscImplementation\n>  {\n>  public:\n> +       using Components = std::map<std::string, std::vector<float>, std::less<>>;\n> +       using ComponentsMap = std::map<unsigned int, Components>;\n> +\n>         virtual ~LscImplementation() {}\n>  \n>         virtual int parseLscData(const ValueNode &tuningData,\n>                                  const LscDescriptor &descriptor) = 0;\n>  \n> -       virtual lsc::ComponentsMap\n> +       virtual ComponentsMap\n>         sampleForCrop(const Rectangle &cropRectangle,\n>                       std::vector<double> xPos, std::vector<double> yPos) = 0;\n>  };\n> diff --git a/src/ipa/libipa/lsc_polynomial.cpp b/src/ipa/libipa/lsc_polynomial.cpp\n> index 419de2c12cf0..6c38c7f7eeb1 100644\n> --- a/src/ipa/libipa/lsc_polynomial.cpp\n> +++ b/src/ipa/libipa/lsc_polynomial.cpp\n> @@ -194,15 +194,15 @@ int LscPolynomial::parseLscData(const ValueNode &sets,\n>   * [0, 0.0625, 0.125, 0.1875, ... , 1]. It is expected that the first position\n>   * is 0 and the last position is 1.\n>   */\n> -lsc::ComponentsMap\n> +LscImplementation::ComponentsMap\n>  LscPolynomial::sampleForCrop(const Rectangle &cropRectangle,\n>                              std::vector<double> xPos, std::vector<double> yPos)\n>  {\n>  \n> -       lsc::ComponentsMap components;\n> +       LscImplementation::ComponentsMap components;\n>  \n>         for (const auto &[t, c] : lscData_) {\n> -               lsc::Components &comp = components[t];\n> +               LscImplementation::Components &comp = components[t];\n>  \n>                 for (const auto &[k, p] : c)\n>                         comp.try_emplace(k, samplePolynomial(p, xPos, yPos,\n> diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h\n> index 24b216a1aa25..b9d3d834662b 100644\n> --- a/src/ipa/libipa/lsc_polynomial.h\n> +++ b/src/ipa/libipa/lsc_polynomial.h\n> @@ -60,7 +60,7 @@ public:\n>         int parseLscData(const ValueNode &sets,\n>                          const LscDescriptor &descriptor) override;\n>  \n> -       lsc::ComponentsMap\n> +       LscImplementation::ComponentsMap\n>         sampleForCrop(const Rectangle &cropRectangle,\n>                       std::vector<double> xPos, std::vector<double> yPos) override;\n>  \n> diff --git a/src/ipa/libipa/lsc_table.cpp b/src/ipa/libipa/lsc_table.cpp\n> index 3e8b1a060ce7..74ea8a7266e1 100644\n> --- a/src/ipa/libipa/lsc_table.cpp\n> +++ b/src/ipa/libipa/lsc_table.cpp\n> @@ -59,7 +59,7 @@ int LscTable::parseLscData(const ValueNode &sets,\n>  int LscTable::parseLscComponent(const ValueNode &yamlSet,\n>                                 unsigned int ct, const LscDescriptor &descriptor)\n>  {\n> -       lsc::Components component;\n> +       LscImplementation::Components component;\n>         for (auto &k : descriptor.keys) {\n>                 auto [it, inserted] =\n>                         component.try_emplace(k, parseTable(yamlSet,\n> diff --git a/src/ipa/libipa/lsc_table.h b/src/ipa/libipa/lsc_table.h\n> index 6d9b0c692b7f..9f39b38e258c 100644\n> --- a/src/ipa/libipa/lsc_table.h\n> +++ b/src/ipa/libipa/lsc_table.h\n> @@ -29,7 +29,7 @@ public:\n>         int parseLscData(const ValueNode &sets,\n>                          const LscDescriptor &descriptor) override;\n>  \n> -       lsc::ComponentsMap\n> +       LscImplementation::ComponentsMap\n>         sampleForCrop([[maybe_unused]] const Rectangle &cropRectangle,\n>                       [[maybe_unused]] std::vector<double> xPos,\n>                       [[maybe_unused]] std::vector<double> yPos) override\n> @@ -47,7 +47,7 @@ private:\n>                                          unsigned int numHSamples,\n>                                          unsigned int numVSamples);\n>  \n> -       lsc::ComponentsMap lscData_;\n> +       LscImplementation::ComponentsMap lscData_;\n>  };\n>  \n>  } /* namespace ipa */\n> diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp\n> index 911ac6895a14..f2858656023e 100644\n> --- a/src/ipa/rkisp1/algorithms/lsc.cpp\n> +++ b/src/ipa/rkisp1/algorithms/lsc.cpp\n> @@ -175,23 +175,16 @@ void LensShadingCorrection::setParameters(rkisp1_cif_isp_lsc_config &config)\n>  }\n>  \n>  void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config,\n> -                                     const lsc::Components &set)\n> +                                     const lsc::Components<uint16_t> &set)\n>  {\n> -       /*\n> -        * The hardware uses 2.10 fixed point format and limits the legal values\n> -        * to [1..3.999]. Scale and clamp the sampled values accordingly.\n> -        */\n> -       const auto quantizeSet = [&](const std::string &key, uint16_t *dst) {\n> -               const auto &s = set.at(key);\n> -               std::transform(s.begin(), s.end(), dst, [](float f) {\n> -                               return std::clamp<uint16_t>(f * 1024, 1024, 4095);\n> -                               });\n> -       };\n> -\n> -       quantizeSet(\"r\", &config.r_data_tbl[0][0]);\n> -       quantizeSet(\"gr\", &config.gr_data_tbl[0][0]);\n> -       quantizeSet(\"gb\", &config.gb_data_tbl[0][0]);\n> -       quantizeSet(\"b\", &config.b_data_tbl[0][0]);\n> +       const auto &r = set.at(\"r\");\n> +       std::copy(r.begin(), r.end(), &config.r_data_tbl[0][0]);\n> +       const auto &gr = set.at(\"gr\");\n> +       std::copy(gr.begin(), gr.end(), &config.gr_data_tbl[0][0]);\n> +       const auto &gb = set.at(\"gb\");\n> +       std::copy(gb.begin(), gb.end(), &config.gb_data_tbl[0][0]);\n> +       const auto &b = set.at(\"b\");\n> +       std::copy(b.begin(), b.end(), &config.b_data_tbl[0][0]);\n>  }\n>  \n>  /**\n> @@ -241,8 +234,7 @@ void LensShadingCorrection::prepare([[maybe_unused]] IPAContext &context,\n>  \n>         setParameters(*config);\n>  \n> -       const lsc::Components &set = lscAlgo_.interpolateComponents(quantizedCt);\n> -       copyTable(*config, set);\n> +       copyTable(*config, lscAlgo_.interpolateComponents(quantizedCt));\n>  \n>         lastAppliedCt_ = ct;\n>         lastAppliedQuantizedCt_ = quantizedCt;\n> diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h\n> index 63f0887f0838..c1f8904426c0 100644\n> --- a/src/ipa/rkisp1/algorithms/lsc.h\n> +++ b/src/ipa/rkisp1/algorithms/lsc.h\n> @@ -12,6 +12,7 @@\n>  #include <linux/rkisp1-config.h>\n>  \n>  #include \"libcamera/internal/value_node.h\"\n> +#include \"libipa/fixedpoint.h\"\n>  \n>  #include \"libipa/lsc.h\"\n>  \n> @@ -41,7 +42,8 @@ public:\n>                      ControlList &metadata) override;\n>  private:\n>         void setParameters(rkisp1_cif_isp_lsc_config &config);\n> -       void copyTable(rkisp1_cif_isp_lsc_config &config, const lsc::Components &set0);\n> +       void copyTable(rkisp1_cif_isp_lsc_config &config,\n> +                      const lsc::Components<uint16_t> &set);\n>  \n>         std::vector<double> xSize_;\n>         std::vector<double> ySize_;\n> @@ -55,7 +57,7 @@ private:\n>         unsigned int lastAppliedCt_;\n>         unsigned int lastAppliedQuantizedCt_;\n>  \n> -       LscAlgorithm lscAlgo_;\n> +       LscAlgorithm<UQ<2, 10>> lscAlgo_;\n\nNice ;-)\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n>  };\n>  \n>  } /* namespace ipa::rkisp1::algorithms */\n> \n> -- \n> 2.54.0\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 498FEC3303\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Aug 2026 17:18:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5C0896812B;\n\tWed,  5 Aug 2026 19:18:34 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 50A8A68124\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  5 Aug 2026 19:18:33 +0200 (CEST)","from monstersaurus.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8B6A1929;\n\tWed,  5 Aug 2026 19:17:22 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"v/ifEq3P\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1785950242;\n\tbh=stYtb88sRu+mZzukahr1HUs5alGOu52jYFHQj1d9+58=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=v/ifEq3PQQ20lDRvJsb1pMVp9DofupjqQ0j9WuAVG817E5vKGCUu7i1xhjo0vHUhw\n\tCv1AgMMcJMsBb2v7cIotXFtOGHlwmpK201Y4YCV1JZY1zADsrsqsjBJnKWbZUG9KJg\n\t/dP4Hnahx9NVtx2wd7emdSmbF01bkUkbWzhNzg+w=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20260805-libipa-algorithms-v7-28-7425b5b795d4@ideasonboard.com>","References":"<20260805-libipa-algorithms-v7-0-7425b5b795d4@ideasonboard.com>\n\t<20260805-libipa-algorithms-v7-28-7425b5b795d4@ideasonboard.com>","Subject":"Re: [PATCH v7 28/32] ipa: libipa: lsc: Quantize gains in\n\tLscAlgorithm","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>,\n\tMilan Zamazal <mzamazal@redhat.com>,\n\tStefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Wed, 05 Aug 2026 18:18:29 +0100","Message-ID":"<178595030998.2300677.11408272210622088958@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]