[libcamera-devel] [PATCH 12/13] libcamera: ipa: rkisp1: Add basic control of auto exposure
Niklas Söderlund
niklas.soderlund at ragnatech.se
Fri Aug 30 00:25:29 CEST 2019
Hi Kieran,
Thanks for your feedback.
On 2019-08-29 16:16:06 +0100, Kieran Bingham wrote:
> Hi Niklas,
>
> On 28/08/2019 02:17, Niklas Söderlund wrote:
> > Add an IPA which controls the exposure time and analog gain for a sensor
> > connected to the rkisp1 pipeline. The IPA supports turning AE on and off
> > but lacks the support to inform the camera of the status of the AE
> > control loop.
> >
> > Signed-off-by: Niklas Söderlund <niklas.soderlund at ragnatech.se>
> > ---
> > src/ipa/ipa_rkisp1.cpp | 165 +++++++++++++++++++++++++++++++++++++++++
> > src/ipa/meson.build | 1 +
> > 2 files changed, 166 insertions(+)
> > create mode 100644 src/ipa/ipa_rkisp1.cpp
> >
> > diff --git a/src/ipa/ipa_rkisp1.cpp b/src/ipa/ipa_rkisp1.cpp
> > new file mode 100644
> > index 0000000000000000..950efb244cfe7879
> > --- /dev/null
> > +++ b/src/ipa/ipa_rkisp1.cpp
> > @@ -0,0 +1,165 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2019, Google Inc.
> > + *
> > + * ipa_rkisp1.cpp - RkISP1 Image Processing Algorithms
> > + */
> > +
> > +#include <algorithm>
> > +#include <string.h>
> > +
> > +#include <linux/rkisp1-config.h>
> > +
> > +#include <libcamera/buffer.h>
> > +#include <libcamera/ipa/ipa_interface.h>
> > +#include <libcamera/ipa/ipa_module_info.h>
> > +#include <libcamera/request.h>
> > +
> > +#include "log.h"
> > +#include "utils.h"
> > +
> > +namespace libcamera {
> > +
> > +LOG_DEFINE_CATEGORY(IPARkISP1)
> > +
> > +class IPARkISP1 : public IPAInterface
> > +{
> > +public:
> > + int initSensor(const V4L2ControlInfoMap &controls) override;
> > + void processRequest(const void *cookie, const ControlList &controls,
> > + Buffer ¶meters) override;
> > + void updateStatistics(const void *cookie, Buffer &statistics) override;
> > +
> > +private:
> > + void setControls();
> > +
> > + uint64_t statFrame_;
> > +
> > + /* Camera sensor controls. */
> > + bool autoExposure_;
> > + uint64_t exposure_;
> > + uint64_t minExposure_;
> > + uint64_t maxExposure_;
> > + uint64_t gain_;
> > + uint64_t minGain_;
> > + uint64_t maxGain_;
>
> I don't think you need to store the min/max value of each. You should
> instead store a reference to the relevant ControlInfo for the control,
> which will be populated with the min/max.
>
> And perhaps all of those would be stored in a single ControlList ...
The IPA will be isolated so we can't store references. We could have a
local ControlList, but why have all that extra code in a hot path?
>
>
> > +};
> > +
> > +int IPARkISP1::initSensor(const V4L2ControlInfoMap &controls)
> > +{
> > + statFrame_ = 0;
> > +
> > + const auto itExp = controls.find(V4L2_CID_EXPOSURE);
> > + if (itExp == controls.end())
> > + return -ENODEV;
> > +
> > + const auto itGain = controls.find(V4L2_CID_ANALOGUE_GAIN);
> > + if (itGain == controls.end())
> > + return -ENODEV;
> > +
> > + autoExposure_ = true;
> > +
> > + minExposure_ = std::max<uint64_t>(itExp->second.min(), 1);
> > + maxExposure_ = itExp->second.max();
> > + exposure_ = minExposure_;
> > +
> > + minGain_ = std::max<uint64_t>(itGain->second.min(), 1);
> > + maxGain_ = itGain->second.max();
> > + gain_ = minGain_;
> > +
> > + LOG(IPARkISP1, Info)
> > + << "Exposure: " << minExposure_ << "-" << maxExposure_
> > + << " Gain: " << minGain_ << "-" << maxGain_;
> > +
> > + setControls();
> > +
> > + return 0;
> > +}
> > +
> > +void IPARkISP1::setControls()
> > +{
> > + V4L2ControlList ctrls;
> > + ctrls.add(V4L2_CID_EXPOSURE);
> > + ctrls.add(V4L2_CID_ANALOGUE_GAIN);
> > + ctrls[V4L2_CID_EXPOSURE]->setValue(exposure_);
> > + ctrls[V4L2_CID_ANALOGUE_GAIN]->setValue(gain_);
> > +
> > + updateSensor.emit(ctrls);
> > +}
> > +
> > +void IPARkISP1::processRequest(const void *cookie, const ControlList &controls,
> > + Buffer ¶meters)
>
> Are you preventing the Request being passed in directly here to stop
> direct access to the Request object? If so - why not pass it in as a const?
>
> Are you perhaps trying to optimise this for serialisation already?
Yes, this is a step to reduce the information passed between pipeline
and IPA to visualize what is really needed and make it easier to
serialize.
>
> > +{
> > + rkisp1_isp_params_cfg *params =
> > + static_cast<rkisp1_isp_params_cfg *>(parameters.mem()->planes()[0].mem());
> > +
> > + memset(params, 0, sizeof(*params));
> > +
> > + /* Auto Exposure on/off*/
>
> s#off*/#off. */#
>
> > + if (controls.contains(AeEnable)) {
> > + autoExposure_ = controls[AeEnable].getBool();
> > + if (autoExposure_)
> > + params->module_ens = CIFISP_MODULE_AEC;
> > +
> > + params->module_en_update = CIFISP_MODULE_AEC;
> > + }
> > +
> > + queueRequest.emit(cookie);
> > +}
> > +
> > +void IPARkISP1::updateStatistics(const void *cookie, Buffer &statistics)
> > +{
> > + const rkisp1_stat_buffer *stats =
> > + static_cast<rkisp1_stat_buffer *>(statistics.mem()->planes()[0].mem());
> > + const cifisp_stat *params = &stats->params;
> > +
> > + if ((stats->meas_type & CIFISP_STAT_AUTOEXP) && (statFrame_ % 2 == 0)) {
> > + const cifisp_ae_stat *ae = ¶ms->ae;
> > +
> > + const unsigned int target = 60;
> > +
> > + unsigned int value = 0;
> > + unsigned int num = 0;
> > + for (int i = 0; i < CIFISP_AE_MEAN_MAX; i++) {
> > + if (ae->exp_mean[i] > 15) {
> > + value += ae->exp_mean[i];
> > + num++;
> > + }
> > + }
> > + value /= num;
> > +
> > + double factor = (double)target / value;
> > + double tmp;
> > +
> > + tmp = factor * exposure_ * gain_ / minGain_;
> > + exposure_ = utils::clamp<uint64_t>((uint64_t)tmp, minExposure_, maxExposure_);
> > +
> > + tmp = tmp / exposure_ * minGain_;
> > + gain_ = utils::clamp<uint64_t>((uint64_t)tmp, minGain_, maxGain_);
> > +
> > + setControls();
> > + }
> > +
> > + statFrame_++;
> > +}
>
> I think this would update the local ControlList, which would at the end
> be submitted, and reused.
>
> I'll ignore all the calculations for now, and assume they do 'something'
> useful :D
:-)
>
>
> > +
> > +/*
> > + * External IPA module interface
> > + */
> > +
> > +extern "C" {
> > +const struct IPAModuleInfo ipaModuleInfo = {
> > + IPA_MODULE_API_VERSION,
> > + 1,
> > + "PipelineHandlerRkISP1",
> > + "RkISP1 IPA",
> > + "LGPL-2.1-or-later",
> > +};
> > +
> > +IPAInterface *ipaCreate()
> > +{
> > + return new IPARkISP1();
> > +}
> > +};
> > +
> > +}; /* namespace libcamera */
> > diff --git a/src/ipa/meson.build b/src/ipa/meson.build
> > index dca7a9461385b68d..16592a71e03990ce 100644
> > --- a/src/ipa/meson.build
> > +++ b/src/ipa/meson.build
> > @@ -1,6 +1,7 @@
> > ipa_dummy_sources = [
> > ['ipa_dummy', 'ipa_dummy.cpp'],
> > ['ipa_dummy_isolate', 'ipa_dummy_isolate.cpp'],
> > + ['ipa_rkisp1', 'ipa_rkisp1.cpp'],
>
> As mentioned in an earlier patch, I don't think this is an
> 'ipa_dummy_source' file, but I do think this list can be re-used.
>
> It just needs renaming first.
>
>
> > ]
> >
> > ipa_install_dir = join_paths(get_option('libdir'), 'libcamera')
> >
>
> --
> Regards
> --
> Kieran
--
Regards,
Niklas Söderlund
More information about the libcamera-devel
mailing list