[libcamera-devel,v4,2/2] ipa: rpi: common: Handle AEC/AGC flicker controls
diff mbox series

Message ID 20230713151218.26045-3-david.plowman@raspberrypi.com
State Accepted
Headers show
Series
  • Add flicker avoidance controls
Related show

Commit Message

David Plowman July 13, 2023, 3:12 p.m. UTC
We handle the flicker modes by passing the correct period to the
AEC/AGC algorithm which already contains the necessary code.

The "Auto" mode, as well as reporting the detected flicker period via
the "AeFlickerDetected" metadata, are unsupported for now.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
---
 src/ipa/rpi/common/ipa_base.cpp | 64 ++++++++++++++++++++++++++++++++-
 src/ipa/rpi/common/ipa_base.h   |  6 ++++
 2 files changed, 69 insertions(+), 1 deletion(-)

Comments

Jacopo Mondi July 18, 2023, 7:09 a.m. UTC | #1
Hi David

On Thu, Jul 13, 2023 at 04:12:18PM +0100, David Plowman via libcamera-devel wrote:
> We handle the flicker modes by passing the correct period to the
> AEC/AGC algorithm which already contains the necessary code.
>
> The "Auto" mode, as well as reporting the detected flicker period via

If so you shouldn't register

	{ &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },

otherwise AeFlickerMode::FlickerAuto will be exposed to applications ?


