libcamera: af: re-baseline the scene reference after a sweep (r5)
Some checks failed
image / image (push) Has been cancelled

Hardware finding from the first r4 run on the phone: the focus-loss
detector compared the settled scene against the maximum sharpness
recorded during the sweep, which was measured under a different AGC
state. On the FP6 that maximum read 4848088 and the same position two
stats frames later read 1995445, so the detector fired immediately and
the lens swept forever. Patch 0014 takes the reference after the lens
settles and requires four consecutive out-of-band frames.

Verified on the phone: r4 re-swept without end, r5 completes one sweep
and stays put.
This commit is contained in:
Jorijn van der Graaf 2026-08-17 02:35:59 +02:00
commit 236015ef36
2 changed files with 117 additions and 1 deletions

View file

@ -0,0 +1,114 @@
From b99ba09cd463cbfbb6530a82c9e2702bfd0bb29a Mon Sep 17 00:00:00 2001
From: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Date: Mon, 17 Aug 2026 02:18:32 +0200
Subject: [PATCH] libcamera: software_isp: af: Re-baseline the scene reference
after a sweep
Measured on the FP6 with the DW9784: a sweep converged on 38% with a
recorded maximum sharpness of 4848088, and two stats frames later the
metric at that same position read 1995445 - a 59% drop with the lens
parked and the scene unchanged. The sweep maximum was measured under
whatever exposure and gain the AGC happened to be using several hundred
milliseconds earlier, so comparing the settled scene against it fires
the focus-loss detector immediately and the lens sweeps forever.
Take the reference from the first measurement after the lens has
settled, and require the scene to look different for four consecutive
stats frames before re-focusing, so that a single AGC excursion does not
count as the scene changing.
Signed-off-by: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
---
src/ipa/simple/algorithms/af.cpp | 37 ++++++++++++++++++++++++++------
src/ipa/simple/ipa_context.h | 4 ++++
2 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/src/ipa/simple/algorithms/af.cpp b/src/ipa/simple/algorithms/af.cpp
index 00148ce1b..6c9510389 100644
--- a/src/ipa/simple/algorithms/af.cpp
+++ b/src/ipa/simple/algorithms/af.cpp
@@ -34,6 +34,8 @@ constexpr double kFineStepMin = 1.0;
constexpr uint32_t kSettleSkipLong = 3;
/* Relative sharpness change that makes a settled scene worth re-focusing. */
constexpr double kFocusLossThreshold = 0.3;
+/* Consecutive stats frames that change must persist for before re-focusing. */
+constexpr uint32_t kFocusLossFrames = 4;
/*
* Focus continuously by default: applications that know nothing about AF never
* send an AfMode or an AfTrigger, and they are the ones this exists for.
@@ -140,6 +142,8 @@ void Af::restart(IPAContext &context)
focus.stop = 100;
focus.step = kCoarseStep;
focus.skip = kSettleSkipLong;
+ focus.settled = false;
+ focus.lossCount = 0;
context.activeState.knobs.focus_pos = 0;
context.activeState.knobs.focus_sweep = true;
LOG(IPASoftAutoFocus, Info) << "Starting focus sweep";
@@ -165,13 +169,34 @@ void Af::step(IPAContext &context, double &focus_pos, uint64_t sharp, bool &swee
if (context.activeState.knobs.af_mode != controls::AfModeContinuous)
return;
- if (utils::abs_diff(sharp, focus.sharpness_max) >
+ /*
+ * Take the reference the scene is compared against here, at
+ * the settled position, rather than reusing the maximum found
+ * during the sweep: that maximum was measured under whatever
+ * exposure and gain the AGC happened to be using at the time.
+ */
+ if (!focus.settled) {
+ focus.sharpness_max = sharp;
+ focus.lossCount = 0;
+ focus.settled = true;
+ LOG(IPASoftAutoFocus, Debug)
+ << "Settled sharpness reference " << sharp;
+ return;
+ }
+
+ if (utils::abs_diff(sharp, focus.sharpness_max) <=
focus.sharpness_max * kFocusLossThreshold) {
- LOG(IPASoftAutoFocus, Info)
- << "Focus lost: " << sharp << " vs "
- << focus.sharpness_max;
- restart(context);
+ focus.lossCount = 0;
+ return;
}
+
+ if (++focus.lossCount < kFocusLossFrames)
+ return;
+
+ LOG(IPASoftAutoFocus, Info)
+ << "Focus lost: " << sharp << " vs "
+ << focus.sharpness_max;
+ restart(context);
return;
}
@@ -204,10 +229,10 @@ void Af::step(IPAContext &context, double &focus_pos, uint64_t sharp, bool &swee
sweep = false;
focus_pos = focus.focus_max_pos;
+ focus.settled = false;
LOG(IPASoftAutoFocus, Info)
<< "Sweep end. Best focus: " << focus.focus_max_pos
<< " after " << steps_ << " frames";
- /* sharpness_max is kept to detect the scene changing later on. */
}
void Af::process(IPAContext &context,
diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
index 4c594cd29..36a28256b 100644
--- a/src/ipa/simple/ipa_context.h
+++ b/src/ipa/simple/ipa_context.h
@@ -41,6 +41,10 @@ struct IPASessionConfiguration {
double start, stop, step;
/* Stats frames to ignore while the lens settles */
uint32_t skip;
+ /* Set once the post-sweep sharpness reference has been taken */
+ bool settled;
+ /* Consecutive stats frames the scene has looked different */
+ uint32_t lossCount;
} focus;
};

