Patch Detail
Show a patch.
GET /api/1.1/patches/18396/?format=api
{ "id": 18396, "url": "https://patchwork.libcamera.org/api/1.1/patches/18396/?format=api", "web_url": "https://patchwork.libcamera.org/patch/18396/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20230314144834.85193-5-dse@thaumatec.com>", "date": "2023-03-14T14:48:28", "name": "[libcamera-devel,v4,04/10] ipa: Add common contrast based AF implementation", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "9f3d7d7fddc1e4b0cfb30081bd46c1ceea95579c", "submitter": { "id": 126, "url": "https://patchwork.libcamera.org/api/1.1/people/126/?format=api", "name": "Daniel Semkowicz", "email": "dse@thaumatec.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/18396/mbox/", "series": [ { "id": 3805, "url": "https://patchwork.libcamera.org/api/1.1/series/3805/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3805", "date": "2023-03-14T14:48:24", "name": "ipa: rkisp1: Add autofocus algorithm", "version": 4, "mbox": "https://patchwork.libcamera.org/series/3805/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/18396/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/18396/checks/", "tags": {}, "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 1FA4FC329C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 14 Mar 2023 14:50:04 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 07C0662709;\n\tTue, 14 Mar 2023 15:50:03 +0100 (CET)", "from mail-lj1-x22b.google.com (mail-lj1-x22b.google.com\n\t[IPv6:2a00:1450:4864:20::22b])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E63A062710\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 14 Mar 2023 15:49:53 +0100 (CET)", "by mail-lj1-x22b.google.com with SMTP id a32so16293423ljq.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 14 Mar 2023 07:49:53 -0700 (PDT)", "from localhost.localdomain ([91.90.166.178])\n\tby smtp.gmail.com with ESMTPSA id\n\tq25-20020a2eb4b9000000b00295a35f30e1sm470961ljm.115.2023.03.14.07.49.52\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tTue, 14 Mar 2023 07:49:52 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1678805403;\n\tbh=1bstBxppEM9TyGd4rPlh5RFkJCmHXVsZ3O5DHBFeUaA=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=O1ErC9y59eAUhOKQ5HLlELY8gQ5+Pm1oE7r2whZQptr5tysL/MpnsQooDsg+RKMJU\n\tF54Hzf425NedElKs6cY/gucRzlVfRt9HwZW3S4kbKd4rPndpgFW2VUCl6du70iIzGs\n\t3Q80MnBfnIMkzoOaQupThbigDuI13XsIbNRTwzrUsXZdBDeIyg6wO4DajAwFOGsQPH\n\t5CqpPtHBdYmiYBpn5nh8T/YjVWCOD9GgCHb62qzbqEe8xB4D8doHkbUHAyjsBOk2fM\n\tSR+3t60XNjfYFHFBDix69XpTKY52nQMhIV6i3W5U2svu0ROqpsUfXg64eCiDDUkVEh\n\twNUWdq2MU9WNQ==", "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=thaumatec-com.20210112.gappssmtp.com; s=20210112; t=1678805393;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=vS1Kbj5O9OP4CxUMem8zYbMA/KhxmOSW/DP3/vIXaOw=;\n\tb=s0QTt5Ibi10mwdn/8i354u2fJvdXpQG9YLQZfxJYI6KC/3S5koov8ZIbLJjKDE755e\n\ti/fKeaKm13gxcGJOBgGfpN+MrUye5zBm3KJwqVT9Qx7z4xKCeO084mEMUORGdNRM6pYn\n\tR5kcmTXiDEyMyQUQDEFWB5Ze5BsdePYhC8dkf25mT71SaZg5KsdysKKFPRg46dQSMrsw\n\tP4Bo3ijF6Qa1dfRop3jx5r0FdsN47NEK01+KUz70iysjgHYG3GXlvhdkk2tMM0ikESi5\n\tiNiwA7+VdokjmBPb6nbFrm90Qhh+5JI5h04U0DCXBgs5mYQicZRPM5027m/JI05GwKFi\n\tUUmg==" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected)\n\theader.d=thaumatec-com.20210112.gappssmtp.com\n\theader.i=@thaumatec-com.20210112.gappssmtp.com header.b=\"s0QTt5Ib\"; \n\tdkim-atps=neutral", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112; t=1678805393;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n\t:subject:date:message-id:reply-to;\n\tbh=vS1Kbj5O9OP4CxUMem8zYbMA/KhxmOSW/DP3/vIXaOw=;\n\tb=YZeSPWPV5z1ufGdoxRMzaZ+Ki2Hn1VvetV8BbUvBdPodajvjBO++6xtRc9lwK5O1sI\n\tGFt4U0IjpPDlO6+9BLdbb2TTWgFlrdTICN6xEiHkLI8uWeEkmiAjqjUY2hlvaSm2/H/7\n\tLPsd4ipkn9sIx+8Xqt33HG/v9FmhMXoRN32W9O3FED3yWDT4onLABh2WYWF43ncYCmsR\n\tvm5trVNDyCZ24HLvvd5ne8rUYotxWsvlVHT22hVm1omi+82HZSVrVG4W1YazTjCtyWIt\n\tUboEiRwEwizzhDnCsM2d6rUFDi6KKJcD3V3B5HgT3mCegm7icP0qGh5U/j/n1g1kLbcC\n\tcGDA==", "X-Gm-Message-State": "AO0yUKU1vZxMTk5jUvZvcvsrZy9nlNGDZVlNbQx15tVl/FjHD7VBTBCz\n\tO0ozQ/qmW8t6TudYfBhS+Gx023Zj4ZSEkMDT+VU=", "X-Google-Smtp-Source": "AK7set8Ubo3GF8quTODs1UwgQ6b1Ih7tzgAyLvxZwzEOV3gwdtOGpCBaTkNEH3sFTPS8scew1GGSSg==", "X-Received": "by 2002:a2e:a586:0:b0:298:39fa:b04d with SMTP id\n\tm6-20020a2ea586000000b0029839fab04dmr13988860ljp.33.1678805393018; \n\tTue, 14 Mar 2023 07:49:53 -0700 (PDT)", "To": "libcamera-devel@lists.libcamera.org", "Date": "Tue, 14 Mar 2023 15:48:28 +0100", "Message-Id": "<20230314144834.85193-5-dse@thaumatec.com>", "X-Mailer": "git-send-email 2.39.2", "In-Reply-To": "<20230314144834.85193-1-dse@thaumatec.com>", "References": "<20230314144834.85193-1-dse@thaumatec.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v4 04/10] ipa: Add common contrast based\n\tAF implementation", "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>", "From": "Daniel Semkowicz via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>", "Reply-To": "Daniel Semkowicz <dse@thaumatec.com>", "Cc": "jacopo.mondi@ideasonboard.com", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Create a new class with contrast based Auto Focus implementation\nusing hill climbing algorithm. This common implementation is independent\nof platform specific code. This way, each platform can just implement\ncontrast calculation and run the AF control loop basing on this class.\nThis implementation is based on the code that was common for IPU3\nand RPi AF algorithms.\n\nSigned-off-by: Daniel Semkowicz <dse@thaumatec.com>\n---\n .../libipa/algorithms/af_hill_climbing.cpp | 358 ++++++++++++++++++\n src/ipa/libipa/algorithms/af_hill_climbing.h | 93 +++++\n src/ipa/libipa/algorithms/meson.build | 2 +\n 3 files changed, 453 insertions(+)\n create mode 100644 src/ipa/libipa/algorithms/af_hill_climbing.cpp\n create mode 100644 src/ipa/libipa/algorithms/af_hill_climbing.h", "diff": "diff --git a/src/ipa/libipa/algorithms/af_hill_climbing.cpp b/src/ipa/libipa/algorithms/af_hill_climbing.cpp\nnew file mode 100644\nindex 00000000..2b99afef\n--- /dev/null\n+++ b/src/ipa/libipa/algorithms/af_hill_climbing.cpp\n@@ -0,0 +1,358 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2021, Red Hat\n+ * Copyright (C) 2022, Ideas On Board\n+ * Copyright (C) 2023, Theobroma Systems\n+ *\n+ * af_hill_climbing.cpp - AF contrast based hill climbing common algorithm\n+ */\n+\n+#include \"af_hill_climbing.h\"\n+\n+#include \"libcamera/internal/yaml_parser.h\"\n+\n+/**\n+ * \\file af_hill_climbing.h\n+ * \\brief AF contrast based hill climbing common algorithm\n+ */\n+\n+namespace libcamera::ipa::common::algorithms {\n+\n+LOG_DEFINE_CATEGORY(Af)\n+\n+/**\n+ * \\class AfHillClimbing\n+ * \\brief Contrast based hill climbing auto focus control algorithm\n+ * implementation\n+ *\n+ * Control part of the auto focus algorithm. It calculates the lens position\n+ * basing on the contrast measure supplied by the higher level. This way it is\n+ * independent from the platform.\n+ *\n+ * Platform layer that use this algorithm should call process() method\n+ * for each measured contrast value and set the lens to the calculated position.\n+ *\n+ * Focus search is divided into two phases:\n+ * 1. coarse search,\n+ * 2. fine search.\n+ *\n+ * In the first phase, lens are moved with bigger steps to quickly find a rough\n+ * position for the best focus. Then, basing on the outcome of coarse search,\n+ * the second phase is executed. Lens are moved with smaller steps in a limited\n+ * range within the rough position to find the exact position for best focus.\n+ *\n+ * Tuning file parameters:\n+ * - **coarse-search-step:** The value by which the position will change in one\n+ * step in the *coarse search* phase.\n+ * - **fine-search-step:** The value by which the position will change in one\n+ * step in the *fine search* phase.\n+ * - **fine-search-range:** Search range in the *fine search* phase.\n+ * Valid values are in the [0.0, 1.0] interval. Value 0.05 means 5%. If coarse\n+ * search stopped at value 200, the fine search range will be [190, 210]\n+ * - **max-variance-change:** ratio of contrast variance change in the\n+ * *continuous mode* needed for triggering the focus change. When the variance\n+ * change exceeds this value, focus search will be triggered. Valid values are\n+ * in the [0.0, 1.0] interval.\n+ */\n+\n+/**\n+ * \\brief Initialize the AfHillClimbing with tuning data\n+ * \\param[in] tuningData The tuning data for the algorithm\n+ *\n+ * This method should be called in the libcamera::ipa::Algorithm::init()\n+ * method of the platform layer.\n+ *\n+ * \\return 0 if successful, an error code otherwise\n+ */\n+int AfHillClimbing::init(const YamlObject &tuningData)\n+{\n+\tcoarseSearchStep_ = tuningData[\"coarse-search-step\"].get<uint32_t>(30);\n+\tfineSearchStep_ = tuningData[\"fine-search-step\"].get<uint32_t>(1);\n+\tfineRange_ = tuningData[\"fine-search-range\"].get<double>(0.05);\n+\tmaxChange_ = tuningData[\"max-variance-change\"].get<double>(0.5);\n+\n+\tLOG(Af, Debug) << \"coarseSearchStep_: \" << coarseSearchStep_\n+\t\t << \", fineSearchStep_: \" << fineSearchStep_\n+\t\t << \", fineRange_: \" << fineRange_\n+\t\t << \", maxChange_: \" << maxChange_;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Configure the AfHillClimbing with sensor and lens information\n+ * \\param[in] minFocusPosition Minimum position supported by camera lens\n+ * \\param[in] maxFocusPosition Maximum position supported by camera lens\n+ *\n+ * This method should be called in the libcamera::ipa::Algorithm::configure()\n+ * method of the platform layer.\n+ */\n+void AfHillClimbing::configure(int32_t minFocusPosition,\n+\t\t\t int32_t maxFocusPosition)\n+{\n+\tminLensPosition_ = minFocusPosition;\n+\tmaxLensPosition_ = maxFocusPosition;\n+}\n+\n+/**\n+ * \\brief Run the auto focus algorithm loop\n+ * \\param[in] currentContrast New value of contrast measured for current frame\n+ *\n+ * This method should be called in the libcamera::ipa::Algorithm::process()\n+ * method of the platform layer for each new contrast value that was measured.\n+ *\n+ * \\return New lens position calculated by AF algorithm\n+ */\n+int32_t AfHillClimbing::process(double currentContrast)\n+{\n+\tcurrentContrast_ = currentContrast;\n+\n+\tif (shouldSkipFrame())\n+\t\treturn lensPosition_;\n+\n+\tswitch (mode_) {\n+\tcase controls::AfModeManual:\n+\t\t/* Nothing to process. */\n+\t\tbreak;\n+\tcase controls::AfModeAuto:\n+\t\tprocessAutoMode();\n+\t\tbreak;\n+\tcase controls::AfModeContinuous:\n+\t\tprocessContinousMode();\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn lensPosition_;\n+}\n+\n+void AfHillClimbing::processAutoMode()\n+{\n+\tif (state_ == controls::AfStateScanning) {\n+\t\tafCoarseScan();\n+\t\tafFineScan();\n+\t}\n+}\n+\n+void AfHillClimbing::processContinousMode()\n+{\n+\t/* If we are in a paused state, we won't process the stats */\n+\tif (pauseState_ == controls::AfPauseStatePaused)\n+\t\treturn;\n+\n+\tif (state_ == controls::AfStateScanning) {\n+\t\tafCoarseScan();\n+\t\tafFineScan();\n+\t\treturn;\n+\t}\n+\n+\t/* We can re-start the scan at any moment in AfModeContinuous */\n+\tif (afIsOutOfFocus()) {\n+\t\tafReset();\n+\t}\n+}\n+\n+/**\n+ * \\brief Request AF to skip n frames\n+ * \\param[in] n Number of frames to be skipped\n+ *\n+ * Requested number of frames will not be used for AF calculation.\n+ */\n+void AfHillClimbing::setFramesToSkip(uint32_t n)\n+{\n+\tif (n > framesToSkip_)\n+\t\tframesToSkip_ = n;\n+}\n+\n+void AfHillClimbing::setMode(controls::AfModeEnum mode)\n+{\n+\tif (mode == mode_)\n+\t\treturn;\n+\n+\tLOG(Af, Debug) << \"Switched AF mode from \" << mode_ << \" to \" << mode;\n+\tmode_ = mode;\n+\n+\tstate_ = controls::AfStateIdle;\n+\tpauseState_ = controls::AfPauseStateRunning;\n+\n+\tif (mode_ == controls::AfModeContinuous)\n+\t\tafReset();\n+}\n+\n+void AfHillClimbing::setRange([[maybe_unused]] controls::AfRangeEnum range)\n+{\n+\tLOG(Af, Error) << __FUNCTION__ << \" not implemented!\";\n+}\n+\n+void AfHillClimbing::setSpeed([[maybe_unused]] controls::AfSpeedEnum speed)\n+{\n+\tLOG(Af, Error) << __FUNCTION__ << \" not implemented!\";\n+}\n+\n+void AfHillClimbing::setMeteringMode([[maybe_unused]] controls::AfMeteringEnum metering)\n+{\n+\tLOG(Af, Error) << __FUNCTION__ << \" not implemented!\";\n+}\n+\n+void AfHillClimbing::setWindows([[maybe_unused]] Span<const Rectangle> windows)\n+{\n+\tLOG(Af, Error) << __FUNCTION__ << \" not implemented!\";\n+}\n+\n+void AfHillClimbing::setTrigger(controls::AfTriggerEnum trigger)\n+{\n+\tif (mode_ != controls::AfModeAuto) {\n+\t\tLOG(Af, Warning)\n+\t\t\t<< __FUNCTION__ << \" not possible in mode \" << mode_;\n+\t\treturn;\n+\t}\n+\n+\tLOG(Af, Debug) << \"Trigger called with \" << trigger;\n+\n+\tif (trigger == controls::AfTriggerStart)\n+\t\tafReset();\n+\telse\n+\t\tstate_ = controls::AfStateIdle;\n+}\n+\n+void AfHillClimbing::setPause(controls::AfPauseEnum pause)\n+{\n+\tif (mode_ != controls::AfModeContinuous) {\n+\t\tLOG(Af, Warning)\n+\t\t\t<< __FUNCTION__ << \" not possible in mode \" << mode_;\n+\t\treturn;\n+\t}\n+\n+\tswitch (pause) {\n+\tcase controls::AfPauseImmediate:\n+\t\tpauseState_ = controls::AfPauseStatePaused;\n+\t\tbreak;\n+\tcase controls::AfPauseDeferred:\n+\t\t/* \\todo: add the AfPauseDeferred mode */\n+\t\tLOG(Af, Warning) << \"AfPauseDeferred is not supported!\";\n+\t\tbreak;\n+\tcase controls::AfPauseResume:\n+\t\tpauseState_ = controls::AfPauseStateRunning;\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+}\n+\n+void AfHillClimbing::setLensPosition(float lensPosition)\n+{\n+\tif (mode_ != controls::AfModeManual) {\n+\t\tLOG(Af, Warning)\n+\t\t\t<< __FUNCTION__ << \" not possible in mode \" << mode_;\n+\t\treturn;\n+\t}\n+\n+\tlensPosition_ = lensPosition;\n+\n+\tLOG(Af, Debug) << \"Requesting lens position \" << lensPosition_;\n+}\n+\n+void AfHillClimbing::afCoarseScan()\n+{\n+\tif (coarseCompleted_)\n+\t\treturn;\n+\n+\tif (afScan(coarseSearchStep_)) {\n+\t\tcoarseCompleted_ = true;\n+\t\tmaxContrast_ = 0;\n+\t\tint32_t diff = std::abs(lensPosition_) * fineRange_;\n+\t\tlensPosition_ = std::max(lensPosition_ - diff, minLensPosition_);\n+\t\tmaxStep_ = std::min(lensPosition_ + diff, maxLensPosition_);\n+\t\tpreviousContrast_ = 0;\n+\t}\n+}\n+\n+void AfHillClimbing::afFineScan()\n+{\n+\tif (!coarseCompleted_)\n+\t\treturn;\n+\n+\tif (afScan(fineSearchStep_)) {\n+\t\tLOG(Af, Debug) << \"AF found the best focus position!\";\n+\t\tstate_ = controls::AfStateFocused;\n+\t\tfineCompleted_ = true;\n+\t}\n+}\n+\n+bool AfHillClimbing::afScan(uint32_t steps)\n+{\n+\tif (lensPosition_ + static_cast<int32_t>(steps) > maxStep_) {\n+\t\t/* If the max step is reached, move lens to the position. */\n+\t\tlensPosition_ = bestPosition_;\n+\t\treturn true;\n+\t} else {\n+\t\t/*\n+\t\t* Find the maximum of the variance by estimating its\n+\t\t* derivative. If the direction changes, it means we have passed\n+\t\t* a maximum one step before.\n+\t\t*/\n+\t\tif ((currentContrast_ - maxContrast_) >= -(maxContrast_ * 0.1)) {\n+\t\t\t/*\n+\t\t\t* Positive and zero derivative:\n+\t\t\t* The variance is still increasing. The focus could be\n+\t\t\t* increased for the next comparison. Also, the max\n+\t\t\t* variance and previous focus value are updated.\n+\t\t\t*/\n+\t\t\tbestPosition_ = lensPosition_;\n+\t\t\tlensPosition_ += steps;\n+\t\t\tmaxContrast_ = currentContrast_;\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t* Negative derivative:\n+\t\t\t* The variance starts to decrease which means the maximum\n+\t\t\t* variance is found. Set focus step to previous good one\n+\t\t\t* then return immediately.\n+\t\t\t*/\n+\t\t\tlensPosition_ = bestPosition_;\n+\t\t\treturn true;\n+\t\t}\n+\t}\n+\n+\tpreviousContrast_ = currentContrast_;\n+\tLOG(Af, Debug) << \"Previous step is \" << bestPosition_\n+\t\t << \", Current step is \" << lensPosition_;\n+\treturn false;\n+}\n+\n+void AfHillClimbing::afReset()\n+{\n+\tLOG(Af, Debug) << \"Reset AF parameters\";\n+\tlensPosition_ = minLensPosition_;\n+\tmaxStep_ = maxLensPosition_;\n+\tstate_ = controls::AfStateScanning;\n+\tpreviousContrast_ = 0.0;\n+\tcoarseCompleted_ = false;\n+\tfineCompleted_ = false;\n+\tmaxContrast_ = 0.0;\n+\tsetFramesToSkip(1);\n+}\n+\n+bool AfHillClimbing::afIsOutOfFocus()\n+{\n+\tconst uint32_t diff_var = std::abs(currentContrast_ - maxContrast_);\n+\tconst double var_ratio = diff_var / maxContrast_;\n+\tLOG(Af, Debug) << \"Variance change rate: \" << var_ratio\n+\t\t << \", Current lens step: \" << lensPosition_;\n+\tif (var_ratio > maxChange_)\n+\t\treturn true;\n+\telse\n+\t\treturn false;\n+}\n+\n+bool AfHillClimbing::shouldSkipFrame()\n+{\n+\tif (framesToSkip_ > 0) {\n+\t\tframesToSkip_--;\n+\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+\n+} /* namespace libcamera::ipa::common::algorithms */\ndiff --git a/src/ipa/libipa/algorithms/af_hill_climbing.h b/src/ipa/libipa/algorithms/af_hill_climbing.h\nnew file mode 100644\nindex 00000000..b361e5a1\n--- /dev/null\n+++ b/src/ipa/libipa/algorithms/af_hill_climbing.h\n@@ -0,0 +1,93 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2021, Red Hat\n+ * Copyright (C) 2022, Ideas On Board\n+ * Copyright (C) 2023, Theobroma Systems\n+ *\n+ * af_hill_climbing.h - AF contrast based hill climbing common algorithm\n+ */\n+\n+#pragma once\n+\n+#include <libcamera/base/log.h>\n+\n+#include \"af.h\"\n+\n+namespace libcamera {\n+\n+class YamlObject;\n+\n+namespace ipa::common::algorithms {\n+\n+LOG_DECLARE_CATEGORY(Af)\n+\n+class AfHillClimbing : public Af\n+{\n+public:\n+\tint init(const YamlObject &tuningData);\n+\tvoid configure(int32_t minFocusPosition, int32_t maxFocusPosition);\n+\tint32_t process(double currentContrast);\n+\tvoid setFramesToSkip(uint32_t n);\n+\n+\tcontrols::AfStateEnum state() final { return state_; }\n+\tcontrols::AfPauseStateEnum pauseState() final { return pauseState_; }\n+\n+private:\n+\tvoid setMode(controls::AfModeEnum mode) final;\n+\tvoid setRange(controls::AfRangeEnum range) final;\n+\tvoid setSpeed(controls::AfSpeedEnum speed) final;\n+\tvoid setMeteringMode(controls::AfMeteringEnum metering) final;\n+\tvoid setWindows(Span<const Rectangle> windows) final;\n+\tvoid setTrigger(controls::AfTriggerEnum trigger) final;\n+\tvoid setPause(controls::AfPauseEnum pause) final;\n+\tvoid setLensPosition(float lensPosition) final;\n+\n+\tvoid processAutoMode();\n+\tvoid processContinousMode();\n+\tvoid afCoarseScan();\n+\tvoid afFineScan();\n+\tbool afScan(uint32_t steps);\n+\tvoid afReset();\n+\tbool afIsOutOfFocus();\n+\tbool shouldSkipFrame();\n+\n+\tcontrols::AfModeEnum mode_ = controls::AfModeManual;\n+\tcontrols::AfStateEnum state_ = controls::AfStateIdle;\n+\tcontrols::AfPauseStateEnum pauseState_ = controls::AfPauseStateRunning;\n+\n+\t/* VCM step configuration. It is the current setting of the VCM step. */\n+\tint32_t lensPosition_ = 0;\n+\t/* The best VCM step. It is a local optimum VCM step during scanning. */\n+\tint32_t bestPosition_ = 0;\n+\n+\t/* Current AF statistic contrast. */\n+\tdouble currentContrast_ = 0;\n+\t/* It is used to determine the derivative during scanning */\n+\tdouble previousContrast_ = 0;\n+\tdouble maxContrast_ = 0;\n+\t/* The designated maximum range of focus scanning. */\n+\tint32_t maxStep_ = 0;\n+\t/* If the coarse scan completes, it is set to true. */\n+\tbool coarseCompleted_ = false;\n+\t/* If the fine scan completes, it is set to true. */\n+\tbool fineCompleted_ = false;\n+\n+\tuint32_t framesToSkip_ = 0;\n+\n+\t/* Focus steps range of the lens */\n+\tint32_t minLensPosition_;\n+\tint32_t maxLensPosition_;\n+\n+\t/* Minimum focus step for searching appropriate focus */\n+\tuint32_t coarseSearchStep_;\n+\tuint32_t fineSearchStep_;\n+\n+\t/* Fine scan range 0 < fineRange_ < 1 */\n+\tdouble fineRange_;\n+\n+\t/* Max ratio of variance change, 0.0 < maxChange_ < 1.0 */\n+\tdouble maxChange_;\n+};\n+\n+} /* namespace ipa::common::algorithms */\n+} /* namespace libcamera */\ndiff --git a/src/ipa/libipa/algorithms/meson.build b/src/ipa/libipa/algorithms/meson.build\nindex 7602976c..fa4b9564 100644\n--- a/src/ipa/libipa/algorithms/meson.build\n+++ b/src/ipa/libipa/algorithms/meson.build\n@@ -2,8 +2,10 @@\n \n common_ipa_algorithms_headers = files([\n 'af.h',\n+ 'af_hill_climbing.h',\n ])\n \n common_ipa_algorithms_sources = files([\n 'af.cpp',\n+ 'af_hill_climbing.cpp',\n ])\n", "prefixes": [ "libcamera-devel", "v4", "04/10" ] }