> the "AeFlickerDetected" metadata, are unsupported for now.
>
> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> ---
>  src/ipa/rpi/common/ipa_base.cpp | 64 ++++++++++++++++++++++++++++++++-
>  src/ipa/rpi/common/ipa_base.h   |  6 ++++
>  2 files changed, 69 insertions(+), 1 deletion(-)
>
> diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
> index f40f2e71..81d65ea5 100644
> --- a/src/ipa/rpi/common/ipa_base.cpp
> +++ b/src/ipa/rpi/common/ipa_base.cpp
> @@ -61,6 +61,8 @@ const ControlInfoMap::Map ipaControls{
>  	{ &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
>  	{ &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
>  	{ &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) },
> +	{ &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> +	{ &controls::AeFlickerCustom, ControlInfo(100, 1000000) },
>  	{ &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
>  	{ &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
>  	{ &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
> @@ -97,7 +99,7 @@ namespace ipa::RPi {
>
>  IpaBase::IpaBase()
>  	: controller_(), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),
> -	  firstStart_(true)
> +	  firstStart_(true), flickerState_({ 0, 0s })
>  {
>  }
>
> @@ -812,6 +814,66 @@ void IpaBase::applyControls(const ControlList &controls)
>  			break;
>  		}
>
> +		case controls::AE_FLICKER_MODE: {
> +			RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> +				controller_.getAlgorithm("agc"));
> +			if (!agc) {
> +				LOG(IPARPI, Warning)
> +					<< "Could not set AeFlickerMode - no AGC algorithm";
> +				break;
> +			}
> +
> +			int32_t mode = ctrl.second.get<int32_t>();
> +			bool modeValid = true;
> +
> +			switch (mode) {
> +			case controls::FlickerOff: {
> +				agc->setFlickerPeriod(0us);
> +
> +				break;
> +			}

Do you need braces ?

> +
> +			case controls::FlickerCustom: {
> +				agc->setFlickerPeriod(flickerState_.customPeriod);
> +
> +				break;
> +			}
> +
> +			default:
> +				LOG(IPARPI, Error) << "Flicker mode " << mode << " is not supported";
> +				modeValid = false;
> +
> +				break;
> +			}
> +
> +			if (modeValid)
> +				flickerState_.mode = mode;
> +
> +			break;
> +		}
> +
> +		case controls::AE_FLICKER_CUSTOM: {
> +			RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> +				controller_.getAlgorithm("agc"));
> +			if (!agc) {
> +				LOG(IPARPI, Warning)
> +					<< "Could not set AeFlickerCustom - no AGC algorithm";
> +				break;
> +			}
> +
> +			uint32_t customPeriod = ctrl.second.get<int32_t>();
> +			flickerState_.customPeriod = customPeriod * 1.0us;
> +
> +			/*
> +			 * We note that it makes no difference if the mode gets set to "custom"
> +			 * first, and the period updated after, or vice versa.
> +			 */

True, but what if the app never provides a value for
controls::AE_FLICKER_CUSTOM ? Should you initialize customPeriod to
some sensible default ?

> +			if (flickerState_.mode == controls::FlickerCustom)
> +				agc->setFlickerPeriod(flickerState_.customPeriod);
> +
> +			break;
> +		}
> +
>  		case controls::AWB_ENABLE: {
>  			/* Silently ignore this control for a mono sensor. */
>  			if (monoSensor_)
> diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h
> index 39d00760..22823176 100644
> --- a/src/ipa/rpi/common/ipa_base.h
> +++ b/src/ipa/rpi/common/ipa_base.h
> @@ -116,6 +116,12 @@ private:
>  	/* Frame duration (1/fps) limits. */
>  	utils::Duration minFrameDuration_;
>  	utils::Duration maxFrameDuration_;
> +
> +	/* The current state of flicker avoidance. */
> +	struct FlickerState {
> +		int32_t mode;
> +		utils::Duration customPeriod;
> +	} flickerState_;
>  };
>
>  } /* namespace ipa::RPi */
> --
> 2.30.2
>
David Plowman July 18, 2023, 8:24 a.m. UTC | #2
Hi Jacopo

Thanks for the comments.

On Tue, 18 Jul 2023 at 08:09, Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi David
>
> On Thu, Jul 13, 2023 at 04:12:18PM +0100, David Plowman via libcamera-devel wrote:
> > We handle the flicker modes by passing the correct period to the
> > AEC/AGC algorithm which already contains the necessary code.
> >
> > The "Auto" mode, as well as reporting the detected flicker period via
>
> If so you shouldn't register
>
>         { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
>
> otherwise AeFlickerMode::FlickerAuto will be exposed to applications ?

Indeed, it might be better not to expose the auto value!

>
>
> > the "AeFlickerDetected" metadata, are unsupported for now.
> >
> > Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> > ---
> >  src/ipa/rpi/common/ipa_base.cpp | 64 ++++++++++++++++++++++++++++++++-
> >  src/ipa/rpi/common/ipa_base.h   |  6 ++++
> >  2 files changed, 69 insertions(+), 1 deletion(-)
> >
> > diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
> > index f40f2e71..81d65ea5 100644
> > --- a/src/ipa/rpi/common/ipa_base.cpp
> > +++ b/src/ipa/rpi/common/ipa_base.cpp
> > @@ -61,6 +61,8 @@ const ControlInfoMap::Map ipaControls{
> >       { &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
> >       { &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
> >       { &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) },
> > +     { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> > +     { &controls::AeFlickerCustom, ControlInfo(100, 1000000) },
> >       { &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
> >       { &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
> >       { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
> > @@ -97,7 +99,7 @@ namespace ipa::RPi {
> >
> >  IpaBase::IpaBase()
> >       : controller_(), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),
> > -       firstStart_(true)
> > +       firstStart_(true), flickerState_({ 0, 0s })
> >  {
> >  }
> >
> > @@ -812,6 +814,66 @@ void IpaBase::applyControls(const ControlList &controls)
> >                       break;
> >               }
> >
> > +             case controls::AE_FLICKER_MODE: {
> > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > +                             controller_.getAlgorithm("agc"));
> > +                     if (!agc) {
> > +                             LOG(IPARPI, Warning)
> > +                                     << "Could not set AeFlickerMode - no AGC algorithm";
> > +                             break;
> > +                     }
> > +
> > +                     int32_t mode = ctrl.second.get<int32_t>();
> > +                     bool modeValid = true;
> > +
> > +                     switch (mode) {
> > +                     case controls::FlickerOff: {
> > +                             agc->setFlickerPeriod(0us);
> > +
> > +                             break;
> > +                     }
>
> Do you need braces ?

Sure, I can remove them!

>
> > +
> > +                     case controls::FlickerCustom: {
> > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > +
> > +                             break;
> > +                     }
> > +
> > +                     default:
> > +                             LOG(IPARPI, Error) << "Flicker mode " << mode << " is not supported";
> > +                             modeValid = false;
> > +
> > +                             break;
> > +                     }
> > +
> > +                     if (modeValid)
> > +                             flickerState_.mode = mode;
> > +
> > +                     break;
> > +             }
> > +
> > +             case controls::AE_FLICKER_CUSTOM: {
> > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > +                             controller_.getAlgorithm("agc"));
> > +                     if (!agc) {
> > +                             LOG(IPARPI, Warning)
> > +                                     << "Could not set AeFlickerCustom - no AGC algorithm";
> > +                             break;
> > +                     }
> > +
> > +                     uint32_t customPeriod = ctrl.second.get<int32_t>();
> > +                     flickerState_.customPeriod = customPeriod * 1.0us;
> > +
> > +                     /*
> > +                      * We note that it makes no difference if the mode gets set to "custom"
> > +                      * first, and the period updated after, or vice versa.
> > +                      */
>
> True, but what if the app never provides a value for
> controls::AE_FLICKER_CUSTOM ? Should you initialize customPeriod to
> some sensible default ?

In our implementation we use the value 0 to mean "no flicker
cancellection", so setting the mode to custom/manual without a flicker
period will cause it to do nothing (until you set one). I'm OK with
this behaviour unless anyone objects, though I don't feel strongly!
I'd rather document that setting custom/manual mode without a period
leads to undefined behaviour rather than mandating a particular value,
I think, as not every PH may wish to do the same. What do you think?

Thanks!
David

>
> > +                     if (flickerState_.mode == controls::FlickerCustom)
> > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > +
> > +                     break;
> > +             }
> > +
> >               case controls::AWB_ENABLE: {
> >                       /* Silently ignore this control for a mono sensor. */
> >                       if (monoSensor_)
> > diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h
> > index 39d00760..22823176 100644
> > --- a/src/ipa/rpi/common/ipa_base.h
> > +++ b/src/ipa/rpi/common/ipa_base.h
> > @@ -116,6 +116,12 @@ private:
> >       /* Frame duration (1/fps) limits. */
> >       utils::Duration minFrameDuration_;
> >       utils::Duration maxFrameDuration_;
> > +
> > +     /* The current state of flicker avoidance. */
> > +     struct FlickerState {
> > +             int32_t mode;
> > +             utils::Duration customPeriod;
> > +     } flickerState_;
> >  };
> >
> >  } /* namespace ipa::RPi */
> > --
> > 2.30.2
> >
Kieran Bingham July 18, 2023, 8:48 a.m. UTC | #3
Quoting David Plowman via libcamera-devel (2023-07-18 09:24:24)
> Hi Jacopo
> 
> Thanks for the comments.
> 
> On Tue, 18 Jul 2023 at 08:09, Jacopo Mondi
> <jacopo.mondi@ideasonboard.com> wrote:
> >
> > Hi David
> >
> > On Thu, Jul 13, 2023 at 04:12:18PM +0100, David Plowman via libcamera-devel wrote:
> > > We handle the flicker modes by passing the correct period to the
> > > AEC/AGC algorithm which already contains the necessary code.
> > >
> > > The "Auto" mode, as well as reporting the detected flicker period via
> >
> > If so you shouldn't register
> >
> >         { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> >
> > otherwise AeFlickerMode::FlickerAuto will be exposed to applications ?
> 
> Indeed, it might be better not to expose the auto value!
> 
> >
> >
> > > the "AeFlickerDetected" metadata, are unsupported for now.
> > >
> > > Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> > > ---
> > >  src/ipa/rpi/common/ipa_base.cpp | 64 ++++++++++++++++++++++++++++++++-
> > >  src/ipa/rpi/common/ipa_base.h   |  6 ++++
> > >  2 files changed, 69 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
> > > index f40f2e71..81d65ea5 100644
> > > --- a/src/ipa/rpi/common/ipa_base.cpp
> > > +++ b/src/ipa/rpi/common/ipa_base.cpp
> > > @@ -61,6 +61,8 @@ const ControlInfoMap::Map ipaControls{
> > >       { &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
> > >       { &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
> > >       { &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) },
> > > +     { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> > > +     { &controls::AeFlickerCustom, ControlInfo(100, 1000000) },
> > >       { &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
> > >       { &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
> > >       { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
> > > @@ -97,7 +99,7 @@ namespace ipa::RPi {
> > >
> > >  IpaBase::IpaBase()
> > >       : controller_(), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),
> > > -       firstStart_(true)
> > > +       firstStart_(true), flickerState_({ 0, 0s })
> > >  {
> > >  }
> > >
> > > @@ -812,6 +814,66 @@ void IpaBase::applyControls(const ControlList &controls)
> > >                       break;
> > >               }
> > >
> > > +             case controls::AE_FLICKER_MODE: {
> > > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > > +                             controller_.getAlgorithm("agc"));
> > > +                     if (!agc) {
> > > +                             LOG(IPARPI, Warning)
> > > +                                     << "Could not set AeFlickerMode - no AGC algorithm";
> > > +                             break;
> > > +                     }
> > > +
> > > +                     int32_t mode = ctrl.second.get<int32_t>();
> > > +                     bool modeValid = true;
> > > +
> > > +                     switch (mode) {
> > > +                     case controls::FlickerOff: {
> > > +                             agc->setFlickerPeriod(0us);
> > > +
> > > +                             break;
> > > +                     }
> >
> > Do you need braces ?
> 
> Sure, I can remove them!
> 
> >
> > > +
> > > +                     case controls::FlickerCustom: {
> > > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > > +
> > > +                             break;
> > > +                     }
> > > +
> > > +                     default:
> > > +                             LOG(IPARPI, Error) << "Flicker mode " << mode << " is not supported";
> > > +                             modeValid = false;
> > > +
> > > +                             break;
> > > +                     }
> > > +
> > > +                     if (modeValid)
> > > +                             flickerState_.mode = mode;
> > > +
> > > +                     break;
> > > +             }
> > > +
> > > +             case controls::AE_FLICKER_CUSTOM: {
> > > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > > +                             controller_.getAlgorithm("agc"));
> > > +                     if (!agc) {
> > > +                             LOG(IPARPI, Warning)
> > > +                                     << "Could not set AeFlickerCustom - no AGC algorithm";
> > > +                             break;
> > > +                     }
> > > +
> > > +                     uint32_t customPeriod = ctrl.second.get<int32_t>();
> > > +                     flickerState_.customPeriod = customPeriod * 1.0us;
> > > +
> > > +                     /*
> > > +                      * We note that it makes no difference if the mode gets set to "custom"
> > > +                      * first, and the period updated after, or vice versa.
> > > +                      */
> >
> > True, but what if the app never provides a value for
> > controls::AE_FLICKER_CUSTOM ? Should you initialize customPeriod to
> > some sensible default ?
> 
> In our implementation we use the value 0 to mean "no flicker
> cancellection", so setting the mode to custom/manual without a flicker
> period will cause it to do nothing (until you set one). I'm OK with
> this behaviour unless anyone objects, though I don't feel strongly!
> I'd rather document that setting custom/manual mode without a period
> leads to undefined behaviour rather than mandating a particular value,
> I think, as not every PH may wish to do the same. What do you think?


I had wondered if we could get this down to a single control by having
'0' as no flicker cancellation - but we'd have no way to express 'auto'
so I think we do still need two, and it lets us report in the metadata
if the value was determined automatically or explicitly I guess? (Though
the app should know that anyway as it would know if it set the manual
value?)

Throwing paint on the bike shed I feel like 'AeFlickerPeriod' would be
better suited for the control name as then it would be usable to report
the period back in the metadata when it's detected with 'Auto' mode.

What limits will be imposed for the period ? I think setting
AeFlickerMode to 'Custom' (or 'Manual'?) and not setting an
{AeFlickerCustom,AeFlickerPeriod} control should be the same as not
changing anything, but I expect that maybe the ControlInfo might have
limits that would preclude setting a period of '0' ?

It might be reasonable to say setting AeFlickerMode to Manual/Custom
will only take effect when the corresponding AeFlickerPeriod control is
applied ?


--
Kieran


> 
> Thanks!
> David
> 
> >
> > > +                     if (flickerState_.mode == controls::FlickerCustom)
> > > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > > +
> > > +                     break;
> > > +             }
> > > +
> > >               case controls::AWB_ENABLE: {
> > >                       /* Silently ignore this control for a mono sensor. */
> > >                       if (monoSensor_)
> > > diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h
> > > index 39d00760..22823176 100644
> > > --- a/src/ipa/rpi/common/ipa_base.h
> > > +++ b/src/ipa/rpi/common/ipa_base.h
> > > @@ -116,6 +116,12 @@ private:
> > >       /* Frame duration (1/fps) limits. */
> > >       utils::Duration minFrameDuration_;
> > >       utils::Duration maxFrameDuration_;
> > > +
> > > +     /* The current state of flicker avoidance. */
> > > +     struct FlickerState {
> > > +             int32_t mode;
> > > +             utils::Duration customPeriod;
> > > +     } flickerState_;
> > >  };
> > >
> > >  } /* namespace ipa::RPi */
> > > --
> > > 2.30.2
> > >
David Plowman July 18, 2023, 9:13 a.m. UTC | #4
Hi Kieran

On Tue, 18 Jul 2023 at 09:48, Kieran Bingham
<kieran.bingham@ideasonboard.com> wrote:
>
> Quoting David Plowman via libcamera-devel (2023-07-18 09:24:24)
> > Hi Jacopo
> >
> > Thanks for the comments.
> >
> > On Tue, 18 Jul 2023 at 08:09, Jacopo Mondi
> > <jacopo.mondi@ideasonboard.com> wrote:
> > >
> > > Hi David
> > >
> > > On Thu, Jul 13, 2023 at 04:12:18PM +0100, David Plowman via libcamera-devel wrote:
> > > > We handle the flicker modes by passing the correct period to the
> > > > AEC/AGC algorithm which already contains the necessary code.
> > > >
> > > > The "Auto" mode, as well as reporting the detected flicker period via
> > >
> > > If so you shouldn't register
> > >
> > >         { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> > >
> > > otherwise AeFlickerMode::FlickerAuto will be exposed to applications ?
> >
> > Indeed, it might be better not to expose the auto value!
> >
> > >
> > >
> > > > the "AeFlickerDetected" metadata, are unsupported for now.
> > > >
> > > > Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> > > > ---
> > > >  src/ipa/rpi/common/ipa_base.cpp | 64 ++++++++++++++++++++++++++++++++-
> > > >  src/ipa/rpi/common/ipa_base.h   |  6 ++++
> > > >  2 files changed, 69 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
> > > > index f40f2e71..81d65ea5 100644
> > > > --- a/src/ipa/rpi/common/ipa_base.cpp
> > > > +++ b/src/ipa/rpi/common/ipa_base.cpp
> > > > @@ -61,6 +61,8 @@ const ControlInfoMap::Map ipaControls{
> > > >       { &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
> > > >       { &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
> > > >       { &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) },
> > > > +     { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> > > > +     { &controls::AeFlickerCustom, ControlInfo(100, 1000000) },
> > > >       { &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
> > > >       { &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
> > > >       { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
> > > > @@ -97,7 +99,7 @@ namespace ipa::RPi {
> > > >
> > > >  IpaBase::IpaBase()
> > > >       : controller_(), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),
> > > > -       firstStart_(true)
> > > > +       firstStart_(true), flickerState_({ 0, 0s })
> > > >  {
> > > >  }
> > > >
> > > > @@ -812,6 +814,66 @@ void IpaBase::applyControls(const ControlList &controls)
> > > >                       break;
> > > >               }
> > > >
> > > > +             case controls::AE_FLICKER_MODE: {
> > > > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > > > +                             controller_.getAlgorithm("agc"));
> > > > +                     if (!agc) {
> > > > +                             LOG(IPARPI, Warning)
> > > > +                                     << "Could not set AeFlickerMode - no AGC algorithm";
> > > > +                             break;
> > > > +                     }
> > > > +
> > > > +                     int32_t mode = ctrl.second.get<int32_t>();
> > > > +                     bool modeValid = true;
> > > > +
> > > > +                     switch (mode) {
> > > > +                     case controls::FlickerOff: {
> > > > +                             agc->setFlickerPeriod(0us);
> > > > +
> > > > +                             break;
> > > > +                     }
> > >
> > > Do you need braces ?
> >
> > Sure, I can remove them!
> >
> > >
> > > > +
> > > > +                     case controls::FlickerCustom: {
> > > > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > > > +
> > > > +                             break;
> > > > +                     }
> > > > +
> > > > +                     default:
> > > > +                             LOG(IPARPI, Error) << "Flicker mode " << mode << " is not supported";
> > > > +                             modeValid = false;
> > > > +
> > > > +                             break;
> > > > +                     }
> > > > +
> > > > +                     if (modeValid)
> > > > +                             flickerState_.mode = mode;
> > > > +
> > > > +                     break;
> > > > +             }
> > > > +
> > > > +             case controls::AE_FLICKER_CUSTOM: {
> > > > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > > > +                             controller_.getAlgorithm("agc"));
> > > > +                     if (!agc) {
> > > > +                             LOG(IPARPI, Warning)
> > > > +                                     << "Could not set AeFlickerCustom - no AGC algorithm";
> > > > +                             break;
> > > > +                     }
> > > > +
> > > > +                     uint32_t customPeriod = ctrl.second.get<int32_t>();
> > > > +                     flickerState_.customPeriod = customPeriod * 1.0us;
> > > > +
> > > > +                     /*
> > > > +                      * We note that it makes no difference if the mode gets set to "custom"
> > > > +                      * first, and the period updated after, or vice versa.
> > > > +                      */
> > >
> > > True, but what if the app never provides a value for
> > > controls::AE_FLICKER_CUSTOM ? Should you initialize customPeriod to
> > > some sensible default ?
> >
> > In our implementation we use the value 0 to mean "no flicker
> > cancellection", so setting the mode to custom/manual without a flicker
> > period will cause it to do nothing (until you set one). I'm OK with
> > this behaviour unless anyone objects, though I don't feel strongly!
> > I'd rather document that setting custom/manual mode without a period
> > leads to undefined behaviour rather than mandating a particular value,
> > I think, as not every PH may wish to do the same. What do you think?
>
>
> I had wondered if we could get this down to a single control by having
> '0' as no flicker cancellation - but we'd have no way to express 'auto'
> so I think we do still need two, and it lets us report in the metadata
> if the value was determined automatically or explicitly I guess? (Though
> the app should know that anyway as it would know if it set the manual
> value?)

Yes, I think we do still want a separate control.

>
> Throwing paint on the bike shed I feel like 'AeFlickerPeriod' would be
> better suited for the control name as then it would be usable to report
> the period back in the metadata when it's detected with 'Auto' mode.

I agree that 'AeFlickerPeriod' is a better name, particularly if the
'custom' mode becomes the 'manual' mode.

>
> What limits will be imposed for the period ? I think setting
> AeFlickerMode to 'Custom' (or 'Manual'?) and not setting an
> {AeFlickerCustom,AeFlickerPeriod} control should be the same as not
> changing anything, but I expect that maybe the ControlInfo might have
> limits that would preclude setting a period of '0' ?

I guess implementations get to choose their own limits though, for the
most part, I see no reason not to allow them to be very wide.
Extremely short or extremely long periods are going to be increasingly
unlikely IRL, of course.

>
> It might be reasonable to say setting AeFlickerMode to Manual/Custom
> will only take effect when the corresponding AeFlickerPeriod control is
> applied ?

Yes, we could say that setting the mode to manual has no effect until
a period is also set. Though of course I assume the period is
"sticky", so once you've set it, you can go to and fro to manual mode
without re-sending the said period.

I guess we have to ask what happens if you go from auto to manual
mode, having never set a period. What happens then? TBH, I think I'd
be quite happy to say that selecting manual mode, without ever setting
a period, is the same as being "off". Thoughts?

David

>
>
> --
> Kieran
>
>
> >
> > Thanks!
> > David
> >
> > >
> > > > +                     if (flickerState_.mode == controls::FlickerCustom)
> > > > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > > > +
> > > > +                     break;
> > > > +             }
> > > > +
> > > >               case controls::AWB_ENABLE: {
> > > >                       /* Silently ignore this control for a mono sensor. */
> > > >                       if (monoSensor_)
> > > > diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h
> > > > index 39d00760..22823176 100644
> > > > --- a/src/ipa/rpi/common/ipa_base.h
> > > > +++ b/src/ipa/rpi/common/ipa_base.h
> > > > @@ -116,6 +116,12 @@ private:
> > > >       /* Frame duration (1/fps) limits. */
> > > >       utils::Duration minFrameDuration_;
> > > >       utils::Duration maxFrameDuration_;
> > > > +
> > > > +     /* The current state of flicker avoidance. */
> > > > +     struct FlickerState {
> > > > +             int32_t mode;
> > > > +             utils::Duration customPeriod;
> > > > +     } flickerState_;
> > > >  };
> > > >
> > > >  } /* namespace ipa::RPi */
> > > > --
> > > > 2.30.2
> > > >
Kieran Bingham July 18, 2023, 11:16 a.m. UTC | #5
Quoting David Plowman (2023-07-18 10:13:42)
> Hi Kieran
> 
> On Tue, 18 Jul 2023 at 09:48, Kieran Bingham
> <kieran.bingham@ideasonboard.com> wrote:
> >
> > Quoting David Plowman via libcamera-devel (2023-07-18 09:24:24)
> > > Hi Jacopo
> > >
> > > Thanks for the comments.
> > >
> > > On Tue, 18 Jul 2023 at 08:09, Jacopo Mondi
> > > <jacopo.mondi@ideasonboard.com> wrote:
> > > >
> > > > Hi David
> > > >
> > > > On Thu, Jul 13, 2023 at 04:12:18PM +0100, David Plowman via libcamera-devel wrote:
> > > > > We handle the flicker modes by passing the correct period to the
> > > > > AEC/AGC algorithm which already contains the necessary code.
> > > > >
> > > > > The "Auto" mode, as well as reporting the detected flicker period via
> > > >
> > > > If so you shouldn't register
> > > >
> > > >         { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> > > >
> > > > otherwise AeFlickerMode::FlickerAuto will be exposed to applications ?
> > >
> > > Indeed, it might be better not to expose the auto value!
> > >
> > > >
> > > >
> > > > > the "AeFlickerDetected" metadata, are unsupported for now.
> > > > >
> > > > > Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> > > > > ---
> > > > >  src/ipa/rpi/common/ipa_base.cpp | 64 ++++++++++++++++++++++++++++++++-
> > > > >  src/ipa/rpi/common/ipa_base.h   |  6 ++++
> > > > >  2 files changed, 69 insertions(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
> > > > > index f40f2e71..81d65ea5 100644
> > > > > --- a/src/ipa/rpi/common/ipa_base.cpp
> > > > > +++ b/src/ipa/rpi/common/ipa_base.cpp
> > > > > @@ -61,6 +61,8 @@ const ControlInfoMap::Map ipaControls{
> > > > >       { &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
> > > > >       { &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
> > > > >       { &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) },
> > > > > +     { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> > > > > +     { &controls::AeFlickerCustom, ControlInfo(100, 1000000) },
> > > > >       { &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
> > > > >       { &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
> > > > >       { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
> > > > > @@ -97,7 +99,7 @@ namespace ipa::RPi {
> > > > >
> > > > >  IpaBase::IpaBase()
> > > > >       : controller_(), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),
> > > > > -       firstStart_(true)
> > > > > +       firstStart_(true), flickerState_({ 0, 0s })
> > > > >  {
> > > > >  }
> > > > >
> > > > > @@ -812,6 +814,66 @@ void IpaBase::applyControls(const ControlList &controls)
> > > > >                       break;
> > > > >               }
> > > > >
> > > > > +             case controls::AE_FLICKER_MODE: {
> > > > > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > > > > +                             controller_.getAlgorithm("agc"));
> > > > > +                     if (!agc) {
> > > > > +                             LOG(IPARPI, Warning)
> > > > > +                                     << "Could not set AeFlickerMode - no AGC algorithm";
> > > > > +                             break;
> > > > > +                     }
> > > > > +
> > > > > +                     int32_t mode = ctrl.second.get<int32_t>();
> > > > > +                     bool modeValid = true;
> > > > > +
> > > > > +                     switch (mode) {
> > > > > +                     case controls::FlickerOff: {
> > > > > +                             agc->setFlickerPeriod(0us);
> > > > > +
> > > > > +                             break;
> > > > > +                     }
> > > >
> > > > Do you need braces ?
> > >
> > > Sure, I can remove them!
> > >
> > > >
> > > > > +
> > > > > +                     case controls::FlickerCustom: {
> > > > > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > > > > +
> > > > > +                             break;
> > > > > +                     }
> > > > > +
> > > > > +                     default:
> > > > > +                             LOG(IPARPI, Error) << "Flicker mode " << mode << " is not supported";
> > > > > +                             modeValid = false;
> > > > > +
> > > > > +                             break;
> > > > > +                     }
> > > > > +
> > > > > +                     if (modeValid)
> > > > > +                             flickerState_.mode = mode;
> > > > > +
> > > > > +                     break;
> > > > > +             }
> > > > > +
> > > > > +             case controls::AE_FLICKER_CUSTOM: {
> > > > > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > > > > +                             controller_.getAlgorithm("agc"));
> > > > > +                     if (!agc) {
> > > > > +                             LOG(IPARPI, Warning)
> > > > > +                                     << "Could not set AeFlickerCustom - no AGC algorithm";
> > > > > +                             break;
> > > > > +                     }
> > > > > +
> > > > > +                     uint32_t customPeriod = ctrl.second.get<int32_t>();
> > > > > +                     flickerState_.customPeriod = customPeriod * 1.0us;
> > > > > +
> > > > > +                     /*
> > > > > +                      * We note that it makes no difference if the mode gets set to "custom"
> > > > > +                      * first, and the period updated after, or vice versa.
> > > > > +                      */
> > > >
> > > > True, but what if the app never provides a value for
> > > > controls::AE_FLICKER_CUSTOM ? Should you initialize customPeriod to
> > > > some sensible default ?
> > >
> > > In our implementation we use the value 0 to mean "no flicker
> > > cancellection", so setting the mode to custom/manual without a flicker
> > > period will cause it to do nothing (until you set one). I'm OK with
> > > this behaviour unless anyone objects, though I don't feel strongly!
> > > I'd rather document that setting custom/manual mode without a period
> > > leads to undefined behaviour rather than mandating a particular value,
> > > I think, as not every PH may wish to do the same. What do you think?
> >
> >
> > I had wondered if we could get this down to a single control by having
> > '0' as no flicker cancellation - but we'd have no way to express 'auto'
> > so I think we do still need two, and it lets us report in the metadata
> > if the value was determined automatically or explicitly I guess? (Though
> > the app should know that anyway as it would know if it set the manual
> > value?)
> 
> Yes, I think we do still want a separate control.
> 
> >
> > Throwing paint on the bike shed I feel like 'AeFlickerPeriod' would be
> > better suited for the control name as then it would be usable to report
> > the period back in the metadata when it's detected with 'Auto' mode.
> 
> I agree that 'AeFlickerPeriod' is a better name, particularly if the
> 'custom' mode becomes the 'manual' mode.
> 
> >
> > What limits will be imposed for the period ? I think setting
> > AeFlickerMode to 'Custom' (or 'Manual'?) and not setting an
> > {AeFlickerCustom,AeFlickerPeriod} control should be the same as not
> > changing anything, but I expect that maybe the ControlInfo might have
> > limits that would preclude setting a period of '0' ?
> 
> I guess implementations get to choose their own limits though, for the
> most part, I see no reason not to allow them to be very wide.
> Extremely short or extremely long periods are going to be increasingly
> unlikely IRL, of course.

The part I was wondering about is if we 'defined' 0 as being equivalent
to controls::FlickerOff, but that could make it harder to validate the
control if it means any value from 0...MAX is 'acceptable'.

But yes, I think this can all be handled by the pipeline handler/IPA.


> > It might be reasonable to say setting AeFlickerMode to Manual/Custom
> > will only take effect when the corresponding AeFlickerPeriod control is
> > applied ?
> 
> Yes, we could say that setting the mode to manual has no effect until
> a period is also set. Though of course I assume the period is
> "sticky", so once you've set it, you can go to and fro to manual mode
> without re-sending the said period.

<Time jump, now that I've writen the below> Does this mean you expect to
go from AeFlickerMode=Manaul(20000) to AeFlickerMode=Auto(Detected
30000) and then go to AeFlickerMode=Off(0) and when you finally go back
to AeFlickerMode=Manual(no period applied) you expect period to be
'remembered' as 20000?



> 
> I guess we have to ask what happens if you go from auto to manual
> mode, having never set a period. What happens then? TBH, I think I'd
> be quite happy to say that selecting manual mode, without ever setting
> a period, is the same as being "off". Thoughts?

I suspect this might be something we should consider for all
'multi-control' settings, for how they react if they are only partially
set. As long as the behaviour is documented I think it's fine though.

I think I would consider setting the period to be 'sticky' as well so if
you did:

1) AeFlickerMode = Off
2) AeFlickerPeriod = 10000
3) AeFlickerMode = Manual

The Period would be defined at 10000

I think for switching from Auto to Manual it would remain at whatever
the autodetected value was...

1) AeFlickerMode = Off
2) AeFlickerMode = Auto #Metadata reports AeFlickerPeriod as 20000
3) AeFlickerMode = Manual # AeFlickerPeriod now 'locked' at manual 20000

Hrm... but that seems to differ with your suggestion above of setting it
to 'off'

To me 'sticky' to last actual value (where off = 0, auto == detected
value) seems easiest to codify and explain? But maybe I'm missing
something obvious.

And now I feel like we should have helper classes in libipa to codify
the behaviour of all these controls in a common way for each PH!?
(later)

--
Kieran

> 
> David
> 
> >
> >
> > --
> > Kieran
> >
> >
> > >
> > > Thanks!
> > > David
> > >
> > > >
> > > > > +                     if (flickerState_.mode == controls::FlickerCustom)
> > > > > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > > > > +
> > > > > +                     break;
> > > > > +             }
> > > > > +
> > > > >               case controls::AWB_ENABLE: {
> > > > >                       /* Silently ignore this control for a mono sensor. */
> > > > >                       if (monoSensor_)
> > > > > diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h
> > > > > index 39d00760..22823176 100644
> > > > > --- a/src/ipa/rpi/common/ipa_base.h
> > > > > +++ b/src/ipa/rpi/common/ipa_base.h
> > > > > @@ -116,6 +116,12 @@ private:
> > > > >       /* Frame duration (1/fps) limits. */
> > > > >       utils::Duration minFrameDuration_;
> > > > >       utils::Duration maxFrameDuration_;
> > > > > +
> > > > > +     /* The current state of flicker avoidance. */
> > > > > +     struct FlickerState {
> > > > > +             int32_t mode;
> > > > > +             utils::Duration customPeriod;
> > > > > +     } flickerState_;
> > > > >  };
> > > > >
> > > > >  } /* namespace ipa::RPi */
> > > > > --
> > > > > 2.30.2
> > > > >
Jacopo Mondi July 18, 2023, 12:29 p.m. UTC | #6
Just to throw more confusion to the mix

On Tue, Jul 18, 2023 at 12:16:02PM +0100, Kieran Bingham via libcamera-devel wrote:
> Quoting David Plowman (2023-07-18 10:13:42)
> > Hi Kieran
> >
> > On Tue, 18 Jul 2023 at 09:48, Kieran Bingham
> > <kieran.bingham@ideasonboard.com> wrote:
> > >
> > > Quoting David Plowman via libcamera-devel (2023-07-18 09:24:24)
> > > > Hi Jacopo
> > > >
> > > > Thanks for the comments.
> > > >
> > > > On Tue, 18 Jul 2023 at 08:09, Jacopo Mondi
> > > > <jacopo.mondi@ideasonboard.com> wrote:
> > > > >
> > > > > Hi David
> > > > >
> > > > > On Thu, Jul 13, 2023 at 04:12:18PM +0100, David Plowman via libcamera-devel wrote:
> > > > > > We handle the flicker modes by passing the correct period to the
> > > > > > AEC/AGC algorithm which already contains the necessary code.
> > > > > >
> > > > > > The "Auto" mode, as well as reporting the detected flicker period via
> > > > >
> > > > > If so you shouldn't register
> > > > >
> > > > >         { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> > > > >
> > > > > otherwise AeFlickerMode::FlickerAuto will be exposed to applications ?
> > > >
> > > > Indeed, it might be better not to expose the auto value!
> > > >
> > > > >
> > > > >
> > > > > > the "AeFlickerDetected" metadata, are unsupported for now.
> > > > > >
> > > > > > Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> > > > > > ---
> > > > > >  src/ipa/rpi/common/ipa_base.cpp | 64 ++++++++++++++++++++++++++++++++-
> > > > > >  src/ipa/rpi/common/ipa_base.h   |  6 ++++
> > > > > >  2 files changed, 69 insertions(+), 1 deletion(-)
> > > > > >
> > > > > > diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
> > > > > > index f40f2e71..81d65ea5 100644
> > > > > > --- a/src/ipa/rpi/common/ipa_base.cpp
> > > > > > +++ b/src/ipa/rpi/common/ipa_base.cpp
> > > > > > @@ -61,6 +61,8 @@ const ControlInfoMap::Map ipaControls{
> > > > > >       { &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
> > > > > >       { &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
> > > > > >       { &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) },
> > > > > > +     { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> > > > > > +     { &controls::AeFlickerCustom, ControlInfo(100, 1000000) },
> > > > > >       { &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
> > > > > >       { &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
> > > > > >       { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
> > > > > > @@ -97,7 +99,7 @@ namespace ipa::RPi {
> > > > > >
> > > > > >  IpaBase::IpaBase()
> > > > > >       : controller_(), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),
> > > > > > -       firstStart_(true)
> > > > > > +       firstStart_(true), flickerState_({ 0, 0s })
> > > > > >  {
> > > > > >  }
> > > > > >
> > > > > > @@ -812,6 +814,66 @@ void IpaBase::applyControls(const ControlList &controls)
> > > > > >                       break;
> > > > > >               }
> > > > > >
> > > > > > +             case controls::AE_FLICKER_MODE: {
> > > > > > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > > > > > +                             controller_.getAlgorithm("agc"));
> > > > > > +                     if (!agc) {
> > > > > > +                             LOG(IPARPI, Warning)
> > > > > > +                                     << "Could not set AeFlickerMode - no AGC algorithm";
> > > > > > +                             break;
> > > > > > +                     }
> > > > > > +
> > > > > > +                     int32_t mode = ctrl.second.get<int32_t>();
> > > > > > +                     bool modeValid = true;
> > > > > > +
> > > > > > +                     switch (mode) {
> > > > > > +                     case controls::FlickerOff: {
> > > > > > +                             agc->setFlickerPeriod(0us);
> > > > > > +
> > > > > > +                             break;
> > > > > > +                     }
> > > > >
> > > > > Do you need braces ?
> > > >
> > > > Sure, I can remove them!
> > > >
> > > > >
> > > > > > +
> > > > > > +                     case controls::FlickerCustom: {
> > > > > > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > > > > > +
> > > > > > +                             break;
> > > > > > +                     }
> > > > > > +
> > > > > > +                     default:
> > > > > > +                             LOG(IPARPI, Error) << "Flicker mode " << mode << " is not supported";
> > > > > > +                             modeValid = false;
> > > > > > +
> > > > > > +                             break;
> > > > > > +                     }
> > > > > > +
> > > > > > +                     if (modeValid)
> > > > > > +                             flickerState_.mode = mode;
> > > > > > +
> > > > > > +                     break;
> > > > > > +             }
> > > > > > +
> > > > > > +             case controls::AE_FLICKER_CUSTOM: {
> > > > > > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > > > > > +                             controller_.getAlgorithm("agc"));
> > > > > > +                     if (!agc) {
> > > > > > +                             LOG(IPARPI, Warning)
> > > > > > +                                     << "Could not set AeFlickerCustom - no AGC algorithm";
> > > > > > +                             break;
> > > > > > +                     }
> > > > > > +
> > > > > > +                     uint32_t customPeriod = ctrl.second.get<int32_t>();
> > > > > > +                     flickerState_.customPeriod = customPeriod * 1.0us;
> > > > > > +
> > > > > > +                     /*
> > > > > > +                      * We note that it makes no difference if the mode gets set to "custom"
> > > > > > +                      * first, and the period updated after, or vice versa.
> > > > > > +                      */
> > > > >
> > > > > True, but what if the app never provides a value for
> > > > > controls::AE_FLICKER_CUSTOM ? Should you initialize customPeriod to
> > > > > some sensible default ?
> > > >
> > > > In our implementation we use the value 0 to mean "no flicker
> > > > cancellection", so setting the mode to custom/manual without a flicker
> > > > period will cause it to do nothing (until you set one). I'm OK with
> > > > this behaviour unless anyone objects, though I don't feel strongly!
> > > > I'd rather document that setting custom/manual mode without a period
> > > > leads to undefined behaviour rather than mandating a particular value,
> > > > I think, as not every PH may wish to do the same. What do you think?
> > >
> > >
> > > I had wondered if we could get this down to a single control by having
> > > '0' as no flicker cancellation - but we'd have no way to express 'auto'
> > > so I think we do still need two, and it lets us report in the metadata
> > > if the value was determined automatically or explicitly I guess? (Though
> > > the app should know that anyway as it would know if it set the manual
> > > value?)
> >
> > Yes, I think we do still want a separate control.
> >
> > >
> > > Throwing paint on the bike shed I feel like 'AeFlickerPeriod' would be
> > > better suited for the control name as then it would be usable to report
> > > the period back in the metadata when it's detected with 'Auto' mode.
> >
> > I agree that 'AeFlickerPeriod' is a better name, particularly if the
> > 'custom' mode becomes the 'manual' mode.
> >
> > >
> > > What limits will be imposed for the period ? I think setting
> > > AeFlickerMode to 'Custom' (or 'Manual'?) and not setting an
> > > {AeFlickerCustom,AeFlickerPeriod} control should be the same as not
> > > changing anything, but I expect that maybe the ControlInfo might have
> > > limits that would preclude setting a period of '0' ?
> >
> > I guess implementations get to choose their own limits though, for the
> > most part, I see no reason not to allow them to be very wide.
> > Extremely short or extremely long periods are going to be increasingly
> > unlikely IRL, of course.
>
> The part I was wondering about is if we 'defined' 0 as being equivalent
> to controls::FlickerOff, but that could make it harder to validate the
> control if it means any value from 0...MAX is 'acceptable'.
>
> But yes, I think this can all be handled by the pipeline handler/IPA.
>
>
> > > It might be reasonable to say setting AeFlickerMode to Manual/Custom
> > > will only take effect when the corresponding AeFlickerPeriod control is
> > > applied ?
> >
> > Yes, we could say that setting the mode to manual has no effect until
> > a period is also set. Though of course I assume the period is
> > "sticky", so once you've set it, you can go to and fro to manual mode
> > without re-sending the said period.
>
> <Time jump, now that I've writen the below> Does this mean you expect to
> go from AeFlickerMode=Manaul(20000) to AeFlickerMode=Auto(Detected
> 30000) and then go to AeFlickerMode=Off(0) and when you finally go back
> to AeFlickerMode=Manual(no period applied) you expect period to be
> 'remembered' as 20000?
>
>
>
> >
> > I guess we have to ask what happens if you go from auto to manual
> > mode, having never set a period. What happens then? TBH, I think I'd
> > be quite happy to say that selecting manual mode, without ever setting
> > a period, is the same as being "off". Thoughts?
>
> I suspect this might be something we should consider for all
> 'multi-control' settings, for how they react if they are only partially
> set. As long as the behaviour is documented I think it's fine though.
>
> I think I would consider setting the period to be 'sticky' as well so if
> you did:
>
> 1) AeFlickerMode = Off
> 2) AeFlickerPeriod = 10000
> 3) AeFlickerMode = Manual

I guess it depends if 1) 2) and 3) are separate requests ? If that's
the case, we discussed in the past how this should work for the AEGC
controls that I still wish someone review:
https://patchwork.libcamera.org/patch/17077/

A similar case is the one represented by 'ExposureTime' which is only
meaningful when the mode is set to Manual. From the description of the
control we drafted:

- ExposureTime:
  This control will only take effect if ExposureTimeMode is Manual. If
  this control is set when ExposureTimeMode is Auto, the value will be
  ignored and will not be retained.

I guess the situation here is similar ?


>
> The Period would be defined at 10000
>
> I think for switching from Auto to Manual it would remain at whatever
> the autodetected value was...
>
> 1) AeFlickerMode = Off
> 2) AeFlickerMode = Auto #Metadata reports AeFlickerPeriod as 20000
> 3) AeFlickerMode = Manual # AeFlickerPeriod now 'locked' at manual 20000

Looking at the behaviour we defined for AEGC, this seems to match

+        When transitioning from Auto to Manual mode and no ExposureTime control
+        is provided by the application, the last value computed by the AE
+        algorithm when the mode was Auto will be used.


>
> Hrm... but that seems to differ with your suggestion above of setting it
> to 'off'
>
> To me 'sticky' to last actual value (where off = 0, auto == detected
> value) seems easiest to codify and explain? But maybe I'm missing
> something obvious.
>
> And now I feel like we should have helper classes in libipa to codify
> the behaviour of all these controls in a common way for each PH!?
> (later)

I wish

>
> --
> Kieran
>
> >
> > David
> >
> > >
> > >
> > > --
> > > Kieran
> > >
> > >
> > > >
> > > > Thanks!
> > > > David
> > > >
> > > > >
> > > > > > +                     if (flickerState_.mode == controls::FlickerCustom)
> > > > > > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > > > > > +
> > > > > > +                     break;
> > > > > > +             }
> > > > > > +
> > > > > >               case controls::AWB_ENABLE: {
> > > > > >                       /* Silently ignore this control for a mono sensor. */
> > > > > >                       if (monoSensor_)
> > > > > > diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h
> > > > > > index 39d00760..22823176 100644
> > > > > > --- a/src/ipa/rpi/common/ipa_base.h
> > > > > > +++ b/src/ipa/rpi/common/ipa_base.h
> > > > > > @@ -116,6 +116,12 @@ private:
> > > > > >       /* Frame duration (1/fps) limits. */
> > > > > >       utils::Duration minFrameDuration_;
> > > > > >       utils::Duration maxFrameDuration_;
> > > > > > +
> > > > > > +     /* The current state of flicker avoidance. */
> > > > > > +     struct FlickerState {
> > > > > > +             int32_t mode;
> > > > > > +             utils::Duration customPeriod;
> > > > > > +     } flickerState_;
> > > > > >  };
> > > > > >
> > > > > >  } /* namespace ipa::RPi */
> > > > > > --
> > > > > > 2.30.2
> > > > > >
David Plowman July 18, 2023, 12:50 p.m. UTC | #7
Thanks for the extra dose of confusion!!

Actually I worry slightly that we're over-thinking a bit here.

The AE/AGC analogy is indeed interesting because there's a specific
reason why you would change from auto to manual. Because the exposure
is always changing and you just want to fix it at whatever it is for a
bit while you (for example) take some pictures. And for this to work
you need manual mode to adopt whatever values auto mode had at the
time

Flicker isn't really like that. In auto mode it's not changing all
over the place, so I don't see any benefit in, for example, setting
the manual period to what the auto was doing when switching to manual
mode. Indeed, maybe you have some a priori guess as to what the
flicker period might be and that you want to use if the auto algorithm
fails to detect an exact value for itself. So it might just be more
helpful to applications to leave the manual period completely alone,
unless the application changes it. As always, it's hard to predict.

So my gut reaction is simply not to couple these things together, it
has the virtue of simplicity, if nothing else. Perhaps let me post a
revised version with some of the name changes and improved wording,
and then we can take up the discussion again!

Thanks
David

On Tue, 18 Jul 2023 at 13:29, Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Just to throw more confusion to the mix
>
> On Tue, Jul 18, 2023 at 12:16:02PM +0100, Kieran Bingham via libcamera-devel wrote:
> > Quoting David Plowman (2023-07-18 10:13:42)
> > > Hi Kieran
> > >
> > > On Tue, 18 Jul 2023 at 09:48, Kieran Bingham
> > > <kieran.bingham@ideasonboard.com> wrote:
> > > >
> > > > Quoting David Plowman via libcamera-devel (2023-07-18 09:24:24)
> > > > > Hi Jacopo
> > > > >
> > > > > Thanks for the comments.
> > > > >
> > > > > On Tue, 18 Jul 2023 at 08:09, Jacopo Mondi
> > > > > <jacopo.mondi@ideasonboard.com> wrote:
> > > > > >
> > > > > > Hi David
> > > > > >
> > > > > > On Thu, Jul 13, 2023 at 04:12:18PM +0100, David Plowman via libcamera-devel wrote:
> > > > > > > We handle the flicker modes by passing the correct period to the
> > > > > > > AEC/AGC algorithm which already contains the necessary code.
> > > > > > >
> > > > > > > The "Auto" mode, as well as reporting the detected flicker period via
> > > > > >
> > > > > > If so you shouldn't register
> > > > > >
> > > > > >         { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> > > > > >
> > > > > > otherwise AeFlickerMode::FlickerAuto will be exposed to applications ?
> > > > >
> > > > > Indeed, it might be better not to expose the auto value!
> > > > >
> > > > > >
> > > > > >
> > > > > > > the "AeFlickerDetected" metadata, are unsupported for now.
> > > > > > >
> > > > > > > Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> > > > > > > ---
> > > > > > >  src/ipa/rpi/common/ipa_base.cpp | 64 ++++++++++++++++++++++++++++++++-
> > > > > > >  src/ipa/rpi/common/ipa_base.h   |  6 ++++
> > > > > > >  2 files changed, 69 insertions(+), 1 deletion(-)
> > > > > > >
> > > > > > > diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
> > > > > > > index f40f2e71..81d65ea5 100644
> > > > > > > --- a/src/ipa/rpi/common/ipa_base.cpp
> > > > > > > +++ b/src/ipa/rpi/common/ipa_base.cpp
> > > > > > > @@ -61,6 +61,8 @@ const ControlInfoMap::Map ipaControls{
> > > > > > >       { &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
> > > > > > >       { &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
> > > > > > >       { &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) },
> > > > > > > +     { &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
> > > > > > > +     { &controls::AeFlickerCustom, ControlInfo(100, 1000000) },
> > > > > > >       { &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
> > > > > > >       { &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
> > > > > > >       { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
> > > > > > > @@ -97,7 +99,7 @@ namespace ipa::RPi {
> > > > > > >
> > > > > > >  IpaBase::IpaBase()
> > > > > > >       : controller_(), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),
> > > > > > > -       firstStart_(true)
> > > > > > > +       firstStart_(true), flickerState_({ 0, 0s })
> > > > > > >  {
> > > > > > >  }
> > > > > > >
> > > > > > > @@ -812,6 +814,66 @@ void IpaBase::applyControls(const ControlList &controls)
> > > > > > >                       break;
> > > > > > >               }
> > > > > > >
> > > > > > > +             case controls::AE_FLICKER_MODE: {
> > > > > > > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > > > > > > +                             controller_.getAlgorithm("agc"));
> > > > > > > +                     if (!agc) {
> > > > > > > +                             LOG(IPARPI, Warning)
> > > > > > > +                                     << "Could not set AeFlickerMode - no AGC algorithm";
> > > > > > > +                             break;
> > > > > > > +                     }
> > > > > > > +
> > > > > > > +                     int32_t mode = ctrl.second.get<int32_t>();
> > > > > > > +                     bool modeValid = true;
> > > > > > > +
> > > > > > > +                     switch (mode) {
> > > > > > > +                     case controls::FlickerOff: {
> > > > > > > +                             agc->setFlickerPeriod(0us);
> > > > > > > +
> > > > > > > +                             break;
> > > > > > > +                     }
> > > > > >
> > > > > > Do you need braces ?
> > > > >
> > > > > Sure, I can remove them!
> > > > >
> > > > > >
> > > > > > > +
> > > > > > > +                     case controls::FlickerCustom: {
> > > > > > > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > > > > > > +
> > > > > > > +                             break;
> > > > > > > +                     }
> > > > > > > +
> > > > > > > +                     default:
> > > > > > > +                             LOG(IPARPI, Error) << "Flicker mode " << mode << " is not supported";
> > > > > > > +                             modeValid = false;
> > > > > > > +
> > > > > > > +                             break;
> > > > > > > +                     }
> > > > > > > +
> > > > > > > +                     if (modeValid)
> > > > > > > +                             flickerState_.mode = mode;
> > > > > > > +
> > > > > > > +                     break;
> > > > > > > +             }
> > > > > > > +
> > > > > > > +             case controls::AE_FLICKER_CUSTOM: {
> > > > > > > +                     RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
> > > > > > > +                             controller_.getAlgorithm("agc"));
> > > > > > > +                     if (!agc) {
> > > > > > > +                             LOG(IPARPI, Warning)
> > > > > > > +                                     << "Could not set AeFlickerCustom - no AGC algorithm";
> > > > > > > +                             break;
> > > > > > > +                     }
> > > > > > > +
> > > > > > > +                     uint32_t customPeriod = ctrl.second.get<int32_t>();
> > > > > > > +                     flickerState_.customPeriod = customPeriod * 1.0us;
> > > > > > > +
> > > > > > > +                     /*
> > > > > > > +                      * We note that it makes no difference if the mode gets set to "custom"
> > > > > > > +                      * first, and the period updated after, or vice versa.
> > > > > > > +                      */
> > > > > >
> > > > > > True, but what if the app never provides a value for
> > > > > > controls::AE_FLICKER_CUSTOM ? Should you initialize customPeriod to
> > > > > > some sensible default ?
> > > > >
> > > > > In our implementation we use the value 0 to mean "no flicker
> > > > > cancellection", so setting the mode to custom/manual without a flicker
> > > > > period will cause it to do nothing (until you set one). I'm OK with
> > > > > this behaviour unless anyone objects, though I don't feel strongly!
> > > > > I'd rather document that setting custom/manual mode without a period
> > > > > leads to undefined behaviour rather than mandating a particular value,
> > > > > I think, as not every PH may wish to do the same. What do you think?
> > > >
> > > >
> > > > I had wondered if we could get this down to a single control by having
> > > > '0' as no flicker cancellation - but we'd have no way to express 'auto'
> > > > so I think we do still need two, and it lets us report in the metadata
> > > > if the value was determined automatically or explicitly I guess? (Though
> > > > the app should know that anyway as it would know if it set the manual
> > > > value?)
> > >
> > > Yes, I think we do still want a separate control.
> > >
> > > >
> > > > Throwing paint on the bike shed I feel like 'AeFlickerPeriod' would be
> > > > better suited for the control name as then it would be usable to report
> > > > the period back in the metadata when it's detected with 'Auto' mode.
> > >
> > > I agree that 'AeFlickerPeriod' is a better name, particularly if the
> > > 'custom' mode becomes the 'manual' mode.
> > >
> > > >
> > > > What limits will be imposed for the period ? I think setting
> > > > AeFlickerMode to 'Custom' (or 'Manual'?) and not setting an
> > > > {AeFlickerCustom,AeFlickerPeriod} control should be the same as not
> > > > changing anything, but I expect that maybe the ControlInfo might have
> > > > limits that would preclude setting a period of '0' ?
> > >
> > > I guess implementations get to choose their own limits though, for the
> > > most part, I see no reason not to allow them to be very wide.
> > > Extremely short or extremely long periods are going to be increasingly
> > > unlikely IRL, of course.
> >
> > The part I was wondering about is if we 'defined' 0 as being equivalent
> > to controls::FlickerOff, but that could make it harder to validate the
> > control if it means any value from 0...MAX is 'acceptable'.
> >
> > But yes, I think this can all be handled by the pipeline handler/IPA.
> >
> >
> > > > It might be reasonable to say setting AeFlickerMode to Manual/Custom
> > > > will only take effect when the corresponding AeFlickerPeriod control is
> > > > applied ?
> > >
> > > Yes, we could say that setting the mode to manual has no effect until
> > > a period is also set. Though of course I assume the period is
> > > "sticky", so once you've set it, you can go to and fro to manual mode
> > > without re-sending the said period.
> >
> > <Time jump, now that I've writen the below> Does this mean you expect to
> > go from AeFlickerMode=Manaul(20000) to AeFlickerMode=Auto(Detected
> > 30000) and then go to AeFlickerMode=Off(0) and when you finally go back
> > to AeFlickerMode=Manual(no period applied) you expect period to be
> > 'remembered' as 20000?
> >
> >
> >
> > >
> > > I guess we have to ask what happens if you go from auto to manual
> > > mode, having never set a period. What happens then? TBH, I think I'd
> > > be quite happy to say that selecting manual mode, without ever setting
> > > a period, is the same as being "off". Thoughts?
> >
> > I suspect this might be something we should consider for all
> > 'multi-control' settings, for how they react if they are only partially
> > set. As long as the behaviour is documented I think it's fine though.
> >
> > I think I would consider setting the period to be 'sticky' as well so if
> > you did:
> >
> > 1) AeFlickerMode = Off
> > 2) AeFlickerPeriod = 10000
> > 3) AeFlickerMode = Manual
>
> I guess it depends if 1) 2) and 3) are separate requests ? If that's
> the case, we discussed in the past how this should work for the AEGC
> controls that I still wish someone review:
> https://patchwork.libcamera.org/patch/17077/
>
> A similar case is the one represented by 'ExposureTime' which is only
> meaningful when the mode is set to Manual. From the description of the
> control we drafted:
>
> - ExposureTime:
>   This control will only take effect if ExposureTimeMode is Manual. If
>   this control is set when ExposureTimeMode is Auto, the value will be
>   ignored and will not be retained.
>
> I guess the situation here is similar ?
>
>
> >
> > The Period would be defined at 10000
> >
> > I think for switching from Auto to Manual it would remain at whatever
> > the autodetected value was...
> >
> > 1) AeFlickerMode = Off
> > 2) AeFlickerMode = Auto #Metadata reports AeFlickerPeriod as 20000
> > 3) AeFlickerMode = Manual # AeFlickerPeriod now 'locked' at manual 20000
>
> Looking at the behaviour we defined for AEGC, this seems to match
>
> +        When transitioning from Auto to Manual mode and no ExposureTime control
> +        is provided by the application, the last value computed by the AE
> +        algorithm when the mode was Auto will be used.
>
>
> >
> > Hrm... but that seems to differ with your suggestion above of setting it
> > to 'off'
> >
> > To me 'sticky' to last actual value (where off = 0, auto == detected
> > value) seems easiest to codify and explain? But maybe I'm missing
> > something obvious.
> >
> > And now I feel like we should have helper classes in libipa to codify
> > the behaviour of all these controls in a common way for each PH!?
> > (later)
>
> I wish
>
> >
> > --
> > Kieran
> >
> > >
> > > David
> > >
> > > >
> > > >
> > > > --
> > > > Kieran
> > > >
> > > >
> > > > >
> > > > > Thanks!
> > > > > David
> > > > >
> > > > > >
> > > > > > > +                     if (flickerState_.mode == controls::FlickerCustom)
> > > > > > > +                             agc->setFlickerPeriod(flickerState_.customPeriod);
> > > > > > > +
> > > > > > > +                     break;
> > > > > > > +             }
> > > > > > > +
> > > > > > >               case controls::AWB_ENABLE: {
> > > > > > >                       /* Silently ignore this control for a mono sensor. */
> > > > > > >                       if (monoSensor_)
> > > > > > > diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h
> > > > > > > index 39d00760..22823176 100644
> > > > > > > --- a/src/ipa/rpi/common/ipa_base.h
> > > > > > > +++ b/src/ipa/rpi/common/ipa_base.h
> > > > > > > @@ -116,6 +116,12 @@ private:
> > > > > > >       /* Frame duration (1/fps) limits. */
> > > > > > >       utils::Duration minFrameDuration_;
> > > > > > >       utils::Duration maxFrameDuration_;
> > > > > > > +
> > > > > > > +     /* The current state of flicker avoidance. */
> > > > > > > +     struct FlickerState {
> > > > > > > +             int32_t mode;
> > > > > > > +             utils::Duration customPeriod;
> > > > > > > +     } flickerState_;
> > > > > > >  };
> > > > > > >
> > > > > > >  } /* namespace ipa::RPi */
> > > > > > > --
> > > > > > > 2.30.2
> > > > > > >

