[v4,09/15] libcamera: ipa: simple: Make gamma adjustable
diff mbox series

Message ID 20260122161935.208562-10-mzamazal@redhat.com
State Superseded
Headers show
Series
  • Simple pipeline IPA cleanup
Related show

Commit Message

Milan Zamazal Jan. 22, 2026, 4:19 p.m. UTC
The gamma value is fixed in software ISP.  Let's make it adjustable,
similarly to contrast and saturation, and report it in metadata.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
---
 src/ipa/simple/algorithms/adjust.cpp | 12 ++++++++++++
 src/ipa/simple/algorithms/adjust.h   |  4 +++-
 src/ipa/simple/algorithms/lut.cpp    | 11 ++++++-----
 src/ipa/simple/ipa_context.h         |  6 ++++--
 4 files changed, 25 insertions(+), 8 deletions(-)

Comments

Robert Mader Jan. 26, 2026, 4:02 p.m. UTC | #1
Reviewed-by: Robert Mader <robert.mader@collabora.com>

On 22.01.26 17:19, Milan Zamazal wrote:
> The gamma value is fixed in software ISP.  Let's make it adjustable,
> similarly to contrast and saturation, and report it in metadata.
>
> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> ---
>   src/ipa/simple/algorithms/adjust.cpp | 12 ++++++++++++
>   src/ipa/simple/algorithms/adjust.h   |  4 +++-
>   src/ipa/simple/algorithms/lut.cpp    | 11 ++++++-----
>   src/ipa/simple/ipa_context.h         |  6 ++++--
>   4 files changed, 25 insertions(+), 8 deletions(-)
>
> diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp
> index 27ae2a53a..a067f7b84 100644
> --- a/src/ipa/simple/algorithms/adjust.cpp
> +++ b/src/ipa/simple/algorithms/adjust.cpp
> @@ -23,6 +23,7 @@ LOG_DEFINE_CATEGORY(IPASoftAdjust)
>   
>   int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
>   {
> +	context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, kDefaultGamma);
>   	context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f);
>   	if (context.ccmEnabled)
>   		context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f);
> @@ -32,6 +33,7 @@ int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningD
>   int Adjust::configure(IPAContext &context,
>   		      [[maybe_unused]] const IPAConfigInfo &configInfo)
>   {
> +	context.activeState.knobs.gamma = kDefaultGamma;
>   	context.activeState.knobs.contrast = std::optional<double>();
>   	context.activeState.knobs.saturation = std::optional<double>();
>   
> @@ -43,6 +45,12 @@ void Adjust::queueRequest(typename Module::Context &context,
>   			  [[maybe_unused]] typename Module::FrameContext &frameContext,
>   			  const ControlList &controls)
>   {
> +	const auto &gamma = controls.get(controls::Gamma);
> +	if (gamma.has_value()) {
> +		context.activeState.knobs.gamma = gamma.value();
> +		LOG(IPASoftAdjust, Debug) << "Setting gamma to " << gamma.value();
> +	}
> +
>   	const auto &contrast = controls.get(controls::Contrast);
>   	if (contrast.has_value()) {
>   		context.activeState.knobs.contrast = contrast;
> @@ -83,6 +91,7 @@ void Adjust::prepare(IPAContext &context,
>   		     IPAFrameContext &frameContext,
>   		     [[maybe_unused]] DebayerParams *params)
>   {
> +	frameContext.gamma = context.activeState.knobs.gamma;
>   	frameContext.contrast = context.activeState.knobs.contrast;
>   
>   	if (!context.ccmEnabled)
> @@ -105,6 +114,9 @@ void Adjust::process([[maybe_unused]] IPAContext &context,
>   		     [[maybe_unused]] const SwIspStats *stats,
>   		     ControlList &metadata)
>   {
> +	const auto &gamma = frameContext.gamma;
> +	metadata.set(controls::Gamma, gamma);
> +
>   	const auto &contrast = frameContext.contrast;
>   	if (contrast)
>   		metadata.set(controls::Contrast, contrast.value());
> diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h
> index c4baa2503..7644138ff 100644
> --- a/src/ipa/simple/algorithms/adjust.h
> +++ b/src/ipa/simple/algorithms/adjust.h
> @@ -1,6 +1,6 @@
>   /* SPDX-License-Identifier: LGPL-2.1-or-later */
>   /*
> - * Copyright (C) 2024-2025, Red Hat Inc.
> + * Copyright (C) 2024-2026, Red Hat Inc.
>    *
>    * Color correction matrix
>    */
> @@ -19,6 +19,8 @@ namespace libcamera {
>   
>   namespace ipa::soft::algorithms {
>   
> +constexpr float kDefaultGamma = 2.2f;
> +
>   class Adjust : public Algorithm
>   {
>   public:
> diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp
> index 3a00bf4ee..9740f8c8d 100644
> --- a/src/ipa/simple/algorithms/lut.cpp
> +++ b/src/ipa/simple/algorithms/lut.cpp
> @@ -18,6 +18,8 @@
>   
>   #include "simple/ipa_context.h"
>   
> +#include "adjust.h"
> +
>   namespace libcamera {
>   
>   LOG_DEFINE_CATEGORY(IPASoftLut)
> @@ -27,8 +29,6 @@ namespace ipa::soft::algorithms {
>   int Lut::configure(IPAContext &context,
>   		   [[maybe_unused]] const IPAConfigInfo &configInfo)
>   {
> -	/* Gamma value is fixed */
> -	context.configuration.gamma = 1.0 / 2.2;
>   	updateGammaTable(context);
>   
>   	return 0;
> @@ -37,6 +37,7 @@ int Lut::configure(IPAContext &context,
>   void Lut::updateGammaTable(IPAContext &context)
>   {
>   	const auto blackLevel = context.activeState.blc.level;
> +	const auto gamma = 1.0 / context.activeState.knobs.gamma;
>   	const auto contrast = context.activeState.knobs.contrast.value_or(1.0);
>   	/* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */
>   	double contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001));
> @@ -52,8 +53,7 @@ void Lut::updateGammaTable(IPAContext &context)
>   				normalized = 0.5 * std::pow(normalized / 0.5, contrastExp);
>   			else
>   				normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp);
> -			gammaTable[i] = UINT8_MAX *
> -					std::pow(normalized, context.configuration.gamma);
> +			gammaTable[i] = UINT8_MAX * std::pow(normalized, gamma);
>   		}
>   		/*
>   		 * Due to CCM operations, the table lookup may reach indices below the black
> @@ -65,6 +65,7 @@ void Lut::updateGammaTable(IPAContext &context)
>   			  gammaTable[blackIndex]);
>   	}
>   
> +	context.activeState.gamma.gamma = gamma;
>   	context.activeState.gamma.blackLevel = blackLevel;
>   	context.activeState.gamma.contrastExp = contrastExp;
>   }
> @@ -131,7 +132,7 @@ void Lut::prepare(IPAContext &context,
>   		context.activeState.matrixChanged = false;
>   	}
>   
> -	params->gamma = context.configuration.gamma;
> +	params->gamma = context.activeState.gamma.gamma;
>   	params->contrastExp = context.activeState.gamma.contrastExp;
>   }
>   
> diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
> index a3ff3d038..eeade9d35 100644
> --- a/src/ipa/simple/ipa_context.h
> +++ b/src/ipa/simple/ipa_context.h
> @@ -1,6 +1,6 @@
>   /* SPDX-License-Identifier: LGPL-2.1-or-later */
>   /*
> - * Copyright (C) 2024-2025 Red Hat, Inc.
> + * Copyright (C) 2024-2026 Red Hat, Inc.
>    *
>    * Simple pipeline IPA Context
>    */
> @@ -25,7 +25,6 @@ namespace libcamera {
>   namespace ipa::soft {
>   
>   struct IPASessionConfiguration {
> -	float gamma;
>   	struct {
>   		int32_t exposureMin, exposureMax;
>   		double againMin, againMax, again10, againMinStep;
> @@ -58,6 +57,7 @@ struct IPAActiveState {
>   	struct {
>   		std::array<double, kGammaLookupSize> gammaTable;
>   		uint8_t blackLevel;
> +		float gamma;
>   		double contrast;
>   		double contrastExp;
>   	} gamma;
> @@ -66,6 +66,7 @@ struct IPAActiveState {
>   	bool matrixChanged = false;
>   
>   	struct {
> +		float gamma;
>   		/* 0..2 range, 1.0 = normal */
>   		std::optional<double> contrast;
>   		std::optional<float> saturation;
> @@ -85,6 +86,7 @@ struct IPAFrameContext : public FrameContext {
>   		double blue;
>   	} gains;
>   
> +	float gamma;
>   	std::optional<double> contrast;
>   	std::optional<float> saturation;
>   };
Barnabás Pőcze Jan. 27, 2026, 10:01 a.m. UTC | #2
2026. 01. 22. 17:19 keltezéssel, Milan Zamazal írta:
> The gamma value is fixed in software ISP.  Let's make it adjustable,
> similarly to contrast and saturation, and report it in metadata.
> 
> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> ---

Seems fine to me.

Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>


>   src/ipa/simple/algorithms/adjust.cpp | 12 ++++++++++++
>   src/ipa/simple/algorithms/adjust.h   |  4 +++-
>   src/ipa/simple/algorithms/lut.cpp    | 11 ++++++-----
>   src/ipa/simple/ipa_context.h         |  6 ++++--
>   4 files changed, 25 insertions(+), 8 deletions(-)
> 
> diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp
> index 27ae2a53a..a067f7b84 100644
> --- a/src/ipa/simple/algorithms/adjust.cpp
> +++ b/src/ipa/simple/algorithms/adjust.cpp
> @@ -23,6 +23,7 @@ LOG_DEFINE_CATEGORY(IPASoftAdjust)
>   
>   int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
>   {
> +	context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, kDefaultGamma);
>   	context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f);
>   	if (context.ccmEnabled)
>   		context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f);
> @@ -32,6 +33,7 @@ int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningD
>   int Adjust::configure(IPAContext &context,
>   		      [[maybe_unused]] const IPAConfigInfo &configInfo)
>   {
> +	context.activeState.knobs.gamma = kDefaultGamma;
>   	context.activeState.knobs.contrast = std::optional<double>();
>   	context.activeState.knobs.saturation = std::optional<double>();
>   
> @@ -43,6 +45,12 @@ void Adjust::queueRequest(typename Module::Context &context,
>   			  [[maybe_unused]] typename Module::FrameContext &frameContext,
>   			  const ControlList &controls)
>   {
> +	const auto &gamma = controls.get(controls::Gamma);
> +	if (gamma.has_value()) {
> +		context.activeState.knobs.gamma = gamma.value();
> +		LOG(IPASoftAdjust, Debug) << "Setting gamma to " << gamma.value();
> +	}
> +
>   	const auto &contrast = controls.get(controls::Contrast);
>   	if (contrast.has_value()) {
>   		context.activeState.knobs.contrast = contrast;
> @@ -83,6 +91,7 @@ void Adjust::prepare(IPAContext &context,
>   		     IPAFrameContext &frameContext,
>   		     [[maybe_unused]] DebayerParams *params)
>   {
> +	frameContext.gamma = context.activeState.knobs.gamma;
>   	frameContext.contrast = context.activeState.knobs.contrast;
>   
>   	if (!context.ccmEnabled)
> @@ -105,6 +114,9 @@ void Adjust::process([[maybe_unused]] IPAContext &context,
>   		     [[maybe_unused]] const SwIspStats *stats,
>   		     ControlList &metadata)
>   {
> +	const auto &gamma = frameContext.gamma;
> +	metadata.set(controls::Gamma, gamma);
> +
>   	const auto &contrast = frameContext.contrast;
>   	if (contrast)
>   		metadata.set(controls::Contrast, contrast.value());
> diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h
> index c4baa2503..7644138ff 100644
> --- a/src/ipa/simple/algorithms/adjust.h
> +++ b/src/ipa/simple/algorithms/adjust.h
> @@ -1,6 +1,6 @@
>   /* SPDX-License-Identifier: LGPL-2.1-or-later */
>   /*
> - * Copyright (C) 2024-2025, Red Hat Inc.
> + * Copyright (C) 2024-2026, Red Hat Inc.
>    *
>    * Color correction matrix
>    */
> @@ -19,6 +19,8 @@ namespace libcamera {
>   
>   namespace ipa::soft::algorithms {
>   
> +constexpr float kDefaultGamma = 2.2f;
> +
>   class Adjust : public Algorithm
>   {
>   public:
> diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp
> index 3a00bf4ee..9740f8c8d 100644
> --- a/src/ipa/simple/algorithms/lut.cpp
> +++ b/src/ipa/simple/algorithms/lut.cpp
> @@ -18,6 +18,8 @@
>   
>   #include "simple/ipa_context.h"
>   
> +#include "adjust.h"
> +
>   namespace libcamera {
>   
>   LOG_DEFINE_CATEGORY(IPASoftLut)
> @@ -27,8 +29,6 @@ namespace ipa::soft::algorithms {
>   int Lut::configure(IPAContext &context,
>   		   [[maybe_unused]] const IPAConfigInfo &configInfo)
>   {
> -	/* Gamma value is fixed */
> -	context.configuration.gamma = 1.0 / 2.2;
>   	updateGammaTable(context);
>   
>   	return 0;
> @@ -37,6 +37,7 @@ int Lut::configure(IPAContext &context,
>   void Lut::updateGammaTable(IPAContext &context)
>   {
>   	const auto blackLevel = context.activeState.blc.level;
> +	const auto gamma = 1.0 / context.activeState.knobs.gamma;
>   	const auto contrast = context.activeState.knobs.contrast.value_or(1.0);
>   	/* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */
>   	double contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001));
> @@ -52,8 +53,7 @@ void Lut::updateGammaTable(IPAContext &context)
>   				normalized = 0.5 * std::pow(normalized / 0.5, contrastExp);
>   			else
>   				normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp);
> -			gammaTable[i] = UINT8_MAX *
> -					std::pow(normalized, context.configuration.gamma);
> +			gammaTable[i] = UINT8_MAX * std::pow(normalized, gamma);
>   		}
>   		/*
>   		 * Due to CCM operations, the table lookup may reach indices below the black
> @@ -65,6 +65,7 @@ void Lut::updateGammaTable(IPAContext &context)
>   			  gammaTable[blackIndex]);
>   	}
>   
> +	context.activeState.gamma.gamma = gamma;
>   	context.activeState.gamma.blackLevel = blackLevel;
>   	context.activeState.gamma.contrastExp = contrastExp;
>   }
> @@ -131,7 +132,7 @@ void Lut::prepare(IPAContext &context,
>   		context.activeState.matrixChanged = false;
>   	}
>   
> -	params->gamma = context.configuration.gamma;
> +	params->gamma = context.activeState.gamma.gamma;
>   	params->contrastExp = context.activeState.gamma.contrastExp;
>   }
>   
> diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
> index a3ff3d038..eeade9d35 100644
> --- a/src/ipa/simple/ipa_context.h
> +++ b/src/ipa/simple/ipa_context.h
> @@ -1,6 +1,6 @@
>   /* SPDX-License-Identifier: LGPL-2.1-or-later */
>   /*
> - * Copyright (C) 2024-2025 Red Hat, Inc.
> + * Copyright (C) 2024-2026 Red Hat, Inc.
>    *
>    * Simple pipeline IPA Context
>    */
> @@ -25,7 +25,6 @@ namespace libcamera {
>   namespace ipa::soft {
>   
>   struct IPASessionConfiguration {
> -	float gamma;
>   	struct {
>   		int32_t exposureMin, exposureMax;
>   		double againMin, againMax, again10, againMinStep;
> @@ -58,6 +57,7 @@ struct IPAActiveState {
>   	struct {
>   		std::array<double, kGammaLookupSize> gammaTable;
>   		uint8_t blackLevel;
> +		float gamma;
>   		double contrast;
>   		double contrastExp;
>   	} gamma;
> @@ -66,6 +66,7 @@ struct IPAActiveState {
>   	bool matrixChanged = false;
>   
>   	struct {
> +		float gamma;
>   		/* 0..2 range, 1.0 = normal */
>   		std::optional<double> contrast;
>   		std::optional<float> saturation;
> @@ -85,6 +86,7 @@ struct IPAFrameContext : public FrameContext {
>   		double blue;
>   	} gains;
>   
> +	float gamma;
>   	std::optional<double> contrast;
>   	std::optional<float> saturation;
>   };

Patch
diff mbox series

diff --git a/src/ipa/simple/algorithms/adjust.cpp b/src/ipa/simple/algorithms/adjust.cpp
index 27ae2a53a..a067f7b84 100644
--- a/src/ipa/simple/algorithms/adjust.cpp
+++ b/src/ipa/simple/algorithms/adjust.cpp
@@ -23,6 +23,7 @@  LOG_DEFINE_CATEGORY(IPASoftAdjust)
 
 int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
 {
+	context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, kDefaultGamma);
 	context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f);
 	if (context.ccmEnabled)
 		context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f);
@@ -32,6 +33,7 @@  int Adjust::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningD
 int Adjust::configure(IPAContext &context,
 		      [[maybe_unused]] const IPAConfigInfo &configInfo)
 {
+	context.activeState.knobs.gamma = kDefaultGamma;
 	context.activeState.knobs.contrast = std::optional<double>();
 	context.activeState.knobs.saturation = std::optional<double>();
 
@@ -43,6 +45,12 @@  void Adjust::queueRequest(typename Module::Context &context,
 			  [[maybe_unused]] typename Module::FrameContext &frameContext,
 			  const ControlList &controls)
 {
+	const auto &gamma = controls.get(controls::Gamma);
+	if (gamma.has_value()) {
+		context.activeState.knobs.gamma = gamma.value();
+		LOG(IPASoftAdjust, Debug) << "Setting gamma to " << gamma.value();
+	}
+
 	const auto &contrast = controls.get(controls::Contrast);
 	if (contrast.has_value()) {
 		context.activeState.knobs.contrast = contrast;
@@ -83,6 +91,7 @@  void Adjust::prepare(IPAContext &context,
 		     IPAFrameContext &frameContext,
 		     [[maybe_unused]] DebayerParams *params)
 {
+	frameContext.gamma = context.activeState.knobs.gamma;
 	frameContext.contrast = context.activeState.knobs.contrast;
 
 	if (!context.ccmEnabled)
@@ -105,6 +114,9 @@  void Adjust::process([[maybe_unused]] IPAContext &context,
 		     [[maybe_unused]] const SwIspStats *stats,
 		     ControlList &metadata)
 {
+	const auto &gamma = frameContext.gamma;
+	metadata.set(controls::Gamma, gamma);
+
 	const auto &contrast = frameContext.contrast;
 	if (contrast)
 		metadata.set(controls::Contrast, contrast.value());
diff --git a/src/ipa/simple/algorithms/adjust.h b/src/ipa/simple/algorithms/adjust.h
index c4baa2503..7644138ff 100644
--- a/src/ipa/simple/algorithms/adjust.h
+++ b/src/ipa/simple/algorithms/adjust.h
@@ -1,6 +1,6 @@ 
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
- * Copyright (C) 2024-2025, Red Hat Inc.
+ * Copyright (C) 2024-2026, Red Hat Inc.
  *
  * Color correction matrix
  */
@@ -19,6 +19,8 @@  namespace libcamera {
 
 namespace ipa::soft::algorithms {
 
+constexpr float kDefaultGamma = 2.2f;
+
 class Adjust : public Algorithm
 {
 public:
diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp
index 3a00bf4ee..9740f8c8d 100644
--- a/src/ipa/simple/algorithms/lut.cpp
+++ b/src/ipa/simple/algorithms/lut.cpp
@@ -18,6 +18,8 @@ 
 
 #include "simple/ipa_context.h"
 
+#include "adjust.h"
+
 namespace libcamera {
 
 LOG_DEFINE_CATEGORY(IPASoftLut)
@@ -27,8 +29,6 @@  namespace ipa::soft::algorithms {
 int Lut::configure(IPAContext &context,
 		   [[maybe_unused]] const IPAConfigInfo &configInfo)
 {
-	/* Gamma value is fixed */
-	context.configuration.gamma = 1.0 / 2.2;
 	updateGammaTable(context);
 
 	return 0;
@@ -37,6 +37,7 @@  int Lut::configure(IPAContext &context,
 void Lut::updateGammaTable(IPAContext &context)
 {
 	const auto blackLevel = context.activeState.blc.level;
+	const auto gamma = 1.0 / context.activeState.knobs.gamma;
 	const auto contrast = context.activeState.knobs.contrast.value_or(1.0);
 	/* Convert 0..2 to 0..infinity; avoid actual inifinity at tan(pi/2) */
 	double contrastExp = tan(std::clamp(contrast * M_PI_4, 0.0, M_PI_2 - 0.00001));
@@ -52,8 +53,7 @@  void Lut::updateGammaTable(IPAContext &context)
 				normalized = 0.5 * std::pow(normalized / 0.5, contrastExp);
 			else
 				normalized = 1.0 - 0.5 * std::pow((1.0 - normalized) / 0.5, contrastExp);
-			gammaTable[i] = UINT8_MAX *
-					std::pow(normalized, context.configuration.gamma);
+			gammaTable[i] = UINT8_MAX * std::pow(normalized, gamma);
 		}
 		/*
 		 * Due to CCM operations, the table lookup may reach indices below the black
@@ -65,6 +65,7 @@  void Lut::updateGammaTable(IPAContext &context)
 			  gammaTable[blackIndex]);
 	}
 
+	context.activeState.gamma.gamma = gamma;
 	context.activeState.gamma.blackLevel = blackLevel;
 	context.activeState.gamma.contrastExp = contrastExp;
 }
@@ -131,7 +132,7 @@  void Lut::prepare(IPAContext &context,
 		context.activeState.matrixChanged = false;
 	}
 
-	params->gamma = context.configuration.gamma;
+	params->gamma = context.activeState.gamma.gamma;
 	params->contrastExp = context.activeState.gamma.contrastExp;
 }
 
diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
index a3ff3d038..eeade9d35 100644
--- a/src/ipa/simple/ipa_context.h
+++ b/src/ipa/simple/ipa_context.h
@@ -1,6 +1,6 @@ 
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 /*
- * Copyright (C) 2024-2025 Red Hat, Inc.
+ * Copyright (C) 2024-2026 Red Hat, Inc.
  *
  * Simple pipeline IPA Context
  */
@@ -25,7 +25,6 @@  namespace libcamera {
 namespace ipa::soft {
 
 struct IPASessionConfiguration {
-	float gamma;
 	struct {
 		int32_t exposureMin, exposureMax;
 		double againMin, againMax, again10, againMinStep;
@@ -58,6 +57,7 @@  struct IPAActiveState {
 	struct {
 		std::array<double, kGammaLookupSize> gammaTable;
 		uint8_t blackLevel;
+		float gamma;
 		double contrast;
 		double contrastExp;
 	} gamma;
@@ -66,6 +66,7 @@  struct IPAActiveState {
 	bool matrixChanged = false;
 
 	struct {
+		float gamma;
 		/* 0..2 range, 1.0 = normal */
 		std::optional<double> contrast;
 		std::optional<float> saturation;
@@ -85,6 +86,7 @@  struct IPAFrameContext : public FrameContext {
 		double blue;
 	} gains;
 
+	float gamma;
 	std::optional<double> contrast;
 	std::optional<float> saturation;
 };