View file

@ -3,7 +3,7 @@ maintainer="Robert Mader <robert.mader@collabora.com>"
pkgname=libcamera pkgname=libcamera
_pkgver=0.7.2 _pkgver=0.7.2
pkgver=9999$_pkgver pkgver=9999$_pkgver
pkgrel=4 pkgrel=5
pkgdesc="Linux camera framework" pkgdesc="Linux camera framework"
url="https://libcamera.org/" url="https://libcamera.org/"
arch="all" arch="all"
@ -70,6 +70,7 @@ source="https://gitlab.freedesktop.org/camera/libcamera/-/archive/v$_pkgver/libc
0011-libcamera-pipeline-simple-Apply-the-lens-position-un.patch 0011-libcamera-pipeline-simple-Apply-the-lens-position-un.patch
0012-libcamera-software_isp-af-Harden-the-lens-control-ha.patch 0012-libcamera-software_isp-af-Harden-the-lens-control-ha.patch
0013-libcamera-software_isp-af-Add-AfMode-and-focus-witho.patch 0013-libcamera-software_isp-af-Add-AfMode-and-focus-witho.patch
0014-libcamera-software_isp-af-Re-baseline-the-scene-refe.patch
qcam.desktop qcam.desktop
$_tuning_files $_tuning_files
" "
@ -196,6 +197,7 @@ c8ddc64ab943d9b215f4c997f2629e4403744c0ddb5de88cd42d384506a27c0762191eddd4d13559
3965405be757917030c89db5da60aa226d0f0591e213d055d8312dbddee0c3d81fbcaa5928d6de4d3cb636572a27261d8686d87ae39b9634d70b33cf8388dc73 0011-libcamera-pipeline-simple-Apply-the-lens-position-un.patch 3965405be757917030c89db5da60aa226d0f0591e213d055d8312dbddee0c3d81fbcaa5928d6de4d3cb636572a27261d8686d87ae39b9634d70b33cf8388dc73 0011-libcamera-pipeline-simple-Apply-the-lens-position-un.patch
8734e27966bd1bc5a4b0324a3d8a3ee4ab371f6d1536500b2bb44d49bde874c3fd6f244312332226c352e4716ba12fb72bf51e64b9e97cf6fa646aa6dfef3277 0012-libcamera-software_isp-af-Harden-the-lens-control-ha.patch 8734e27966bd1bc5a4b0324a3d8a3ee4ab371f6d1536500b2bb44d49bde874c3fd6f244312332226c352e4716ba12fb72bf51e64b9e97cf6fa646aa6dfef3277 0012-libcamera-software_isp-af-Harden-the-lens-control-ha.patch
dce81c2863ff6b9374115325f4af14ac5d396a6e2aee12f87d55a47d1d0bda860ae2af5a9d6885b126e20a12b7f3f40e49a2b9d738f94c04177bff6149da64e1 0013-libcamera-software_isp-af-Add-AfMode-and-focus-witho.patch dce81c2863ff6b9374115325f4af14ac5d396a6e2aee12f87d55a47d1d0bda860ae2af5a9d6885b126e20a12b7f3f40e49a2b9d738f94c04177bff6149da64e1 0013-libcamera-software_isp-af-Add-AfMode-and-focus-witho.patch
18ed6a03fbe3bcd750fac280831d1e62f2a2e35f76880535f2dc16ff196308512535e077ab0d8a923ad241090713edb53d1777e0dd6e5dcc10de495db1277052 0014-libcamera-software_isp-af-Re-baseline-the-scene-refe.patch
22167a4eceb6d1b40b0b7c45fdf116c71684f5340de7f767535cb8e160ad9d2ae0f00cb3d461f73a344520a48a4641cf46226841d78bee06bfbfd2a91337f754 qcam.desktop 22167a4eceb6d1b40b0b7c45fdf116c71684f5340de7f767535cb8e160ad9d2ae0f00cb3d461f73a344520a48a4641cf46226841d78bee06bfbfd2a91337f754 qcam.desktop
2ee566653b17d565d2c0de67cbe6ebad25df5aac6051cad78e6bae52016fa2ca0247e964d735ac776b3e0bd447ead9c4fcb26cd7629228cdba3ceb91b46f378f hi846.yaml 2ee566653b17d565d2c0de67cbe6ebad25df5aac6051cad78e6bae52016fa2ca0247e964d735ac776b3e0bd447ead9c4fcb26cd7629228cdba3ceb91b46f378f hi846.yaml
55dab9dcb9b1982143b9d63e4e51138c5bffd7c8f4a8e4e122750b3ad9d475446956bc297d9f564819ade4be75a575d06633a064e14f8ba3ef77986783c5f067 imx355.yaml 55dab9dcb9b1982143b9d63e4e51138c5bffd7c8f4a8e4e122750b3ad9d475446956bc297d9f564819ade4be75a575d06633a064e14f8ba3ef77986783c5f067 imx355.yaml