From 6914fc487f6a861fcce295f37ffe8a5079921b0d Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Tue, 8 Jun 2021 12:03:35 +0100 Subject: ipa: raspberrypi: Switch the AGC/Lux code to use utils::Duration Convert the core AGC and Lux controller code to use utils::Duration for all exposure time related variables and calculations. Convert the exposure/shutter time fields in AgcStatus and DeviceStatus to use utils::Duration. Signed-off-by: Naushir Patuck Reviewed-by: David Plowman Reviewed-by: Jacopo Mondi Signed-off-by: Laurent Pinchart --- src/ipa/raspberrypi/cam_helper.cpp | 2 +- src/ipa/raspberrypi/controller/agc_status.h | 12 ++-- src/ipa/raspberrypi/controller/device_status.h | 6 +- src/ipa/raspberrypi/controller/rpi/agc.cpp | 82 ++++++++++++++------------ src/ipa/raspberrypi/controller/rpi/agc.hpp | 28 +++++---- src/ipa/raspberrypi/controller/rpi/lux.cpp | 19 +++--- src/ipa/raspberrypi/controller/rpi/lux.hpp | 4 +- src/ipa/raspberrypi/raspberrypi.cpp | 13 ++-- 8 files changed, 93 insertions(+), 73 deletions(-) (limited to 'src') diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp index 92a38007..062e94c4 100644 --- a/src/ipa/raspberrypi/cam_helper.cpp +++ b/src/ipa/raspberrypi/cam_helper.cpp @@ -184,7 +184,7 @@ void CamHelper::parseEmbeddedData(Span buffer, return; } - deviceStatus.shutter_speed = Exposure(exposureLines).get(); + deviceStatus.shutter_speed = Exposure(exposureLines); deviceStatus.analogue_gain = Gain(gainCode); LOG(IPARPI, Debug) << "Metadata updated - Exposure : " diff --git a/src/ipa/raspberrypi/controller/agc_status.h b/src/ipa/raspberrypi/controller/agc_status.h index 10381c90..5d50e177 100644 --- a/src/ipa/raspberrypi/controller/agc_status.h +++ b/src/ipa/raspberrypi/controller/agc_status.h @@ -6,6 +6,8 @@ */ #pragma once +#include "libcamera/internal/utils.h" + // The AGC algorithm should post the following structure into the image's // "agc.status" metadata. @@ -18,17 +20,17 @@ extern "C" { // ignored until then. struct AgcStatus { - double total_exposure_value; // value for all exposure and gain for this image - double target_exposure_value; // (unfiltered) target total exposure AGC is aiming for - double shutter_time; + libcamera::utils::Duration total_exposure_value; // value for all exposure and gain for this image + libcamera::utils::Duration target_exposure_value; // (unfiltered) target total exposure AGC is aiming for + libcamera::utils::Duration shutter_time; double analogue_gain; char exposure_mode[32]; char constraint_mode[32]; char metering_mode[32]; double ev; - double flicker_period; + libcamera::utils::Duration flicker_period; int floating_region_enable; - double fixed_shutter; + libcamera::utils::Duration fixed_shutter; double fixed_analogue_gain; double digital_gain; int locked; diff --git a/src/ipa/raspberrypi/controller/device_status.h b/src/ipa/raspberrypi/controller/device_status.h index aa08608b..131b4cd3 100644 --- a/src/ipa/raspberrypi/controller/device_status.h +++ b/src/ipa/raspberrypi/controller/device_status.h @@ -6,6 +6,8 @@ */ #pragma once +#include "libcamera/internal/utils.h" + // Definition of "device metadata" which stores things like shutter time and // analogue gain that downstream control algorithms will want to know. @@ -14,8 +16,8 @@ extern "C" { #endif struct DeviceStatus { - // time shutter is open, in microseconds - double shutter_speed; + // time shutter is open + libcamera::utils::Duration shutter_speed; double analogue_gain; // 1.0/distance-in-metres, or 0 if unknown double lens_position; diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp index fd3359a7..55e80ac7 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp @@ -56,19 +56,26 @@ read_metering_modes(std::map &metering_modes, return first; } -static int read_double_list(std::vector &list, - boost::property_tree::ptree const ¶ms) +static int read_list(std::vector &list, + boost::property_tree::ptree const ¶ms) { for (auto &p : params) list.push_back(p.second.get_value()); return list.size(); } +static int read_list(std::vector &list, + boost::property_tree::ptree const ¶ms) +{ + for (auto &p : params) + list.push_back(p.second.get_value() * 1us); + return list.size(); +} + void AgcExposureMode::Read(boost::property_tree::ptree const ¶ms) { - int num_shutters = - read_double_list(shutter, params.get_child("shutter")); - int num_ags = read_double_list(gain, params.get_child("gain")); + int num_shutters = read_list(shutter, params.get_child("shutter")); + int num_ags = read_list(gain, params.get_child("gain")); if (num_shutters < 2 || num_ags < 2) throw std::runtime_error( "AgcConfig: must have at least two entries in exposure profile"); @@ -148,7 +155,7 @@ void AgcConfig::Read(boost::property_tree::ptree const ¶ms) 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_exposure_time = params.get("default_exposure_time", 1000) * 1us; default_analogue_gain = params.get("default_analogue_gain", 1.0); } @@ -156,9 +163,9 @@ Agc::Agc(Controller *controller) : AgcAlgorithm(controller), metering_mode_(nullptr), exposure_mode_(nullptr), constraint_mode_(nullptr), frame_count_(0), lock_count_(0), - last_target_exposure_(0.0), - ev_(1.0), flicker_period_(0.0), - max_shutter_(0), fixed_shutter_(0), fixed_analogue_gain_(0.0) + last_target_exposure_(0s), + ev_(1.0), flicker_period_(0s), + max_shutter_(0s), fixed_shutter_(0s), fixed_analogue_gain_(0.0) { memset(&awb_, 0, sizeof(awb_)); // Setting status_.total_exposure_value_ to zero initially tells us @@ -204,7 +211,7 @@ void Agc::Pause() void Agc::Resume() { - fixed_shutter_ = 0; + fixed_shutter_ = 0s; fixed_analogue_gain_ = 0; } @@ -225,17 +232,17 @@ void Agc::SetEv(double ev) void Agc::SetFlickerPeriod(Duration flicker_period) { - flicker_period_ = flicker_period.get(); + flicker_period_ = flicker_period; } void Agc::SetMaxShutter(Duration max_shutter) { - max_shutter_ = max_shutter.get(); + max_shutter_ = max_shutter; } void Agc::SetFixedShutter(Duration fixed_shutter) { - fixed_shutter_ = fixed_shutter.get(); + fixed_shutter_ = fixed_shutter; // Set this in case someone calls Pause() straight after. status_.shutter_time = clipShutter(fixed_shutter_); } @@ -267,8 +274,8 @@ void Agc::SwitchMode([[maybe_unused]] CameraMode const &camera_mode, { housekeepConfig(); - double fixed_shutter = clipShutter(fixed_shutter_); - if (fixed_shutter != 0.0 && fixed_analogue_gain_ != 0.0) { + Duration fixed_shutter = clipShutter(fixed_shutter_); + if (fixed_shutter && fixed_analogue_gain_) { // We're going to reset the algorithm here with these fixed values. fetchAwbStatus(metadata); @@ -313,8 +320,8 @@ void Agc::Prepare(Metadata *image_metadata) // Process has run, so we have meaningful values. DeviceStatus device_status; if (image_metadata->Get("device.status", device_status) == 0) { - double actual_exposure = device_status.shutter_speed * - device_status.analogue_gain; + Duration actual_exposure = device_status.shutter_speed * + device_status.analogue_gain; if (actual_exposure) { status_.digital_gain = status_.total_exposure_value / @@ -327,7 +334,8 @@ void Agc::Prepare(Metadata *image_metadata) std::min(status_.digital_gain, 4.0)); LOG(RPiAgc, Debug) << "Actual exposure " << actual_exposure; LOG(RPiAgc, Debug) << "Use digital_gain " << status_.digital_gain; - LOG(RPiAgc, Debug) << "Effective exposure " << actual_exposure * status_.digital_gain; + LOG(RPiAgc, Debug) << "Effective exposure " + << actual_exposure * status_.digital_gain; // Decide whether AEC/AGC has converged. updateLockStatus(device_status); } @@ -371,9 +379,9 @@ void Agc::updateLockStatus(DeviceStatus const &device_status) const double RESET_MARGIN = 1.5; // Add 200us to the exposure time error to allow for line quantisation. - double exposure_error = last_device_status_.shutter_speed * ERROR_FACTOR + 200; + Duration exposure_error = last_device_status_.shutter_speed * ERROR_FACTOR + 200us; double gain_error = last_device_status_.analogue_gain * ERROR_FACTOR; - double target_error = last_target_exposure_ * ERROR_FACTOR; + Duration target_error = last_target_exposure_ * ERROR_FACTOR; // Note that we don't know the exposure/gain limits of the sensor, so // the values we keep requesting may be unachievable. For this reason @@ -463,7 +471,7 @@ void Agc::fetchCurrentExposure(Metadata *image_metadata) current_.analogue_gain = device_status->analogue_gain; AgcStatus *agc_status = image_metadata->GetLocked("agc.status"); - current_.total_exposure = agc_status ? agc_status->total_exposure_value : 0; + current_.total_exposure = agc_status ? agc_status->total_exposure_value : 0s; current_.total_exposure_no_dg = current_.shutter * current_.analogue_gain; } @@ -574,7 +582,7 @@ void Agc::computeGain(bcm2835_isp_stats *statistics, Metadata *image_metadata, void Agc::computeTargetExposure(double gain) { - if (status_.fixed_shutter != 0.0 && status_.fixed_analogue_gain != 0.0) { + if (status_.fixed_shutter && status_.fixed_analogue_gain) { // 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 @@ -589,11 +597,11 @@ void Agc::computeTargetExposure(double gain) target_.total_exposure = current_.total_exposure_no_dg * gain; // The final target exposure is also limited to what the exposure // mode allows. - double max_shutter = status_.fixed_shutter != 0.0 + Duration max_shutter = status_.fixed_shutter ? status_.fixed_shutter : exposure_mode_->shutter.back(); max_shutter = clipShutter(max_shutter); - double max_total_exposure = + Duration max_total_exposure = max_shutter * (status_.fixed_analogue_gain != 0.0 ? status_.fixed_analogue_gain @@ -638,7 +646,7 @@ void Agc::filterExposure(bool desaturate) if ((status_.fixed_shutter && status_.fixed_analogue_gain) || frame_count_ <= config_.startup_frames) speed = 1.0; - if (filtered_.total_exposure == 0.0) { + if (!filtered_.total_exposure) { filtered_.total_exposure = target_.total_exposure; filtered_.total_exposure_no_dg = target_.total_exposure_no_dg; } else { @@ -675,9 +683,10 @@ void Agc::divideUpExposure() // Sending the fixed shutter/gain cases through the same code may seem // unnecessary, but it will make more sense when extend this to cover // variable aperture. - double exposure_value = filtered_.total_exposure_no_dg; - double shutter_time, analogue_gain; - shutter_time = status_.fixed_shutter != 0.0 + Duration exposure_value = filtered_.total_exposure_no_dg; + Duration shutter_time; + double analogue_gain; + shutter_time = status_.fixed_shutter ? status_.fixed_shutter : exposure_mode_->shutter[0]; shutter_time = clipShutter(shutter_time); @@ -687,8 +696,8 @@ void Agc::divideUpExposure() if (shutter_time * analogue_gain < exposure_value) { for (unsigned int stage = 1; stage < exposure_mode_->gain.size(); stage++) { - if (status_.fixed_shutter == 0.0) { - double stage_shutter = + if (!status_.fixed_shutter) { + Duration stage_shutter = clipShutter(exposure_mode_->shutter[stage]); if (stage_shutter * analogue_gain >= exposure_value) { @@ -714,12 +723,11 @@ void Agc::divideUpExposure() << analogue_gain; // Finally adjust shutter time for flicker avoidance (require both // shutter and gain not to be fixed). - if (status_.fixed_shutter == 0.0 && - status_.fixed_analogue_gain == 0.0 && - status_.flicker_period != 0.0) { + if (!status_.fixed_shutter && !status_.fixed_analogue_gain && + status_.flicker_period) { int flicker_periods = shutter_time / status_.flicker_period; - if (flicker_periods > 0) { - double new_shutter_time = flicker_periods * status_.flicker_period; + if (flicker_periods) { + Duration new_shutter_time = flicker_periods * status_.flicker_period; analogue_gain *= shutter_time / new_shutter_time; // We should still not allow the ag to go over the // largest value in the exposure mode. Note that this @@ -739,7 +747,7 @@ void Agc::divideUpExposure() void Agc::writeAndFinish(Metadata *image_metadata, bool desaturate) { status_.total_exposure_value = filtered_.total_exposure; - status_.target_exposure_value = desaturate ? 0 : target_.total_exposure_no_dg; + status_.target_exposure_value = desaturate ? 0s : target_.total_exposure_no_dg; status_.shutter_time = filtered_.shutter; status_.analogue_gain = filtered_.analogue_gain; // Write to metadata as well, in case anyone wants to update the camera @@ -751,7 +759,7 @@ void Agc::writeAndFinish(Metadata *image_metadata, bool desaturate) << " analogue gain " << filtered_.analogue_gain; } -double Agc::clipShutter(double shutter) +Duration Agc::clipShutter(Duration shutter) { if (max_shutter_) shutter = std::min(shutter, max_shutter_); diff --git a/src/ipa/raspberrypi/controller/rpi/agc.hpp b/src/ipa/raspberrypi/controller/rpi/agc.hpp index b52aaa23..75078948 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.hpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.hpp @@ -9,6 +9,8 @@ #include #include +#include "libcamera/internal/utils.h" + #include "../agc_algorithm.hpp" #include "../agc_status.h" #include "../pwl.hpp" @@ -22,13 +24,15 @@ namespace RPiController { +using namespace std::literals::chrono_literals; + struct AgcMeteringMode { double weights[AGC_STATS_SIZE]; void Read(boost::property_tree::ptree const ¶ms); }; struct AgcExposureMode { - std::vector shutter; + std::vector shutter; std::vector gain; void Read(boost::property_tree::ptree const ¶ms); }; @@ -61,7 +65,7 @@ struct AgcConfig { std::string default_exposure_mode; std::string default_constraint_mode; double base_ev; - double default_exposure_time; + libcamera::utils::Duration default_exposure_time; double default_analogue_gain; }; @@ -101,19 +105,19 @@ private: void filterExposure(bool desaturate); void divideUpExposure(); void writeAndFinish(Metadata *image_metadata, bool desaturate); - double clipShutter(double shutter); + libcamera::utils::Duration clipShutter(libcamera::utils::Duration shutter); AgcMeteringMode *metering_mode_; AgcExposureMode *exposure_mode_; AgcConstraintMode *constraint_mode_; uint64_t frame_count_; AwbStatus awb_; struct ExposureValues { - ExposureValues() : shutter(0), analogue_gain(0), - total_exposure(0), total_exposure_no_dg(0) {} - double shutter; + ExposureValues() : shutter(0s), analogue_gain(0), + total_exposure(0s), total_exposure_no_dg(0s) {} + libcamera::utils::Duration shutter; double analogue_gain; - double total_exposure; - double total_exposure_no_dg; // without digital gain + libcamera::utils::Duration total_exposure; + libcamera::utils::Duration total_exposure_no_dg; // without digital gain }; ExposureValues current_; // values for the current frame ExposureValues target_; // calculate the values we want here @@ -121,15 +125,15 @@ private: AgcStatus status_; int lock_count_; DeviceStatus last_device_status_; - double last_target_exposure_; + libcamera::utils::Duration last_target_exposure_; // Below here the "settings" that applications can change. std::string metering_mode_name_; std::string exposure_mode_name_; std::string constraint_mode_name_; double ev_; - double flicker_period_; - double max_shutter_; - double fixed_shutter_; + libcamera::utils::Duration flicker_period_; + libcamera::utils::Duration max_shutter_; + libcamera::utils::Duration fixed_shutter_; double fixed_analogue_gain_; }; diff --git a/src/ipa/raspberrypi/controller/rpi/lux.cpp b/src/ipa/raspberrypi/controller/rpi/lux.cpp index f74381ca..258e44f4 100644 --- a/src/ipa/raspberrypi/controller/rpi/lux.cpp +++ b/src/ipa/raspberrypi/controller/rpi/lux.cpp @@ -16,6 +16,7 @@ using namespace RPiController; using namespace libcamera; +using namespace std::literals::chrono_literals; LOG_DEFINE_CATEGORY(RPiLux) @@ -38,7 +39,7 @@ char const *Lux::Name() const void Lux::Read(boost::property_tree::ptree const ¶ms) { reference_shutter_speed_ = - params.get("reference_shutter_speed"); + params.get("reference_shutter_speed") * 1.0us; reference_gain_ = params.get("reference_gain"); reference_aperture_ = params.get("reference_aperture", 1.0); reference_Y_ = params.get("reference_Y"); @@ -60,15 +61,15 @@ void Lux::Prepare(Metadata *image_metadata) void Lux::Process(StatisticsPtr &stats, Metadata *image_metadata) { // set some initial values to shut the compiler up - DeviceStatus device_status = - { .shutter_speed = 1.0, - .analogue_gain = 1.0, - .lens_position = 0.0, - .aperture = 0.0, - .flash_intensity = 0.0 }; + DeviceStatus device_status = { + .shutter_speed = 1.0ms, + .analogue_gain = 1.0, + .lens_position = 0.0, + .aperture = 0.0, + .flash_intensity = 0.0 + }; if (image_metadata->Get("device.status", device_status) == 0) { double current_gain = device_status.analogue_gain; - double current_shutter_speed = device_status.shutter_speed; double current_aperture = device_status.aperture; if (current_aperture == 0) current_aperture = current_aperture_; @@ -83,7 +84,7 @@ void Lux::Process(StatisticsPtr &stats, Metadata *image_metadata) double current_Y = sum / (double)num + .5; double gain_ratio = reference_gain_ / current_gain; double shutter_speed_ratio = - reference_shutter_speed_ / current_shutter_speed; + reference_shutter_speed_ / device_status.shutter_speed; double aperture_ratio = reference_aperture_ / current_aperture; double Y_ratio = current_Y * (65536 / num_bins) / reference_Y_; double estimated_lux = shutter_speed_ratio * gain_ratio * diff --git a/src/ipa/raspberrypi/controller/rpi/lux.hpp b/src/ipa/raspberrypi/controller/rpi/lux.hpp index f9090484..45c84439 100644 --- a/src/ipa/raspberrypi/controller/rpi/lux.hpp +++ b/src/ipa/raspberrypi/controller/rpi/lux.hpp @@ -8,6 +8,8 @@ #include +#include "libcamera/internal/utils.h" + #include "../lux_status.h" #include "../algorithm.hpp" @@ -28,7 +30,7 @@ public: private: // These values define the conditions of the reference image, against // which we compare the new image. - double reference_shutter_speed_; // in micro-seconds + libcamera::utils::Duration reference_shutter_speed_; double reference_gain_; double reference_aperture_; // units of 1/f double reference_Y_; // out of 65536 diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 4aa8ccaa..1c1e802a 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -222,11 +222,11 @@ void IPARPi::start(const ControlList &controls, ipa::RPi::StartConfig *startConf /* SwitchMode may supply updated exposure/gain values to use. */ AgcStatus agcStatus; - agcStatus.shutter_time = 0.0; + agcStatus.shutter_time = 0.0s; agcStatus.analogue_gain = 0.0; metadata.Get("agc.status", agcStatus); - if (agcStatus.shutter_time != 0.0 && agcStatus.analogue_gain != 0.0) { + if (agcStatus.shutter_time && agcStatus.analogue_gain) { ControlList ctrls(sensorCtrls_); applyAGC(&agcStatus, ctrls); startConfig->controls = std::move(ctrls); @@ -389,7 +389,7 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, /* Supply initial values for gain and exposure. */ ControlList ctrls(sensorCtrls_); AgcStatus agcStatus; - agcStatus.shutter_time = DefaultExposureTime.get(); + agcStatus.shutter_time = DefaultExposureTime; agcStatus.analogue_gain = DefaultAnalogueGain; applyAGC(&agcStatus, ctrls); @@ -461,7 +461,8 @@ void IPARPi::reportMetadata() */ DeviceStatus *deviceStatus = rpiMetadata_.GetLocked("device.status"); if (deviceStatus) { - libcameraMetadata_.set(controls::ExposureTime, deviceStatus->shutter_speed); + libcameraMetadata_.set(controls::ExposureTime, + deviceStatus->shutter_speed.get()); libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogue_gain); } @@ -1014,7 +1015,7 @@ void IPARPi::fillDeviceStatus(const ControlList &sensorControls) int32_t exposureLines = sensorControls.get(V4L2_CID_EXPOSURE).get(); int32_t gainCode = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get(); - deviceStatus.shutter_speed = helper_->Exposure(exposureLines).get(); + deviceStatus.shutter_speed = helper_->Exposure(exposureLines); deviceStatus.analogue_gain = helper_->Gain(gainCode); LOG(IPARPI, Debug) << "Metadata - Exposure : " @@ -1099,7 +1100,7 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) int32_t gainCode = helper_->GainCode(agcStatus->analogue_gain); /* GetVBlanking might clip exposure time to the fps limits. */ - Duration exposure = agcStatus->shutter_time * 1.0us; + Duration exposure = agcStatus->shutter_time; int32_t vblanking = helper_->GetVBlanking(exposure, minFrameDuration_, maxFrameDuration_); int32_t exposureLines = helper_->ExposureLines(exposure); -- cgit v1.2.1