From patchwork Thu May 9 17:55:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 20032 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 DBAACC3226 for ; Thu, 9 May 2024 17:56:14 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7CAE963460; Thu, 9 May 2024 19:56:14 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="i6qUtvkJ"; dkim-atps=neutral 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 A46736342D for ; Thu, 9 May 2024 19:56:10 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 13B13CC8; Thu, 9 May 2024 19:56:05 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1715277367; bh=j8VZCSkkcZLMN2zX2QeGaWrjvEQUsYeFyZKWKb1XFOY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=i6qUtvkJ8xb/yUVnIy/HnojD1mxpfGogMw0+jIu44i+kU01HdJx2EmMLLdL20iga4 02Rjru6iUy5lShsGLRPCGLXExJ0JrFhgDx4ti7YDN2bVXgcY+J5w9oMGkFQptfQOwZ suXwfOKiSI9/9NdP4P7POjd99+kxe+f76NJaJKQY= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder Subject: [PATCH v2 1/3] ipa: rkisp1: agc: Read histogram weights from tuning file Date: Fri, 10 May 2024 02:55:53 +0900 Message-Id: <20240509175555.43121-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240509175555.43121-1-paul.elder@ideasonboard.com> References: <20240509175555.43121-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 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" Add support to the rkisp1 AGC to read histogram weights from the tuning file. As controls for selecting the metering mode are not yet supported, for now hardcode the matrix metering mode, which is the same as what the AGC previously hardcoded. Signed-off-by: Paul Elder --- Changes in v2: - add default metering mode if none are read successfully from the tuning file - compute the predivider instead of using a table --- src/ipa/rkisp1/algorithms/agc.cpp | 99 ++++++++++++++++++++++++++++++- src/ipa/rkisp1/algorithms/agc.h | 6 ++ 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 5aad5c7c..4fc06277 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -17,6 +17,8 @@ #include #include +#include "libcamera/internal/yaml_parser.h" + #include "libipa/histogram.h" /** @@ -36,6 +38,81 @@ namespace ipa::rkisp1::algorithms { LOG_DEFINE_CATEGORY(RkISP1Agc) +int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData, + const char *prop) +{ + const YamlObject &yamlMeteringModes = tuningData[prop]; + if (!yamlMeteringModes.isDictionary()) { + LOG(RkISP1Agc, Error) + << "'" << prop << "' parameter not found in tuning file"; + return -EINVAL; + } + + for (const auto &[key, value] : yamlMeteringModes.asDict()) { + if (controls::AeMeteringModeNameValueMap.find(key) == + controls::AeMeteringModeNameValueMap.end()) { + LOG(RkISP1Agc, Warning) + << "Skipping unknown metering mode '" << key << "'"; + continue; + } + + std::vector weights = + value.getList().value_or(std::vector{}); + if (weights.size() != context.hw->numHistogramWeights) { + LOG(RkISP1Agc, Error) + << "Invalid '" << key << "' values: expected " + << context.hw->numHistogramWeights + << " elements, got " << weights.size(); + return -ENODATA; + } + + meteringModes_[controls::AeMeteringModeNameValueMap.at(key)] = weights; + } + + if (meteringModes_.empty()) { + int32_t meteringModeId = controls::AeMeteringModeNameValueMap.at("MeteringMatrix"); + std::vector weights = { + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + }; + + meteringModes_[meteringModeId] = weights; + } + + return 0; +} + +uint8_t Agc::predivider(Size &size) +{ + /* + * The maximum number of pixels that could potentially be in one bin is + * if all the pixels of the image are in it, multiplied by 3 for the + * three color channels. The counter for each bin is 16 bits wide, so + * `factor` thus contains the number of times we'd wrap around. This is + * obviously the number of pixels that we need to skip to make sure + * that we don't wrap around, but we compute the square root of it + * instead, as the skip that we need to program is for both the x and y + * directions. + * + * There's a bit of extra rounding math to make sure the rounding goes + * the correct direction so that the square of the step is big enough + * to encompass the `factor` number of pixels that we need to skip. + */ + double factor = size.width * size.height * 3 / 65535.0; + double root = std::sqrt(factor); + uint8_t ret; + + if (std::pow(std::floor(root), 2) + 0.01 < factor) + ret = static_cast(std::ceil(root)); + else + ret = static_cast(std::floor(root)); + + return std::clamp(ret, 3, 127); +} + Agc::Agc() { supportsRaw_ = true; @@ -59,6 +136,10 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData) if (ret) return ret; + ret = parseMeteringModes(context, tuningData, "AeMeteringMode"); + if (ret) + return ret; + context.ctrlMap.merge(controls()); return 0; @@ -160,6 +241,7 @@ void Agc::prepare(IPAContext &context, const uint32_t frame, frameContext.agc.gain = context.activeState.agc.automatic.gain; } + /* \todo Remove this when we can set the below with controls */ if (frame > 0) return; @@ -178,14 +260,25 @@ void Agc::prepare(IPAContext &context, const uint32_t frame, params->meas.hst_config.meas_window = context.configuration.agc.measureWindow; /* Produce the luminance histogram. */ params->meas.hst_config.mode = RKISP1_CIF_ISP_HISTOGRAM_MODE_Y_HISTOGRAM; + /* Set an average weighted histogram. */ Span weights{ params->meas.hst_config.hist_weight, context.hw->numHistogramWeights }; - std::fill(weights.begin(), weights.end(), 1); - /* Step size can't be less than 3. */ - params->meas.hst_config.histogram_predivider = 4; + /* \todo Get this from control */ + std::vector &modeWeights = meteringModes_.at(controls::MeteringMatrix); + std::copy(modeWeights.begin(), modeWeights.end(), weights.begin()); + + std::stringstream str; + str << "Histogram weights : "; + for (size_t i = 0; i < context.hw->numHistogramWeights; i++) + str << (int)params->meas.hst_config.hist_weight[i] << " "; + LOG(RkISP1Agc, Debug) << str.str(); + + /* \todo Add a control for this? */ + params->meas.hst_config.histogram_predivider = + predivider(context.configuration.sensor.size); /* Update the configuration for histogram. */ params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_HST; diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h index f2f5b59d..77d94423 100644 --- a/src/ipa/rkisp1/algorithms/agc.h +++ b/src/ipa/rkisp1/algorithms/agc.h @@ -44,11 +44,17 @@ public: ControlList &metadata) override; private: + int parseMeteringModes(IPAContext &context, const YamlObject &tuningData, + const char *prop); + uint8_t predivider(Size &size); + void fillMetadata(IPAContext &context, IPAFrameContext &frameContext, ControlList &metadata); double estimateLuminance(double gain) const override; Span expMeans_; + + std::map> meteringModes_; }; } /* namespace ipa::rkisp1::algorithms */