summaryrefslogtreecommitdiff
path: root/src/ipa/simple/algorithms/ccm.cpp
diff options
context:
space:
mode:
authorMilan Zamazal <mzamazal@redhat.com>2025-05-15 18:04:31 +0200
committerKieran Bingham <kieran.bingham@ideasonboard.com>2025-06-01 23:08:01 +0100
commit59ac34b728c573317fd37ad10e6d1c9df486c5e4 (patch)
treea2f176280ab4df10291b0a5b2dd49c782b9988c8 /src/ipa/simple/algorithms/ccm.cpp
parente342f050c283283eec1191c94a692325362c170e (diff)
libcamera: software_isp: Add saturation control
Saturation control is added on top of the colour correction matrix. A method of saturation adjustment that can be fully integrated into the colour correction matrix is used. The control is available only if Ccm algorithm is enabled. The control uses 0.0-2.0 value range, with 1.0 being unmodified saturation, 0.0 full desaturation and 2.0 quite saturated. The saturation is adjusted by converting to Y'CbCr colour space, applying the saturation value on the colour axes, and converting back to RGB. ITU-R BT.601 conversion is used to convert between the colour spaces, for no particular reason. The colour correction matrix is applied before gamma and the given matrix is suitable for such a case. Alternatively, the transformation used in libcamera rpi ccm.cpp could be used. Signed-off-by: Milan Zamazal <mzamazal@redhat.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Diffstat (limited to 'src/ipa/simple/algorithms/ccm.cpp')
-rw-r--r--src/ipa/simple/algorithms/ccm.cpp60
1 files changed, 57 insertions, 3 deletions
diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp
index d5ba928d..0a98406c 100644
--- a/src/ipa/simple/algorithms/ccm.cpp
+++ b/src/ipa/simple/algorithms/ccm.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2024, Ideas On Board
* Copyright (C) 2024-2025, Red Hat Inc.
*
- * Color correction matrix
+ * Color correction matrix + saturation
*/
#include "ccm.h"
@@ -13,6 +13,8 @@
#include <libcamera/control_ids.h>
+#include "libcamera/internal/matrix.h"
+
namespace {
constexpr unsigned int kTemperatureThreshold = 100;
@@ -35,28 +37,77 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData
}
context.ccmEnabled = true;
+ context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f);
+
+ return 0;
+}
+
+int Ccm::configure(IPAContext &context,
+ [[maybe_unused]] const IPAConfigInfo &configInfo)
+{
+ context.activeState.knobs.saturation = std::optional<double>();
return 0;
}
+void Ccm::queueRequest(typename Module::Context &context,
+ [[maybe_unused]] const uint32_t frame,
+ [[maybe_unused]] typename Module::FrameContext &frameContext,
+ const ControlList &controls)
+{
+ const auto &saturation = controls.get(controls::Saturation);
+ if (saturation.has_value()) {
+ context.activeState.knobs.saturation = saturation;
+ LOG(IPASoftCcm, Debug) << "Setting saturation to " << saturation.value();
+ }
+}
+
+void Ccm::applySaturation(Matrix<float, 3, 3> &ccm, float saturation)
+{
+ /* https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion */
+ const Matrix<float, 3, 3> rgb2ycbcr{
+ { 0.256788235294, 0.504129411765, 0.0979058823529,
+ -0.148223529412, -0.290992156863, 0.439215686275,
+ 0.439215686275, -0.367788235294, -0.0714274509804 }
+ };
+ const Matrix<float, 3, 3> ycbcr2rgb{
+ { 1.16438356164, 0, 1.59602678571,
+ 1.16438356164, -0.391762290094, -0.812967647235,
+ 1.16438356164, 2.01723214285, 0 }
+ };
+ const Matrix<float, 3, 3> saturationMatrix{
+ { 1, 0, 0,
+ 0, saturation, 0,
+ 0, 0, saturation }
+ };
+ ccm = ycbcr2rgb * saturationMatrix * rgb2ycbcr * ccm;
+}
+
void Ccm::prepare(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
{
+ auto &saturation = context.activeState.knobs.saturation;
+
const unsigned int ct = context.activeState.awb.temperatureK;
- /* Change CCM only on bigger temperature changes. */
+ /* Change CCM only on saturation or bigger temperature changes. */
if (frame > 0 &&
- utils::abs_diff(ct, lastCt_) < kTemperatureThreshold) {
+ utils::abs_diff(ct, lastCt_) < kTemperatureThreshold &&
+ saturation == lastSaturation_) {
frameContext.ccm.ccm = context.activeState.ccm.ccm;
context.activeState.ccm.changed = false;
return;
}
lastCt_ = ct;
+ lastSaturation_ = saturation;
Matrix<float, 3, 3> ccm = ccm_.getInterpolated(ct);
+ if (saturation)
+ applySaturation(ccm, saturation.value());
context.activeState.ccm.ccm = ccm;
frameContext.ccm.ccm = ccm;
+ frameContext.saturation = saturation;
context.activeState.ccm.changed = true;
}
@@ -67,6 +118,9 @@ void Ccm::process([[maybe_unused]] IPAContext &context,
ControlList &metadata)
{
metadata.set(controls::ColourCorrectionMatrix, frameContext.ccm.ccm.data());
+
+ const auto &saturation = frameContext.saturation;
+ metadata.set(controls::Saturation, saturation.value_or(1.0));
}
REGISTER_IPA_ALGORITHM(Ccm, "Ccm")