[v14,4/6] libcamera: shaders: Add LSC support
diff mbox series

Message ID 20260821125958.95928-5-mzamazal@redhat.com
State Superseded
Headers show
Series
  • LSC for SoftISP simple pipeline
Related show

Commit Message

Milan Zamazal Aug. 21, 2026, 12:59 p.m. UTC
From: Xander Pronk <xander.c.pronk@gmail.com>

Lens shading correction should be applied after black level
subtraction (in order to make the computations with meaningful values)
and before white balance (especially before white balance stats are
computed).

Note that lens shading correction depends on temperature, which is
computed from the preceding, rather than current, frame (this is due to
how white balance is currently computed).

The shaders are compiled on initialisation, while lens shading
correction can be enabled or disabled dynamically, using the
corresponding control.  The whole correction in the shader is wrapped by
a conditional macro to be able to disable it and all the related
overheads if Lsc algorithm is not enabled at all.

If Lsc algorithm is enabled, the correction is always applied, whether
the given control is enabled or not.  If it is not, 1.0 multipliers are
expected to be provided.  This is because lens shading correction is
supposed to be rarely switched off using the runtime control when Lsc
algorithm is enabled, e.g. only for demoing or debugging purposes.
We prefer accepting the performance penalty of applying a no-op
correction in such cases rather than disturbing GPU computation in the
regular flow by introducing a runtime `if'.

lsc_tex texture contains 8-bit UQ<2, 6> quantized data as provided by
the IPA algorithm.  It is converted to float in the shader by
multiplying it by 4.0.

Co-developed-by: Rick ten Wolde <rick_libcamera@wolde.info>
Signed-off-by: Rick ten Wolde <rick_libcamera@wolde.info>
Signed-off-by: Xander Pronk <xander.c.pronk@gmail.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
 src/libcamera/shaders/bayer_1x_packed.frag | 9 +++++++++
 src/libcamera/shaders/bayer_unpacked.frag  | 9 +++++++++
 2 files changed, 18 insertions(+)

Comments

Barnabás Pőcze Aug. 25, 2026, 11:18 a.m. UTC | #1
2026. 08. 21. 14:59 keltezéssel, Milan Zamazal írta:
> From: Xander Pronk <xander.c.pronk@gmail.com>
> 
> Lens shading correction should be applied after black level
> subtraction (in order to make the computations with meaningful values)
> and before white balance (especially before white balance stats are
> computed).
> 
> Note that lens shading correction depends on temperature, which is
> computed from the preceding, rather than current, frame (this is due to
> how white balance is currently computed).
> 
> The shaders are compiled on initialisation, while lens shading
> correction can be enabled or disabled dynamically, using the
> corresponding control.  The whole correction in the shader is wrapped by
> a conditional macro to be able to disable it and all the related
> overheads if Lsc algorithm is not enabled at all.
> 
> If Lsc algorithm is enabled, the correction is always applied, whether
> the given control is enabled or not.  If it is not, 1.0 multipliers are
> expected to be provided.  This is because lens shading correction is
> supposed to be rarely switched off using the runtime control when Lsc
> algorithm is enabled, e.g. only for demoing or debugging purposes.
> We prefer accepting the performance penalty of applying a no-op
> correction in such cases rather than disturbing GPU computation in the
> regular flow by introducing a runtime `if'.
> 
> lsc_tex texture contains 8-bit UQ<2, 6> quantized data as provided by
> the IPA algorithm.  It is converted to float in the shader by
> multiplying it by 4.0.
> 
> Co-developed-by: Rick ten Wolde <rick_libcamera@wolde.info>
> Signed-off-by: Rick ten Wolde <rick_libcamera@wolde.info>
> Signed-off-by: Xander Pronk <xander.c.pronk@gmail.com>
> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
> ---
>   src/libcamera/shaders/bayer_1x_packed.frag | 9 +++++++++
>   src/libcamera/shaders/bayer_unpacked.frag  | 9 +++++++++
>   2 files changed, 18 insertions(+)
> 
> diff --git a/src/libcamera/shaders/bayer_1x_packed.frag b/src/libcamera/shaders/bayer_1x_packed.frag
> index 0b641a5f3..765fc8e88 100644
> --- a/src/libcamera/shaders/bayer_1x_packed.frag
> +++ b/src/libcamera/shaders/bayer_1x_packed.frag
> @@ -71,6 +71,10 @@ uniform vec3 blacklevel;
>   uniform float gamma;
>   uniform float contrastExp;
>   
> +#if defined(APPLY_LSC)
> +uniform sampler2D lsc_tex;
> +#endif
> +
>   float apply_contrast(float value)
>   {
>   	// Apply simple S-curve
> @@ -232,6 +236,11 @@ void main(void)
>   	 */
>   	rgb = (rgb - blacklevel) / (1.0 - blacklevel);
>   
> +#if defined(APPLY_LSC)
> +        /* Multiple by 4.0 for UQ<2, 6> -> float conversion */
> +        rgb = rgb * (texture2D(lsc_tex, textureOut).rgb * 4.0);
> +#endif
> +
>   	/* Apply AWB gains, and saturate each channel at sensor range */
>   	rgb = clamp(rgb * awb, vec3(0.0), vec3(1.0));
>   
> diff --git a/src/libcamera/shaders/bayer_unpacked.frag b/src/libcamera/shaders/bayer_unpacked.frag
> index 10c5e941b..be2f89dd0 100644
> --- a/src/libcamera/shaders/bayer_unpacked.frag
> +++ b/src/libcamera/shaders/bayer_unpacked.frag
> @@ -30,6 +30,10 @@ uniform vec3            blacklevel;
>   uniform float           gamma;
>   uniform float           contrastExp;
>   
> +#if defined(APPLY_LSC)
> +uniform sampler2D lsc_tex;
> +#endif
> +
>   float apply_contrast(float value)
>   {
>       // Apply simple S-curve
> @@ -135,6 +139,11 @@ void main(void) {
>        */
>       rgb = (rgb - blacklevel) / (1.0 - blacklevel);
>   
> +#if defined(APPLY_LSC)
> +    /* Multiple by 4.0 for UQ<2, 6> -> float conversion */

I'd expand it, noting that:

    texture2D(...) = original * 2^6 (quantization) * 1/2^8 (texture byte to float)
-> original = texture2D(...) / 2^6 * 2^8 = texture2D(...) * 2^2

because it was not immediately obvious to me.

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


> +    rgb = rgb * (texture2D(lsc_tex, center.xy).rgb * 4.0);
> +#endif
> +
>       /* Apply AWB gains, and saturate each channel at sensor range */
>       rgb = clamp(rgb * awb, vec3(0.0), vec3(1.0));
>

Patch
diff mbox series

diff --git a/src/libcamera/shaders/bayer_1x_packed.frag b/src/libcamera/shaders/bayer_1x_packed.frag
index 0b641a5f3..765fc8e88 100644
--- a/src/libcamera/shaders/bayer_1x_packed.frag
+++ b/src/libcamera/shaders/bayer_1x_packed.frag
@@ -71,6 +71,10 @@  uniform vec3 blacklevel;
 uniform float gamma;
 uniform float contrastExp;
 
+#if defined(APPLY_LSC)
+uniform sampler2D lsc_tex;
+#endif
+
 float apply_contrast(float value)
 {
 	// Apply simple S-curve
@@ -232,6 +236,11 @@  void main(void)
 	 */
 	rgb = (rgb - blacklevel) / (1.0 - blacklevel);
 
+#if defined(APPLY_LSC)
+        /* Multiple by 4.0 for UQ<2, 6> -> float conversion */
+        rgb = rgb * (texture2D(lsc_tex, textureOut).rgb * 4.0);
+#endif
+
 	/* Apply AWB gains, and saturate each channel at sensor range */
 	rgb = clamp(rgb * awb, vec3(0.0), vec3(1.0));
 
diff --git a/src/libcamera/shaders/bayer_unpacked.frag b/src/libcamera/shaders/bayer_unpacked.frag
index 10c5e941b..be2f89dd0 100644
--- a/src/libcamera/shaders/bayer_unpacked.frag
+++ b/src/libcamera/shaders/bayer_unpacked.frag
@@ -30,6 +30,10 @@  uniform vec3            blacklevel;
 uniform float           gamma;
 uniform float           contrastExp;
 
+#if defined(APPLY_LSC)
+uniform sampler2D lsc_tex;
+#endif
+
 float apply_contrast(float value)
 {
     // Apply simple S-curve
@@ -135,6 +139,11 @@  void main(void) {
      */
     rgb = (rgb - blacklevel) / (1.0 - blacklevel);
 
+#if defined(APPLY_LSC)
+    /* Multiple by 4.0 for UQ<2, 6> -> float conversion */
+    rgb = rgb * (texture2D(lsc_tex, center.xy).rgb * 4.0);
+#endif
+
     /* Apply AWB gains, and saturate each channel at sensor range */
     rgb = clamp(rgb * awb, vec3(0.0), vec3(1.0));