[1/2] libcamera: software_isp: Add contrast algorithm
diff mbox series

Message ID 20240904074500.106019-2-mzamazal@redhat.com
State New
Headers show
Series
  • Add contrast control to software ISP
Related show

Commit Message

Milan Zamazal Sept. 4, 2024, 7:44 a.m. UTC
Software ISP is currently fully automatic and doesn't allow image
modifications by explicitly set control values.  The user has no means
to make the image looking better.

This patch introduces contrast control algorithm, which can improve
e.g. a flat looking image.  Based on the provided contrast value with a
range 0..infinity and 1.0 being the normal value, it applies a simple
S-curve modification to the image.  The contrast algorithm just handles
the provided values, while the S-curve is applied in the gamma algorithm
on the computed gamma curve whenever the contrast value changes.  Since
the algorithm is applied only on the lookup table already present, its
overhead is negligible.

This is a preparation patch without actually activating the contrast
algorithm, which will be done in the following patch.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
---
 src/ipa/simple/algorithms/contrast.cpp | 45 ++++++++++++++++++++++++++
 src/ipa/simple/algorithms/contrast.h   | 37 +++++++++++++++++++++
 src/ipa/simple/algorithms/gamma.cpp    | 22 ++++++++++---
 src/ipa/simple/algorithms/meson.build  |  1 +
 src/ipa/simple/ipa_context.h           |  4 +++
 5 files changed, 104 insertions(+), 5 deletions(-)
 create mode 100644 src/ipa/simple/algorithms/contrast.cpp
 create mode 100644 src/ipa/simple/algorithms/contrast.h

Patch
diff mbox series

diff --git a/src/ipa/simple/algorithms/contrast.cpp b/src/ipa/simple/algorithms/contrast.cpp
new file mode 100644
index 00000000..75bf37ad
--- /dev/null
+++ b/src/ipa/simple/algorithms/contrast.cpp
@@ -0,0 +1,45 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat Inc.
+ *
+ * Contrast adjustment
+ */
+
+#include "contrast.h"
+
+#include <optional>
+
+#include <libcamera/base/log.h>
+
+#include "control_ids.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPASoftContrast)
+
+namespace ipa::soft::algorithms {
+
+int Contrast::configure(typename Module::Context &context,
+			[[maybe_unused]] const typename Module::Config &configInfo)
+{
+	context.activeState.knobs.contrast = std::optional<double>();
+	return 0;
+}
+
+void Contrast::queueRequest(typename Module::Context &context,
+			    [[maybe_unused]] const uint32_t frame,
+			    [[maybe_unused]] typename Module::FrameContext &frameContext,
+			    const ControlList &controls)
+{
+	const auto &contrast = controls.get(controls::Contrast);
+	if (contrast.has_value()) {
+		context.activeState.knobs.contrast = contrast;
+		LOG(IPASoftContrast, Debug) << "Setting contrast to" << contrast.value();
+	}
+}
+
+REGISTER_IPA_ALGORITHM(Contrast, "Contrast")
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/contrast.h b/src/ipa/simple/algorithms/contrast.h
new file mode 100644
index 00000000..0b393309
--- /dev/null
+++ b/src/ipa/simple/algorithms/contrast.h
@@ -0,0 +1,37 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Red Hat Inc.
+ *
+ * Contrast adjustment
+ */
+
+#pragma once
+
+#include <libcamera/controls.h>
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::soft::algorithms {
+
+class Contrast : public Algorithm
+{
+public:
+	Contrast() = default;
+	~Contrast() = default;
+
+	int configure(typename Module::Context &context,
+		      const typename Module::Config &configInfo)
+		override;
+
+	void queueRequest(typename Module::Context &context,
+			  const uint32_t frame,
+			  typename Module::FrameContext &frameContext,
+			  const ControlList &controls)
+		override;
+};
+
+} /* namespace ipa::soft::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/simple/algorithms/gamma.cpp b/src/ipa/simple/algorithms/gamma.cpp
index b03dff25..fe59ca9f 100644
--- a/src/ipa/simple/algorithms/gamma.cpp
+++ b/src/ipa/simple/algorithms/gamma.cpp
@@ -32,15 +32,26 @@  int Gamma::init(IPAContext &context,
 void Gamma::updateGammaTable(IPAContext &context)
 {
 	auto &gammaTable = context.activeState.gamma.gammaTable;
-	auto blackLevel = context.activeState.black.level;
+	const auto blackLevel = context.activeState.black.level;
 	const unsigned int blackIndex =
 		blackLevel * IPAActiveState::kGammaLookupSize;
+	const auto contrast = context.activeState.knobs.contrast.value_or(1.0);
+
 	std::fill(gammaTable.begin(), gammaTable.begin() + blackIndex, 0);
 	const float divisor = kGammaLookupSize - blackIndex - 1.0;
-	for (unsigned int i = blackIndex; i < kGammaLookupSize; i++)
-		gammaTable[i] = UINT8_MAX * powf((i - blackIndex) / divisor,
-						 context.configuration.gamma);
+	for (unsigned int i = blackIndex; i < kGammaLookupSize; i++) {
+		double normalized = (i - blackIndex) / divisor;
+		/* Apply simple S-curve */
+		if (normalized < 0.5)
+			normalized = 0.5 * std::pow(normalized / 0.5, contrast);
+		else
+			normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrast);
+		gammaTable[i] = UINT8_MAX *
+				std::pow(normalized, context.configuration.gamma);
+	}
+
 	context.activeState.gamma.blackLevel = blackLevel;
+	context.activeState.gamma.contrast = contrast;
 }
 
 void Gamma::prepare(IPAContext &context,
@@ -53,7 +64,8 @@  void Gamma::prepare(IPAContext &context,
 	 * since the black level gets updated only if a lower value is observed,
 	 * it's not permanently prone to minor fluctuations or rounding errors.
 	 */
-	if (context.activeState.gamma.blackLevel != context.activeState.black.level)
+	if (context.activeState.gamma.blackLevel != context.activeState.black.level ||
+	    context.activeState.gamma.contrast != context.activeState.knobs.contrast)
 		updateGammaTable(context);
 }
 
diff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build
index ec2638c2..73dc077e 100644
--- a/src/ipa/simple/algorithms/meson.build
+++ b/src/ipa/simple/algorithms/meson.build
@@ -4,6 +4,7 @@  soft_simple_ipa_algorithms = files([
     'awb.cpp',
     'agc.cpp',
     'blc.cpp',
+    'contrast.cpp',
     'gamma.cpp',
     'lut.cpp',
 ])
diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
index 08f965f4..6655baa9 100644
--- a/src/ipa/simple/ipa_context.h
+++ b/src/ipa/simple/ipa_context.h
@@ -46,7 +46,11 @@  struct IPAActiveState {
 	struct {
 		std::array<double, kGammaLookupSize> gammaTable;
 		double blackLevel;
+		double contrast;
 	} gamma;
+	struct {
+		std::optional<double> contrast; // 0..inf, 1 = neutral
+	} knobs;
 };
 
 struct IPAFrameContext : public FrameContext {