From patchwork Wed Oct 21 07:59:41 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 28536 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 230EAC324C for ; Wed, 21 Oct 2026 08:00:14 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2109368F1B; Wed, 21 Oct 2026 10:00:13 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="XAP1iGUY"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1F82E68E07 for ; Wed, 21 Oct 2026 10:00:11 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792569610; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IfWkb9RruG2o9WSDmFwwVSxVvYHxg8P1acWGiv1fG9I=; b=XAP1iGUYNO1VH7ZLymdSQIfuiGFuQKLX30JlG4Z83ff1gCJT2SV5tgdn57abvUUd/HeVx0 +W+5HG4fUBLhRyADWXql1z6nN6vhaIrqZ0exn41OVF1Ns7yMFVj8/ZmXPNLHXZlkj1y5Me enIx+r22yitTn9b8HUjWM3hBjVwsYx4= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-25-HQU3UtsUNcWIsGB6gocoCQ-1; Wed, 21 Oct 2026 04:00:08 -0400 X-MC-Unique: HQU3UtsUNcWIsGB6gocoCQ-1 X-Mimecast-MFC-AGG-ID: HQU3UtsUNcWIsGB6gocoCQ_1792569607 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4D5B81956040 for ; Wed, 21 Oct 2026 08:00:07 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.43.17.75]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 80E5A1800444; Wed, 21 Oct 2026 08:00:06 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal Subject: [RFC PATCH 1/7] libcamera: software_isp: Let a single point decide about computing stats Date: Wed, 21 Oct 2026 09:59:41 +0200 Message-ID: <20261021080000.16624-2-mzamazal@redhat.com> In-Reply-To: <20261021080000.16624-1-mzamazal@redhat.com> References: <20261021080000.16624-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: x_per4KNi_rEsGsu0sVKka3Zte8xtQ-8MnAGwVtaUZ8_1792569607 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Currently, the decision whether to compute stats for a given frame is implemented in SwStatsCpu in several places and duplicated in debayer_egl.cpp. Let's decide it solely in SwStatsCpu. This will also help if a more flexible or complex update frequency is introduced, e.g. to support stats for autofocus. Signed-off-by: Milan Zamazal --- .../internal/software_isp/swstats_cpu.h | 17 ++++++------- src/libcamera/software_isp/debayer_egl.cpp | 24 ++++++++----------- src/libcamera/software_isp/swstats_cpu.cpp | 16 ++++++++++--- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/include/libcamera/internal/software_isp/swstats_cpu.h b/include/libcamera/internal/software_isp/swstats_cpu.h index 551870921..cd5303a51 100644 --- a/include/libcamera/internal/software_isp/swstats_cpu.h +++ b/include/libcamera/internal/software_isp/swstats_cpu.h @@ -38,14 +38,6 @@ public: SwStatsCpu(const CameraManager &cm); ~SwStatsCpu() = default; - /* - * The combination of pipeline + sensor delays means that - * exposure changes can take up to 3 frames to get applied, - * Run stats once every 4 frames to ensure any previous - * exposure changes have been applied. - */ - static constexpr uint32_t kStatPerNumFrames = 4; - bool isValid() const { return sharedStats_.fd().isValid(); } const SharedFD &getStatsFD() { return sharedStats_.fd(); } @@ -54,6 +46,7 @@ public: int configure(const StreamConfiguration &inputCfg, unsigned int statsBufferCount = 1); void setWindow(const Rectangle &window); + bool updating(uint32_t frame); void startFrame(uint32_t frame); void finishFrame(uint32_t frame, uint32_t bufferId); void processFrame(uint32_t frame, uint32_t bufferId, MappedFrameBuffer &input); @@ -88,6 +81,14 @@ private: using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[], SwIspStats &stats); using processFrameFn = void (SwStatsCpu::*)(MappedFrameBuffer &in); + /* + * The combination of pipeline + sensor delays means that + * exposure changes can take up to 3 frames to get applied, + * Run stats once every 4 frames to ensure any previous + * exposure changes have been applied. + */ + static constexpr uint32_t kStatPerNumFrames = 4; + int setupStandardBayerOrder(BayerFormat::Order order); /* Bayer 8 bpp unpacked */ void statsBGGR8Line0(const uint8_t *src[], SwIspStats &stats); diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 300822f9d..1a9dc1841 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -630,21 +630,17 @@ void DebayerEGL::process(uint32_t frame, FrameBuffer *input, FrameBuffer *output metadata.planes()[0].bytesused = output->planes()[0].length; /* Calculate stats for the whole frame */ - if (frame % SwStatsCpu::kStatPerNumFrames) { - stats_->finishFrame(frame, 0); - } else { - if (!inMapped) { - /* - * The buffer was directly imported into EGL and thus - * not mapped for texture upload. Do it now for the - * CPU-based stats calculation. - */ - assert(!inDmaSyncer); - inDmaSyncer.emplace(input->planes()[0].fd, DmaSyncer::SyncType::Read); - inMapped.emplace(input, MappedFrameBuffer::MapFlag::Read); - } - stats_->processFrame(frame, 0, inMapped.value()); + if (stats_->updating(frame) && !inMapped) { + /* + * The buffer was directly imported into EGL and thus + * not mapped for texture upload. Do it now for the + * CPU-based stats calculation. + */ + assert(!inDmaSyncer); + inDmaSyncer.emplace(input->planes()[0].fd, DmaSyncer::SyncType::Read); + inMapped.emplace(input, MappedFrameBuffer::MapFlag::Read); } + stats_->processFrame(frame, 0, inMapped.value()); inDmaSyncer.reset(); egl_.syncOutput(); diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp index 7fb77ce7d..362fb6a86 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -383,7 +383,7 @@ void SwStatsCpu::statsGBRG12PLine0(const uint8_t *src[], SwIspStats &stats) */ void SwStatsCpu::startFrame(uint32_t frame) { - if (frame % kStatPerNumFrames) + if (!updating(frame)) return; if (window_.width == 0) @@ -404,7 +404,7 @@ void SwStatsCpu::startFrame(uint32_t frame) */ void SwStatsCpu::finishFrame(uint32_t frame, uint32_t bufferId) { - bool valid = frame % kStatPerNumFrames == 0; + bool valid = updating(frame); if (valid) { sharedStats_->sum_ = RGB({ 0, 0, 0 }); @@ -604,7 +604,7 @@ void SwStatsCpu::processBayerFrame2(MappedFrameBuffer &in) */ void SwStatsCpu::processFrame(uint32_t frame, uint32_t bufferId, MappedFrameBuffer &input) { - if (frame % kStatPerNumFrames) { + if (!updating(frame)) { finishFrame(frame, bufferId); return; } @@ -616,4 +616,14 @@ void SwStatsCpu::processFrame(uint32_t frame, uint32_t bufferId, MappedFrameBuff bench_.finishFrame(); } +/** + * \brief Determine whether stats update is required for the given frame + * \param[in] frame The frame number + * \return Whether the stats are updated for the given frame + */ +bool SwStatsCpu::updating(uint32_t frame) +{ + return frame % kStatPerNumFrames == 0; +} + } /* namespace libcamera */ From patchwork Wed Oct 21 07:59:42 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 28537 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 48868C324C for ; Wed, 21 Oct 2026 08:00:16 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A693C68F24; Wed, 21 Oct 2026 10:00:15 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="aI7fiRAK"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5CE0E68F1D for ; Wed, 21 Oct 2026 10:00:13 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792569612; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=jPWwtr5v60XlMlufzFYuhv6IR/Dpe5IErQFFlfNV1X8=; b=aI7fiRAKjs9yNdi+9875iLcmyaxwIbu6dZLQ4utsO5K2Uh9fcYVh2EjLrZU0707tfMDary SK41wALPAZDz/th7yZ8PzklKsudMaYwJ39dnw51ffgCVoGlrufWyE1WranwLvEZHReveXz oI9Ez4y12UUz1fd4VRTz73T+kfhRnmE= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-655-TBur3ctBMl6HTOgi9P0QhQ-1; Wed, 21 Oct 2026 08:00:09 +0000 X-MC-Unique: TBur3ctBMl6HTOgi9P0QhQ-1 X-Mimecast-MFC-AGG-ID: TBur3ctBMl6HTOgi9P0QhQ_1792569608 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A11941955F17 for ; Wed, 21 Oct 2026 08:00:08 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.43.17.75]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D49F81800444; Wed, 21 Oct 2026 08:00:07 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal Subject: [RFC PATCH 2/7] ipa: ipu3: Move non-specific parts of Af to libipa Date: Wed, 21 Oct 2026 09:59:42 +0200 Message-ID: <20261021080000.16624-3-mzamazal@redhat.com> In-Reply-To: <20261021080000.16624-1-mzamazal@redhat.com> References: <20261021080000.16624-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: WT-5Z6_j_2akyfcA_mNIWhMp5KVFQfksOokUkg4GW24_1792569608 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Some parts of the ipu3 autofocus algorithm could be reused by other pipelines, most notably softisp. Let's move the parts of the algorithm that are not specific to the IPU3 hardware to libipa and use them from there in ipu3. Signed-off-by: Milan Zamazal --- src/ipa/ipu3/algorithms/af.cpp | 219 +----------------------- src/ipa/ipu3/algorithms/af.h | 27 +-- src/ipa/ipu3/ipa_context.cpp | 17 +- src/ipa/ipu3/ipa_context.h | 8 +- src/ipa/libipa/af.cpp | 299 +++++++++++++++++++++++++++++++++ src/ipa/libipa/af.h | 69 ++++++++ src/ipa/libipa/meson.build | 2 + 7 files changed, 382 insertions(+), 259 deletions(-) create mode 100644 src/ipa/libipa/af.cpp create mode 100644 src/ipa/libipa/af.h diff --git a/src/ipa/ipu3/algorithms/af.cpp b/src/ipa/ipu3/algorithms/af.cpp index e796b06a2..bb412e165 100644 --- a/src/ipa/ipu3/algorithms/af.cpp +++ b/src/ipa/ipu3/algorithms/af.cpp @@ -59,25 +59,6 @@ namespace ipa::ipu3::algorithms { LOG_DEFINE_CATEGORY(IPU3Af) -/** - * Maximum focus steps of the VCM control - * \todo should be obtained from the VCM driver - */ -static constexpr uint32_t kMaxFocusSteps = 1023; - -/* Minimum focus step for searching appropriate focus */ -static constexpr uint32_t kCoarseSearchStep = 30; -static constexpr uint32_t kFineSearchStep = 1; - -/* Max ratio of variance change, 0.0 < kMaxChange < 1.0 */ -static constexpr double kMaxChange = 0.5; - -/* The numbers of frame to be ignored, before performing focus scan. */ -static constexpr uint32_t kIgnoreFrame = 10; - -/* Fine scan range 0 < kFineRange < 1 */ -static constexpr double kFineRange = 0.05; - /* Settings for IPU3 AF filter */ static struct ipu3_uapi_af_filter_config afFilterConfigDefault = { .y1_coeff_0 = { 0, 1, 3, 7 }, @@ -105,8 +86,6 @@ static struct ipu3_uapi_af_filter_config afFilterConfigDefault = { * image. */ Af::Af() - : focus_(0), bestFocus_(0), currentVariance_(0.0), previousVariance_(0.0), - coarseCompleted_(false), fineCompleted_(false) { } @@ -162,18 +141,7 @@ int Af::configure(IPAContext &context, const IPAConfigInfo &configInfo) grid.y_start = utils::alignDown(start.y, 2); grid.y_start |= IPU3_UAPI_GRID_Y_START_EN; - /* Initial max focus step */ - maxStep_ = kMaxFocusSteps; - - /* Initial frame ignore counter */ - afIgnoreFrameReset(); - - /* Initial focus value */ - context.activeState.af.focus = 0; - /* Maximum variance of the AF statistics */ - context.activeState.af.maxVariance = 0; - /* The stable AF value flag. if it is true, the AF should be in a stable state. */ - context.activeState.af.stable = false; + afAlgo_.configure(context.activeState.af); return 0; } @@ -194,151 +162,6 @@ void Af::prepare(IPAContext &context, params->use.acc_af = 1; } -/** - * \brief AF coarse scan - * \param[in] context The shared IPA context - * - * Find a near focused image using a coarse step. The step is determined by - * kCoarseSearchStep. - */ -void Af::afCoarseScan(IPAContext &context) -{ - if (coarseCompleted_) - return; - - if (afNeedIgnoreFrame()) - return; - - if (afScan(context, kCoarseSearchStep)) { - coarseCompleted_ = true; - context.activeState.af.maxVariance = 0; - focus_ = context.activeState.af.focus - - (context.activeState.af.focus * kFineRange); - context.activeState.af.focus = focus_; - previousVariance_ = 0; - maxStep_ = std::clamp(focus_ + static_cast((focus_ * kFineRange)), - 0U, kMaxFocusSteps); - } -} - -/** - * \brief AF fine scan - * \param[in] context The shared IPA context - * - * Find an optimum lens position with moving 1 step for each search. - */ -void Af::afFineScan(IPAContext &context) -{ - if (!coarseCompleted_) - return; - - if (afNeedIgnoreFrame()) - return; - - if (afScan(context, kFineSearchStep)) { - context.activeState.af.stable = true; - fineCompleted_ = true; - } -} - -/** - * \brief AF reset - * \param[in] context The shared IPA context - * - * Reset all the parameters to start over the AF process. - */ -void Af::afReset(IPAContext &context) -{ - if (afNeedIgnoreFrame()) - return; - - context.activeState.af.maxVariance = 0; - context.activeState.af.focus = 0; - focus_ = 0; - context.activeState.af.stable = false; - ignoreCounter_ = kIgnoreFrame; - previousVariance_ = 0.0; - coarseCompleted_ = false; - fineCompleted_ = false; - maxStep_ = kMaxFocusSteps; -} - -/** - * \brief AF variance comparison - * \param[in] context The IPA context - * \param[in] min_step The VCM movement step - * - * We always pick the largest variance to replace the previous one. The image - * with a larger variance also indicates it is a clearer image than previous - * one. If we find a negative derivative, we return immediately. - * - * \return True, if it finds a AF value. - */ -bool Af::afScan(IPAContext &context, int min_step) -{ - if (focus_ > maxStep_) { - /* If reach the max step, move lens to the position. */ - context.activeState.af.focus = bestFocus_; - return true; - } else { - /* - * Find the maximum of the variance by estimating its - * derivative. If the direction changes, it means we have - * passed a maximum one step before. - */ - if ((currentVariance_ - context.activeState.af.maxVariance) >= - -(context.activeState.af.maxVariance * 0.1)) { - /* - * Positive and zero derivative: - * The variance is still increasing. The focus could be - * increased for the next comparison. Also, the max variance - * and previous focus value are updated. - */ - bestFocus_ = focus_; - focus_ += min_step; - context.activeState.af.focus = focus_; - context.activeState.af.maxVariance = currentVariance_; - } else { - /* - * Negative derivative: - * The variance starts to decrease which means the maximum - * variance is found. Set focus step to previous good one - * then return immediately. - */ - context.activeState.af.focus = bestFocus_; - return true; - } - } - - previousVariance_ = currentVariance_; - LOG(IPU3Af, Debug) << " Previous step is " - << bestFocus_ - << " Current step is " - << focus_; - return false; -} - -/** - * \brief Determine the frame to be ignored - * \return Return True if the frame should be ignored, false otherwise - */ -bool Af::afNeedIgnoreFrame() -{ - if (ignoreCounter_ == 0) - return false; - else - ignoreCounter_--; - return true; -} - -/** - * \brief Reset frame ignore counter - */ -void Af::afIgnoreFrameReset() -{ - ignoreCounter_ = kIgnoreFrame; -} - /** * \brief Estimate variance * \param[in] y_items The AF filter data set from the IPU3 statistics buffer @@ -373,33 +196,6 @@ double Af::afEstimateVariance(std::span y_items, bool isY1 return var_sum / y_items.size(); } -/** - * \brief Determine out-of-focus situation - * \param[in] context The IPA context - * - * Out-of-focus means that the variance change rate for a focused and a new - * variance is greater than a threshold. - * - * \return True if the variance threshold is crossed indicating lost focus, - * false otherwise - */ -bool Af::afIsOutOfFocus(IPAContext &context) -{ - const uint32_t diff_var = std::abs(currentVariance_ - - context.activeState.af.maxVariance); - const double var_ratio = diff_var / context.activeState.af.maxVariance; - - LOG(IPU3Af, Debug) << "Variance change rate: " - << var_ratio - << " Current VCM step: " - << context.activeState.af.focus; - - if (var_ratio > kMaxChange) - return true; - else - return false; -} - /** * \brief Determine the max contrast image and lens position * \param[in] context The IPA context @@ -438,17 +234,8 @@ void Af::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, * For coarse: y1 are used. * For fine: y2 results are used. */ - currentVariance_ = afEstimateVariance(y_items, !coarseCompleted_); - - if (!context.activeState.af.stable) { - afCoarseScan(context); - afFineScan(context); - } else { - if (afIsOutOfFocus(context)) - afReset(context); - else - afIgnoreFrameReset(); - } + double variance = afEstimateVariance(y_items, !afAlgo_.coarseCompleted()); + afAlgo_.process(context.activeState.af, variance); } REGISTER_IPA_ALGORITHM(Af, "Af") diff --git a/src/ipa/ipu3/algorithms/af.h b/src/ipa/ipu3/algorithms/af.h index 320b67496..fcf1c9cf6 100644 --- a/src/ipa/ipu3/algorithms/af.h +++ b/src/ipa/ipu3/algorithms/af.h @@ -13,6 +13,8 @@ #include +#include "libipa/af.h" + #include "algorithm.h" namespace libcamera { @@ -40,32 +42,9 @@ public: ControlList &metadata) override; private: - void afCoarseScan(IPAContext &context); - void afFineScan(IPAContext &context); - bool afScan(IPAContext &context, int min_step); - void afReset(IPAContext &context); - bool afNeedIgnoreFrame(); - void afIgnoreFrameReset(); double afEstimateVariance(std::span y_items, bool isY1); - bool afIsOutOfFocus(IPAContext &context); - - /* VCM step configuration. It is the current setting of the VCM step. */ - uint32_t focus_; - /* The best VCM step. It is a local optimum VCM step during scanning. */ - uint32_t bestFocus_; - /* Current AF statistic variance. */ - double currentVariance_; - /* The frames are ignore before starting measuring. */ - uint32_t ignoreCounter_; - /* It is used to determine the derivative during scanning */ - double previousVariance_; - /* The designated maximum range of focus scanning. */ - uint32_t maxStep_; - /* If the coarse scan completes, it is set to true. */ - bool coarseCompleted_; - /* If the fine scan completes, it is set to true. */ - bool fineCompleted_; + AfAlgorithm afAlgo_; }; } /* namespace ipa::ipu3::algorithms */ diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp index 6632c6f45..9f628cb45 100644 --- a/src/ipa/ipu3/ipa_context.cpp +++ b/src/ipa/ipu3/ipa_context.cpp @@ -88,22 +88,13 @@ namespace libcamera::ipa::ipu3 { */ /** - * \var IPAActiveState::af - * \brief Context for the Automatic Focus algorithm - * - * \var IPAActiveState::af.focus - * \brief Current position of the lens - * - * \var IPAActiveState::af.maxVariance - * \brief The maximum variance of the current image - * - * \var IPAActiveState::af.stable - * \brief It is set to true, if the best focus is found + * \var IPASessionConfiguration::agc + * \brief AGC parameters configuration of the IPA */ /** - * \var IPASessionConfiguration::agc - * \brief AGC parameters configuration of the IPA + * \var IPAActiveState::af + * \brief Autofocus state */ /** diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index fde628c22..ca3a9fc9d 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -24,6 +24,7 @@ #include #include #include +#include "libipa/af.h" namespace libcamera { @@ -44,12 +45,7 @@ struct IPASessionConfiguration { }; struct IPAActiveState { - struct { - uint32_t focus; - double maxVariance; - bool stable; - } af; - + ipa::af::ActiveState af; agc::ActiveState agc; ipa::awb::ActiveState awb; ipa::ccm::ActiveState ccm; diff --git a/src/ipa/libipa/af.cpp b/src/ipa/libipa/af.cpp new file mode 100644 index 000000000..492d65d1e --- /dev/null +++ b/src/ipa/libipa/af.cpp @@ -0,0 +1,299 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021-2026 Red Hat + * + * Autofocus algorithm + */ + +#include "af.h" + +namespace libcamera { + +namespace ipa { + +LOG_DEFINE_CATEGORY(Af) + +namespace af { + +/** + * \struct ActiveState + * \brief Autofocus algorithm state + * + * \var ActiveState::focus + * \brief The focus value for the V4L2 control + * + * \var ActiveState::maxVariance + * \brief Maximum variance of the AF statistics + * + * \var ActiveState::stable + * \brief Flag determining whether focus is in a stable state + */ + +} /* namespace af */ + +/** + * \class AfAlgorithm + * \brief Implementation of a common autofocus algorithm + * + * The algorithm is provided with a single variance value in + * \a AfAlgorithm::process and adjusts \a ActiveState::focus accordingly. + */ + +/** + * Maximum focus steps of the VCM control + * \todo should be obtained from the VCM driver + */ +static constexpr uint32_t kMaxFocusSteps = 1023; + +/* Minimum focus step for searching appropriate focus */ +static constexpr uint32_t kCoarseSearchStep = 30; +static constexpr uint32_t kFineSearchStep = 1; + +/* Max ratio of variance change, 0.0 < kMaxChange < 1.0 */ +static constexpr double kMaxChange = 0.5; + +/* The numbers of frame to be ignored, before performing focus scan. */ +static constexpr uint32_t kIgnoreFrame = 10; + +/* Fine scan range 0 < kFineRange < 1 */ +static constexpr double kFineRange = 0.05; + +AfAlgorithm::AfAlgorithm() + : focus_(0), bestFocus_(0), currentVariance_(0.0), previousVariance_(0.0), + coarseCompleted_(false), fineCompleted_(false) +{ +} + +/** + * \fn AfAlgorithm::coarseCompleted() + * \brief Return whether the coarse autofocus scan is completed + */ + +/** + * \brief AF coarse scan + * \param[in] state The autofocus state + * + * Find a near focused image using a coarse step. The step is determined by + * kCoarseSearchStep. + */ +void AfAlgorithm::afCoarseScan(af::ActiveState &state) +{ + if (coarseCompleted_) + return; + + if (afNeedIgnoreFrame()) + return; + + if (afScan(state, kCoarseSearchStep)) { + coarseCompleted_ = true; + state.maxVariance = 0; + focus_ = state.focus - + (state.focus * kFineRange); + state.focus = focus_; + previousVariance_ = 0; + maxStep_ = std::clamp(focus_ + static_cast((focus_ * kFineRange)), + 0U, kMaxFocusSteps); + } +} + +/** + * \brief AF fine scan + * \param[in] state The autofocus state + * + * Find an optimum lens position with moving 1 step for each search. + */ +void AfAlgorithm::afFineScan(af::ActiveState &state) +{ + if (!coarseCompleted_) + return; + + if (afNeedIgnoreFrame()) + return; + + if (afScan(state, kFineSearchStep)) { + state.stable = true; + fineCompleted_ = true; + } +} + +/** + * \brief AF reset + * \param[in] state The autofocus state + * + * Reset all the parameters to start over the AF process. + */ +void AfAlgorithm::afReset(af::ActiveState &state) +{ + if (afNeedIgnoreFrame()) + return; + + state.maxVariance = 0; + state.focus = 0; + focus_ = 0; + state.stable = false; + ignoreCounter_ = kIgnoreFrame; + previousVariance_ = 0.0; + coarseCompleted_ = false; + fineCompleted_ = false; + maxStep_ = kMaxFocusSteps; +} + +/** + * \brief AF variance comparison + * \param[in] state The autofocus state + * \param[in] min_step The VCM movement step + * + * We always pick the largest variance to replace the previous one. The image + * with a larger variance also indicates it is a clearer image than previous + * one. If we find a negative derivative, we return immediately. + * + * \return True, if it finds a AF value + */ +bool AfAlgorithm::afScan(af::ActiveState &state, int min_step) +{ + if (focus_ > maxStep_) { + /* If reach the max step, move lens to the position. */ + state.focus = bestFocus_; + return true; + } else { + /* + * Find the maximum of the variance by estimating its + * derivative. If the direction changes, it means we have + * passed a maximum one step before. + */ + if ((currentVariance_ - state.maxVariance) >= + -(state.maxVariance * 0.1)) { + /* + * Positive and zero derivative: + * The variance is still increasing. The focus could be + * increased for the next comparison. Also, the max variance + * and previous focus value are updated. + */ + bestFocus_ = focus_; + focus_ += min_step; + state.focus = focus_; + state.maxVariance = currentVariance_; + } else { + /* + * Negative derivative: + * The variance starts to decrease which means the maximum + * variance is found. Set focus step to previous good one + * then return immediately. + */ + state.focus = bestFocus_; + return true; + } + } + + previousVariance_ = currentVariance_; + LOG(Af, Debug) << " Previous step is " + << bestFocus_ + << " Current step is " + << focus_; + return false; +} + +/** + * \brief Determine the frame to be ignored + * \return Return True if the frame should be ignored, false otherwise + */ +bool AfAlgorithm::afNeedIgnoreFrame() +{ + if (ignoreCounter_ == 0) + return false; + else + ignoreCounter_--; + return true; +} + +/** + * \brief Reset frame ignore counter + */ +void AfAlgorithm::afIgnoreFrameReset() +{ + ignoreCounter_ = kIgnoreFrame; +} + +/** + * \brief Determine out-of-focus situation + * \param[in] state The autofocus state + * + * Out-of-focus means that the variance change rate for a focused and a new + * variance is greater than a threshold. + * + * \return True if the variance threshold is crossed indicating lost focus, + * false otherwise + */ +bool AfAlgorithm::afIsOutOfFocus(af::ActiveState &state) +{ + const uint32_t diff_var = std::abs(currentVariance_ - + state.maxVariance); + const double var_ratio = diff_var / state.maxVariance; + + LOG(Af, Debug) << "Variance change rate: " + << var_ratio + << " Current VCM step: " + << state.focus; + + if (var_ratio > kMaxChange) + return true; + else + return false; +} + +/** + * \brief Configure the algorithm and \a state + * \param[in] state The autofocus state + * + * The method just sets some initial variable values rather than performing real + * configuration but it is supposed to be called from IPA's `configure`. + */ +void AfAlgorithm::configure(af::ActiveState &state) +{ + /* Initial max focus step */ + maxStep_ = kMaxFocusSteps; + + /* Initial frame ignore counter */ + afIgnoreFrameReset(); + + /* Initial focus value */ + state.focus = 0; + /* Maximum variance of the AF statistics */ + state.maxVariance = 0; + /* The stable AF value flag. if it is true, the AF should be in a stable state. */ + state.stable = false; +} + +/** + * \brief Determine the max contrast image and lens position + * \param[in] state The autofocus state + * \param[in] variance The variance of autofocus statistics + * + * Ideally, a clear image also has a relatively higher contrast. So, every + * image for each focus step should be tested to find an optimal focus step. + * + * The Hill Climbing Algorithm[1] is used to find the maximum variance of the AF + * statistics. The focus step is increased then the variance of the AF + * statistics are estimated. If it finds the negative derivative we have just + * passed the peak, and we infer that the best focus is found. + * + * [1] Hill Climbing Algorithm, https://en.wikipedia.org/wiki/Hill_climbing + */ +void AfAlgorithm::process(af::ActiveState &state, double variance) +{ + currentVariance_ = variance; + + if (!state.stable) { + afCoarseScan(state); + afFineScan(state); + } else { + if (afIsOutOfFocus(state)) + afReset(state); + else + afIgnoreFrameReset(); + } +} + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/af.h b/src/ipa/libipa/af.h new file mode 100644 index 000000000..c902b434c --- /dev/null +++ b/src/ipa/libipa/af.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021-2026 Red Hat + * + * Autofocus algorithm + */ + +#pragma once + +#include + +#include + +namespace libcamera { + +namespace ipa { + +LOG_DECLARE_CATEGORY(Af) + +namespace af { + +struct ActiveState { + uint32_t focus; + double maxVariance; + bool stable; +}; + +} /* namespace af */ + +class AfAlgorithm +{ +public: + AfAlgorithm(); + + void configure(af::ActiveState &state); + void process(af::ActiveState &state, double variance); + bool coarseCompleted() { return coarseCompleted_; } + +private: + void afCoarseScan(af::ActiveState &af); + void afFineScan(af::ActiveState &af); + bool afScan(af::ActiveState &af, int min_step); + void afReset(af::ActiveState &af); + bool afNeedIgnoreFrame(); + void afIgnoreFrameReset(); + + bool afIsOutOfFocus(af::ActiveState &af); + + /* VCM step configuration. It is the current setting of the VCM step. */ + uint32_t focus_; + /* The best VCM step. It is a local optimum VCM step during scanning. */ + uint32_t bestFocus_; + /* Current AF statistic variance. */ + double currentVariance_; + /* The frames are ignore before starting measuring. */ + uint32_t ignoreCounter_; + /* It is used to determine the derivative during scanning */ + double previousVariance_; + /* The designated maximum range of focus scanning. */ + uint32_t maxStep_; + /* If the coarse scan completes, it is set to true. */ + bool coarseCompleted_; + /* If the fine scan completes, it is set to true. */ + bool fineCompleted_; +}; + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index a38d5d528..b80c3b017 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 libipa_headers = files([ + 'af.h', 'agc.h', 'agc_mean_luminance.h', 'agc_msv.h', @@ -30,6 +31,7 @@ libipa_headers = files([ ]) libipa_sources = files([ + 'af.cpp', 'agc.cpp', 'agc_mean_luminance.cpp', 'agc_msv.cpp', From patchwork Wed Oct 21 07:59:43 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 28538 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 8ABD9C324C for ; Wed, 21 Oct 2026 08:00:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 17DC768F22; Wed, 21 Oct 2026 10:00:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="OYVAiHZZ"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0DB7A68F1A for ; Wed, 21 Oct 2026 10:00:13 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792569612; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=6iWimVyt+3nE7xfuM1PdwgaO/Oiom//J1hc+c0GTt/8=; b=OYVAiHZZ45FHGXwilsA2eJOW+WBDRoqbO6c5/ah8pKryxJxpWSgCYWa2JCGBlYSvdsGtN1 CFZoj/zk66QpWySfHcB/cdMVICol/fI546wRarTwRrsHM99tBjUXdpXWPyvrGufpSXaIDg BUj9qR3JLNqHe+GXQy2g/Dmn3nIya6E= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-635-3-3nUm5mM9yGDulUvZ0PSQ-1; Wed, 21 Oct 2026 08:00:11 +0000 X-MC-Unique: 3-3nUm5mM9yGDulUvZ0PSQ-1 X-Mimecast-MFC-AGG-ID: 3-3nUm5mM9yGDulUvZ0PSQ_1792569610 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8DA191800361 for ; Wed, 21 Oct 2026 08:00:10 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.43.17.75]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 054E21800444; Wed, 21 Oct 2026 08:00:08 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal Subject: [RFC PATCH 3/7] libcamera: software_isp: Add variance to stats Date: Wed, 21 Oct 2026 09:59:43 +0200 Message-ID: <20261021080000.16624-4-mzamazal@redhat.com> In-Reply-To: <20261021080000.16624-1-mzamazal@redhat.com> References: <20261021080000.16624-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: qNkIEouh9_Dk4LoGGbPvPGU6v2wxBUL7pCcf_689SKA_1792569610 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The libipa Af algorithm requests variance value as its input. Let's add the corresponding member to SwIspStats and initialise it. This commit is preparatory and doesn't implement computation of the variance nor introduces other changes to the stats like when to update them for autofocus. Signed-off-by: Milan Zamazal --- include/libcamera/internal/software_isp/swisp_stats.h | 4 ++++ src/libcamera/software_isp/swstats_cpu.cpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h index d9d0d9be8..43ca85b26 100644 --- a/include/libcamera/internal/software_isp/swisp_stats.h +++ b/include/libcamera/internal/software_isp/swisp_stats.h @@ -43,6 +43,10 @@ struct SwIspStats { * \brief A histogram of luminance values of all the sampled pixels */ Histogram yHistogram; + /** + * \brief A variance value for the autofocus algorithm + */ + double variance; }; } /* namespace libcamera */ diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp index 362fb6a86..0d2bbd686 100644 --- a/src/libcamera/software_isp/swstats_cpu.cpp +++ b/src/libcamera/software_isp/swstats_cpu.cpp @@ -391,6 +391,7 @@ void SwStatsCpu::startFrame(uint32_t frame) for (auto &s : stats_) { s.sum_ = RGB({ 0, 0, 0 }); + s.variance = 0.0; s.yHistogram.fill(0); } } @@ -408,9 +409,11 @@ void SwStatsCpu::finishFrame(uint32_t frame, uint32_t bufferId) if (valid) { sharedStats_->sum_ = RGB({ 0, 0, 0 }); + sharedStats_->variance = 0.0; sharedStats_->yHistogram.fill(0); for (const auto &s : stats_) { sharedStats_->sum_ += s.sum_; + sharedStats_->variance += s.variance; for (unsigned int j = 0; j < SwIspStats::kYHistogramSize; j++) sharedStats_->yHistogram[j] += s.yHistogram[j]; } From patchwork Wed Oct 21 07:59:44 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 28539 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id ACC98C324C for ; Wed, 21 Oct 2026 08:00:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 74F6E68F2A; Wed, 21 Oct 2026 10:00:21 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="cEUXbhjp"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 719A568F20 for ; Wed, 21 Oct 2026 10:00:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792569614; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4RaYM9L83LcmHwvm3HaQA5CPiQ62G9Frtzj8f+BB7IQ=; b=cEUXbhjpCpoEPWSzdSOU/xLe6FujC9gmiU73Iiq1Kh7zwL5TWujfp023a5YjcH+sz7BywW BHWpc5AemsUC+PwtRsubf+nFz3AmZFAlch+MmB4DVesALIi75V0fyn1/TaF7fWhS1MnsJV A0u0hSdg5Qh7WK7d7TXqD5ZCvTeTkso= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-197-K8kU5PYMPZmht9VKTu8hIA-1; Wed, 21 Oct 2026 04:00:12 -0400 X-MC-Unique: K8kU5PYMPZmht9VKTu8hIA-1 X-Mimecast-MFC-AGG-ID: K8kU5PYMPZmht9VKTu8hIA_1792569612 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 042C71956041 for ; Wed, 21 Oct 2026 08:00:12 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.43.17.75]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2E44518005AD; Wed, 21 Oct 2026 08:00:10 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal Subject: [RFC PATCH 4/7] ipa: softisp: Remove unused imports from ipa_context.h Date: Wed, 21 Oct 2026 09:59:44 +0200 Message-ID: <20261021080000.16624-5-mzamazal@redhat.com> In-Reply-To: <20261021080000.16624-1-mzamazal@redhat.com> References: <20261021080000.16624-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: Eoz16kY_gEAH4yjEBFTF_fU_IpIW1oyFJOLHH1bphmI_1792569612 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Signed-off-by: Milan Zamazal --- src/ipa/softisp/ipa_context.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ipa/softisp/ipa_context.h b/src/ipa/softisp/ipa_context.h index 397e6c99b..05643296e 100644 --- a/src/ipa/softisp/ipa_context.h +++ b/src/ipa/softisp/ipa_context.h @@ -7,7 +7,6 @@ #pragma once -#include #include #include #include @@ -15,7 +14,6 @@ #include #include "libcamera/internal/matrix.h" -#include "libcamera/internal/vector.h" #include #include From patchwork Wed Oct 21 07:59:45 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 28541 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 851D0C324C for ; Wed, 21 Oct 2026 08:00:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E010A68F38; Wed, 21 Oct 2026 10:00:23 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="jFFBFAyW"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C1C4D68F2C for ; Wed, 21 Oct 2026 10:00:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792569615; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1+EO1z2ZdGvuOu5qiMOAacEXBuwGww69M1w/V9JAwOk=; b=jFFBFAyWBZRLjNXHhMIJgK4hnQuoAnUn+bwv7sV4/QxZF1tpBu8uiO+lJgx8IOI8AoNMsO kF9ff04b4zxjO5RxTnA0P3C2EvClbAAwXa2QFRripTGWrzCoqZwrlpTxJLcHJpadEZhreE m7ikP9PgwPpG5wCtEYC1KD/mqJIxltU= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-385-CnXcP1QAMTu6z2gTlqZZgw-1; Wed, 21 Oct 2026 08:00:14 +0000 X-MC-Unique: CnXcP1QAMTu6z2gTlqZZgw-1 X-Mimecast-MFC-AGG-ID: CnXcP1QAMTu6z2gTlqZZgw_1792569613 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 27CEF195606F for ; Wed, 21 Oct 2026 08:00:13 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.43.17.75]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 5C1C8180029C; Wed, 21 Oct 2026 08:00:12 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal Subject: [RFC PATCH 5/7] ipa: softisp: Add Af algorithm Date: Wed, 21 Oct 2026 09:59:45 +0200 Message-ID: <20261021080000.16624-6-mzamazal@redhat.com> In-Reply-To: <20261021080000.16624-1-mzamazal@redhat.com> References: <20261021080000.16624-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: OeiEyaB3TIIp-DucgrukIjaAKOUlVWKlRQxKZo7A1DM_1792569613 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Let's add Af algorithm, which is mostly a proxy to the libipa Af. The only special feature is setting afEnabled flag to prevent attempting lens movements when the algorithm is not enabled. The algorithm is not enabled yet because there are no autofocus stats computed. Signed-off-by: Milan Zamazal --- src/ipa/softisp/algorithms/af.cpp | 53 ++++++++++++++++++++++++++ src/ipa/softisp/algorithms/af.h | 39 +++++++++++++++++++ src/ipa/softisp/algorithms/meson.build | 1 + src/ipa/softisp/data/uncalibrated.yaml | 1 + src/ipa/softisp/ipa_context.h | 3 ++ 5 files changed, 97 insertions(+) create mode 100644 src/ipa/softisp/algorithms/af.cpp create mode 100644 src/ipa/softisp/algorithms/af.h diff --git a/src/ipa/softisp/algorithms/af.cpp b/src/ipa/softisp/algorithms/af.cpp new file mode 100644 index 000000000..557cca004 --- /dev/null +++ b/src/ipa/softisp/algorithms/af.cpp @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026 Red Hat Inc. + * + * Autofocus + */ + +#include "af.h" + +#include + +#include + +#include "libcamera/internal/software_isp/swisp_stats.h" + +namespace libcamera { + +namespace ipa::softisp::algorithms { + +LOG_DEFINE_CATEGORY(IPASoftIspAf) + +int Af::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData) +{ + /* Informs that lens controls can get focus data */ + context.afEnabled = true; + + return 0; +} + +int Af::configure(IPAContext &context, + [[maybe_unused]] const IPAConfigInfo &configInfo) +{ + afAlgo_.configure(context.activeState.af); + + return 0; +} + +void Af::process(IPAContext &context, + [[maybe_unused]] const uint32_t frame, + [[maybe_unused]] IPAFrameContext &frameContext, + const SwIspStats *stats, + [[maybe_unused]] ControlList &metadata) +{ + const double variance = stats->variance; + LOG(IPASoftIspAf, Debug) << "Focus stats: " << variance; + afAlgo_.process(context.activeState.af, variance); +} + +REGISTER_IPA_ALGORITHM(Af, "Af") + +} /* namespace ipa::softisp::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/softisp/algorithms/af.h b/src/ipa/softisp/algorithms/af.h new file mode 100644 index 000000000..881cb4475 --- /dev/null +++ b/src/ipa/softisp/algorithms/af.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026 Red Hat Inc. + * + * Autofocus + */ + +#pragma once + +#include "libipa/af.h" + +#include "algorithm.h" +#include "ipa_context.h" + +namespace libcamera { + +namespace ipa::softisp::algorithms { + +class Af : public Algorithm +{ +public: + Af() = default; + ~Af() = default; + + int init(IPAContext &context, const ValueNode &tuningData) override; + int configure(IPAContext &context, + const IPAConfigInfo &configInfo) override; + void process(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, + const SwIspStats *stats, + ControlList &metadata) override; + +private: + AfAlgorithm afAlgo_; +}; + +} /* namespace ipa::softisp::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/softisp/algorithms/meson.build b/src/ipa/softisp/algorithms/meson.build index d240409e5..a386401db 100644 --- a/src/ipa/softisp/algorithms/meson.build +++ b/src/ipa/softisp/algorithms/meson.build @@ -2,6 +2,7 @@ softisp_ipa_algorithms = files([ 'adjust.cpp', + 'af.cpp', 'awb.cpp', 'agc.cpp', 'blc.cpp', diff --git a/src/ipa/softisp/data/uncalibrated.yaml b/src/ipa/softisp/data/uncalibrated.yaml index fc90ca526..53d3792a2 100644 --- a/src/ipa/softisp/data/uncalibrated.yaml +++ b/src/ipa/softisp/data/uncalibrated.yaml @@ -4,6 +4,7 @@ version: 1 algorithms: - BlackLevel: + # - Af: - Awb: # Color correction matrices can be defined here. The CCM algorithm # has a significant performance impact, and should only be enabled diff --git a/src/ipa/softisp/ipa_context.h b/src/ipa/softisp/ipa_context.h index 05643296e..8546dedd8 100644 --- a/src/ipa/softisp/ipa_context.h +++ b/src/ipa/softisp/ipa_context.h @@ -20,6 +20,7 @@ #include #include #include +#include "libipa/af.h" #include "core_ipa_interface.h" @@ -38,6 +39,7 @@ struct IPAActiveState { ipa::awb::ActiveState awb; ipa::ccm::ActiveState ccm; ipa::agc::ActiveState agc; + ipa::af::ActiveState af; struct { uint8_t level; @@ -84,6 +86,7 @@ struct IPAContext { FCQueue frameContexts; ControlInfoMap::Map ctrlMap; bool ccmEnabled = false; + bool afEnabled = false; }; } /* namespace ipa::softisp */ From patchwork Wed Oct 21 07:59:46 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 28543 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 4B882C324C for ; Wed, 21 Oct 2026 08:00:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D818968F37; Wed, 21 Oct 2026 10:00:26 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="fvIxU564"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EDDA368F3A for ; Wed, 21 Oct 2026 10:00:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792569623; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RVfTEPWDE5PdmqoeK1PHoyPtHOC0r/H8XsX9Hi2GTCE=; b=fvIxU564zKn9vwbHilxWVhGO4/pDjl5UZA5MHdkdZbkrXmDC5tXYdG3g1a8a5Lpazdtw5B tMlX4pyD25O4X3QHENubuEguknxgqD0iYLV15jD5LBO4cVAoTLapayCRQBeQlFU5/IZ8zR 41mcwbUOftXLeMQpUeZPxd9fdhXmXhY= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-676-6PXvlTonMVGeeiGBqzcunQ-1; Wed, 21 Oct 2026 08:00:15 +0000 X-MC-Unique: 6PXvlTonMVGeeiGBqzcunQ-1 X-Mimecast-MFC-AGG-ID: 6PXvlTonMVGeeiGBqzcunQ_1792569614 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 87C0B18001E2 for ; Wed, 21 Oct 2026 08:00:14 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.43.17.75]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id BB129180029C; Wed, 21 Oct 2026 08:00:13 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal Subject: [RFC PATCH 6/7] libcamera: software_isp: Add autofocus controls Date: Wed, 21 Oct 2026 09:59:46 +0200 Message-ID: <20261021080000.16624-7-mzamazal@redhat.com> In-Reply-To: <20261021080000.16624-1-mzamazal@redhat.com> References: <20261021080000.16624-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: WExeLhIao0SQ2JWVgN8d1RnstulJ6cjVbPVHI2F65DU_1792569614 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" When the camera supports autofocus, the lens controls are passed around and autofocus values are read and written. This is similar to what ipu3 pipeline already does. Signed-off-by: Milan Zamazal --- .../libcamera/internal/software_isp/software_isp.h | 4 ++-- include/libcamera/ipa/softisp.mojom | 3 ++- src/ipa/softisp/ipa_context.h | 1 + src/ipa/softisp/softisp.cpp | 7 ++++++- src/libcamera/pipeline/simple/simple.cpp | 13 +++++++++++-- src/libcamera/software_isp/software_isp.cpp | 4 ++-- 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h index b8b24bb1b..4491226ce 100644 --- a/include/libcamera/internal/software_isp/software_isp.h +++ b/include/libcamera/internal/software_isp/software_isp.h @@ -87,11 +87,11 @@ public: Signal outputBufferReady; Signal ispStatsReady; Signal metadataReady; - Signal setSensorControls; + Signal setSensorControls; private: void saveIspParams(uint32_t frame); - void setSensorCtrls(const ControlList &sensorControls); + void setSensorCtrls(const ControlList &sensorControls, const ControlList &lensControls); void statsReady(uint32_t frame, uint32_t bufferId); void inputReady(FrameBuffer *input); void outputReady(FrameBuffer *output); diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom index 209af1fde..37b8c0f11 100644 --- a/include/libcamera/ipa/softisp.mojom +++ b/include/libcamera/ipa/softisp.mojom @@ -10,6 +10,7 @@ import "include/libcamera/ipa/core.mojom"; struct IPAConfigInfo { libcamera.ControlInfoMap sensorControls; + libcamera.ControlInfoMap lensControls; }; interface IPASoftIspInterface { @@ -32,7 +33,7 @@ interface IPASoftIspInterface { }; interface IPASoftIspEventInterface { - setSensorControls(libcamera.ControlList sensorControls); + setSensorControls(libcamera.ControlList sensorControls, libcamera.ControlList lensControls); paramsComputed(uint32 frame); metadataReady(uint32 frame, libcamera.ControlList metadata); }; diff --git a/src/ipa/softisp/ipa_context.h b/src/ipa/softisp/ipa_context.h index 8546dedd8..8e9ef3850 100644 --- a/src/ipa/softisp/ipa_context.h +++ b/src/ipa/softisp/ipa_context.h @@ -80,6 +80,7 @@ struct IPAContext { IPACameraSensorInfo sensorInfo; ControlInfoMap sensorControls; + ControlInfoMap lensControls; std::unique_ptr camHelper; IPASessionConfiguration configuration; IPAActiveState activeState; diff --git a/src/ipa/softisp/softisp.cpp b/src/ipa/softisp/softisp.cpp index 8acfcd1a0..daee794b2 100644 --- a/src/ipa/softisp/softisp.cpp +++ b/src/ipa/softisp/softisp.cpp @@ -187,6 +187,7 @@ int IPASoftIsp::init(const IPASettings &settings, int IPASoftIsp::configure(const IPAConfigInfo &configInfo, ControlInfoMap *ipaControls) { context_.sensorControls = configInfo.sensorControls; + context_.lensControls = configInfo.lensControls; /* Clear the IPA context before the streaming session. */ context_.configuration = {}; @@ -251,7 +252,11 @@ void IPASoftIsp::processStats(const uint32_t frame, ControlList ctrls(context_.sensorControls); agc::prepareControls(ctrls, context_.camHelper.get(), frameContext.agc.exposure, frameContext.agc.gain); - setSensorControls.emit(ctrls); + ControlList lensCtrls(context_.lensControls); + if (context_.afEnabled) + lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, + static_cast(context_.activeState.af.focus)); + setSensorControls.emit(ctrls, lensCtrls); } std::string IPASoftIsp::logPrefix() const diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index 35c29ceca..a55672cb4 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -33,6 +33,7 @@ #include #include "libcamera/internal/camera.h" +#include "libcamera/internal/camera_lens.h" #include "libcamera/internal/camera_manager.h" #include "libcamera/internal/camera_sensor.h" #include "libcamera/internal/camera_sensor_properties.h" @@ -371,7 +372,7 @@ private: void ispStatsReady(uint32_t frame, uint32_t bufferId); void metadataReady(uint32_t frame, const ControlList &metadata); - void setSensorControls(const ControlList &sensorControls); + void setSensorControls(const ControlList &sensorControls, const ControlList &lensControls); }; class SimpleCameraConfiguration : public CameraConfiguration @@ -1041,7 +1042,7 @@ void SimpleCameraData::metadataReady(uint32_t frame, const ControlList &metadata tryCompleteRequest(info->request); } -void SimpleCameraData::setSensorControls(const ControlList &sensorControls) +void SimpleCameraData::setSensorControls(const ControlList &sensorControls, const ControlList &lensControls) { delayedCtrls_->push(sensorControls); /* @@ -1055,6 +1056,12 @@ void SimpleCameraData::setSensorControls(const ControlList &sensorControls) if (!frameStartEmitter_) { ControlList ctrls(sensorControls); sensor_->setControls(&ctrls); + + CameraLens *lens = sensor_->focusLens(); + if (lens && lensControls.contains(V4L2_CID_FOCUS_ABSOLUTE)) { + ControlValue const &focusValue = lensControls.get(V4L2_CID_FOCUS_ABSOLUTE); + lens->setFocusPosition(focusValue.get()); + } } } @@ -1605,6 +1612,8 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c) } else { ipa::softisp::IPAConfigInfo configInfo; configInfo.sensorControls = data->sensor_->controls(); + if (data->sensor_->focusLens()) + configInfo.lensControls = data->sensor_->focusLens()->controls(); return data->swIsp_->configure(inputCfg, outputCfgs, configInfo, &data->controlInfo_); } } diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index a40976055..c10445d72 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -442,9 +442,9 @@ void SoftwareIsp::saveIspParams([[maybe_unused]] uint32_t frame) debayerParams_ = *sharedParams_; } -void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls) +void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls, const ControlList &lensControls) { - setSensorControls.emit(sensorControls); + setSensorControls.emit(sensorControls, lensControls); } void SoftwareIsp::statsReady(uint32_t frame, uint32_t bufferId) From patchwork Wed Oct 21 07:59:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 28542 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 13C39C3302 for ; Wed, 21 Oct 2026 08:00:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 369B868F2C; Wed, 21 Oct 2026 10:00:25 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="UnswMlDT"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 48A3668F25 for ; Wed, 21 Oct 2026 10:00:19 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792569618; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5C8yuOe8K7piUL1jlph79dtj+SgM/MSQLIhnbnkMGMk=; b=UnswMlDTYx45KCcOykVH+8h+6dB7r775mKIKLQa/U2Zn5aDEUYhv78+AnS5f0qbUK06Tq0 k0aALeEKCM6kJ7mtMSq22X8bDtE6x0W9aRHdSZaE40d315Qt7zs1XNpSiX0t04ovOf+q1H BAMnLHJA0o+XoVSl0u+ApAlMrAQa8OE= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-584-TZ7XRbv3OGKfk1Dm9LWW2A-1; Wed, 21 Oct 2026 08:00:16 +0000 X-MC-Unique: TZ7XRbv3OGKfk1Dm9LWW2A-1 X-Mimecast-MFC-AGG-ID: TZ7XRbv3OGKfk1Dm9LWW2A_1792569615 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id AB35818007F7 for ; Wed, 21 Oct 2026 08:00:15 +0000 (UTC) Received: from mzamazal-thinkpadp1gen7.tpbc.com (unknown [10.43.17.75]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id DF1C2180029C; Wed, 21 Oct 2026 08:00:14 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal Subject: [RFC PATCH 7/7] ipa: libipa: Use autofocus limits if available Date: Wed, 21 Oct 2026 09:59:47 +0200 Message-ID: <20261021080000.16624-8-mzamazal@redhat.com> In-Reply-To: <20261021080000.16624-1-mzamazal@redhat.com> References: <20261021080000.16624-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: aYeYMOcc309WHS-BLG21zIg1VCmN-AslKGJOg3GGYyM_1792569615 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Let's try to set the maximum and the initial focusing positions rather than using hard-wired values. Signed-off-by: Milan Zamazal --- src/ipa/ipu3/algorithms/af.cpp | 2 +- src/ipa/libipa/af.cpp | 26 +++++++++++++++++++++----- src/ipa/libipa/af.h | 5 ++++- src/ipa/softisp/algorithms/af.cpp | 5 ++--- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/ipa/ipu3/algorithms/af.cpp b/src/ipa/ipu3/algorithms/af.cpp index bb412e165..f79ef4b99 100644 --- a/src/ipa/ipu3/algorithms/af.cpp +++ b/src/ipa/ipu3/algorithms/af.cpp @@ -141,7 +141,7 @@ int Af::configure(IPAContext &context, const IPAConfigInfo &configInfo) grid.y_start = utils::alignDown(start.y, 2); grid.y_start |= IPU3_UAPI_GRID_Y_START_EN; - afAlgo_.configure(context.activeState.af); + afAlgo_.configure(context.activeState.af, configInfo.lensControls); return 0; } diff --git a/src/ipa/libipa/af.cpp b/src/ipa/libipa/af.cpp index 492d65d1e..fc71bcd7b 100644 --- a/src/ipa/libipa/af.cpp +++ b/src/ipa/libipa/af.cpp @@ -7,6 +7,8 @@ #include "af.h" +#include + namespace libcamera { namespace ipa { @@ -40,8 +42,7 @@ namespace af { */ /** - * Maximum focus steps of the VCM control - * \todo should be obtained from the VCM driver + * Default maximum focus steps of the VCM control */ static constexpr uint32_t kMaxFocusSteps = 1023; @@ -135,7 +136,7 @@ void AfAlgorithm::afReset(af::ActiveState &state) previousVariance_ = 0.0; coarseCompleted_ = false; fineCompleted_ = false; - maxStep_ = kMaxFocusSteps; + maxStep_ = initMaxStep_; } /** @@ -244,14 +245,15 @@ bool AfAlgorithm::afIsOutOfFocus(af::ActiveState &state) /** * \brief Configure the algorithm and \a state * \param[in] state The autofocus state + * \param[in] lensControls The lens controls * * The method just sets some initial variable values rather than performing real * configuration but it is supposed to be called from IPA's `configure`. */ -void AfAlgorithm::configure(af::ActiveState &state) +void AfAlgorithm::configure(af::ActiveState &state, const ControlInfoMap &lensControls) { /* Initial max focus step */ - maxStep_ = kMaxFocusSteps; + initMaxStep_ = kMaxFocusSteps; /* Initial frame ignore counter */ afIgnoreFrameReset(); @@ -262,6 +264,20 @@ void AfAlgorithm::configure(af::ActiveState &state) state.maxVariance = 0; /* The stable AF value flag. if it is true, the AF should be in a stable state. */ state.stable = false; + + const auto it = lensControls.find(V4L2_CID_FOCUS_ABSOLUTE); + if (it != lensControls.end()) { + const ControlInfo &focusInfo = it->second; + if (!focusInfo.max().isNone()) + initMaxStep_ = static_cast(focusInfo.max().get()); + if (!focusInfo.def().isNone()) + state.focus = static_cast(focusInfo.def().get()); + } + + maxStep_ = initMaxStep_; + + LOG(Af, Debug) << "Configured focus parameters: max=" << initMaxStep_ + << " focus=" << state.focus; } /** diff --git a/src/ipa/libipa/af.h b/src/ipa/libipa/af.h index c902b434c..0b2587ed9 100644 --- a/src/ipa/libipa/af.h +++ b/src/ipa/libipa/af.h @@ -10,6 +10,7 @@ #include #include +#include namespace libcamera { @@ -32,7 +33,7 @@ class AfAlgorithm public: AfAlgorithm(); - void configure(af::ActiveState &state); + void configure(af::ActiveState &state, const ControlInfoMap &lensControls); void process(af::ActiveState &state, double variance); bool coarseCompleted() { return coarseCompleted_; } @@ -58,6 +59,8 @@ private: double previousVariance_; /* The designated maximum range of focus scanning. */ uint32_t maxStep_; + /* Initial value of maxStep_ */ + uint32_t initMaxStep_; /* If the coarse scan completes, it is set to true. */ bool coarseCompleted_; /* If the fine scan completes, it is set to true. */ diff --git a/src/ipa/softisp/algorithms/af.cpp b/src/ipa/softisp/algorithms/af.cpp index 557cca004..4fb834f8c 100644 --- a/src/ipa/softisp/algorithms/af.cpp +++ b/src/ipa/softisp/algorithms/af.cpp @@ -27,10 +27,9 @@ int Af::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData) return 0; } -int Af::configure(IPAContext &context, - [[maybe_unused]] const IPAConfigInfo &configInfo) +int Af::configure(IPAContext &context, const IPAConfigInfo &configInfo) { - afAlgo_.configure(context.activeState.af); + afAlgo_.configure(context.activeState.af, configInfo.lensControls); return 0; }