[libcamera-devel] [PATCH v3] ipa: rkisp1: Add support for manual gain and exposure

Laurent Pinchart laurent.pinchart at ideasonboard.com
Sat Oct 22 00:35:33 CEST 2022


Hi Paul,

Another comment.

On Tue, Oct 18, 2022 at 02:49:13PM +0900, Paul Elder via libcamera-devel wrote:
> Add support for manual gain and exposure in the rkisp1 IPA.
> 
> Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
> 
> ---
> Changes in v3:
> - Report the exposure time and analogue gain in metadata
> 
> Changes in v2:
> - set the min and max values of ExposureTime and AnalogueGain properly
>   - to that end, plumb the sensorControls through the pipeline handler's
>     loadIPA() and the IPA's init()
> ---
>  include/libcamera/ipa/rkisp1.mojom       |  2 +-
>  src/ipa/rkisp1/algorithms/agc.cpp        | 59 +++++++++++++++++++++---
>  src/ipa/rkisp1/algorithms/agc.h          |  4 ++
>  src/ipa/rkisp1/ipa_context.h             | 13 +++++-
>  src/ipa/rkisp1/rkisp1.cpp                | 21 +++++++++
>  src/libcamera/pipeline/rkisp1/rkisp1.cpp |  9 ++--
>  6 files changed, 95 insertions(+), 13 deletions(-)
> 
> diff --git a/include/libcamera/ipa/rkisp1.mojom b/include/libcamera/ipa/rkisp1.mojom
> index eaf3955e..b526450d 100644
> --- a/include/libcamera/ipa/rkisp1.mojom
> +++ b/include/libcamera/ipa/rkisp1.mojom
> @@ -10,7 +10,7 @@ import "include/libcamera/ipa/core.mojom";
>  
>  interface IPARkISP1Interface {
>  	init(libcamera.IPASettings settings,
> -	     uint32 hwRevision)
> +	     uint32 hwRevision, libcamera.ControlInfoMap sensorControls)
>  		=> (int32 ret, libcamera.ControlInfoMap ipaControls);
>  	start() => (int32 ret);
>  	stop();
> diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> index 04062a36..09ec6ac4 100644
> --- a/src/ipa/rkisp1/algorithms/agc.cpp
> +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> @@ -14,6 +14,7 @@
>  #include <libcamera/base/log.h>
>  #include <libcamera/base/utils.h>
>  
> +#include <libcamera/control_ids.h>
>  #include <libcamera/ipa/core_ipa_interface.h>
>  
>  #include "libipa/histogram.h"
> @@ -73,8 +74,11 @@ Agc::Agc()
>  int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)
>  {
>  	/* Configure the default exposure and gain. */
> -	context.activeState.agc.gain = std::max(context.configuration.agc.minAnalogueGain, kMinAnalogueGain);
> -	context.activeState.agc.exposure = 10ms / context.configuration.sensor.lineDuration;
> +	context.activeState.agc.automatic.gain = std::max(context.configuration.agc.minAnalogueGain, kMinAnalogueGain);
> +	context.activeState.agc.automatic.exposure = 10ms / context.configuration.sensor.lineDuration;
> +	context.activeState.agc.manual.gain = context.activeState.agc.automatic.gain;
> +	context.activeState.agc.manual.exposure = context.activeState.agc.automatic.exposure;
> +	context.activeState.agc.autoEnabled = true;
>  
>  	/*
>  	 * According to the RkISP1 documentation:
> @@ -221,8 +225,8 @@ void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext,
>  			      << stepGain;
>  
>  	/* Update the estimated exposure and gain. */
> -	activeState.agc.exposure = shutterTime / configuration.sensor.lineDuration;
> -	activeState.agc.gain = stepGain;
> +	activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration;
> +	activeState.agc.automatic.gain = stepGain;
>  }
>  
>  /**
> @@ -339,8 +343,10 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,
>  void Agc::prepare(IPAContext &context, const uint32_t frame,
>  		  IPAFrameContext &frameContext, rkisp1_params_cfg *params)
>  {
> -	frameContext.agc.exposure = context.activeState.agc.exposure;
> -	frameContext.agc.gain = context.activeState.agc.gain;
> +	if (frameContext.agc.autoEnabled) {
> +		frameContext.agc.exposure = context.activeState.agc.automatic.exposure;
> +		frameContext.agc.gain = context.activeState.agc.automatic.gain;
> +	}
>  
>  	if (frame > 0)
>  		return;
> @@ -373,6 +379,47 @@ void Agc::prepare(IPAContext &context, const uint32_t frame,
>  	params->module_en_update |= RKISP1_CIF_ISP_MODULE_HST;
>  }
>  
> +/**
> + * \copydoc libcamera::ipa::Algorithm::queueRequest
> + */
> +void Agc::queueRequest(IPAContext &context,
> +		       [[maybe_unused]] const uint32_t frame,
> +		       IPAFrameContext &frameContext,
> +		       const ControlList &controls)
> +{
> +	auto &agc = context.activeState.agc;
> +
> +	const auto &agcEnable = controls.get(controls::AeEnable);
> +	if (agcEnable && *agcEnable != agc.autoEnabled) {
> +		agc.autoEnabled = *agcEnable;
> +
> +		LOG(RkISP1Agc, Debug)
> +			<< (*agcEnable ? "Enabling" : "Disabling") << " AGC";
> +	}
> +
> +	const auto &exposure = controls.get(controls::ExposureTime);
> +	if (exposure && !agc.autoEnabled) {
> +		agc.manual.exposure = *exposure;
> +
> +		LOG(RkISP1Agc, Debug)
> +			<< "Set exposure to: " << agc.manual.exposure;
> +	}
> +
> +	const auto &gain = controls.get(controls::AnalogueGain);
> +	if (gain && !agc.autoEnabled) {
> +		agc.manual.gain = *gain;
> +
> +		LOG(RkISP1Agc, Debug) << "Set gain to: " << agc.manual.gain;
> +	}

This needs to take into account the boundaries for the exposure time and
gain.

> +
> +	frameContext.agc.autoEnabled = agc.autoEnabled;
> +
> +	if (!frameContext.agc.autoEnabled) {
> +		frameContext.agc.exposure = agc.manual.exposure;
> +		frameContext.agc.gain = agc.manual.gain;
> +	}
> +}
> +
>  REGISTER_IPA_ALGORITHM(Agc, "Agc")
>  
>  } /* namespace ipa::rkisp1::algorithms */
> diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h
> index 9ad5c32f..ebceeef4 100644
> --- a/src/ipa/rkisp1/algorithms/agc.h
> +++ b/src/ipa/rkisp1/algorithms/agc.h
> @@ -32,6 +32,10 @@ public:
>  	void process(IPAContext &context, const uint32_t frame,
>  		     IPAFrameContext &frameContext,
>  		     const rkisp1_stat_buffer *stats) override;
> +	void queueRequest(IPAContext &context,
> +			  const uint32_t frame,
> +			  IPAFrameContext &frameContext,
> +			  const ControlList &controls) override;
>  
>  private:
>  	void computeExposure(IPAContext &Context, IPAFrameContext &frameContext,
> diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
> index c85d8abe..035d67e2 100644
> --- a/src/ipa/rkisp1/ipa_context.h
> +++ b/src/ipa/rkisp1/ipa_context.h
> @@ -50,8 +50,16 @@ struct IPASessionConfiguration {
>  
>  struct IPAActiveState {
>  	struct {
> -		uint32_t exposure;
> -		double gain;
> +		struct {
> +			uint32_t exposure;
> +			double gain;
> +		} manual;
> +		struct {
> +			uint32_t exposure;
> +			double gain;
> +		} automatic;
> +
> +		bool autoEnabled;
>  	} agc;
>  
>  	struct {
> @@ -92,6 +100,7 @@ struct IPAFrameContext : public FrameContext {
>  	struct {
>  		uint32_t exposure;
>  		double gain;
> +		bool autoEnabled;
>  	} agc;
>  
>  	struct {
> diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
> index 3f5c1a58..33692e5d 100644
> --- a/src/ipa/rkisp1/rkisp1.cpp
> +++ b/src/ipa/rkisp1/rkisp1.cpp
> @@ -49,6 +49,7 @@ public:
>  	IPARkISP1();
>  
>  	int init(const IPASettings &settings, unsigned int hwRevision,
> +		 const ControlInfoMap &sensorControls,
>  		 ControlInfoMap *ipaControls) override;
>  	int start() override;
>  	void stop() override;
> @@ -116,6 +117,7 @@ std::string IPARkISP1::logPrefix() const
>  }
>  
>  int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
> +		    const ControlInfoMap &sensorControls,
>  		    ControlInfoMap *ipaControls)
>  {
>  	/* \todo Add support for other revisions */
> @@ -183,6 +185,21 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
>  
>  	/* Return the controls handled by the IPA. */
>  	ControlInfoMap::Map ctrlMap = rkisp1Controls;
> +
> +	auto exposureTimeInfo = sensorControls.find(V4L2_CID_EXPOSURE);
> +	if (exposureTimeInfo != sensorControls.end()) {
> +		ctrlMap.emplace(&controls::ExposureTime,
> +				ControlInfo(exposureTimeInfo->second.min(),
> +					    exposureTimeInfo->second.max()));
> +	}
> +
> +	auto analogueGainInfo = sensorControls.find(V4L2_CID_ANALOGUE_GAIN);
> +	if (analogueGainInfo != sensorControls.end()) {
> +		ctrlMap.emplace(&controls::AnalogueGain,
> +				ControlInfo(static_cast<float>(analogueGainInfo->second.min().get<int32_t>()),
> +					    static_cast<float>(analogueGainInfo->second.max().get<int32_t>())));
> +	}
> +
>  	*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
>  
>  	return 0;
> @@ -368,11 +385,15 @@ void IPARkISP1::setControls(unsigned int frame)
>  
>  void IPARkISP1::prepareMetadata(unsigned int frame, unsigned int aeState)
>  {
> +	IPAFrameContext &frameContext = context_.frameContexts.get(frame);
>  	ControlList ctrls(controls::controls);
>  
>  	if (aeState)
>  		ctrls.set(controls::AeLocked, aeState == 2);
>  
> +	ctrls.set(controls::ExposureTime, frameContext.agc.exposure);
> +	ctrls.set(controls::AnalogueGain, frameContext.agc.gain);
> +
>  	metadataReady.emit(frame, ctrls);
>  }
>  
> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> index 455ee2a0..4f8e467a 100644
> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> @@ -91,7 +91,7 @@ public:
>  	}
>  
>  	PipelineHandlerRkISP1 *pipe();
> -	int loadIPA(unsigned int hwRevision);
> +	int loadIPA(unsigned int hwRevision, const ControlInfoMap &sensorControls);
>  
>  	Stream mainPathStream_;
>  	Stream selfPathStream_;
> @@ -319,7 +319,8 @@ PipelineHandlerRkISP1 *RkISP1CameraData::pipe()
>  	return static_cast<PipelineHandlerRkISP1 *>(Camera::Private::pipe());
>  }
>  
> -int RkISP1CameraData::loadIPA(unsigned int hwRevision)
> +int RkISP1CameraData::loadIPA(unsigned int hwRevision,
> +			      const ControlInfoMap &sensorControls)
>  {
>  	ipa_ = IPAManager::createIPA<ipa::rkisp1::IPAProxyRkISP1>(pipe(), 1, 1);
>  	if (!ipa_)
> @@ -348,7 +349,7 @@ int RkISP1CameraData::loadIPA(unsigned int hwRevision)
>  	}
>  
>  	int ret = ipa_->init({ ipaTuningFile, sensor_->model() }, hwRevision,
> -			     &controlInfo_);
> +			     sensorControls, &controlInfo_);
>  	if (ret < 0) {
>  		LOG(RkISP1, Error) << "IPA initialization failure";
>  		return ret;
> @@ -1023,7 +1024,7 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
>  	isp_->frameStart.connect(data->delayedCtrls_.get(),
>  				 &DelayedControls::applyControls);
>  
> -	ret = data->loadIPA(media_->hwRevision());
> +	ret = data->loadIPA(media_->hwRevision(), data->sensor_->controls());
>  	if (ret)
>  		return ret;
>  

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list