summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKieran Bingham <kieran.bingham@ideasonboard.com>2021-05-10 23:02:43 +0530
committerUmang Jain <umang.jain@ideasonboard.com>2021-06-28 17:54:08 +0530
commitc1c0f554051200ea051e5e31ee286e5f5e83c744 (patch)
tree189d52fa931397345bfa6a138272c323ad224000
parent03d07648fe3ba288922609b5f310d159cc41001a (diff)
aiq: Provide initial Intel AIQ Wrapper support
The AIQ class is the entry point for running IPA algorithms for the requested frame and handles the statistics being generated. It comprises of wrapping the ia_aiq_*_run() functions and provide helper functions that take in the input parameters and location for their results after the run. These results(AiqResults) can be used for subsequent runs and setting statistics as required. Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Umang Jain <umang.jain@ideasonboard.com>
-rw-r--r--aiq/aiq.cpp353
-rw-r--r--aiq/aiq.h72
-rw-r--r--aiq/meson.build1
3 files changed, 426 insertions, 0 deletions
diff --git a/aiq/aiq.cpp b/aiq/aiq.cpp
new file mode 100644
index 0000000..708e9d6
--- /dev/null
+++ b/aiq/aiq.cpp
@@ -0,0 +1,353 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/*
+ * Copyright (C) 2021, Google Inc.
+ *
+ * aiq.cpp - Intel IA Imaging library C++ wrapper
+ */
+
+#include "aiq.h"
+
+#include <libcamera/base/class.h>
+#include <libcamera/base/log.h>
+
+#include "binary_data.h"
+#include "stats/ipa_ipu3_stats.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(AIQ)
+
+namespace ipa::ipu3::aiq {
+
+AIQ::AIQ()
+ : aiq_(nullptr)
+{
+ LOG(AIQ, Info) << "Creating IA AIQ Wrapper";
+}
+
+AIQ::~AIQ()
+{
+ LOG(AIQ, Info) << "Destroying IA AIQ Wrapper";
+ if (aiq_)
+ ia_aiq_deinit(aiq_);
+
+ if (iaCmc_)
+ ia_cmc_parser_deinit(iaCmc_);
+
+ delete aiqStats_;
+ aiqStats_ = nullptr;
+}
+
+std::string AIQ::decodeError(ia_err err)
+{
+ static const char *errors[] = {
+ "None", /*!< No errors*/
+ "General", /*!< General error*/
+ "Memory", /*!< Out of memory*/
+ "Corrupted", /*!< Corrupted data*/
+ "Internal", /*!< Error in code*/
+ "Invalid", /*!< Invalid argument for a function*/
+ "Disabled", /*!< Functionality is disabled*/
+ };
+
+ std::ostringstream o;
+
+ o << "[";
+
+ unsigned int count = 0;
+ for (unsigned int i = 0; i <= 6; i++) {
+ if (err & (1 << i)) {
+ if (count)
+ o << ",";
+
+ o << errors[i];
+ count++;
+ }
+ }
+
+ o << "]";
+
+ return o.str();
+}
+
+int AIQ::init(BinaryData &aiqb, BinaryData &nvm, BinaryData &aiqd)
+{
+ constexpr unsigned int maxGridW = 80;
+ constexpr unsigned int maxGridH = 60;
+ constexpr unsigned int maxExposures = 1;
+
+ /* \todo: No maker note provided. */
+ ia_mkn *ia_mkn = nullptr;
+
+ /*
+ * \todo: Both the AIC and the AIQ use the iaCmc_.
+ * Can this be the same instance or do they need their own instances?
+ */
+ iaCmc_ = ia_cmc_parser_init(aiqb.data());
+ if (iaCmc_ == nullptr) {
+ LOG(AIQ, Error) << "Failed to initialise CMC Parser";
+ return -EINVAL;
+ }
+
+ aiq_ = ia_aiq_init(aiqb.data(), nvm.data(), aiqd.data(),
+ maxGridW, maxGridH, maxExposures,
+ iaCmc_, ia_mkn);
+ if (!aiq_) {
+ LOG(AIQ, Error) << "Failed to initialise aiq library";
+ return -ENODATA;
+ }
+
+ version_ = ia_aiq_get_version();
+ LOG(AIQ, Info) << "AIQ Library version: " << version_;
+
+ aiqStats_ = new IPAIPU3Stats;
+
+ return 0;
+}
+
+int AIQ::configure()
+{
+ LOG(AIQ, Debug) << "Configure AIQ";
+
+ return 0;
+}
+
+int AIQ::setStatistics(unsigned int frame,
+ int64_t timestamp,
+ AiqResults &results,
+ const ipu3_uapi_stats_3a *stats)
+{
+ ia_aiq_statistics_input_params *statParams =
+ aiqStats_->getInputStatsParams(frame, &results, stats);
+
+ statParams->frame_timestamp = timestamp;
+
+ ia_err err = ia_aiq_statistics_set(aiq_, statParams);
+ if (err) {
+ LOG(AIQ, Error) << "Failed to set statistics: "
+ << decodeError(err);
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+/*
+ * Run algorithms, and store the configuration in the parameters buffers
+ * This is likely to change drastically as we progress, and the algorithms
+ * might run asycnronously, or after receipt of statistics, with the filling
+ * of the parameter buffer being the only part handled when called for.
+ */
+int AIQ::run2a(unsigned int frame, AiqInputParameters &params,
+ AiqResults &results)
+{
+ (void)frame;
+
+ /* Run AWB algorithms, using the config structures. */
+ aeRun(params.aeInputParams, results);
+ gbceRun(params.gbceParams, results);
+
+ awbRun(params.awbParams, results);
+
+ params.paParams.color_gains = nullptr;
+ params.paParams.awb_results = results.awb();
+ params.paParams.exposure_params = results.ae()->exposures[0].exposure;
+ parameterAdapterRun(params.paParams, results);
+
+ afRun(params.afParams, results);
+
+ return 0;
+}
+
+int AIQ::afRun(ia_aiq_af_input_params &afParams, AiqResults &results)
+{
+ ia_aiq_af_results *afResults = nullptr;
+
+ ia_err err = ia_aiq_af_run(aiq_, &afParams, &afResults);
+ if (err) {
+ LOG(AIQ, Error) << "Failed to run Auto-focus: "
+ << decodeError(err);
+ return err;
+ }
+
+ if (afResults) {
+ LOG(AIQ, Debug) << "AF: Focal distance " << afResults->current_focus_distance;
+ LOG(AIQ, Debug) << "=== AUTO FOCUS ==="
+ << "AutoFocus status: " << afResults->status << "\n"
+ << "Focal distance: " << afResults->current_focus_distance << "\n"
+ << "next_lens_position: " << afResults->next_lens_position << "\n"
+ << "lens_driver_action: " << afResults->lens_driver_action << "\n"
+ << "use_af_assist: " << afResults->use_af_assist << "\n"
+ << "Final lens pos: " << afResults->final_lens_position_reached << "\n\n";
+
+ results.setAf(afResults);
+ } else {
+ LOG(AIQ, Error) << "Auto Focus produced no results";
+ }
+
+ return 0;
+}
+
+int AIQ::afBracketRun(ia_aiq_af_bracket_input_params &afBracketParams,
+ AiqResults &results)
+{
+ ia_aiq_af_bracket_results *afBracketResults = nullptr;
+ ia_err err = ia_aiq_af_bracket(aiq_, &afBracketParams, &afBracketResults);
+ if (err) {
+ LOG(AIQ, Error) << "Failed to run AF Bracket: "
+ << decodeError(err);
+ return err;
+ }
+
+ if (afBracketResults) {
+ LOG(AIQ, Debug) << "=== AF Bracket ==="
+ << "distances_bracketing: " << *afBracketResults->distances_bracketing << "\n"
+ << "lens_positions_bracketing: " << *afBracketResults->lens_positions_bracketing << "\n";
+
+ results.setAfBracket(afBracketResults);
+ } else {
+ LOG(AIQ, Error) << "AFBracket produced no results";
+ }
+
+ return 0;
+}
+
+/* Global Brightness and Contrast Enhancement */
+int AIQ::gbceRun(ia_aiq_gbce_input_params &gbceParams, AiqResults &results)
+{
+ ia_aiq_gbce_results *gbceResults = nullptr;
+
+ ia_err err = ia_aiq_gbce_run(aiq_, &gbceParams, &gbceResults);
+ if (err) {
+ LOG(AIQ, Error) << "Failed to run GBCE: "
+ << decodeError(err);
+ return err;
+ }
+
+ if (gbceResults) {
+ LOG(AIQ, Debug) << "GBCE: GammaLutSize: " << gbceResults->gamma_lut_size
+ << " ToneMap Size: " << gbceResults->tone_map_lut_size;
+
+ results.setGbce(gbceResults);
+ } else {
+ LOG(AIQ, Error) << "GBCE produced no results";
+ }
+
+ return 0;
+}
+
+int AIQ::aeRun(ia_aiq_ae_input_params &aeParams, AiqResults &results)
+{
+ ia_aiq_ae_results *aeResults = nullptr;
+ ia_err err = ia_aiq_ae_run(aiq_, &aeParams, &aeResults);
+ if (err) {
+ LOG(AIQ, Error) << "Failed to run AutoExposure: "
+ << decodeError(err);
+ return err;
+ }
+
+ if (aeResults) {
+ LOG(AIQ, Debug) << "AE Coarse:" << aeResults->exposures->sensor_exposure->coarse_integration_time
+ << " AE LLP: " << aeResults->exposures->sensor_exposure->line_length_pixels
+ << " AE FLL: " << aeResults->exposures->sensor_exposure->frame_length_lines;
+
+ results.setAe(aeResults);
+ } else {
+ LOG(AIQ, Error) << "AE: No results";
+ }
+
+ return 0;
+}
+
+int AIQ::awbRun(ia_aiq_awb_input_params &awbParams, AiqResults &results)
+{
+ /* Todo: Determine if this is required, or can be a nullptr */
+ ia_aiq_awb_results awb_result_alloc = {};
+ ia_aiq_awb_results *awbResults = &awb_result_alloc;
+ ia_err err = ia_aiq_awb_run(aiq_, &awbParams, &awbResults);
+ if (err) {
+ LOG(AIQ, Error) << "Failed to run Auto-white-balance: "
+ << decodeError(err);
+ return err;
+ }
+
+ if (awbResults) {
+ LOG(AIQ, Debug)
+ << "AWB: Final R/G: " << awbResults->final_r_per_g
+ << " B/G: " << awbResults->final_b_per_g
+ << " ConvergenceDistance. : " << awbResults->distance_from_convergence;
+
+ results.setAwb(awbResults);
+ } else {
+ LOG(AIQ, Error) << "No AWB results...";
+ }
+
+ return 0;
+}
+
+int AIQ::dsdRun(ia_aiq_dsd_input_params &dsdParams, AiqResults &results)
+{
+ ia_aiq_scene_mode detectedSceneMode;
+ ia_err err = ia_aiq_dsd_run(aiq_, &dsdParams, &detectedSceneMode);
+ if (err) {
+ LOG(AIQ, Error) << "Failed to run Determine Scene detection: "
+ << decodeError(err);
+ return err;
+ }
+
+ results.setDetectedSceneMode(detectedSceneMode);
+
+ return 0;
+}
+
+int AIQ::parameterAdapterRun(ia_aiq_pa_input_params &paParams,
+ AiqResults &results)
+{
+ ia_aiq_pa_results *paResults = nullptr;
+ ia_err err = ia_aiq_pa_run(aiq_, &paParams, &paResults);
+ if (err) {
+ LOG(AIQ, Error) << "Failed to run parameter adapter: "
+ << decodeError(err);
+ return err;
+ }
+
+ if (paResults) {
+ LOG(AIQ, Debug) << "Parameter Adapter brightness level "
+ << paResults->brightness_level << "\n";
+
+ results.setPa(paResults);
+ } else {
+ LOG(AIQ, Error) << "No Parameter Adapater results...";
+ }
+
+ return 0;
+}
+
+int AIQ::shadingAdapterRun(ia_aiq_sa_input_params &saParams,
+ AiqResults &results)
+{
+ ia_aiq_sa_results *saResults = nullptr;
+ ia_err err = ia_aiq_sa_run(aiq_, &saParams, &saResults);
+ if (err) {
+ LOG(AIQ, Error) << "Failed to run shading adapter: "
+ << decodeError(err);
+ return err;
+ }
+
+ if (saResults) {
+ LOG(AIQ, Debug) << "LSC width: " << saResults->width
+ << " LSC height: " << saResults->height
+ << " LSC updated: "
+ << (saResults->lsc_update ? "True" : "False");
+
+ results.setSa(saResults);
+ } else {
+ LOG(AIQ, Error) << "No Shading Adapater results...";
+ }
+
+ return 0;
+}
+
+} /* namespace ipa::ipu3::aiq */
+
+} /* namespace libcamera */
diff --git a/aiq/aiq.h b/aiq/aiq.h
new file mode 100644
index 0000000..fcd02d2
--- /dev/null
+++ b/aiq/aiq.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/*
+ * Copyright (C) 2021, Google Inc.
+ *
+ * aiq.h - Intel IA Imaging library C++ wrapper
+ *
+ * To simplify naming, and prevent confusion the wrapper is named simply aiq
+ * rather than ia_aiq.
+ */
+
+#include <string>
+
+#include <ia_imaging/ia_aiq.h>
+#include <ia_imaging/ia_cmc_parser.h>
+#include <linux/intel-ipu3.h>
+
+#include <libcamera/geometry.h>
+
+#include "aiq_input_parameters.h"
+#include "aiq_results.h"
+#include "binary_data.h"
+#include "stats/ipa_ipu3_stats.h"
+
+#ifndef IPA_IPU3_AIQ_H
+#define IPA_IPU3_AIQ_H
+
+namespace libcamera {
+
+namespace ipa::ipu3::aiq {
+
+class AIQ
+{
+public:
+ AIQ();
+ ~AIQ();
+
+ int init(BinaryData &aiqb, BinaryData &nvm, BinaryData &aiqd);
+ int configure();
+ int setStatistics(unsigned int frame,
+ int64_t timestamp, AiqResults &results,
+ const ipu3_uapi_stats_3a *stats);
+
+ int run2a(unsigned int frame, AiqInputParameters &params,
+ AiqResults &results);
+
+private:
+ std::string decodeError(ia_err err);
+
+ int aeRun(ia_aiq_ae_input_params &aeParams, AiqResults &results);
+ int afRun(ia_aiq_af_input_params &afParams, AiqResults &results);
+ int afBracketRun(ia_aiq_af_bracket_input_params &afBracketParams,
+ AiqResults &results);
+ int awbRun(ia_aiq_awb_input_params &awbParams, AiqResults &results);
+ int dsdRun(ia_aiq_dsd_input_params &dsdParams, AiqResults &results);
+ int gbceRun(ia_aiq_gbce_input_params &gbceParams, AiqResults &results);
+ int parameterAdapterRun(ia_aiq_pa_input_params &paParams,
+ AiqResults &results);
+ int shadingAdapterRun(ia_aiq_sa_input_params &saParams,
+ AiqResults &results);
+
+ ia_aiq *aiq_;
+ ia_cmc_t *iaCmc_;
+ std::string version_;
+
+ IPAIPU3Stats *aiqStats_;
+};
+
+} /* namespace ipa::ipu3::aiq */
+
+} /* namespace libcamera */
+
+#endif /* IPA_IPU3_AIQ_H */
diff --git a/aiq/meson.build b/aiq/meson.build
index 811515d..e3732f9 100644
--- a/aiq/meson.build
+++ b/aiq/meson.build
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: CC0-1.0
ipu3_ipa_files += files([
+ 'aiq.cpp',
'aiq_input_parameters.cpp',
'aiq_results.cpp',
])