[libcamera-devel] [PATCH v3 12/13] libcamera: ipa: rkisp1: Add basic control of auto exposure
Niklas Söderlund
niklas.soderlund at ragnatech.se
Fri Sep 27 04:44:16 CEST 2019
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
and informing 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 | 228 +++++++++++++++++++++++++++++++++++++++++
src/ipa/meson.build | 13 +++
2 files changed, 241 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..f90465516c6aff87
--- /dev/null
+++ b/src/ipa/ipa_rkisp1.cpp
@@ -0,0 +1,228 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * ipa_rkisp1.cpp - RkISP1 Image Processing Algorithms
+ */
+
+#include <algorithm>
+#include <math.h>
+#include <queue>
+#include <string.h>
+
+#include <linux/rkisp1-config.h>
+
+#include <ipa/ipa_interface.h>
+#include <ipa/ipa_module_info.h>
+#include <libcamera/buffer.h>
+#include <libcamera/request.h>
+
+#include "log.h"
+#include "utils.h"
+
+#define BUFFER_PARAM 1
+#define BUFFER_STAT 2
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPARkISP1)
+
+class IPARkISP1 : public IPAInterface
+{
+public:
+ int init() override { return 0; }
+
+ void initSensor(const V4L2ControlInfoMap &controls) override;
+ void initBuffers(unsigned int type,
+ const std::vector<BufferMemory> &buffers) override;
+ void signalBuffer(unsigned int type, unsigned int id) override;
+ void queueRequest(unsigned int frame, const ControlList &controls) override;
+
+private:
+ void setControls(unsigned int frame);
+ void updateStatistics(unsigned int frame, BufferMemory &statistics);
+
+ std::map<unsigned int, std::map<unsigned int, BufferMemory>> bufferInfo_;
+ std::map<unsigned int, std::map<unsigned int, unsigned int>> bufferFrame_;
+ std::map<unsigned int, std::queue<unsigned int>> bufferFree_;
+
+ /* Camera sensor controls. */
+ bool autoExposure_;
+ uint64_t exposure_;
+ uint64_t minExposure_;
+ uint64_t maxExposure_;
+ uint64_t gain_;
+ uint64_t minGain_;
+ uint64_t maxGain_;
+};
+
+void IPARkISP1::initSensor(const V4L2ControlInfoMap &controls)
+{
+ const auto itExp = controls.find(V4L2_CID_EXPOSURE);
+ if (itExp == controls.end()) {
+ LOG(IPARkISP1, Error) << "Can't find exposure control";
+ return;
+ }
+
+ const auto itGain = controls.find(V4L2_CID_ANALOGUE_GAIN);
+ if (itGain == controls.end()) {
+ LOG(IPARkISP1, Error) << "Can't find gain control";
+ return;
+ }
+
+ 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(0);
+}
+
+void IPARkISP1::initBuffers(unsigned int type,
+ const std::vector<BufferMemory> &buffers)
+{
+ bufferInfo_[type].clear();
+ for (unsigned int i = 0; i < buffers.size(); i++) {
+ bufferInfo_[type][i] = buffers[i];
+ bufferFree_[type].push(i);
+ }
+}
+
+void IPARkISP1::signalBuffer(unsigned int type, unsigned int id)
+{
+ if (type == BUFFER_STAT) {
+ unsigned int frame = bufferFrame_[type][id];
+ BufferMemory &mem = bufferInfo_[type][id];
+ updateStatistics(frame, mem);
+ }
+
+ bufferFree_[type].push(id);
+}
+
+void IPARkISP1::queueRequest(unsigned int frame, const ControlList &controls)
+{
+ /* Find buffers. */
+ if (bufferFree_[BUFFER_PARAM].empty()) {
+ LOG(IPARkISP1, Error) << "Param buffer underrun";
+ return;
+ }
+
+ if (bufferFree_[BUFFER_STAT].empty()) {
+ LOG(IPARkISP1, Error) << "Statistics buffer underrun";
+ return;
+ }
+
+ unsigned int paramid = bufferFree_[BUFFER_PARAM].front();
+ bufferFree_[BUFFER_PARAM].pop();
+ unsigned int statid = bufferFree_[BUFFER_STAT].front();
+ bufferFree_[BUFFER_STAT].pop();
+
+ bufferFrame_[BUFFER_PARAM][paramid] = frame;
+ bufferFrame_[BUFFER_STAT][statid] = frame;
+
+ /* Prepare parameters buffer. */
+ BufferMemory &mem = bufferInfo_[BUFFER_PARAM][paramid];
+ rkisp1_isp_params_cfg *params =
+ static_cast<rkisp1_isp_params_cfg *>(mem.planes()[0].mem());
+
+ memset(params, 0, sizeof(*params));
+
+ /* Auto Exposure on/off. */
+ if (controls.contains(AeEnable)) {
+ autoExposure_ = controls[AeEnable].getBool();
+ if (autoExposure_)
+ params->module_ens = CIFISP_MODULE_AEC;
+
+ params->module_en_update = CIFISP_MODULE_AEC;
+ }
+
+ /* Queue buffers to pipeline. */
+ queueBuffer.emit(frame, BUFFER_PARAM, paramid);
+ queueBuffer.emit(frame, BUFFER_STAT, statid);
+}
+
+void IPARkISP1::setControls(unsigned int frame)
+{
+ 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(frame, ctrls);
+}
+
+void IPARkISP1::updateStatistics(unsigned int frame, BufferMemory &statistics)
+{
+ const rkisp1_stat_buffer *stats =
+ static_cast<rkisp1_stat_buffer *>(statistics.planes()[0].mem());
+ const cifisp_stat *params = &stats->params;
+ IPAMetaData metaData = {};
+
+ if (stats->meas_type & CIFISP_STAT_AUTOEXP) {
+ 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;
+
+ if (frame % 3 == 0) {
+ 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(frame + 1);
+ }
+
+ metaData.aeState = fabs(factor - 1.0f) < 0.05f ?
+ AeState::Converged : AeState::Searching;
+ } else {
+ metaData.aeState = AeState::Inactive;
+ }
+
+ metaDataReady.emit(frame, metaData);
+}
+
+/*
+ * 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 10448c2ffc76af4b..eb5b852eec282735 100644
--- a/src/ipa/meson.build
+++ b/src/ipa/meson.build
@@ -3,6 +3,10 @@ ipa_dummy_sources = [
['ipa_dummy_isolate', 'Proprietary'],
]
+ipa_sources = [
+ ['ipa_rkisp1', 'ipa_rkisp1.cpp', 'LGPL-2.1-or-later'],
+]
+
ipa_install_dir = join_paths(get_option('libdir'), 'libcamera')
foreach t : ipa_dummy_sources
@@ -14,5 +18,14 @@ foreach t : ipa_dummy_sources
cpp_args : '-DLICENSE="' + t[1] + '"')
endforeach
+foreach t : ipa_sources
+ ipa = shared_module(t[0], t[1],
+ name_prefix : '',
+ include_directories : includes,
+ install : true,
+ install_dir : ipa_install_dir,
+ cpp_args : '-DLICENSE="' + t[2] + '"')
+endforeach
+
config_h.set('IPA_MODULE_DIR',
'"' + join_paths(get_option('prefix'), ipa_install_dir) + '"')
--
2.23.0
More information about the libcamera-devel
mailing list