From patchwork Mon Nov 23 07:38:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10466 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 E2799BE177 for ; Mon, 23 Nov 2020 07:38:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9B47663327; Mon, 23 Nov 2020 08:38:38 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="WajKYdJ7"; dkim-atps=neutral Received: from mail-wr1-x436.google.com (mail-wr1-x436.google.com [IPv6:2a00:1450:4864:20::436]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3E9EC6334E for ; Mon, 23 Nov 2020 08:38:37 +0100 (CET) Received: by mail-wr1-x436.google.com with SMTP id r17so17622056wrw.1 for ; Sun, 22 Nov 2020 23:38:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Xj/mIFBwOUaGUcNu8yackrW21gg3ew3bmgpzWscYFA8=; b=WajKYdJ7RUt1gRAoKxfphhvsFFBPZBjP8K8S0awtYcKOpqWVAOrjGCOiQNwEE7Lei1 yS/lNVJME67gscnn2GNLO/3r6//+d4/wHSpoaW1G87ziDZZcokVB32kgaGhsYAFhrpDn yerdT4tgrK1iCfW7mPUbvEs0A2Xn9/QzT4ytYvZI5uW7M7zE/S6HrdM+Lxc6M9zSAgvg HrHk2iCgfLOz1og9x2idT7pLt0fbsFkmUyOcVA/+1hKM9v5bmbcPUJmhhzWXfoLT4/Vl TK4GXGbCHcX3AEc9NmmZpHQ6/3HujNQwOjynNzsnJF/aepI9B7dl1Zp+ALOEYSE1J2tF uJcQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Xj/mIFBwOUaGUcNu8yackrW21gg3ew3bmgpzWscYFA8=; b=XLgRIxWdooiYWlK+x8pyAzpsPUH0N6fL2tZMReOseWpKjFFt/B5c5nOwZOG9kSQCko LgxpUP4IQKBKCW0FaHoY+uF7JOwlYRmi/rGm81WnBdRSxqbyO0Fe+kUsFqG4EMi6z68R PXwgLaZEAY/8BbN+34nI2ePhKbgubqgxSxMlh4+viT/UhrnfP9Otl0WGmC+faJL9l4BX El2Z5Bx2gzvmJv19Nv6BxDlzU9pEHLzY4DqTy6bmT8kSXDyE0Fl/H4wd4OW2owQX6QiD K7WC90IcEfRO2AITe9cAwFcJDvq2hRW7YnqGiDikI5ag4pYT6iIsJnSVNTDl4LCPx6Qy c6MA== X-Gm-Message-State: AOAM532RgjK1hR35JYvlIgtYV7Yi1CJVoaH/PethlXhiBZEzXKdR4BZc 9zFssw+TKCGgh/FOw4zUSsd0tcvqAv+oCkbD X-Google-Smtp-Source: ABdhPJyR5mSrU7FJgc22jTzRcwApcN1aA4qND0+vGJd3QRsMFnDVNC91Acl0KH7uez8R4c/D8NDhpw== X-Received: by 2002:a5d:6452:: with SMTP id d18mr29174412wrw.200.1606117116505; Sun, 22 Nov 2020 23:38:36 -0800 (PST) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id h15sm17841822wrw.15.2020.11.22.23.38.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 22 Nov 2020 23:38:35 -0800 (PST) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Mon, 23 Nov 2020 07:38:01 +0000 Message-Id: <20201123073804.3125-8-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201123073804.3125-1-david.plowman@raspberrypi.com> References: <20201123073804.3125-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 07/10] libcamera: ipa: raspberrypi: agc: Report fixed exposure/gain values during SwitchMode 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 an application has specified fixed exposure time and/or gain they must be programmed into the sensor immediately, even before the sensor has been started. For this to happen they must be written into the image metadata when the SwitchMode method is invoked. We also make the default exposure/gain, when nothing has been set, customisable in the tuning file. Signed-off-by: David Plowman Reviewed-by: Naushir Patuck --- src/ipa/raspberrypi/controller/rpi/agc.cpp | 74 +++++++++++++++++----- src/ipa/raspberrypi/controller/rpi/agc.hpp | 2 + 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp index 4cf98e66..2533727e 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp @@ -145,6 +145,9 @@ void AgcConfig::Read(boost::property_tree::ptree const ¶ms) fast_reduce_threshold = params.get("fast_reduce_threshold", 0.4); base_ev = params.get("base_ev", 1.0); + // Start with quite a low value as ramping up is easier than ramping down. + default_exposure_time = params.get("default_exposure_time", 1000); + default_analogue_gain = params.get("default_analogue_gain", 1.0); } Agc::Agc(Controller *controller) @@ -220,14 +223,42 @@ void Agc::SetConstraintMode(std::string const &constraint_mode_name) void Agc::SwitchMode([[maybe_unused]] CameraMode const &camera_mode, Metadata *metadata) { - // On a mode switch, it's possible the exposure profile could change, - // so we run through the dividing up of exposure/gain again and - // write the results into the metadata we've been given. - if (status_.total_exposure_value) { - housekeepConfig(); + housekeepConfig(); + + if (fixed_shutter_ != 0.0 && fixed_analogue_gain_ != 0.0) { + // We're going to reset the algorithm here with these fixed values. + + fetchAwbStatus(metadata); + double min_colour_gain = std::min({ awb_.gain_r, awb_.gain_g, awb_.gain_b, 1.0 }); + ASSERT(min_colour_gain != 0.0); + + // This is the equivalent of computeTargetExposure and applyDigitalGain. + target_.total_exposure_no_dg = fixed_shutter_ * fixed_analogue_gain_; + target_.total_exposure = target_.total_exposure_no_dg / min_colour_gain; + + // Equivalent of filterExposure. This resets any "history". + filtered_ = target_; + + // Equivalent of divideUpExposure. + filtered_.shutter = fixed_shutter_; + filtered_.analogue_gain = fixed_analogue_gain_; + } else if (status_.total_exposure_value) { + // On a mode switch, it's possible the exposure profile could change, + // or a fixed exposure/gain might be set so we divide up the exposure/ + // gain again, but we don't change any target values. divideUpExposure(); - writeAndFinish(metadata, false); + } else { + // We come through here on startup, when at least one of the shutter + // or gain has not been fixed. We must still write those values out so + // that they will be applied immediately. We supply some arbitrary defaults + // for any that weren't set. + + // Equivalent of divideUpExposure. + filtered_.shutter = fixed_shutter_ ? fixed_shutter_ : config_.default_exposure_time; + filtered_.analogue_gain = fixed_analogue_gain_ ? fixed_analogue_gain_ : config_.default_analogue_gain; } + + writeAndFinish(metadata, false); } void Agc::Prepare(Metadata *image_metadata) @@ -472,20 +503,31 @@ void Agc::computeGain(bcm2835_isp_stats *statistics, Metadata *image_metadata, void Agc::computeTargetExposure(double gain) { - // The statistics reflect the image without digital gain, so the final - // total exposure we're aiming for is: - target_.total_exposure = current_.total_exposure_no_dg * gain; - // The final target exposure is also limited to what the exposure - // mode allows. - double max_total_exposure = - (status_.fixed_shutter != 0.0 + if (status_.fixed_shutter != 0.0 && status_.fixed_analogue_gain != 0.0) { + // When ag and shutter are both fixed, we need to drive the + // total exposure so that we end up with a digital gain of at least + // 1/min_colour_gain. Otherwise we'd desaturate channels causing + // white to go cyan or magenta. + double min_colour_gain = std::min({ awb_.gain_r, awb_.gain_g, awb_.gain_b, 1.0 }); + ASSERT(min_colour_gain != 0.0); + target_.total_exposure = + status_.fixed_shutter * status_.fixed_analogue_gain / min_colour_gain; + } else { + // The statistics reflect the image without digital gain, so the final + // total exposure we're aiming for is: + target_.total_exposure = current_.total_exposure_no_dg * gain; + // The final target exposure is also limited to what the exposure + // mode allows. + double max_total_exposure = + (status_.fixed_shutter != 0.0 ? status_.fixed_shutter : exposure_mode_->shutter.back()) * - (status_.fixed_analogue_gain != 0.0 + (status_.fixed_analogue_gain != 0.0 ? status_.fixed_analogue_gain : exposure_mode_->gain.back()); - target_.total_exposure = std::min(target_.total_exposure, - max_total_exposure); + target_.total_exposure = std::min(target_.total_exposure, + max_total_exposure); + } LOG(RPiAgc, Debug) << "Target total_exposure " << target_.total_exposure; } diff --git a/src/ipa/raspberrypi/controller/rpi/agc.hpp b/src/ipa/raspberrypi/controller/rpi/agc.hpp index e7ac480f..859a9650 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.hpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.hpp @@ -60,6 +60,8 @@ struct AgcConfig { std::string default_exposure_mode; std::string default_constraint_mode; double base_ev; + double default_exposure_time; + double default_analogue_gain; }; class Agc : public AgcAlgorithm