1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
|
From 259f42f3ade81adf119f7681ca6230f745468745 Mon Sep 17 00:00:00 2001
From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Date: Wed, 1 Jul 2026 11:30:10 +0200
Subject: [PATCH] ipa: libipa: lsc: Introduce LscAlgorithm
Introduce the lsc.h and lsc.cpp files with the definition of the
Lsc algorithm active state, frame context and the LscAlgorithm class.
The LscAlgorithm class uses two backends (LscPolynomial and LscTable) to
perform tuning file parsing and re-scaling of the LSC gains.
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
src/ipa/libipa/lsc.cpp | 253 +++++++++++++++++++++++++++++++++++++
src/ipa/libipa/lsc.h | 71 +++++++++++
src/ipa/libipa/meson.build | 2 +
3 files changed, 326 insertions(+)
create mode 100644 src/ipa/libipa/lsc.cpp
create mode 100644 src/ipa/libipa/lsc.h
--- /dev/null
+++ b/src/ipa/libipa/lsc.cpp
@@ -0,0 +1,253 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Ideas on Board Oy
+ *
+ * libIPA Lsc algorithms
+ */
+
+#include "lsc.h"
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+
+#include "lsc_polynomial.h"
+#include "lsc_table.h"
+
+/**
+ * \file lsc.h
+ * \brief libipa LSC algorithm
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Lsc)
+
+namespace ipa {
+
+namespace lsc {
+
+/**
+ * \struct ActiveState
+ * \brief The LSC active state
+ *
+ * \var ActiveState::enabled
+ * \brief Boolean flag for the LscAlgorithm enable status
+ */
+
+/**
+ * \struct FrameContext
+ * \brief The LSC frame context
+ *
+ * \var FrameContext::enabled
+ * \brief Boolean flag for the LscAlgorithm enable status
+ *
+ * \var FrameContext::update
+ * \brief Boolean flag for the LscAlgorithm updated status
+ */
+
+} /* namespace lsc */
+
+/**
+ * \class LscAlgorithm
+ * \brief libIPA LSC algorithm implementation
+ *
+ * Due to the optical characteristics of the lens, the light intensity received
+ * by the sensor is not uniform. The Lens Shading Correction algorithm applies
+ * multipliers to all pixels to compensate for the lens shading effect.
+ *
+ * The LscAlgorithm implements the libipa Lens Shading Correction algorithm
+ * using an implementation of the LscImplementation interface.
+ *
+ * This class provides support for parsing the tuning file content and
+ * generates tables indexed by colour temperature to store per-colour-channel
+ * gains for the IPA algorithm to be able to program the LSC engine.
+ *
+ * The init() function parses the tuning file and loads the gain tables either
+ * in tabular form (LscTable) or as radial polynomials (LscPolynomial). The gain
+ * tables are indexed by colour temperature with per-colour-components vectors
+ * of gain values or polynomial coefficients.
+ *
+ * At LscAlgorithm::configure() time the LSC tables are re-sampled on the
+ * sensor's crop rectangle in use to adapt them to the configuration in use for
+ * a streaming session. Polynomial LSC tables support re-sampling and can be
+ * applied to any sensor configuration. Grid-based LSC tables cannot currently
+ * be re-sampled and the configuration as parsed from the tuning file is used
+ * for all sensor configurations providing best-effort results.
+ *
+ * \todo Implement grid based re-sampling
+ *
+ * When the IPA algorithms wants to get access to the (re-sampled) tables to
+ * program its LSC engine, it uses LscAlgorithm::interpolateComponents() to get
+ * an LSC table interpolated by the LscAlgorithm class for the specified colour
+ * temperature. If the algorithm wants to access the non-interpolated tables it
+ * can retrieve them using LscAlgorithm::getComponents().
+ */
+
+/**
+ * \param[in] tuningData The tuning data
+ * \param[in] sensorSize The physical sensor size
+ * \param[in] controls The IPA list of supported controls
+ *
+ * Parse \a tuningData according to the settings specified in \a descriptor to
+ * populate the LSC data and registers LSC controls in \a controls.
+ *
+ * \return 0 on success, a negative error code otherwise
+ */
+int LscAlgorithm::init(const ValueNode &tuningData, const Size &sensorSize,
+ ControlInfoMap::Map &controls)
+{
+ polynomial_ = false;
+
+ std::string type = tuningData["type"].get<std::string>("table");
+ if (type == "table") {
+ impl_ = std::make_unique<LscTable>();
+ LOG(Lsc, Debug) << "Using table-based Lsc";
+ } else if (type == "polynomial") {
+ /*
+ * \todo Most likely the reference frame should be native_size.
+ * Let's wait how the internal discussions progress.
+ */
+ impl_ = std::make_unique<LscPolynomial>(sensorSize);
+ polynomial_ = true;
+ LOG(Lsc, Debug) << "Using polynomial Lsc";
+ } else {
+ LOG(Lsc, Error) << "Unsupported Lsc algorithm '"
+ << type << "'";
+ return -EINVAL;
+ }
+
+ const ValueNode &yamlSets = tuningData["sets"];
+ if (!yamlSets.isList()) {
+ LOG(Lsc, Error) << "'sets' parameter not found in tuning file";
+ return -EINVAL;
+ }
+
+ int ret = impl_->parseLscData(yamlSets);
+ if (ret)
+ return ret;
+
+ controls[&controls::LensShadingCorrectionEnable] =
+ ControlInfo(false, true, true);
+
+ return 0;
+}
+
+/**
+ * \param[in] state The LSC active state
+ * \param[in] analogCrop The current sensor analog crop rectangle
+ * \param[in] xPos List of horizontal positions of the LSC grid nodes
+ * \param[in] yPos List of vertical positions of the LSC grid nodes
+ *
+ * Re-sample the LSC data for an \a analogCrop.
+ *
+ * LSC tables are generated at tuning time using a known sensor configuration.
+ * When a new streaming session is started, it might use a different sensor
+ * configuration for which the LSC tables need to be adjusted to.
+ *
+ * This function re-generates the LSC tables to adapt them to a new sensor
+ * configuration, specifically it re-samples the LSC data for a new \a
+ * analogCrop on a grid specified by \a xPos and \a yPos. Re-sampling of
+ * LSC data is currently supported by polynomial-based LSC tables.
+ *
+ * \sa LscImplementation::sampleForCrop
+ *
+ * \return 0 on success, a negative error code otherwise
+ */
+int LscAlgorithm::configure(lsc::ActiveState &state, const Rectangle &analogCrop,
+ const std::vector<double> &xPos,
+ const std::vector<double> &yPos)
+{
+ LOG(Lsc, Debug) << "Sample Lsc data for " << analogCrop;
+ lsc::ComponentsMap lscData =
+ impl_->sampleForCrop(analogCrop, xPos, yPos);
+
+ /*
+ * Retain a copy of the components table.
+ *
+ * We could avoid a copy here if getComponents() could
+ * return sets_.data() but I wasn't able to work around the
+ * compiler refusing it.
+ */
+ lscData_ = lscData;
+
+ sets_.setData(std::move(lscData));
+ state.enabled = true;
+
+ return 0;
+}
+
+/**
+ * \brief Queue a request to the lsc algorithm
+ * \param[in] state The lsc active state
+ * \param[in] context The lsc frame context
+ * \param[in] controls The list of controls associated with a Request
+ *
+ * Queue a new list of \a controls to the lsc algorithm.
+ * The only supported control is controls::LensShadingCorrectionEnable.
+ */
+void LscAlgorithm::queueRequest(lsc::ActiveState &state,
+ lsc::FrameContext &context,
+ const ControlList &controls)
+{
+ const auto &lscEnable = controls.get(controls::LensShadingCorrectionEnable);
+ if (lscEnable && *lscEnable != state.enabled) {
+ state.enabled = *lscEnable;
+
+ LOG(Lsc, Debug)
+ << (state.enabled ? "Enabling" : "Disabling") << " Lsc";
+
+ context.update = true;
+ }
+
+ context.enabled = state.enabled;
+}
+
+/**
+ * \brief Populate the list of lsc metadata
+ * \param[in] context The lsc frame context
+ * \param[in] metadata The list of metadata
+ *
+ * Populates the list of \a metadata with controls handled by the LscAlgorithm
+ * class. The only supported metadata is controls::LensShadingCorrectionEnable.
+ */
+void LscAlgorithm::process(lsc::FrameContext &context, ControlList &metadata)
+{
+ metadata.set(controls::LensShadingCorrectionEnable, context.enabled);
+}
+
+/**
+ * \fn LscAlgorithm::interpolateComponents
+ * \brief Interpolate the LSC tables for a given colour temperature
+ * \param[in] ct The colour temperature
+ *
+ * LSC tables are generated using different colour temperatures during the
+ * tuning phase.
+ *
+ * This function returns the interpolated LSC data for a given \a ct
+ * colour temperature.
+ *
+ * IPA algorithm can use this function to obtain a list of per-colour-component
+ * gains to program their LSC engines with every time a significant enough
+ * change in colour temperature is detected.
+ *
+ * Calling this function is only valid after LscAlgorithm::configure() has been
+ * called. An empty components list is returned otherwise.
+ *
+ * \return The LSC gains table interpolated for temperature \a ct
+ */
+
+/**
+ * \fn LscAlgorithm::getComponents
+ *
+ * Return the map of LSC data per-colour-temperature.
+ *
+ * Calling this function is only valid after LscAlgorithm::configure() has been
+ * called. An empty components list is returned otherwise.
+ *
+ * \return The map of LSC gains tables per-colour-temperature
+ */
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
--- /dev/null
+++ b/src/ipa/libipa/lsc.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Ideas on Board Oy
+ *
+ * libIPA Lsc algorithm
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <libcamera/controls.h>
+#include <libcamera/geometry.h>
+
+#include "libcamera/internal/value_node.h"
+
+#include "interpolator.h"
+#include "lsc_base.h"
+
+namespace libcamera {
+
+namespace ipa {
+
+namespace lsc {
+
+struct ActiveState {
+ bool enabled;
+};
+
+struct FrameContext {
+ bool enabled;
+ bool update;
+};
+
+} /* namespace lsc */
+
+class LscAlgorithm
+{
+public:
+ int init(const ValueNode &tuningData, const Size &sensorSize,
+ ControlInfoMap::Map &controls);
+
+ int configure(lsc::ActiveState &state, const Rectangle &analogCrop,
+ const std::vector<double> &xPos,
+ const std::vector<double> &yPos);
+
+ void queueRequest(lsc::ActiveState &state, lsc::FrameContext &context,
+ const ControlList &controls);
+ void process(lsc::FrameContext &context, ControlList &metadata);
+
+ const lsc::Components interpolateComponents(unsigned int ct)
+ {
+ return sets_.getInterpolated(ct);
+ }
+
+ const lsc::ComponentsMap getComponents()
+ {
+ return lscData_;
+ }
+
+private:
+ std::unique_ptr<LscImplementation> impl_;
+ Interpolator<lsc::Components> sets_;
+ lsc::ComponentsMap lscData_;
+ bool polynomial_;
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
--- a/src/ipa/libipa/meson.build
+++ b/src/ipa/libipa/meson.build
@@ -14,6 +14,7 @@ libipa_headers = files([
'fixedpoint.h',
'histogram.h',
'interpolator.h',
+ 'lsc.h',
'lsc_base.h',
'lsc_polynomial.h',
'lsc_table.h',
@@ -39,6 +40,7 @@ libipa_sources = files([
'fixedpoint.cpp',
'histogram.cpp',
'interpolator.cpp',
+ 'lsc.cpp',
'lsc_base.cpp',
'lsc_polynomial.cpp',
'lsc_table.cpp',
|