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 7c26b65449a5bd895fe6d25dc89869b8645e78c6 Mon Sep 17 00:00:00 2001
From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Date: Wed, 20 May 2026 22:38:49 +0200
Subject: [PATCH] ipa: libipa: Add CcmAlgorithm to libipa
Add a CcmAlgorithm to libipa.
The CcmAlgorithm performs interpolation of the colour correction
matrices as loaded from tuning file on a colour temperature.
The implementation is based on the existing RkISP1 CCM algorihtm.
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Tested-by: Robert Mader <robert.mader@collabora.com>
---
src/ipa/libipa/ccm.cpp | 245 +++++++++++++++++++++++++++++++++++++
src/ipa/libipa/ccm.h | 77 ++++++++++++
src/ipa/libipa/meson.build | 2 +
3 files changed, 324 insertions(+)
create mode 100644 src/ipa/libipa/ccm.cpp
create mode 100644 src/ipa/libipa/ccm.h
--- /dev/null
+++ b/src/ipa/libipa/ccm.cpp
@@ -0,0 +1,245 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Ideas on Board Oy
+ *
+ * libIPA CCM algorithm
+ */
+
+#include "ccm.h"
+
+/**
+ * \file ccm.h
+ * \brief libipa CCM (Colour Correction Matrix) algorithm
+ */
+
+namespace libcamera {
+
+namespace ipa {
+
+LOG_DEFINE_CATEGORY(Ccm)
+
+namespace ccm {
+
+/**
+ * \struct ActiveState
+ * \brief Active CCM state
+ *
+ * \var ActiveState::manual
+ * \brief The most recent manually requested CCM state
+ *
+ * \var ActiveState::automatic
+ * \brief The most recent automatically calculated CCM state
+ */
+
+/**
+ * \struct CcmContext
+ * \brief CCM coefficients and offsets
+ *
+ * \var CcmContext::ccm
+ * \brief Matrix of 3x3 CCM coefficients
+ *
+ * \var CcmContext::offsets
+ * \brief Vector of RGB CCM offsets
+ */
+
+/**
+ * \typedef FrameContext
+ * \brief Per-frame CCM state
+ */
+
+} /* namespace ccm */
+
+/**
+ * \class CcmAlgorithmBase
+ * \brief Base class for CcmAlgorithm for non-templated functions implementation
+ *
+ * Base class for CcmAlgorithm where non-templated functions are implemented.
+ * IPA implementations shall use CcmAlgorithm and not this class.
+ */
+
+/**
+ * \brief Initialize the algorithm with the given tuning data
+ * \param[in] tuningData The tuning data to use for the algorithm
+ *
+ * Parse \a tuningData to initialize the CCM algorithm and register controls.
+ * IPA modules are expected to call this function as part of their
+ * implementation of Algorithm::init().
+ *
+ * \return 0 on success, a negative error code otherwise
+ */
+int CcmAlgorithmBase::init(const ValueNode &tuningData)
+{
+ int ret = ccm_.readYaml(tuningData["ccms"], "ct", "ccm");
+ if (ret < 0) {
+ LOG(Ccm, Warning)
+ << "Failed to parse 'ccm' "
+ << "parameter from tuning file; falling back to unit matrix";
+ ccm_.setData({ { 0, Matrix<float, 3, 3>::identity() } });
+ }
+
+ ret = offsets_.readYaml(tuningData["ccms"], "ct", "offsets");
+ if (ret < 0) {
+ LOG(Ccm, Warning)
+ << "Failed to parse 'offsets' "
+ << "parameter from tuning file; falling back to zero offsets";
+
+ offsets_.setData({ { 0, Matrix<int16_t, 3, 1>({ 0, 0, 0 }) } });
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Configure the CCM algorithm
+ * \param[in] state The CCM active state
+ * \param[in] temperatureK The colour temperature in Kelvin
+ *
+ * Configure the CCM algorithm by initializing the manual and automatic
+ * states in \a state by interpolating the default colour correction matrix
+ * with the given colour temperature \a temperatureK.
+ *
+ * \return 0 if successful, an error code otherwise
+ */
+int CcmAlgorithmBase::configure(ccm::ActiveState &state, unsigned int temperatureK)
+{
+ state.manual.ccm = ccm_.getInterpolated(temperatureK);
+ state.manual.offsets = offsets_.getInterpolated(temperatureK);
+ state.automatic.ccm = ccm_.getInterpolated(temperatureK);
+ state.automatic.offsets = offsets_.getInterpolated(temperatureK);
+
+ return 0;
+}
+
+/**
+ * \brief Queue a Request to the CCM algorithm
+ * \param[in] state The CCM active state
+ * \param[in] context The CCM frame context
+ * \param[in] controls The list of controls associated with the Request
+ *
+ * Queue a new Request to the CCM algorithm and store the manual colour
+ * correction matrix and temperature in \a frameContext.
+ *
+ * The currently handled controls are:
+ * - controls::ColourTemperature
+ * - controls::ColourCorrectionMatrix
+ *
+ * When controls::ColourCorrectionMatrix is passed in the supplied matrix is
+ * stored in \a state and \a context.
+ *
+ * When controls::ColourTemperature is passed in, the matrices loaded from
+ * configuration file are interpolated with the given temperature and the result
+ * is stored in \a state and \a context.
+ *
+ * If the IPA is running in manual mode, the IPA CCM algorithm implementations
+ * can use the matrix coefficients and offsets directly from \a context after
+ * calling this function to program the HW CCM engine, without calling prepare().
+ */
+void CcmAlgorithmBase::queueRequest(ccm::ActiveState &state,
+ ccm::FrameContext &context,
+ const ControlList &controls)
+{
+ const auto &colourTemperature = controls.get(controls::ColourTemperature);
+ const auto &ccmMatrix = controls.get(controls::ColourCorrectionMatrix);
+ if (ccmMatrix) {
+ state.manual.ccm = Matrix<float, 3, 3>(*ccmMatrix);
+ LOG(Ccm, Debug) << "Setting manual CCM from CCM control to "
+ << state.manual.ccm;
+ } else if (colourTemperature) {
+ state.manual.ccm = ccm_.getInterpolated(*colourTemperature);
+ LOG(Ccm, Debug) << "Setting manual CCM from CT control to "
+ << state.manual.ccm;
+ }
+
+ context = state.manual;
+}
+
+/**
+ * \brief Calculate the matrix coefficients for a colour temperature
+ * \param[in] state The CCM active state
+ * \param[in] context The CCM frame context
+ * \param[in] frame The frame number
+ * \param[in] temperatureK The colour temperature in Kelvin
+ *
+ * Interpolate the colour correction matrices as loaded from configuration file
+ * for colour temperature \a temperatureK.
+ *
+ * The function shall only be called if the IPA algorithm is running in auto
+ * mode. If running in manual mode the application supplied correction matrix is
+ * stored in \a frameContext at queueRequest() time.
+ */
+void CcmAlgorithmBase::prepare(ccm::ActiveState &state,
+ ccm::FrameContext &context,
+ unsigned int frame, unsigned int temperatureK)
+{
+ if (frame > 0 && temperatureK == ct_) {
+ context = state.automatic;
+ return;
+ }
+
+ ct_ = temperatureK;
+ context.ccm = ccm_.getInterpolated(ct_);
+ context.offsets = offsets_.getInterpolated(ct_);
+
+ state.automatic = context;
+}
+
+/**
+ * \brief Populate metadata with the latest correction matrix coefficients
+ * \param[in] context The CCM frame context
+ * \param[out] metadata The metadata list
+ */
+void CcmAlgorithmBase::process(ccm::FrameContext &context, ControlList &metadata)
+{
+ metadata.set(controls::ColourCorrectionMatrix, context.ccm.data());
+}
+
+/**
+ * \class CcmAlgorithm
+ * \brief The libipa CCM algorithm
+ * \tparam Q The fixedpoint register representation of the colour correction
+ * coefficients
+ *
+ * Implement the CCM algorithm for libipa.
+ *
+ * The CcmAlgorithm class implements an interface similar in spirit to the one
+ * of the Algorithm class. IPA modules are expected to store an instance of
+ * CcmAlgorithm as class member, template it with the CCM coefficients register
+ * representation and call its functions in their implementations of the
+ * Algorithm interface.
+ *
+ * The CcmAlgorithm class provides an init() function where tuning data is
+ * parsed and the per-colour temperature correction matrices are loaded from
+ * the tuning file.
+ *
+ * CcmAlgorithm supports both automatic and manual colour correction operations,
+ * but doesn't offer a way to select one of them. Enabling or disabling
+ * automatic CCM operations usually goes through the Awb algorithm
+ * enable/disable as the two algorithms should work with the same mode.
+ *
+ * When the IPA module runs in manual mode a custom colour correction matrix
+ * or a custom colour temperature can be supplied to the CCM algorithm at
+ * queueRequest() time. If the Request contains a color correction matrix
+ * (controls::ColourCorrectionMatrix) then the matrix coefficients gets saved in
+ * the FrameContext and the IPA module can immediately use them and doesn't need
+ * to call prepare(). If a custom colour temperature is provided
+ * (controls::ColourTemperature) then the matrices loaded from configuration are
+ * interpolated with it and the result is saved in the FrameContext. In this
+ * case as well IPA modules can use the result immediately and should avoid
+ * calling prepare().
+ *
+ * When the IPA module runs in automatic mode instead, it estimates the scene
+ * colour temperature. The estimated colour temperature shall be passed to
+ * prepare(), where it is used to interpolate the matrices loaded from the
+ * tuning file. The resulting coefficients are stored in the FrameContext for
+ * the IPA algorithm to use them to program their CCM engine registers.
+ */
+
+/**
+ * \fn CcmAlgorithm::init()
+ * \param[in] controls The info map of the IPA controls
+ * \copydoc CcmAlgorithmBase::init()
+ */
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
--- /dev/null
+++ b/src/ipa/libipa/ccm.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2026 Ideas on Board Oy
+ *
+ * libIPA CCM algorithm
+ */
+
+#pragma once
+
+#include <libcamera/control_ids.h>
+#include <libcamera/controls.h>
+
+#include "libcamera/internal/matrix.h"
+
+#include "fixedpoint.h"
+#include "interpolator.h"
+
+namespace libcamera {
+
+namespace ipa {
+
+namespace ccm {
+
+struct CcmContext {
+ Matrix<float, 3, 3> ccm;
+ Matrix<int16_t, 3, 1> offsets;
+};
+
+struct ActiveState {
+ struct CcmContext manual;
+ struct CcmContext automatic;
+};
+
+using FrameContext = CcmContext;
+
+} /* namespace ccm */
+
+class CcmAlgorithmBase
+{
+public:
+ int init(const ValueNode &tuningData);
+ int configure(ccm::ActiveState &state, unsigned int temperatureK);
+ void queueRequest(ccm::ActiveState &state, ccm::FrameContext &context,
+ const ControlList &controls);
+
+ void prepare(ccm::ActiveState &state, ccm::FrameContext &context,
+ unsigned int frame, unsigned int temperatureK);
+ void process(ccm::FrameContext &context, ControlList &metadata);
+
+private:
+ unsigned int ct_;
+ Interpolator<Matrix<float, 3, 3>> ccm_;
+ Interpolator<Matrix<int16_t, 3, 1>> offsets_;
+};
+
+template<typename Q>
+class CcmAlgorithm : public CcmAlgorithmBase
+{
+public:
+ int init(const ValueNode &tuningData, ControlInfoMap::Map &controls)
+ {
+ int ret = CcmAlgorithmBase::init(tuningData);
+ if (ret)
+ return ret;
+
+ controls[&controls::ColourCorrectionMatrix] =
+ ControlInfo(ControlValue(Q::TraitsType::min),
+ ControlValue(Q::TraitsType::max),
+ ControlValue(Matrix<float, 3, 3>::identity().data()));
+
+ return 0;
+ }
+};
+
+} /* namespace ipa */
+
+} /* namespace libcamera */
--- a/src/ipa/libipa/meson.build
+++ b/src/ipa/libipa/meson.build
@@ -7,6 +7,7 @@ libipa_headers = files([
'awb_grey.h',
'awb.h',
'camera_sensor_helper.h',
+ 'ccm.h',
'colours.h',
'exposure_mode_helper.h',
'fc_queue.h',
@@ -29,6 +30,7 @@ libipa_sources = files([
'awb_grey.cpp',
'awb.cpp',
'camera_sensor_helper.cpp',
+ 'ccm.cpp',
'colours.cpp',
'exposure_mode_helper.cpp',
'fc_queue.cpp',
|