[{"id":29109,"web_url":"https://patchwork.libcamera.org/comment/29109/","msgid":"<20240327191007.GO4721@pendragon.ideasonboard.com>","date":"2024-03-27T19:10:07","subject":"Re: [PATCH v6 18/18] libcamera: Soft IPA: use CameraSensorHelper for\n\tanalogue gain","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Milan and Andrei,\n\nThank you for the patch.\n\nOn Tue, Mar 19, 2024 at 01:36:05PM +0100, Milan Zamazal wrote:\n> From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>\n> \n> Use CameraSensorHelper to convert the analogue gain code read from the\n> camera sensor into real analogue gain value. In the future this makes\n> it possible to use faster AE/AGC algorithm. For now the same AE/AGC\n> algorithm is used, but even then the CameraSensorHelper lets us use the\n> full range of analogue gain values.\n> \n> If there is no CameraSensorHelper for the camera sensor in use, a\n> warning log message is printed, and the AE/AGC works exactly as before\n> this change.\n\nI'd rather not proceed if the sensor is unsupported. New sensors are\neasy to add to libcamera.\n\n> Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>\n> Reviewed-by: Hans de Goede <hdegoede@redhat.com>\n> Signed-off-by: Hans de Goede <hdegoede@redhat.com>\n> Reviewed-by: Milan Zamazal <mzamazal@redhat.com>\n> ---\n>  .../internal/software_isp/software_isp.h      |  3 +-\n>  src/ipa/simple/soft_simple.cpp                | 77 ++++++++++++-------\n>  src/libcamera/pipeline/simple/simple.cpp      |  2 +-\n>  src/libcamera/software_isp/software_isp.cpp   |  8 +-\n>  4 files changed, 57 insertions(+), 33 deletions(-)\n\nIdeally this patch should be split and squashed into the appropriate\nother patches. If that ends up being too difficult I'm OK keeping it\nseparate, but it's not nice to review new code that contains issues\nbeing fixed in later patches in the same series.\n\n> \n> diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h\n> index 8d25e979..2a6db7ba 100644\n> --- a/include/libcamera/internal/software_isp/software_isp.h\n> +++ b/include/libcamera/internal/software_isp/software_isp.h\n> @@ -26,6 +26,7 @@\n>  #include <libcamera/ipa/soft_ipa_interface.h>\n>  #include <libcamera/ipa/soft_ipa_proxy.h>\n>  \n> +#include \"libcamera/internal/camera_sensor.h\"\n>  #include \"libcamera/internal/dma_heaps.h\"\n>  #include \"libcamera/internal/pipeline_handler.h\"\n>  #include \"libcamera/internal/shared_mem_object.h\"\n> @@ -43,7 +44,7 @@ LOG_DECLARE_CATEGORY(SoftwareIsp)\n>  class SoftwareIsp\n>  {\n>  public:\n> -\tSoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);\n> +\tSoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor);\n>  \t~SoftwareIsp();\n>  \n>  \tint loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }\n> diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp\n> index ac027568..e4d64762 100644\n> --- a/src/ipa/simple/soft_simple.cpp\n> +++ b/src/ipa/simple/soft_simple.cpp\n> @@ -22,6 +22,8 @@\n>  #include \"libcamera/internal/software_isp/debayer_params.h\"\n>  #include \"libcamera/internal/software_isp/swisp_stats.h\"\n>  \n> +#include \"libipa/camera_sensor_helper.h\"\n> +\n>  #include \"black_level.h\"\n>  \n>  namespace libcamera {\n> @@ -67,18 +69,27 @@ private:\n>  \tDebayerParams *params_;\n>  \tSwIspStats *stats_;\n>  \tBlackLevel blackLevel_;\n> +\tstd::unique_ptr<CameraSensorHelper> camHelper_;\n>  \n>  \tint32_t exposure_min_, exposure_max_;\n> -\tint32_t again_min_, again_max_;\n> -\tint32_t again_, exposure_;\n> +\tint32_t exposure_;\n> +\tdouble again_min_, again_max_, againMinStep_;\n> +\tdouble again_;\n>  \tunsigned int ignore_updates_;\n>  };\n>  \n> -int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings,\n> +int IPASoftSimple::init(const IPASettings &settings,\n>  \t\t\tconst SharedFD &fdStats,\n>  \t\t\tconst SharedFD &fdParams,\n>  \t\t\tconst ControlInfoMap &sensorInfoMap)\n>  {\n> +\tcamHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);\n> +\tif (camHelper_ == nullptr) {\n\n\tif (!camHelper) {\n\n> +\t\tLOG(IPASoft, Warning)\n> +\t\t\t<< \"Failed to create camera sensor helper for \"\n> +\t\t\t<< settings.sensorModel;\n> +\t}\n> +\n>  \tfdStats_ = fdStats;\n>  \tif (!fdStats_.isValid()) {\n>  \t\tLOG(IPASoft, Error) << \"Invalid Statistics handle\";\n> @@ -132,25 +143,35 @@ int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap)\n>  \t\texposure_min_ = 1;\n>  \t}\n>  \n> -\tagain_min_ = gain_info.min().get<int32_t>();\n> -\tagain_max_ = gain_info.max().get<int32_t>();\n> -\t/*\n> -\t * The camera sensor gain (g) is usually not equal to the value written\n> -\t * into the gain register (x). But the way how the AGC algorithm changes\n> -\t * the gain value to make the total exposure closer to the optimum assumes\n> -\t * that g(x) is not too far from linear function. If the minimal gain is 0,\n> -\t * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).\n> -\t * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near\n> -\t * one edge, and very small near the other) we limit the range of the gain\n> -\t * values used.\n> -\t */\n> -\tif (!again_min_) {\n> -\t\tLOG(IPASoft, Warning) << \"Minimum gain is zero, that can't be linear\";\n> -\t\tagain_min_ = std::min(100, again_min_ / 2 + again_max_ / 2);\n> +\tint32_t again_min = gain_info.min().get<int32_t>();\n> +\tint32_t again_max = gain_info.max().get<int32_t>();\n> +\n> +\tif (camHelper_) {\n> +\t\tagain_min_ = camHelper_->gain(again_min);\n> +\t\tagain_max_ = camHelper_->gain(again_max);\n> +\t\tagainMinStep_ = (again_max_ - again_min_) / 100.0;\n> +\t} else {\n> +\t\t/*\n> +\t\t * The camera sensor gain (g) is usually not equal to the value written\n> +\t\t * into the gain register (x). But the way how the AGC algorithm changes\n> +\t\t * the gain value to make the total exposure closer to the optimum assumes\n> +\t\t * that g(x) is not too far from linear function. If the minimal gain is 0,\n> +\t\t * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).\n> +\t\t * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near\n> +\t\t * one edge, and very small near the other) we limit the range of the gain\n> +\t\t * values used.\n> +\t\t */\n> +\t\tagain_max_ = again_max;\n> +\t\tif (!again_min) {\n> +\t\t\tLOG(IPASoft, Warning) << \"Minimum gain is zero, that can't be linear\";\n> +\t\t\tagain_min_ = std::min(100, again_min / 2 + again_max / 2);\n> +\t\t}\n> +\t\tagainMinStep_ = 1.0;\n>  \t}\n>  \n>  \tLOG(IPASoft, Info) << \"Exposure \" << exposure_min_ << \"-\" << exposure_max_\n> -\t\t\t   << \", gain \" << again_min_ << \"-\" << again_max_;\n> +\t\t\t   << \", gain \" << again_min_ << \"-\" << again_max_\n> +\t\t\t   << \" (\" << againMinStep_ << \")\";\n>  \n>  \treturn 0;\n>  }\n> @@ -252,12 +273,14 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)\n>  \tControlList ctrls(sensorControls);\n>  \n>  \texposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n> -\tagain_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();\n> +\tint32_t again = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();\n> +\tagain_ = camHelper_ ? camHelper_->gain(again) : again;\n>  \n>  \tupdateExposure(exposureMSV);\n>  \n>  \tctrls.set(V4L2_CID_EXPOSURE, exposure_);\n> -\tctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);\n> +\tctrls.set(V4L2_CID_ANALOGUE_GAIN,\n> +\t\t  static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(again_) : again_));\n>  \n>  \tignore_updates_ = 2;\n>  \n> @@ -276,7 +299,7 @@ void IPASoftSimple::updateExposure(double exposureMSV)\n>  \tstatic constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;\n>  \tstatic constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;\n>  \n> -\tint next;\n> +\tdouble next;\n>  \n>  \tif (exposureMSV < kExposureOptimal - kExposureSatisfactory) {\n>  \t\tnext = exposure_ * kExpNumeratorUp / kExpDenominator;\n> @@ -286,18 +309,18 @@ void IPASoftSimple::updateExposure(double exposureMSV)\n>  \t\t\texposure_ = next;\n>  \t\tif (exposure_ >= exposure_max_) {\n>  \t\t\tnext = again_ * kExpNumeratorUp / kExpDenominator;\n> -\t\t\tif (next - again_ < 1)\n> -\t\t\t\tagain_ += 1;\n> +\t\t\tif (next - again_ < againMinStep_)\n> +\t\t\t\tagain_ += againMinStep_;\n>  \t\t\telse\n>  \t\t\t\tagain_ = next;\n>  \t\t}\n>  \t}\n>  \n>  \tif (exposureMSV > kExposureOptimal + kExposureSatisfactory) {\n> -\t\tif (exposure_ == exposure_max_ && again_ != again_min_) {\n> +\t\tif (exposure_ == exposure_max_ && again_ > again_min_) {\n>  \t\t\tnext = again_ * kExpNumeratorDown / kExpDenominator;\n> -\t\t\tif (again_ - next < 1)\n> -\t\t\t\tagain_ -= 1;\n> +\t\t\tif (again_ - next < againMinStep_)\n> +\t\t\t\tagain_ -= againMinStep_;\n>  \t\t\telse\n>  \t\t\t\tagain_ = next;\n>  \t\t} else {\n> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\n> index ac796b9b..22639407 100644\n> --- a/src/libcamera/pipeline/simple/simple.cpp\n> +++ b/src/libcamera/pipeline/simple/simple.cpp\n> @@ -526,7 +526,7 @@ int SimpleCameraData::init()\n>  \t * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.\n>  \t */\n>  \tif (!converter_ && pipe->swIspEnabled()) {\n> -\t\tswIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls());\n> +\t\tswIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get());\n>  \t\tif (!swIsp_->isValid()) {\n>  \t\t\tLOG(SimplePipeline, Warning)\n>  \t\t\t\t<< \"Failed to create software ISP, disabling software debayering\";\n> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp\n> index 9b49be41..ea4d96e4 100644\n> --- a/src/libcamera/software_isp/software_isp.cpp\n> +++ b/src/libcamera/software_isp/software_isp.cpp\n> @@ -60,9 +60,9 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)\n>  /**\n>   * \\brief Constructs SoftwareIsp object\n>   * \\param[in] pipe The pipeline handler in use\n> - * \\param[in] sensorControls ControlInfoMap describing the controls supported by the sensor\n> + * \\param[in] sensor Pointer to the CameraSensor instance owned by the pipeline handler\n>   */\n> -SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)\n> +SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor)\n>  \t: debayer_(nullptr),\n>  \t  debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 },\n>  \t  dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)\n> @@ -97,10 +97,10 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorCont\n>  \t\treturn;\n>  \t}\n>  \n> -\tint ret = ipa_->init(IPASettings{ \"No cfg file\", \"No sensor model\" },\n> +\tint ret = ipa_->init(IPASettings{ \"No cfg file\", sensor->model() },\n>  \t\t\t     debayer_->getStatsFD(),\n>  \t\t\t     sharedParams_.fd(),\n> -\t\t\t     sensorControls);\n> +\t\t\t     sensor->controls());\n>  \tif (ret) {\n>  \t\tLOG(SoftwareIsp, Error) << \"IPA init failed\";\n>  \t\tdebayer_.reset();","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 530EAC0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 27 Mar 2024 19:10:18 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 423F063339;\n\tWed, 27 Mar 2024 20:10:17 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4E80561C41\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 27 Mar 2024 20:10:16 +0100 (CET)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 29963899;\n\tWed, 27 Mar 2024 20:09:43 +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=\"LUSLCiOZ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1711566583;\n\tbh=CvTl/jFAzxZep8Rhetzr9H5kLyLlxBh24FXIw8JB+pw=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=LUSLCiOZp13WfxZemWIiaK0qS+whFbcgoef3dQBmgzRh32jVD1pmMy54dWAOkm/JK\n\t7qsfBrCUxCEfIPxtRBrjtEFxOCTUuSiVV0IcLSxavBkQQ3JSt43/gsvxbzPnvjq09p\n\tKrpliQKV0NphP6BWSYvU0EDDZvYSBJKJ/+W7g3MI=","Date":"Wed, 27 Mar 2024 21:10:07 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Milan Zamazal <mzamazal@redhat.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tAndrei Konovalov <andrey.konovalov.ynk@gmail.com>,\n\tBryan O'Donoghue <bryan.odonoghue@linaro.org>,\n\tMaxime Ripard <mripard@redhat.com>, Pavel Machek <pavel@ucw.cz>,\n\tHans de Goede <hdegoede@redhat.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>","Subject":"Re: [PATCH v6 18/18] libcamera: Soft IPA: use CameraSensorHelper for\n\tanalogue gain","Message-ID":"<20240327191007.GO4721@pendragon.ideasonboard.com>","References":"<20240319123622.675599-1-mzamazal@redhat.com>\n\t<20240319123622.675599-19-mzamazal@redhat.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20240319123622.675599-19-mzamazal@redhat.com>","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>"}}]