From patchwork Mon Aug 9 09:20:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Michel Hautbois X-Patchwork-Id: 13256 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 E2043C3243 for ; Mon, 9 Aug 2021 09:20:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7791668894; Mon, 9 Aug 2021 11:20:21 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="W4lyKNZt"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BE25768826 for ; Mon, 9 Aug 2021 11:20:16 +0200 (CEST) Received: from tatooine.ideasonboard.com (unknown [IPv6:2a01:e0a:169:7140:14c5:c0eb:b1fb:db22]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 79BF6EE; Mon, 9 Aug 2021 11:20:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1628500816; bh=q3xLYhlQWLqVZGaLtxBXVXGidaoqPRlpYIJTIY6j2P4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=W4lyKNZtBa7yNUA3XyizOJJOj4q1eSn0TE98Y7VLQb1ODLMMGDtsqTiv27uDzzRW9 cz4DhFHZhHkvVtyRPfy0XJr4DYsjKOEtAyYGwPN1E824HByLFE4nREZxpoCjb5B+Ei dt4zT2LQSCKWOhGO9cpAtgC1zcxKEfnehv5q7qE4= From: Jean-Michel Hautbois To: libcamera-devel@lists.libcamera.org Date: Mon, 9 Aug 2021 11:20:07 +0200 Message-Id: <20210809092007.79067-6-jeanmichel.hautbois@ideasonboard.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210809092007.79067-1-jeanmichel.hautbois@ideasonboard.com> References: <20210809092007.79067-1-jeanmichel.hautbois@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 5/5] ipa: ipu3: Move IPU3 agc into algorithms 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" Use the Context class and algorithm interface to properly call the AGC algorithm from IPAIPU3. We need to pass shutter speed and analogue gain through Context. Add a dedicated AGC structure for that. Signed-off-by: Jean-Michel Hautbois --- .../ipu3/{ipu3_agc.cpp => algorithms/agc.cpp} | 56 ++++++++++--------- src/ipa/ipu3/{ipu3_agc.h => algorithms/agc.h} | 31 +++++----- src/ipa/ipu3/algorithms/meson.build | 1 + src/ipa/ipu3/ipa_context.h | 11 ++++ src/ipa/ipu3/ipu3.cpp | 25 +++++---- src/ipa/ipu3/meson.build | 1 - 6 files changed, 70 insertions(+), 55 deletions(-) rename src/ipa/ipu3/{ipu3_agc.cpp => algorithms/agc.cpp} (81%) rename src/ipa/ipu3/{ipu3_agc.h => algorithms/agc.h} (61%) diff --git a/src/ipa/ipu3/ipu3_agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp similarity index 81% rename from src/ipa/ipu3/ipu3_agc.cpp rename to src/ipa/ipu3/algorithms/agc.cpp index 733814dd..1146a34a 100644 --- a/src/ipa/ipu3/ipu3_agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -2,26 +2,25 @@ /* * Copyright (C) 2021, Ideas On Board * - * ipu3_agc.cpp - AGC/AEC control algorithm + * agc.cpp - AGC/AEC control algorithm */ -#include "ipu3_agc.h" +#include "agc.h" -#include #include + +#include #include #include -#include - #include "libipa/histogram.h" namespace libcamera { using namespace std::literals::chrono_literals; -namespace ipa::ipu3 { +namespace ipa::ipu3::algorithms { LOG_DEFINE_CATEGORY(IPU3Agc) @@ -36,8 +35,8 @@ static constexpr uint32_t kMaxISO = 1500; /* Maximum analogue gain value * \todo grab it from a camera helper */ -static constexpr uint32_t kMinGain = kMinISO / 100; -static constexpr uint32_t kMaxGain = kMaxISO / 100; +static constexpr double kMinGain = kMinISO / 100; +static constexpr double kMaxGain = kMaxISO / 100; /* \todo use calculated value based on sensor */ static constexpr uint32_t kMinExposure = 1; @@ -50,24 +49,24 @@ static constexpr double kEvGainTarget = 0.5; /* A cell is 8 bytes and contains averages for RGB values and saturation ratio */ static constexpr uint8_t kCellSize = 8; -IPU3Agc::IPU3Agc() +Agc::Agc() : frameCount_(0), lastFrame_(0), converged_(false), - updateControls_(false), iqMean_(0.0), - lineDuration_(0s), maxExposureTime_(0s), + updateControls_(false), iqMean_(0.0), maxExposureTime_(0s), prevExposure_(0s), prevExposureNoDg_(0s), currentExposure_(0s), currentExposureNoDg_(0s) { } -void IPU3Agc::initialise(struct ipu3_uapi_grid_config &bdsGrid, const IPACameraSensorInfo &sensorInfo) +int Agc::configure(IPAContext &context) { - aeGrid_ = bdsGrid; + /* The AGC algorithm uses the AWB statistics */ + aeGrid_ = context.awb.grid.bdsGrid; + maxExposureTime_ = kMaxExposure * context.agc.lineDuration; - lineDuration_ = sensorInfo.lineLength * 1.0s / sensorInfo.pixelRate; - maxExposureTime_ = kMaxExposure * lineDuration_; + return 0; } -void IPU3Agc::processBrightness(const ipu3_uapi_stats_3a *stats) +void Agc::processBrightness(const ipu3_uapi_stats_3a *stats) { const struct ipu3_uapi_grid_config statsAeGrid = stats->stats_4a_config.awb_config.grid; Rectangle aeRegion = { statsAeGrid.x_start, @@ -108,7 +107,7 @@ void IPU3Agc::processBrightness(const ipu3_uapi_stats_3a *stats) iqMean_ = Histogram(Span(hist)).interQuantileMean(0.98, 1.0); } -void IPU3Agc::filterExposure() +void Agc::filterExposure() { double speed = 0.2; if (prevExposure_ == 0s) { @@ -128,7 +127,7 @@ void IPU3Agc::filterExposure() prevExposure_ = speed * currentExposure_ + prevExposure_ * (1.0 - speed); prevExposureNoDg_ = speed * currentExposureNoDg_ + - prevExposureNoDg_ * (1.0 - speed); + prevExposureNoDg_ * (1.0 - speed); } /* * We can't let the no_dg exposure deviate too far below the @@ -142,7 +141,7 @@ void IPU3Agc::filterExposure() LOG(IPU3Agc, Debug) << "After filtering, total_exposure " << prevExposure_; } -void IPU3Agc::lockExposureGain(uint32_t &exposure, double &gain) +void Agc::lockExposureGain(IPAContext &context) { updateControls_ = false; @@ -160,7 +159,9 @@ void IPU3Agc::lockExposureGain(uint32_t &exposure, double &gain) double newGain = kEvGainTarget * knumHistogramBins / iqMean_; /* extracted from Rpi::Agc::computeTargetExposure */ - libcamera::utils::Duration currentShutter = exposure * lineDuration_; + libcamera::utils::Duration currentShutter = context.agc.shutterTime; + uint32_t exposure = currentShutter / context.agc.lineDuration; + double &gain = context.agc.analogueGain; currentExposureNoDg_ = currentShutter * gain; LOG(IPU3Agc, Debug) << "Actual total exposure " << currentExposureNoDg_ << " Shutter speed " << currentShutter @@ -177,26 +178,27 @@ void IPU3Agc::lockExposureGain(uint32_t &exposure, double &gain) if (currentShutter < maxExposureTime_) { exposure = std::clamp(static_cast(exposure * currentExposure_ / currentExposureNoDg_), kMinExposure, kMaxExposure); newExposure = currentExposure_ / exposure; - gain = std::clamp(static_cast(gain * currentExposure_ / newExposure), kMinGain, kMaxGain); + gain = std::clamp(static_cast(gain * currentExposure_ / newExposure), kMinGain, kMaxGain); updateControls_ = true; } else if (currentShutter >= maxExposureTime_) { - gain = std::clamp(static_cast(gain * currentExposure_ / currentExposureNoDg_), kMinGain, kMaxGain); + gain = std::clamp(static_cast(gain * currentExposure_ / currentExposureNoDg_), kMinGain, kMaxGain); newExposure = currentExposure_ / gain; exposure = std::clamp(static_cast(exposure * currentExposure_ / newExposure), kMinExposure, kMaxExposure); updateControls_ = true; } - LOG(IPU3Agc, Debug) << "Adjust exposure " << exposure * lineDuration_ << " and gain " << gain; + context.agc.shutterTime = exposure * context.agc.lineDuration; + LOG(IPU3Agc, Debug) << "Adjust exposure " << exposure * context.agc.lineDuration << " and gain " << gain; } lastFrame_ = frameCount_; } -void IPU3Agc::process(const ipu3_uapi_stats_3a *stats, uint32_t &exposure, double &gain) +void Agc::process(IPAContext &context) { - processBrightness(stats); - lockExposureGain(exposure, gain); + processBrightness(context.stats); + lockExposureGain(context); frameCount_++; } -} /* namespace ipa::ipu3 */ +} /* namespace ipa::ipu3::algorithms */ } /* namespace libcamera */ diff --git a/src/ipa/ipu3/ipu3_agc.h b/src/ipa/ipu3/algorithms/agc.h similarity index 61% rename from src/ipa/ipu3/ipu3_agc.h rename to src/ipa/ipu3/algorithms/agc.h index 3b7f781e..e87a06f3 100644 --- a/src/ipa/ipu3/ipu3_agc.h +++ b/src/ipa/ipu3/algorithms/agc.h @@ -4,44 +4,44 @@ * * ipu3_agc.h - IPU3 AGC/AEC control algorithm */ -#ifndef __LIBCAMERA_IPU3_AGC_H__ -#define __LIBCAMERA_IPU3_AGC_H__ +#ifndef __LIBCAMERA_IPU3_ALGORITHMS_AGC_H__ +#define __LIBCAMERA_IPU3_ALGORITHMS_AGC_H__ + +#include #include #include -#include - #include #include -#include "algorithms/algorithm.h" -#include "libipa/algorithm.h" +#include "algorithm.h" namespace libcamera { struct IPACameraSensorInfo; -namespace ipa::ipu3 { +namespace ipa::ipu3::algorithms { using utils::Duration; -class IPU3Agc : public ipa::Algorithm +class Agc : public Algorithm { public: - IPU3Agc(); - ~IPU3Agc() = default; + Agc(); + ~Agc() = default; + + int configure(IPAContext &context) override; + void process(IPAContext &context) override; - void initialise(struct ipu3_uapi_grid_config &bdsGrid, const IPACameraSensorInfo &sensorInfo); - void process(const ipu3_uapi_stats_3a *stats, uint32_t &exposure, double &gain); bool converged() { return converged_; } bool updateControls() { return updateControls_; } private: void processBrightness(const ipu3_uapi_stats_3a *stats); void filterExposure(); - void lockExposureGain(uint32_t &exposure, double &gain); + void lockExposureGain(IPAContext &context); struct ipu3_uapi_grid_config aeGrid_; @@ -53,7 +53,6 @@ private: double iqMean_; - Duration lineDuration_; Duration maxExposureTime_; Duration prevExposure_; @@ -62,8 +61,8 @@ private: Duration currentExposureNoDg_; }; -} /* namespace ipa::ipu3 */ +} /* namespace ipa::ipu3::algorithms */ } /* namespace libcamera */ -#endif /* __LIBCAMERA_IPU3_AGC_H__ */ +#endif /* __LIBCAMERA_IPU3_ALGORITHMS_AGC_H__ */ diff --git a/src/ipa/ipu3/algorithms/meson.build b/src/ipa/ipu3/algorithms/meson.build index df36d719..3faf913e 100644 --- a/src/ipa/ipu3/algorithms/meson.build +++ b/src/ipa/ipu3/algorithms/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 ipu3_ipa_algorithms = files([ + 'agc.cpp', 'awb.cpp', 'contrast.cpp', ]) diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h index c43b275b..1d7a1984 100644 --- a/src/ipa/ipu3/ipa_context.h +++ b/src/ipa/ipu3/ipa_context.h @@ -11,10 +11,14 @@ #include +#include + #include namespace libcamera { +using libcamera::utils::Duration; + namespace ipa::ipu3 { struct IPAContext { @@ -33,6 +37,13 @@ struct IPAContext { Size bdsOutputSize; } grid; } awb; + + /* AGC specific parameters to share */ + struct Agc { + Duration lineDuration; + Duration shutterTime; + double analogueGain; + } agc; }; } /* namespace ipa::ipu3 */ diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp index 82506461..07b1d11c 100644 --- a/src/ipa/ipu3/ipu3.cpp +++ b/src/ipa/ipu3/ipu3.cpp @@ -22,12 +22,12 @@ #include "libcamera/internal/framebuffer.h" +#include "algorithms/agc.h" #include "algorithms/algorithm.h" #include "algorithms/awb.h" #include "algorithms/contrast.h" #include "ipa_context.h" -#include "ipu3_agc.h" #include "libipa/camera_sensor_helper.h" static constexpr uint32_t kMaxCellWidthPerSet = 160; @@ -37,6 +37,8 @@ namespace libcamera { LOG_DEFINE_CATEGORY(IPAIPU3) +using namespace std::literals::chrono_literals; + namespace ipa::ipu3 { class IPAIPU3 : public IPAIPU3Interface @@ -81,8 +83,6 @@ private: uint32_t minGain_; uint32_t maxGain_; - /* Interface to the AEC/AGC algorithm */ - std::unique_ptr agcAlgo_; /* Interface to the Camera Helper */ std::unique_ptr camHelper_; @@ -103,6 +103,7 @@ int IPAIPU3::init(const IPASettings &settings) } /* Construct our Algorithms */ + algorithms_.emplace_back(new algorithms::Agc()); algorithms_.emplace_back(new algorithms::Awb()); algorithms_.emplace_back(new algorithms::Contrast()); @@ -214,11 +215,14 @@ int IPAIPU3::configure(const IPAConfigInfo &configInfo) calculateBdsGrid(configInfo.bdsOutputSize); context_.awb.grid.bdsOutputSize = configInfo.bdsOutputSize; + /* Prepare AGC parameters */ + context_.agc.lineDuration = sensorInfo_.lineLength * 1.0s / sensorInfo_.pixelRate; + context_.agc.shutterTime = exposure_ * context_.agc.lineDuration; + context_.agc.analogueGain = camHelper_->gain(gain_); + configureAlgorithms(); bdsGrid_ = context_.awb.grid.bdsGrid; - agcAlgo_ = std::make_unique(); - agcAlgo_->initialise(bdsGrid_, sensorInfo_); return 0; } @@ -341,12 +345,7 @@ void IPAIPU3::parseStatistics(unsigned int frame, /* Run the process for each algorithm on the stats */ processAlgorithms(stats); - double gain = camHelper_->gain(gain_); - agcAlgo_->process(stats, exposure_, gain); - gain_ = camHelper_->gainCode(gain); - - if (agcAlgo_->updateControls()) - setControls(frame); + setControls(frame); /* \todo Use VBlank value calculated from each frame exposure. */ int64_t frameDuration = sensorInfo_.lineLength * (defVBlank_ + sensorInfo_.outputSize.height) / @@ -365,6 +364,10 @@ void IPAIPU3::setControls(unsigned int frame) IPU3Action op; op.op = ActionSetSensorControls; + /* Convert gain and exposure */ + gain_ = camHelper_->gainCode(context_.agc.analogueGain); + exposure_ = context_.agc.shutterTime / context_.agc.lineDuration; + ControlList ctrls(ctrls_); ctrls.set(V4L2_CID_EXPOSURE, static_cast(exposure_)); ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast(gain_)); diff --git a/src/ipa/ipu3/meson.build b/src/ipa/ipu3/meson.build index d1126947..39320980 100644 --- a/src/ipa/ipu3/meson.build +++ b/src/ipa/ipu3/meson.build @@ -6,7 +6,6 @@ ipa_name = 'ipa_ipu3' ipu3_ipa_sources = files([ 'ipu3.cpp', - 'ipu3_agc.cpp', ]) ipu3_ipa_sources += ipu3_ipa_algorithms