From patchwork Wed Mar 23 16:01:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Michel Hautbois X-Patchwork-Id: 15526 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 DD635C0F1B for ; Wed, 23 Mar 2022 16:01:53 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F3794604DA; Wed, 23 Mar 2022 17:01:51 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1648051312; bh=V6qYh32XQyVl7EGZ3befZTMaSFGAdenVFNcYroDtjFU=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=NwZJJCRrrv2uXZB4w/+hs+V/2V2EQkdY5psPGNibP7CVMUav/awIlnaVql1p9PPQe fGUVoXWt5kQpEZQcgdvujcqZGkmj9TdMaePBGGkDjLQQniUn7G0nEEMzLPkXhK5zXG xQ71NRtMgofYs0XVickRVbxd8NrD00sFS+xniH7xq9CMHh1v/ClFWmE3yjKCSnrhkv zFQLZy+hXUdy7w+m8k8qky2jqeby2rskThfCfo+ze2TUPiAMGcYBn4tNAp7rj3VEqU VF5WUkHikiU424i76o0P2V/Y7OfkT6e+BHg4sLjX9yRxnWQAB3Gcz6hXB/aUwmY7/O Q+Q+K5CppHOyQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A4CA1604C6 for ; Wed, 23 Mar 2022 17:01:49 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="bjnuxWBe"; dkim-atps=neutral Received: from tatooine.ideasonboard.com (unknown [IPv6:2a01:e0a:169:7140:2e05:b10:1542:d51b]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4ECE2FFD; Wed, 23 Mar 2022 17:01:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1648051309; bh=V6qYh32XQyVl7EGZ3befZTMaSFGAdenVFNcYroDtjFU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bjnuxWBeO0ab1cbgU4Cn5VeivGf4E/udCkZadgiLMEfHlCfWs2KL2M3VXIn4XhlsU k+66aTM1MoGMFLY+wN1LOztPbE5AaT/ucWxTZqS0t4WFmpXt6x17yf69aKZ2jpMImV 6wrEai7vSyc8F7LcOpfooYO03ZoA/P2HfZ63hw/g= To: libcamera-devel@lists.libcamera.org Date: Wed, 23 Mar 2022 17:01:42 +0100 Message-Id: <20220323160145.90606-2-jeanmichel.hautbois@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220323160145.90606-1-jeanmichel.hautbois@ideasonboard.com> References: <20220323160145.90606-1-jeanmichel.hautbois@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH v2 1/4] ipa: RPi: Move the IPA to the ipa::RPi namespace 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: , X-Patchwork-Original-From: Jean-Michel Hautbois via libcamera-devel From: Jean-Michel Hautbois Reply-To: Jean-Michel Hautbois Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Simplify name-spacing of the RPi components by placing it in the ipa::RPi namespace directly. It also aligns the RPi IPA with the other ones (ipa::ipu3 and ipa::rkisp1) which already have this applied. Signed-off-by: Jean-Michel Hautbois Reviewed-by: Kieran Bingham Reviewed-by: Naushir Patuck --- src/ipa/raspberrypi/raspberrypi.cpp | 42 ++++++++++++++++------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 1bf4e270..cf4e6cab 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -74,7 +74,9 @@ constexpr Duration controllerMinFrameDuration = 1.0s / 30.0; LOG_DEFINE_CATEGORY(IPARPI) -class IPARPi : public ipa::RPi::IPARPiInterface +namespace ipa::RPi { + +class IPARPi : public IPARPiInterface { public: IPARPi() @@ -86,23 +88,23 @@ public: ~IPARPi() { if (lsTable_) - munmap(lsTable_, ipa::RPi::MaxLsGridSize); + munmap(lsTable_, MaxLsGridSize); } - int init(const IPASettings &settings, ipa::RPi::SensorConfig *sensorConfig) override; - void start(const ControlList &controls, ipa::RPi::StartConfig *startConfig) override; + int init(const IPASettings &settings, SensorConfig *sensorConfig) override; + void start(const ControlList &controls, StartConfig *startConfig) override; void stop() override {} int configure(const IPACameraSensorInfo &sensorInfo, const std::map &streamConfig, const std::map &entityControls, - const ipa::RPi::IPAConfig &data, + const IPAConfig &data, ControlList *controls) override; void mapBuffers(const std::vector &buffers) override; void unmapBuffers(const std::vector &ids) override; void signalStatReady(const uint32_t bufferId) override; void signalQueueRequest(const ControlList &controls) override; - void signalIspPrepare(const ipa::RPi::ISPConfig &data) override; + void signalIspPrepare(const ISPConfig &data) override; private: void setMode(const IPACameraSensorInfo &sensorInfo); @@ -110,7 +112,7 @@ private: bool validateIspControls(); void queueRequest(const ControlList &controls); void returnEmbeddedBuffer(unsigned int bufferId); - void prepareISP(const ipa::RPi::ISPConfig &data); + void prepareISP(const ISPConfig &data); void reportMetadata(); void fillDeviceStatus(const ControlList &sensorControls); void processStats(unsigned int bufferId); @@ -178,7 +180,7 @@ private: uint32_t maxSensorGainCode_; }; -int IPARPi::init(const IPASettings &settings, ipa::RPi::SensorConfig *sensorConfig) +int IPARPi::init(const IPASettings &settings, SensorConfig *sensorConfig) { /* * Load the "helper" for this sensor. This tells us all the device specific stuff @@ -212,7 +214,7 @@ int IPARPi::init(const IPASettings &settings, ipa::RPi::SensorConfig *sensorConf return 0; } -void IPARPi::start(const ControlList &controls, ipa::RPi::StartConfig *startConfig) +void IPARPi::start(const ControlList &controls, StartConfig *startConfig) { RPiController::Metadata metadata; @@ -339,7 +341,7 @@ void IPARPi::setMode(const IPACameraSensorInfo &sensorInfo) int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, [[maybe_unused]] const std::map &streamConfig, const std::map &entityControls, - const ipa::RPi::IPAConfig &ipaConfig, + const IPAConfig &ipaConfig, ControlList *controls) { if (entityControls.size() != 2) { @@ -374,14 +376,14 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, if (ipaConfig.lsTableHandle.isValid()) { /* Remove any previous table, if there was one. */ if (lsTable_) { - munmap(lsTable_, ipa::RPi::MaxLsGridSize); + munmap(lsTable_, MaxLsGridSize); lsTable_ = nullptr; } /* Map the LS table buffer into user space. */ lsTableHandle_ = std::move(ipaConfig.lsTableHandle); if (lsTableHandle_.isValid()) { - lsTable_ = mmap(nullptr, ipa::RPi::MaxLsGridSize, PROT_READ | PROT_WRITE, + lsTable_ = mmap(nullptr, MaxLsGridSize, PROT_READ | PROT_WRITE, MAP_SHARED, lsTableHandle_.get(), 0); if (lsTable_ == MAP_FAILED) { @@ -446,7 +448,7 @@ void IPARPi::signalStatReady(uint32_t bufferId) reportMetadata(); - statsMetadataComplete.emit(bufferId & ipa::RPi::MaskID, libcameraMetadata_); + statsMetadataComplete.emit(bufferId & MaskID, libcameraMetadata_); } void IPARPi::signalQueueRequest(const ControlList &controls) @@ -454,7 +456,7 @@ void IPARPi::signalQueueRequest(const ControlList &controls) queueRequest(controls); } -void IPARPi::signalIspPrepare(const ipa::RPi::ISPConfig &data) +void IPARPi::signalIspPrepare(const ISPConfig &data) { /* * At start-up, or after a mode-switch, we may want to @@ -465,7 +467,7 @@ void IPARPi::signalIspPrepare(const ipa::RPi::ISPConfig &data) frameCount_++; /* Ready to push the input buffer into the ISP. */ - runIsp.emit(data.bayerBufferId & ipa::RPi::MaskID); + runIsp.emit(data.bayerBufferId & MaskID); } void IPARPi::reportMetadata() @@ -927,10 +929,10 @@ void IPARPi::queueRequest(const ControlList &controls) void IPARPi::returnEmbeddedBuffer(unsigned int bufferId) { - embeddedComplete.emit(bufferId & ipa::RPi::MaskID); + embeddedComplete.emit(bufferId & MaskID); } -void IPARPi::prepareISP(const ipa::RPi::ISPConfig &data) +void IPARPi::prepareISP(const ISPConfig &data) { int64_t frameTimestamp = data.controls.get(controls::SensorTimestamp); RPiController::Metadata lastMetadata; @@ -1316,7 +1318,7 @@ void IPARPi::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls) .gain_format = GAIN_FORMAT_U4P10 }; - if (!lsTable_ || w * h * 4 * sizeof(uint16_t) > ipa::RPi::MaxLsGridSize) { + if (!lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) { LOG(IPARPI, Error) << "Do not have a correctly allocate lens shading table!"; return; } @@ -1376,6 +1378,8 @@ void IPARPi::resampleTable(uint16_t dest[], double const src[12][16], } } +} /* namespace ipa::RPi */ + /* * External IPA module interface */ @@ -1389,7 +1393,7 @@ const struct IPAModuleInfo ipaModuleInfo = { IPAInterface *ipaCreate() { - return new IPARPi(); + return new ipa::RPi::IPARPi(); } } /* extern "C" */ From patchwork Wed Mar 23 16:01:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Michel Hautbois X-Patchwork-Id: 15527 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 9F40BC3264 for ; Wed, 23 Mar 2022 16:01:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 14CA4604E8; Wed, 23 Mar 2022 17:01:53 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1648051313; bh=hAYcoqx5KqhNup/OmO2+ilIeuLssAinCiaas26Uw+ww=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=San467JobppGUaGHSNU0L7svNqPDlciZwYxSG/9gH9iqgUyynK3unrnFj4MXWk5Vb 9G9pz8KGA83sSwTBpOM2mWVKcbjZy9gk4kGzIa+MXF/G5Q+CbYqo3vxEciRUsromeW 962AepVMG+SZtNLmFQrafeLNZYbQOcmjkUaFRxv2+Ct5ZNBobuqaLEtYAjcRWp8WnB AOvXVbYSAVeMPXBtqEWV83bzKW+mthKiXf0VbNl+nmjN1cGi9ikEjZVaGBZAj+YkOu oMH5+v1F2KjsVkuEFEL7dQu7W7lWPJjv2ZFpiphcgQt7MXI7VHQVEgMJUkmnhbumYU fjpsbT8X6w/DA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CF079604D5 for ; Wed, 23 Mar 2022 17:01:49 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="R8UQ1d+n"; dkim-atps=neutral Received: from tatooine.ideasonboard.com (unknown [IPv6:2a01:e0a:169:7140:2e05:b10:1542:d51b]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7D883130D; Wed, 23 Mar 2022 17:01:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1648051309; bh=hAYcoqx5KqhNup/OmO2+ilIeuLssAinCiaas26Uw+ww=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=R8UQ1d+n0gEVHBxdny4OO82oSi/91dq1F/QBk0DgQnjPw0wdZvQyU5gPmK3Y958zw HPNbBi2C3YW/DMXGne88J/QkzIHJ7ekCvnrnVvNqmTdfXiuRYS/XBNXAHcaWDjVJof IZOHFTexUsU48HolvKH5Ha8wSpOzoKbwQmjpQtVI= To: libcamera-devel@lists.libcamera.org Date: Wed, 23 Mar 2022 17:01:43 +0100 Message-Id: <20220323160145.90606-3-jeanmichel.hautbois@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220323160145.90606-1-jeanmichel.hautbois@ideasonboard.com> References: <20220323160145.90606-1-jeanmichel.hautbois@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH v2 2/4] ipa: raspberrypi: Introduce an autofocus algorithm 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: , X-Patchwork-Original-From: Jean-Michel Hautbois via libcamera-devel From: Jean-Michel Hautbois Reply-To: Jean-Michel Hautbois Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Now that the ancillary links are plumbed and we can set the lens position, implement a contrast-based algorithm for RPi. This algorithm is adapted from the one proposed for the IPU3 IPA. It is currently taking all the regions and tries to make the focus on the global scene in a first attempt. Signed-off-by: Jean-Michel Hautbois --- .../raspberrypi/controller/af_algorithm.hpp | 20 ++ src/ipa/raspberrypi/controller/af_status.h | 31 +++ src/ipa/raspberrypi/controller/focus_status.h | 3 + src/ipa/raspberrypi/controller/iob/af.cpp | 231 ++++++++++++++++++ src/ipa/raspberrypi/controller/iob/af.h | 55 +++++ src/ipa/raspberrypi/meson.build | 1 + 6 files changed, 341 insertions(+) create mode 100644 src/ipa/raspberrypi/controller/af_algorithm.hpp create mode 100644 src/ipa/raspberrypi/controller/af_status.h create mode 100644 src/ipa/raspberrypi/controller/iob/af.cpp create mode 100644 src/ipa/raspberrypi/controller/iob/af.h diff --git a/src/ipa/raspberrypi/controller/af_algorithm.hpp b/src/ipa/raspberrypi/controller/af_algorithm.hpp new file mode 100644 index 00000000..553a37e1 --- /dev/null +++ b/src/ipa/raspberrypi/controller/af_algorithm.hpp @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi (Trading) Limited + * + * af_algorithm.hpp - autofocus control algorithm interface + */ +#pragma once + +#include "algorithm.hpp" + +namespace RPiController { + +class AfAlgorithm : public Algorithm +{ +public: + AfAlgorithm(Controller *controller) : Algorithm(controller) {} + // An af algorithm must provide the following: +}; + +} // namespace RPiController diff --git a/src/ipa/raspberrypi/controller/af_status.h b/src/ipa/raspberrypi/controller/af_status.h new file mode 100644 index 00000000..835e1e2f --- /dev/null +++ b/src/ipa/raspberrypi/controller/af_status.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2020, Raspberry Pi (Trading) Limited + * Copyright (C) 2022, Ideas On Board + * + * af_status.h - autofocus measurement status + */ +#pragma once + +#include + +/* + * The focus algorithm should post the following structure into the image's + * "af.status" metadata. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct AfStatus { + unsigned int num; + uint32_t focus_measures[FOCUS_REGIONS]; + bool stable; + uint32_t focus; + double maxVariance; +}; + +#ifdef __cplusplus +} +#endif diff --git a/src/ipa/raspberrypi/controller/focus_status.h b/src/ipa/raspberrypi/controller/focus_status.h index ace2fe2c..8122df4b 100644 --- a/src/ipa/raspberrypi/controller/focus_status.h +++ b/src/ipa/raspberrypi/controller/focus_status.h @@ -19,6 +19,9 @@ extern "C" { struct FocusStatus { unsigned int num; uint32_t focus_measures[FOCUS_REGIONS]; + bool stable; + uint32_t focus; + double maxVariance; }; #ifdef __cplusplus diff --git a/src/ipa/raspberrypi/controller/iob/af.cpp b/src/ipa/raspberrypi/controller/iob/af.cpp new file mode 100644 index 00000000..dc5258ba --- /dev/null +++ b/src/ipa/raspberrypi/controller/iob/af.cpp @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Red Hat + * Copyright (C) 2022, Ideas On Board + * + * af.cpp - automatic contrast-based focus algorithm + */ +#include + +#include + +#include + +#include "af.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(IoBAf) + +#define NAME "iob.af" + +/* + * 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; + +Af::Af(Controller *controller) + : AfAlgorithm(controller), focus_(0), bestFocus_(0), ignoreCounter_(0), + currentVariance_(0.0), previousVariance_(0.0), maxStep_(0), + coarseCompleted_(false), fineCompleted_(false) +{ +} + +char const *Af::Name() const +{ + return NAME; +} + +void Af::Initialise() +{ + status_.focus = 0.0; + status_.maxVariance = 0.0; + status_.stable = false; +} + +void Af::Prepare(Metadata *image_metadata) +{ + image_metadata->Set("af.status", status_); +} + +double Af::estimateVariance() +{ + unsigned int i; + double mean; + uint64_t total = 0; + double var_sum = 0.0; + + /* Compute the mean value. */ + for (i = 0; i < FOCUS_REGIONS; i++) + total += status_.focus_measures[i]; + mean = total / FOCUS_REGIONS; + + /* Compute the sum of the squared variance. */ + for (i = 0; i < FOCUS_REGIONS; i++) + var_sum += std::pow(status_.focus_measures[i] - mean, 2); + + return var_sum / FOCUS_REGIONS; +} + +bool Af::afNeedIgnoreFrame() +{ + if (ignoreCounter_ == 0) + return false; + else + ignoreCounter_--; + return true; +} + +void Af::afCoarseScan() +{ + if (coarseCompleted_) + return; + + if (afNeedIgnoreFrame()) + return; + + if (afScan(kCoarseSearchStep)) { + coarseCompleted_ = true; + status_.maxVariance = 0; + focus_ = status_.focus - (status_.focus * kFineRange); + status_.focus = focus_; + previousVariance_ = 0; + maxStep_ = std::clamp(focus_ + static_cast((focus_ * kFineRange)), + 0U, kMaxFocusSteps); + } +} + +void Af::afFineScan() +{ + if (!coarseCompleted_) + return; + + if (afNeedIgnoreFrame()) + return; + + if (afScan(kFineSearchStep)) { + status_.stable = true; + fineCompleted_ = true; + } +} + +bool Af::afScan(uint32_t minSteps) +{ + if (focus_ > maxStep_) { + /* If the max step is reached, move lens to the position. */ + status_.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_ - status_.maxVariance) >= + -(status_.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_ += minSteps; + status_.focus = focus_; + status_.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. + */ + status_.focus = bestFocus_; + return true; + } + } + + previousVariance_ = currentVariance_; + LOG(IoBAf, Debug) << " Previous step is " + << bestFocus_ + << " Current step is " + << focus_; + return false; +} + +void Af::afReset() +{ + if (afNeedIgnoreFrame()) + return; + + status_.maxVariance = 0; + status_.focus = 0; + focus_ = 0; + status_.stable = false; + ignoreCounter_ = kIgnoreFrame; + previousVariance_ = 0.0; + coarseCompleted_ = false; + fineCompleted_ = false; + maxStep_ = kMaxFocusSteps; +} + +bool Af::afIsOutOfFocus() +{ + const uint32_t diff_var = std::abs(currentVariance_ - + status_.maxVariance); + const double var_ratio = diff_var / status_.maxVariance; + LOG(IoBAf, Debug) << "Variance change rate: " + << var_ratio + << " Current VCM step: " + << status_.focus; + if (var_ratio > kMaxChange) + return true; + else + return false; +} + +void Af::Process(StatisticsPtr &stats, Metadata *image_metadata) +{ + unsigned int i; + image_metadata->Get("af.status", status_); + + /* Use the second filter results only, and cache those. */ + for (i = 0; i < FOCUS_REGIONS; i++) + status_.focus_measures[i] = stats->focus_stats[i].contrast_val[1][1] + / stats->focus_stats[i].contrast_val_num[1][1]; + status_.num = i; + + currentVariance_ = estimateVariance(); + + if (!status_.stable) { + afCoarseScan(); + afFineScan(); + } else { + if (afIsOutOfFocus()) + afReset(); + else + ignoreCounter_ = kIgnoreFrame; + } +} + +/* Register algorithm with the system. */ +static Algorithm *Create(Controller *controller) +{ + return new Af(controller); +} +static RegisterAlgorithm reg(NAME, &Create); diff --git a/src/ipa/raspberrypi/controller/iob/af.h b/src/ipa/raspberrypi/controller/iob/af.h new file mode 100644 index 00000000..45c9711f --- /dev/null +++ b/src/ipa/raspberrypi/controller/iob/af.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Red Hat + * Copyright (C) 2022, Ideas On Board + * + * af.h - automatic contrast-based focus algorithm + */ +#pragma once + +#include "../af_algorithm.hpp" +#include "../af_status.h" +#include "../metadata.hpp" + +namespace RPiController { + +class Af : public AfAlgorithm +{ +public: + Af(Controller *controller); + char const *Name() const override; + void Initialise() override; + void Prepare(Metadata *image_metadata) override; + void Process(StatisticsPtr &stats, Metadata *image_metadata) override; +private: + double estimateVariance(); + bool afNeedIgnoreFrame(); + void afCoarseScan(); + void afFineScan(); + bool afScan(uint32_t minSteps); + void afReset(); + bool afIsOutOfFocus(); + + AfStatus status_; + + /* 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_; + + /* The frames ignored before starting measuring. */ + uint32_t ignoreCounter_; + + /* Current AF statistic variance. */ + double currentVariance_; + /* 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 RPiController */ diff --git a/src/ipa/raspberrypi/meson.build b/src/ipa/raspberrypi/meson.build index 32897e07..37068ecc 100644 --- a/src/ipa/raspberrypi/meson.build +++ b/src/ipa/raspberrypi/meson.build @@ -28,6 +28,7 @@ rpi_ipa_sources = files([ 'controller/controller.cpp', 'controller/histogram.cpp', 'controller/algorithm.cpp', + 'controller/iob/af.cpp', 'controller/rpi/alsc.cpp', 'controller/rpi/awb.cpp', 'controller/rpi/sharpen.cpp', From patchwork Wed Mar 23 16:01:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Michel Hautbois X-Patchwork-Id: 15528 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 0B39AC0F1B for ; Wed, 23 Mar 2022 16:01:56 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 24C99632E6; Wed, 23 Mar 2022 17:01:54 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1648051314; bh=EA287Hwt5glVUgdzaeQKWcbKnnGZWRTVqjWta/B9euI=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=I4TRJqnWapOn8EOaWcrHPpYxvvp0jehnOIKzQGtlIlWb77ywBmIXLEhj0Qiq3hAuE fVDJIfK1AfB+QJWG1u+hD2v1Pa/QyNsoyvnG+cXAWdVIgnvBRosTGmNQM4WgmVm0LK ybjoXvCDinRYpysxIF6GoQ5M2VrYE/P9N1sE8LP5znp9Hf5ykaXOLheJ/lTc5CcYe7 yZe1uL5T10y0RNBtR3LXxKfyxqMZK7eKiyspcBkh++7uAaBAxYkEV4evvuQM6w3FEw P/tMHAASDimfb/LgiC7AT7qwzRjO/GdkLLvGKtFW0zPlttzfcSZxqJacSDOpAu/2nA g3TAbl3WO/gEg== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E5542604DA for ; Wed, 23 Mar 2022 17:01:49 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="j6z6CtRy"; dkim-atps=neutral Received: from tatooine.ideasonboard.com (unknown [IPv6:2a01:e0a:169:7140:2e05:b10:1542:d51b]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A83359DE; Wed, 23 Mar 2022 17:01:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1648051309; bh=EA287Hwt5glVUgdzaeQKWcbKnnGZWRTVqjWta/B9euI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=j6z6CtRy4lT4kjGPxh/3YE2LlmIpozupwbmdUEsb3L6Hcc9+PDiPbo4O4gsIfs4DQ DNR11Jj/hSu5Y9VVZPvI7MtKZmJR4XDItFUN1WlP/bIPDPok1OlLVFWfoHqbnsiDC1 wDhh5wOOQiU9LBXz4apbauQVZO3nH7lk33WKUI40= To: libcamera-devel@lists.libcamera.org Date: Wed, 23 Mar 2022 17:01:44 +0100 Message-Id: <20220323160145.90606-4-jeanmichel.hautbois@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220323160145.90606-1-jeanmichel.hautbois@ideasonboard.com> References: <20220323160145.90606-1-jeanmichel.hautbois@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH v2 3/4] libcamera: raspberrypi: Control the lens from pipeline 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: , X-Patchwork-Original-From: Jean-Michel Hautbois via libcamera-devel From: Jean-Michel Hautbois Reply-To: Jean-Michel Hautbois Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The lens focus is controled by a VCM, which is linked to the sensor using the ancillary links. Pass the control to the config info structure and make it possible to update by the IPA. Signed-off-by: Jean-Michel Hautbois --- include/libcamera/ipa/raspberrypi.mojom | 1 + .../pipeline/raspberrypi/raspberrypi.cpp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom index acd3cafe..0c3922b0 100644 --- a/include/libcamera/ipa/raspberrypi.mojom +++ b/include/libcamera/ipa/raspberrypi.mojom @@ -125,4 +125,5 @@ interface IPARPiEventInterface { embeddedComplete(uint32 bufferId); setIspControls(libcamera.ControlList controls); setDelayedControls(libcamera.ControlList controls); + setLensControls(libcamera.ControlList controls); }; diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index c2230199..970f9fe7 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -33,6 +33,7 @@ #include "libcamera/internal/bayer_format.h" #include "libcamera/internal/camera.h" +#include "libcamera/internal/camera_lens.h" #include "libcamera/internal/camera_sensor.h" #include "libcamera/internal/delayed_controls.h" #include "libcamera/internal/device_enumerator.h" @@ -202,6 +203,7 @@ public: void setIspControls(const ControlList &controls); void setDelayedControls(const ControlList &controls); void setSensorControls(ControlList &controls); + void setLensControls(const ControlList &controls); /* bufferComplete signal handlers. */ void unicamBufferDequeue(FrameBuffer *buffer); @@ -1483,6 +1485,7 @@ int RPiCameraData::loadIPA(ipa::RPi::SensorConfig *sensorConfig) ipa_->embeddedComplete.connect(this, &RPiCameraData::embeddedComplete); ipa_->setIspControls.connect(this, &RPiCameraData::setIspControls); ipa_->setDelayedControls.connect(this, &RPiCameraData::setDelayedControls); + ipa_->setLensControls.connect(this, &RPiCameraData::setLensControls); /* * The configuration (tuning file) is made from the sensor name unless @@ -1519,6 +1522,10 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) entityControls.emplace(0, sensor_->controls()); entityControls.emplace(1, isp_[Isp::Input].dev()->controls()); + CameraLens *lens = sensor_->focusLens(); + if (lens) + entityControls.emplace(2, lens->controls()); + /* Always send the user transform to the IPA. */ ipaConfig.transform = static_cast(config->transform); @@ -1735,6 +1742,16 @@ void RPiCameraData::setDelayedControls(const ControlList &controls) handleState(); } +void RPiCameraData::setLensControls(const ControlList &ctrls) +{ + CameraLens *lens = sensor_->focusLens(); + if (!lens) + return; + + /* \todo Should we keep track of the latest value applied ? */ + lens->setFocusPosition(ctrls.get(V4L2_CID_FOCUS_ABSOLUTE).get()); +} + void RPiCameraData::setSensorControls(ControlList &controls) { /* From patchwork Wed Mar 23 16:01:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Michel Hautbois X-Patchwork-Id: 15529 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 984B2C3265 for ; Wed, 23 Mar 2022 16:01:56 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0DB4A610F8; Wed, 23 Mar 2022 17:01:55 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1648051315; bh=veCQMbxbFw2w3L45sYunZOtpe4x5G1w83ycREIJOKzg=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=drbje7l2F/oLLKWY+QeJZ55ZYsoyBPp041qH1YvbIkUqRhwDkXv/xGnHEmCO9t0v/ vqlKdQChuIqGVxeqUkZISpWEtelSiZjGlJJquuVOlp7afH5JB+pyUr6asHtMPtvxU8 PRQsUB9p+xy0xsGf06zMuBj3+XA4Qw7KHosj0HXV2irUB5s5Z9Z6TfoFyOavq1ERDA jqoI9A3wdYVGFAkIjMGsPI/yNbaUj0fXe2L1sQ9YCMTysctdCqHk5HjdbyNegD4FAC RabIZ37Y/IGnyJqFze/LPhf09I5CgTu3KypKG5PXsX0LF5z1kGT75/Fm7Yd/oJRrpE ye947dAxhR5XA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 26AD0604DB for ; Wed, 23 Mar 2022 17:01:50 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="OFwPHCcP"; dkim-atps=neutral Received: from tatooine.ideasonboard.com (unknown [IPv6:2a01:e0a:169:7140:2e05:b10:1542:d51b]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D43CAFFD; Wed, 23 Mar 2022 17:01:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1648051309; bh=veCQMbxbFw2w3L45sYunZOtpe4x5G1w83ycREIJOKzg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OFwPHCcPA6dXugMKATkkVp8KdSFKfAuK892CQJe452aAlfxfATNUzm/JmVyh6qSRw GLJ0BUyC78s283QdLVf+Wek+sKUqzntRF1OwIDj3usa6k/idULpO203ScJU+AsuL/V W+GIja56lnBOWkmedXdi2aoga5e70JnZfFPkU+H4= To: libcamera-devel@lists.libcamera.org Date: Wed, 23 Mar 2022 17:01:45 +0100 Message-Id: <20220323160145.90606-5-jeanmichel.hautbois@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20220323160145.90606-1-jeanmichel.hautbois@ideasonboard.com> References: <20220323160145.90606-1-jeanmichel.hautbois@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH v2 4/4] ipa: raspberrypi: Control the lens position 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: , X-Patchwork-Original-From: Jean-Michel Hautbois via libcamera-devel From: Jean-Michel Hautbois Reply-To: Jean-Michel Hautbois Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Now that the ancillary links are configured, we can use the CameraLens class and control the VCM through the IPA. For now, force a default value for the lens position, until the AF algorithm is introduced. Signed-off-by: Jean-Michel Hautbois --- v2: Change the need for lens control to be present --- src/ipa/raspberrypi/raspberrypi.cpp | 42 ++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index cf4e6cab..ff302e37 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -30,6 +30,7 @@ #include "libcamera/internal/mapped_framebuffer.h" +#include "af_status.h" #include "agc_algorithm.hpp" #include "agc_status.h" #include "alsc_status.h" @@ -110,6 +111,7 @@ private: void setMode(const IPACameraSensorInfo &sensorInfo); bool validateSensorControls(); bool validateIspControls(); + bool validateLensControls(); void queueRequest(const ControlList &controls); void returnEmbeddedBuffer(unsigned int bufferId); void prepareISP(const ISPConfig &data); @@ -134,6 +136,7 @@ private: ControlInfoMap sensorCtrls_; ControlInfoMap ispCtrls_; + ControlInfoMap lensCtrls_; ControlList libcameraMetadata_; /* Camera sensor params. */ @@ -344,7 +347,7 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, const IPAConfig &ipaConfig, ControlList *controls) { - if (entityControls.size() != 2) { + if (entityControls.size() < 2) { LOG(IPARPI, Error) << "No ISP or sensor controls found."; return -1; } @@ -352,6 +355,14 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, sensorCtrls_ = entityControls.at(0); ispCtrls_ = entityControls.at(1); + /* Lens may not be present, don't make it an hard assumption. */ + auto lensControl = entityControls.find(2); + if (lensControl != entityControls.end()) { + lensCtrls_ = lensControl->second; + if (!validateLensControls()) + LOG(IPARPI, Error) << "Lens control validation failed."; + } + if (!validateSensorControls()) { LOG(IPARPI, Error) << "Sensor control validation failed."; return -1; @@ -362,6 +373,10 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, return -1; } + if (!validateLensControls()) { + LOG(IPARPI, Error) << "Lens control validation failed."; + } + maxSensorGainCode_ = sensorCtrls_.at(V4L2_CID_ANALOGUE_GAIN).max().get(); /* Setup a metadata ControlList to output metadata. */ @@ -580,6 +595,23 @@ bool IPARPi::validateIspControls() return true; } +bool IPARPi::validateLensControls() +{ + static const uint32_t ctrls[] = { + V4L2_CID_FOCUS_ABSOLUTE, + }; + + for (auto c : ctrls) { + if (lensCtrls_.find(c) == lensCtrls_.end()) { + LOG(IPARPI, Error) << "Unable to find lens control " + << utils::hex(c); + return false; + } + } + + return true; +} + /* * Converting between enums (used in the libcamera API) and the names that * we use to identify different modes. Unfortunately, the conversion tables @@ -1068,6 +1100,14 @@ void IPARPi::processStats(unsigned int bufferId) setDelayedControls.emit(ctrls); } + + struct AfStatus afStatus; + if (rpiMetadata_.Get("af.status", afStatus) == 0) { + ControlList lensCtrls(lensCtrls_); + lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, + static_cast(afStatus.focus)); + setLensControls.emit(lensCtrls); + } } void IPARPi::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls)