diff options
Diffstat (limited to 'aiq')
-rw-r--r-- | aiq/aiq.cpp | 353 | ||||
-rw-r--r-- | aiq/aiq.h | 72 | ||||
-rw-r--r-- | aiq/meson.build | 1 |
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 ¶ms, + 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 ¶ms, + 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', ]) |