[{"id":37870,"web_url":"https://patchwork.libcamera.org/comment/37870/","msgid":"<176908629744.1693075.12877698854137743540@ping.linuxembedded.co.uk>","date":"2026-01-22T12:51:37","subject":"Re: [PATCH v2] ipa: rkisp1: cproc: Fix brightness and contrast value\n\thandling","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Stefan Klug (2026-01-21 13:39:07)\n> By reverse engineering it was discovered that the transfer curve applied\n> by the CPROC module is\n> \n> Yout = Yin * contrast + brightness/2.\n> \n> However the most common expectation on contrast is to not change the\n> middle gray of an image. So the expected curve is:\n> \n> Yout = (Yin - 0.5) * contrast + 0.5 + brightness\n> \n> Map the user expectation to the hardware by changing the brightness\n> value written to the hardware. This also includes multiplying the\n> requested brightness value by two.\n> \n> Note: The limits of the brightness control are left as is even though\n> they can not be reached without changing the contrast. But limiting them\n> to [0.5, 0.5] would cut off parts of the combined control space of\n\ndid you mean [-0.5, 0.5] ? \n\nMeasuring the limits on my board with camshark metadata results:\n\n | Contrast | Brightness-min | Brightness-max |\n |----------|----------------|----------------|\n |   1.99   |     -0.0039    |    0.98828     |\n |   1.00   |     -0.5       |    0.49609     |\n |   0.00   |     -1.0       |   -0.00390     |\n\nBut I think that's correct from my understanding so far, just some\nfloating point variance which I'm not concerned about so:\n\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n   \n\n> contrast + brightness.\n> \n> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> \n> ---\n> \n> This patch needs to be applied on top of the quantization series:\n> https://patchwork.libcamera.org/project/libcamera/list/?series=5708\n> \n> Changes in v2:\n> - Moved calculation of brightness into helper function, to be able to\n>   reuse it inside configure()\n> - Moved calculation of actualBrightness from process() to the helper\n>   function\n> - Added documentation for the new context variables\n> - Removed unnecessary curly braces\n> - Improved comment\n> ---\n>  src/ipa/rkisp1/algorithms/cproc.cpp | 58 ++++++++++++++++++++++-------\n>  src/ipa/rkisp1/ipa_context.cpp      | 17 +++++++--\n>  src/ipa/rkisp1/ipa_context.h        |  3 ++\n>  3 files changed, 61 insertions(+), 17 deletions(-)\n> \n> diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp\n> index 7484e4780094..e0f6ac9816f7 100644\n> --- a/src/ipa/rkisp1/algorithms/cproc.cpp\n> +++ b/src/ipa/rkisp1/algorithms/cproc.cpp\n> @@ -46,6 +46,33 @@ constexpr float kDefaultSaturation = 1.0f;\n>   */\n>  constexpr float kHueScale = -90.0f;\n>  \n> +void applyRequestedBrightness(IPAActiveState &activeState)\n> +{\n> +       auto &cproc = activeState.cproc;\n> +\n> +       /*\n> +        * The CPROC module applies the transfer function\n> +        *\n> +        * Yout = Yin * contrast + brightness/2\n> +        *\n> +        * The calculations are done one bit wider than the input data to\n> +        * account for the maximum contrast of 2x without clamping. Brightness\n> +        * is applied after and therefore affects the output only with half the\n> +        * value.\n> +        *\n> +        * Most users expect that changing contrast doesn't change the middle\n> +        * gray and that the brightness value is normalized to one. So they\n> +        * expect a transfer function of\n> +        *\n> +        * Yout = (Yin - 0.5) * contrast + 0.5 + brightness\n> +        *\n> +        * To implement above formula with the given hardware we need adjust the\n> +        * hardware brightness value depending on the contrast.\n> +        */\n> +       cproc.brightness = (1 - cproc.contrast.value()) + 2 * cproc.requestedBrightness;\n> +       cproc.actualBrightness = (cproc.brightness.value() - (1 - cproc.contrast.value())) / 2;\n> +}\n> +\n>  } /* namespace */\n>  \n>  /**\n> @@ -76,8 +103,10 @@ int ColorProcessing::configure(IPAContext &context,\n>  {\n>         auto &cproc = context.activeState.cproc;\n>  \n> -       cproc.brightness = BrightnessQ(kDefaultBrightness);\n> +       cproc.requestedBrightness = kDefaultBrightness;\n>         cproc.contrast = ContrastQ(kDefaultContrast);\n> +       applyRequestedBrightness(context.activeState);\n> +\n>         cproc.hue = HueQ(kDefaultHue);\n>         cproc.saturation = SaturationQ(kDefaultSaturation);\n>  \n> @@ -98,17 +127,6 @@ void ColorProcessing::queueRequest(IPAContext &context,\n>         if (frame == 0)\n>                 update = true;\n>  \n> -       const auto &brightness = controls.get(controls::Brightness);\n> -       if (brightness) {\n> -               BrightnessQ value = *brightness;\n> -               if (cproc.brightness != value) {\n> -                       cproc.brightness = value;\n> -                       update = true;\n> -               }\n> -\n> -               LOG(RkISP1CProc, Debug) << \"Set brightness to \" << value;\n> -       }\n> -\n>         const auto &contrast = controls.get(controls::Contrast);\n>         if (contrast) {\n>                 ContrastQ value = *contrast;\n> @@ -120,6 +138,19 @@ void ColorProcessing::queueRequest(IPAContext &context,\n>                 LOG(RkISP1CProc, Debug) << \"Set contrast to \" << value;\n>         }\n>  \n> +       const auto &brightness = controls.get(controls::Brightness);\n> +       if (brightness)\n> +               cproc.requestedBrightness = *brightness;\n> +\n> +       if (update || brightness) {\n> +               BrightnessQ old = cproc.brightness;\n> +               applyRequestedBrightness(context.activeState);\n> +               if (cproc.brightness != old)\n> +                       update = true;\n> +\n> +               LOG(RkISP1CProc, Debug) << \"Set brightness to \" << cproc.actualBrightness;\n> +       }\n> +\n>         const auto &hue = controls.get(controls::Hue);\n>         if (hue) {\n>                 /* Scale the Hue from ]-90, +90] */\n> @@ -144,6 +175,7 @@ void ColorProcessing::queueRequest(IPAContext &context,\n>         }\n>  \n>         frameContext.cproc.brightness = cproc.brightness;\n> +       frameContext.cproc.actualBrightness = cproc.actualBrightness;\n>         frameContext.cproc.contrast = cproc.contrast;\n>         frameContext.cproc.hue = cproc.hue;\n>         frameContext.cproc.saturation = cproc.saturation;\n> @@ -179,7 +211,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context,\n>                               [[maybe_unused]] const rkisp1_stat_buffer *stats,\n>                               ControlList &metadata)\n>  {\n> -       metadata.set(controls::Brightness, frameContext.cproc.brightness.value());\n> +       metadata.set(controls::Brightness, frameContext.cproc.actualBrightness);\n>         metadata.set(controls::Contrast, frameContext.cproc.contrast.value());\n>         metadata.set(controls::Hue, frameContext.cproc.hue.value() * kHueScale);\n>         metadata.set(controls::Saturation, frameContext.cproc.saturation.value());\n> diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp\n> index 15cb0afe9fe8..f9d0aff058bc 100644\n> --- a/src/ipa/rkisp1/ipa_context.cpp\n> +++ b/src/ipa/rkisp1/ipa_context.cpp\n> @@ -232,9 +232,15 @@ namespace libcamera::ipa::rkisp1 {\n>  /**\n>   * \\var IPAActiveState::cproc\n>   * \\brief State for the Color Processing algorithm\n> +  *\n> + * \\var IPAActiveState::cproc.requestedBrightness\n> + * \\brief Brightness level requested by the user\n> +  *\n> + * \\var IPAActiveState::cproc.actualBrightness\n> + * \\brief Brightness level after contrast compensation\n>   *\n> - * \\struct IPAActiveState::cproc.brightness\n> - * \\brief Brightness level\n> + * \\var IPAActiveState::cproc.brightness\n> + * \\brief Hardware brightness level\n>   *\n>   * \\var IPAActiveState::cproc.contrast\n>   * \\brief Contrast level\n> @@ -400,8 +406,11 @@ namespace libcamera::ipa::rkisp1 {\n>   * \\var IPAFrameContext::cproc\n>   * \\brief Color Processing parameters for this frame\n>   *\n> - * \\struct IPAFrameContext::cproc.brightness\n> - * \\brief Brightness level\n> + * \\var IPAFrameContext::cproc.actualBrightness\n> + * \\brief Brightness level after contrast compensation\n> + *\n> + * \\var IPAFrameContext::cproc.brightness\n> + * \\brief Hardware brightness level\n>   *\n>   * \\var IPAFrameContext::cproc.contrast\n>   * \\brief Contrast level\n> diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h\n> index 80b035044cda..72ec64532dd8 100644\n> --- a/src/ipa/rkisp1/ipa_context.h\n> +++ b/src/ipa/rkisp1/ipa_context.h\n> @@ -122,6 +122,8 @@ struct IPAActiveState {\n>         } ccm;\n>  \n>         struct {\n> +               float requestedBrightness;\n> +               float actualBrightness;\n>                 BrightnessQ brightness;\n>                 ContrastQ contrast;\n>                 HueQ hue;\n> @@ -181,6 +183,7 @@ struct IPAFrameContext : public FrameContext {\n>         } awb;\n>  \n>         struct {\n> +               float actualBrightness;\n>                 BrightnessQ brightness;\n>                 ContrastQ contrast;\n>                 HueQ hue;\n> -- \n> 2.51.0\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 6E15CC3220\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 22 Jan 2026 12:51:43 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B703E61F84;\n\tThu, 22 Jan 2026 13:51:42 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5B8F261F84\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 22 Jan 2026 13:51:40 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 84DE6324;\n\tThu, 22 Jan 2026 13:51:07 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"ZvgHrXuw\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769086267;\n\tbh=edeFSQzhQ3wIYGq4kjHUuX0/S5YnsEndy0dzhoS5CCs=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=ZvgHrXuwJUXuVt0Xcl9rXhNoXHXL3C55obyAVTiIoxJEEmGxeMP73iGBXvf/rRNc6\n\tu/x8AQAsepxDD1QfEd+MnihEkcXUIXpSvd5GchzhnLHd/VTYH1VczCarFeRHHMzQ/3\n\tc+ONj51O1lCyBdR+1bWASnmJ5dHQQ/i7HsDdlBc0=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20260121133910.34925-1-stefan.klug@ideasonboard.com>","References":"<20260121133910.34925-1-stefan.klug@ideasonboard.com>","Subject":"Re: [PATCH v2] ipa: rkisp1: cproc: Fix brightness and contrast value\n\thandling","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Stefan Klug <stefan.klug@ideasonboard.com>","To":"Stefan Klug <stefan.klug@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Thu, 22 Jan 2026 12:51:37 +0000","Message-ID":"<176908629744.1693075.12877698854137743540@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]