From patchwork Mon Nov 16 16:49:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 10436 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 9A5A1BE088 for ; Mon, 16 Nov 2020 16:49:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 645FF632DC; Mon, 16 Nov 2020 17:49:33 +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="kCOeB65J"; dkim-atps=neutral Received: from mail-wr1-x431.google.com (mail-wr1-x431.google.com [IPv6:2a00:1450:4864:20::431]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9A2A8632C8 for ; Mon, 16 Nov 2020 17:49:31 +0100 (CET) Received: by mail-wr1-x431.google.com with SMTP id p1so19376533wrf.12 for ; Mon, 16 Nov 2020 08:49:31 -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=pV5Y4BC+e5WO5rCf4cwaFUFTslXt0uT26nNKHvbbNQI=; b=kCOeB65Jt5AOO+LZ9MqXg2LBwzbt9vz4MJPnrD38qKvuYUsXoOM0reyPFeegtXuFcV 53KYf0eC4fguVS9+B/+/VJSFtRxp24xDso2Pj3BWRjDnbsHJ3fLCP6Ej+L8tXIbEUFGX rzZSdhLKAVdKLymt4tAx3sQbixv5sbhZlzO61JksF/hevYfosLDD25tMaIlAdiGX6TLj h9HIBYD39vPoN0irBvU1OCZDzVYl+9Q9OLim8AI5gy6ao+2zsyMaGNK5JRK0sRy1/cYD d73U7JQX3Dcp7sA6UBsIyuuMqgPaeD9vfg5Q8TK2fJBy7Fzp/Y7b4HkYjNpmLZlunvsO tWHg== 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=pV5Y4BC+e5WO5rCf4cwaFUFTslXt0uT26nNKHvbbNQI=; b=M0azA8AOhtRzIjLjjv02lBisJBzcb5J93FiU7I+YkXt9nnWC0woxd6jpqarHVsi9V2 KzPUIGIupyCGKLe0fc6pblYggYmBTDnZDeErE/IuaLcH7g7fAZq6Ej5YjU3k0PpymiHg GQQNup593r/XdQGaciKiF7eSW+RdjYYuQx8QxM/7RuiU6HrLqXAmoetIhBZ/3thq9rL5 PsqHzQUkYBtZ5jyXI7Od9Ca31XBZ2IL/uhb+6/8bCCoO5ayAa1hIZkjaR3OG0M4En7FO BXoPKWpRRmsnWc7du2KTnFTus7UCU8lBNCKIn8k1B79wwF3c7TeVtHLUizSf5qbysksx 2l9Q== X-Gm-Message-State: AOAM530nxP8Gxx18ChC6yIDIee9i3GQrf3B/1Wsy4Qhpdvx5+gmYzbFG yRW6KIt4cXTKmcov7BnNnkl3Ny9eFG9VmQ== X-Google-Smtp-Source: ABdhPJwlewiG/ptox0APnvmUg8Uu+nc+/1b6K1zXwO8TwStwe5Q9b0y2GKxzThLURE6gjCq7TVrFXQ== X-Received: by 2002:adf:db85:: with SMTP id u5mr20856672wri.155.1605545370919; Mon, 16 Nov 2020 08:49:30 -0800 (PST) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id q16sm23716973wrn.13.2020.11.16.08.49.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Nov 2020 08:49:30 -0800 (PST) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Mon, 16 Nov 2020 16:49:15 +0000 Message-Id: <20201116164918.2055-8-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201116164918.2055-1-david.plowman@raspberrypi.com> References: <20201116164918.2055-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 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 6de56def..7c7944e8 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp @@ -147,6 +147,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) @@ -222,14 +225,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) @@ -475,20 +506,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