[libcamera-devel,07/10] libcamera: ipa: raspberrypi: agc: Report fixed exposure/gain values during SwitchMode
diff mbox series

Message ID 20201116164918.2055-8-david.plowman@raspberrypi.com
State Superseded
Headers show
Series
  • Raspberry Pi AGC
Related show

Commit Message

David Plowman Nov. 16, 2020, 4:49 p.m. UTC
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 <david.plowman@raspberrypi.com>
---
 src/ipa/raspberrypi/controller/rpi/agc.cpp | 74 +++++++++++++++++-----
 src/ipa/raspberrypi/controller/rpi/agc.hpp |  2 +
 2 files changed, 60 insertions(+), 16 deletions(-)

Comments

Naushir Patuck Nov. 17, 2020, 10:56 a.m. UTC | #1
Hi David,

On Mon, 16 Nov 2020 at 16:49, David Plowman <david.plowman@raspberrypi.com>
wrote:

> 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 <david.plowman@raspberrypi.com>
>

Reviewed-by: Naushir Patuck <naush@raspberrypi.com>


> ---
>  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
> &params)
>         fast_reduce_threshold =
>                 params.get<double>("fast_reduce_threshold", 0.4);
>         base_ev = params.get<double>("base_ev", 1.0);
> +       // Start with quite a low value as ramping up is easier than
> ramping down.
> +       default_exposure_time =
> params.get<double>("default_exposure_time", 1000);
> +       default_analogue_gain =
> params.get<double>("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
> --
> 2.20.1
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
>

Patch
diff mbox series

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 &params)
 	fast_reduce_threshold =
 		params.get<double>("fast_reduce_threshold", 0.4);
 	base_ev = params.get<double>("base_ev", 1.0);
+	// Start with quite a low value as ramping up is easier than ramping down.
+	default_exposure_time = params.get<double>("default_exposure_time", 1000);
+	default_analogue_gain = params.get<double>("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