Patch
diff mbox series

diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
index f40f2e71..81d65ea5 100644
--- a/src/ipa/rpi/common/ipa_base.cpp
+++ b/src/ipa/rpi/common/ipa_base.cpp
@@ -61,6 +61,8 @@  const ControlInfoMap::Map ipaControls{
 	{ &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) },
 	{ &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) },
 	{ &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) },
+	{ &controls::AeFlickerMode, ControlInfo(controls::AeFlickerModeValues) },
+	{ &controls::AeFlickerCustom, ControlInfo(100, 1000000) },
 	{ &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) },
 	{ &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) },
 	{ &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) },
@@ -97,7 +99,7 @@  namespace ipa::RPi {
 
 IpaBase::IpaBase()
 	: controller_(), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),
-	  firstStart_(true)
+	  firstStart_(true), flickerState_({ 0, 0s })
 {
 }
 
@@ -812,6 +814,66 @@  void IpaBase::applyControls(const ControlList &controls)
 			break;
 		}
 
+		case controls::AE_FLICKER_MODE: {
+			RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
+				controller_.getAlgorithm("agc"));
+			if (!agc) {
+				LOG(IPARPI, Warning)
+					<< "Could not set AeFlickerMode - no AGC algorithm";
+				break;
+			}
+
+			int32_t mode = ctrl.second.get<int32_t>();
+			bool modeValid = true;
+
+			switch (mode) {
+			case controls::FlickerOff: {
+				agc->setFlickerPeriod(0us);
+
+				break;
+			}
+
+			case controls::FlickerCustom: {
+				agc->setFlickerPeriod(flickerState_.customPeriod);
+
+				break;
+			}
+
+			default:
+				LOG(IPARPI, Error) << "Flicker mode " << mode << " is not supported";
+				modeValid = false;
+
+				break;
+			}
+
+			if (modeValid)
+				flickerState_.mode = mode;
+
+			break;
+		}
+
+		case controls::AE_FLICKER_CUSTOM: {
+			RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>(
+				controller_.getAlgorithm("agc"));
+			if (!agc) {
+				LOG(IPARPI, Warning)
+					<< "Could not set AeFlickerCustom - no AGC algorithm";
+				break;
+			}
+
+			uint32_t customPeriod = ctrl.second.get<int32_t>();
+			flickerState_.customPeriod = customPeriod * 1.0us;
+
+			/*
+			 * We note that it makes no difference if the mode gets set to "custom"
+			 * first, and the period updated after, or vice versa.
+			 */
+			if (flickerState_.mode == controls::FlickerCustom)
+				agc->setFlickerPeriod(flickerState_.customPeriod);
+
+			break;
+		}
+
 		case controls::AWB_ENABLE: {
 			/* Silently ignore this control for a mono sensor. */
 			if (monoSensor_)
diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h
index 39d00760..22823176 100644
--- a/src/ipa/rpi/common/ipa_base.h
+++ b/src/ipa/rpi/common/ipa_base.h
@@ -116,6 +116,12 @@  private:
 	/* Frame duration (1/fps) limits. */
 	utils::Duration minFrameDuration_;
 	utils::Duration maxFrameDuration_;
+
+	/* The current state of flicker avoidance. */
+	struct FlickerState {
+		int32_t mode;
+		utils::Duration customPeriod;
+	} flickerState_;
 };
 
 } /* namespace ipa::RPi */