From b145ae42428b9f21fff01d7353a655e07e818c33 Mon Sep 17 00:00:00 2001 From: Jean-Michel Hautbois Date: Wed, 18 Aug 2021 17:54:02 +0200 Subject: ipa: ipu3: Move IPU3 awb into algorithms Now that the interface is properly used by the AWB class, move it into ipa::ipu3::algorithms and let the loops do the calls. Signed-off-by: Jean-Michel Hautbois Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart --- src/ipa/ipu3/algorithms/awb.cpp | 341 ++++++++++++++++++++++++++++++++++++ src/ipa/ipu3/algorithms/awb.h | 91 ++++++++++ src/ipa/ipu3/algorithms/meson.build | 1 + src/ipa/ipu3/ipu3.cpp | 10 +- src/ipa/ipu3/ipu3_awb.cpp | 341 ------------------------------------ src/ipa/ipu3/ipu3_awb.h | 91 ---------- src/ipa/ipu3/meson.build | 1 - 7 files changed, 435 insertions(+), 441 deletions(-) create mode 100644 src/ipa/ipu3/algorithms/awb.cpp create mode 100644 src/ipa/ipu3/algorithms/awb.h delete mode 100644 src/ipa/ipu3/ipu3_awb.cpp delete mode 100644 src/ipa/ipu3/ipu3_awb.h diff --git a/src/ipa/ipu3/algorithms/awb.cpp b/src/ipa/ipu3/algorithms/awb.cpp new file mode 100644 index 00000000..e15f7406 --- /dev/null +++ b/src/ipa/ipu3/algorithms/awb.cpp @@ -0,0 +1,341 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Ideas On Board + * + * awb.cpp - AWB control algorithm + */ +#include "awb.h" + +#include +#include + +#include + +namespace libcamera { + +namespace ipa::ipu3::algorithms { + +LOG_DEFINE_CATEGORY(IPU3Awb) + +static constexpr uint32_t kMinZonesCounted = 16; +static constexpr uint32_t kMinGreenLevelInZone = 32; + +/** + * \struct IspStatsRegion + * \brief RGB statistics for a given region + * + * The IspStatsRegion structure is intended to abstract the ISP specific + * statistics and use an agnostic algorithm to compute AWB. + * + * \var IspStatsRegion::counted + * \brief Number of pixels used to calculate the sums + * + * \var IspStatsRegion::uncounted + * \brief Remaining number of pixels in the region + * + * \var IspStatsRegion::rSum + * \brief Sum of the red values in the region + * + * \var IspStatsRegion::gSum + * \brief Sum of the green values in the region + * + * \var IspStatsRegion::bSum + * \brief Sum of the blue values in the region + */ + +/** + * \struct AwbStatus + * \brief AWB parameters calculated + * + * The AwbStatus structure is intended to store the AWB + * parameters calculated by the algorithm + * + * \var AwbStatus::temperatureK + * \brief Color temperature calculated + * + * \var AwbStatus::redGain + * \brief Gain calculated for the red channel + * + * \var AwbStatus::greenGain + * \brief Gain calculated for the green channel + * + * \var AwbStatus::blueGain + * \brief Gain calculated for the blue channel + */ + +/** + * \struct Ipu3AwbCell + * \brief Memory layout for each cell in AWB metadata + * + * The Ipu3AwbCell structure is used to get individual values + * such as red average or saturation ratio in a particular cell. + * + * \var Ipu3AwbCell::greenRedAvg + * \brief Green average for red lines in the cell + * + * \var Ipu3AwbCell::redAvg + * \brief Red average in the cell + * + * \var Ipu3AwbCell::blueAvg + * \brief blue average in the cell + * + * \var Ipu3AwbCell::greenBlueAvg + * \brief Green average for blue lines + * + * \var Ipu3AwbCell::satRatio + * \brief Saturation ratio in the cell + * + * \var Ipu3AwbCell::padding + * \brief array of unused bytes for padding + */ + +/* Default settings for Bayer noise reduction replicated from the Kernel */ +static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = { + .wb_gains = { 16, 16, 16, 16 }, + .wb_gains_thr = { 255, 255, 255, 255 }, + .thr_coeffs = { 1700, 0, 31, 31, 0, 16 }, + .thr_ctrl_shd = { 26, 26, 26, 26 }, + .opt_center = { -648, 0, -366, 0 }, + .lut = { + { 17, 23, 28, 32, 36, 39, 42, 45, + 48, 51, 53, 55, 58, 60, 62, 64, + 66, 68, 70, 72, 73, 75, 77, 78, + 80, 82, 83, 85, 86, 88, 89, 90 } }, + .bp_ctrl = { 20, 0, 1, 40, 0, 6, 0, 6, 0 }, + .dn_detect_ctrl = { 9, 3, 4, 0, 8, 0, 1, 1, 1, 1, 0 }, + .column_size = 1296, + .opt_center_sqr = { 419904, 133956 }, +}; + +/* Default color correction matrix defined as an identity matrix */ +static const struct ipu3_uapi_ccm_mat_config imguCssCcmDefault = { + 8191, 0, 0, 0, + 0, 8191, 0, 0, + 0, 0, 8191, 0 +}; + +Awb::Awb() + : Algorithm() +{ + asyncResults_.blueGain = 1.0; + asyncResults_.greenGain = 1.0; + asyncResults_.redGain = 1.0; + asyncResults_.temperatureK = 4500; + + zones_.reserve(kAwbStatsSizeX * kAwbStatsSizeY); +} + +Awb::~Awb() = default; + +/** + * The function estimates the correlated color temperature using + * from RGB color space input. + * In physics and color science, the Planckian locus or black body locus is + * the path or locus that the color of an incandescent black body would take + * in a particular chromaticity space as the blackbody temperature changes. + * + * If a narrow range of color temperatures is considered (those encapsulating + * daylight being the most practical case) one can approximate the Planckian + * locus in order to calculate the CCT in terms of chromaticity coordinates. + * + * More detailed information can be found in: + * https://en.wikipedia.org/wiki/Color_temperature#Approximation + */ +uint32_t Awb::estimateCCT(double red, double green, double blue) +{ + /* Convert the RGB values to CIE tristimulus values (XYZ) */ + double X = (-0.14282) * (red) + (1.54924) * (green) + (-0.95641) * (blue); + double Y = (-0.32466) * (red) + (1.57837) * (green) + (-0.73191) * (blue); + double Z = (-0.68202) * (red) + (0.77073) * (green) + (0.56332) * (blue); + + /* Calculate the normalized chromaticity values */ + double x = X / (X + Y + Z); + double y = Y / (X + Y + Z); + + /* Calculate CCT */ + double n = (x - 0.3320) / (0.1858 - y); + return 449 * n * n * n + 3525 * n * n + 6823.3 * n + 5520.33; +} + +/* Generate an RGB vector with the average values for each region */ +void Awb::generateZones(std::vector &zones) +{ + for (unsigned int i = 0; i < kAwbStatsSizeX * kAwbStatsSizeY; i++) { + RGB zone; + double counted = awbStats_[i].counted; + if (counted >= kMinZonesCounted) { + zone.G = awbStats_[i].gSum / counted; + if (zone.G >= kMinGreenLevelInZone) { + zone.R = awbStats_[i].rSum / counted; + zone.B = awbStats_[i].bSum / counted; + zones.push_back(zone); + } + } + } +} + +/* Translate the IPU3 statistics into the default statistics region array */ +void Awb::generateAwbStats(const ipu3_uapi_stats_3a *stats) +{ + uint32_t regionWidth = round(awbGrid_.width / static_cast(kAwbStatsSizeX)); + uint32_t regionHeight = round(awbGrid_.height / static_cast(kAwbStatsSizeY)); + + /* + * Generate a (kAwbStatsSizeX x kAwbStatsSizeY) array from the IPU3 grid which is + * (awbGrid_.width x awbGrid_.height). + */ + for (unsigned int j = 0; j < kAwbStatsSizeY * regionHeight; j++) { + for (unsigned int i = 0; i < kAwbStatsSizeX * regionWidth; i++) { + uint32_t cellPosition = j * awbGrid_.width + i; + uint32_t cellX = (cellPosition / regionWidth) % kAwbStatsSizeX; + uint32_t cellY = ((cellPosition / awbGrid_.width) / regionHeight) % kAwbStatsSizeY; + + uint32_t awbRegionPosition = cellY * kAwbStatsSizeX + cellX; + cellPosition *= 8; + + /* Cast the initial IPU3 structure to simplify the reading */ + Ipu3AwbCell *currentCell = reinterpret_cast(const_cast(&stats->awb_raw_buffer.meta_data[cellPosition])); + if (currentCell->satRatio == 0) { + /* The cell is not saturated, use the current cell */ + awbStats_[awbRegionPosition].counted++; + uint32_t greenValue = currentCell->greenRedAvg + currentCell->greenBlueAvg; + awbStats_[awbRegionPosition].gSum += greenValue / 2; + awbStats_[awbRegionPosition].rSum += currentCell->redAvg; + awbStats_[awbRegionPosition].bSum += currentCell->blueAvg; + } + } + } +} + +void Awb::clearAwbStats() +{ + for (unsigned int i = 0; i < kAwbStatsSizeX * kAwbStatsSizeY; i++) { + awbStats_[i].bSum = 0; + awbStats_[i].rSum = 0; + awbStats_[i].gSum = 0; + awbStats_[i].counted = 0; + awbStats_[i].uncounted = 0; + } +} + +void Awb::awbGreyWorld() +{ + LOG(IPU3Awb, Debug) << "Grey world AWB"; + /* + * Make a separate list of the derivatives for each of red and blue, so + * that we can sort them to exclude the extreme gains. We could + * consider some variations, such as normalising all the zones first, or + * doing an L2 average etc. + */ + std::vector &redDerivative(zones_); + std::vector blueDerivative(redDerivative); + std::sort(redDerivative.begin(), redDerivative.end(), + [](RGB const &a, RGB const &b) { + return a.G * b.R < b.G * a.R; + }); + std::sort(blueDerivative.begin(), blueDerivative.end(), + [](RGB const &a, RGB const &b) { + return a.G * b.B < b.G * a.B; + }); + + /* Average the middle half of the values. */ + int discard = redDerivative.size() / 4; + + RGB sumRed(0, 0, 0); + RGB sumBlue(0, 0, 0); + for (auto ri = redDerivative.begin() + discard, + bi = blueDerivative.begin() + discard; + ri != redDerivative.end() - discard; ri++, bi++) + sumRed += *ri, sumBlue += *bi; + + double redGain = sumRed.G / (sumRed.R + 1), + blueGain = sumBlue.G / (sumBlue.B + 1); + + /* Color temperature is not relevant in Grey world but still useful to estimate it :-) */ + asyncResults_.temperatureK = estimateCCT(sumRed.R, sumRed.G, sumBlue.B); + asyncResults_.redGain = redGain; + asyncResults_.greenGain = 1.0; + asyncResults_.blueGain = blueGain; +} + +void Awb::calculateWBGains(const ipu3_uapi_stats_3a *stats) +{ + ASSERT(stats->stats_3a_status.awb_en); + zones_.clear(); + clearAwbStats(); + generateAwbStats(stats); + generateZones(zones_); + LOG(IPU3Awb, Debug) << "Valid zones: " << zones_.size(); + if (zones_.size() > 10) { + awbGreyWorld(); + LOG(IPU3Awb, Debug) << "Gain found for red: " << asyncResults_.redGain + << " and for blue: " << asyncResults_.blueGain; + } +} + +void Awb::process(IPAContext &context, const ipu3_uapi_stats_3a *stats) +{ + calculateWBGains(stats); + + /* + * Gains are only recalculated if enough zones were detected. + * The results are cached, so if no results were calculated, we set the + * cached values from asyncResults_ here. + */ + context.frameContext.awb.gains.blue = asyncResults_.blueGain; + context.frameContext.awb.gains.green = asyncResults_.greenGain; + context.frameContext.awb.gains.red = asyncResults_.redGain; +} + +void Awb::prepare(IPAContext &context, ipu3_uapi_params *params) +{ + params->acc_param.awb.config.rgbs_thr_gr = 8191; + params->acc_param.awb.config.rgbs_thr_r = 8191; + params->acc_param.awb.config.rgbs_thr_gb = 8191; + params->acc_param.awb.config.rgbs_thr_b = IPU3_UAPI_AWB_RGBS_THR_B_INCL_SAT + | IPU3_UAPI_AWB_RGBS_THR_B_EN + | 8191; + + awbGrid_ = context.configuration.grid.bdsGrid; + + params->acc_param.awb.config.grid = context.configuration.grid.bdsGrid; + + /* + * Optical center is column start (respectively row start) of the + * region of interest minus its X center (respectively Y center). + * + * For the moment use BDS as a first approximation, but it should + * be calculated based on Shading (SHD) parameters. + */ + params->acc_param.bnr = imguCssBnrDefaults; + Size &bdsOutputSize = context.configuration.grid.bdsOutputSize; + params->acc_param.bnr.column_size = bdsOutputSize.width; + params->acc_param.bnr.opt_center.x_reset = awbGrid_.x_start - (bdsOutputSize.width / 2); + params->acc_param.bnr.opt_center.y_reset = awbGrid_.y_start - (bdsOutputSize.height / 2); + params->acc_param.bnr.opt_center_sqr.x_sqr_reset = params->acc_param.bnr.opt_center.x_reset + * params->acc_param.bnr.opt_center.x_reset; + params->acc_param.bnr.opt_center_sqr.y_sqr_reset = params->acc_param.bnr.opt_center.y_reset + * params->acc_param.bnr.opt_center.y_reset; + /* + * Green gains should not be touched and considered 1. + * Default is 16, so do not change it at all. + * 4096 is the value for a gain of 1.0 + */ + params->acc_param.bnr.wb_gains.gr = 16 * context.frameContext.awb.gains.green; + params->acc_param.bnr.wb_gains.r = 4096 * context.frameContext.awb.gains.red; + params->acc_param.bnr.wb_gains.b = 4096 * context.frameContext.awb.gains.blue; + params->acc_param.bnr.wb_gains.gb = 16 * context.frameContext.awb.gains.green; + + LOG(IPU3Awb, Debug) << "Color temperature estimated: " << asyncResults_.temperatureK; + + /* The CCM matrix may change when color temperature will be used */ + params->acc_param.ccm = imguCssCcmDefault; + + params->use.acc_awb = 1; + params->use.acc_bnr = 1; + params->use.acc_ccm = 1; +} + +} /* namespace ipa::ipu3::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/ipu3/algorithms/awb.h b/src/ipa/ipu3/algorithms/awb.h new file mode 100644 index 00000000..fac54e45 --- /dev/null +++ b/src/ipa/ipu3/algorithms/awb.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Ideas On Board + * + * awb.h - IPU3 AWB control algorithm + */ +#ifndef __LIBCAMERA_IPU3_ALGORITHMS_AWB_H__ +#define __LIBCAMERA_IPU3_ALGORITHMS_AWB_H__ + +#include + +#include + +#include + +#include "algorithm.h" + +namespace libcamera { + +namespace ipa::ipu3::algorithms { + +/* Region size for the statistics generation algorithm */ +static constexpr uint32_t kAwbStatsSizeX = 16; +static constexpr uint32_t kAwbStatsSizeY = 12; + +class Awb : public Algorithm +{ +public: + Awb(); + ~Awb(); + + void prepare(IPAContext &context, ipu3_uapi_params *params) override; + void process(IPAContext &context, const ipu3_uapi_stats_3a *stats) override; + + struct Ipu3AwbCell { + unsigned char greenRedAvg; + unsigned char redAvg; + unsigned char blueAvg; + unsigned char greenBlueAvg; + unsigned char satRatio; + unsigned char padding[3]; + } __attribute__((packed)); + + /* \todo Make these three structs available to all the ISPs ? */ + struct RGB { + RGB(double _R = 0, double _G = 0, double _B = 0) + : R(_R), G(_G), B(_B) + { + } + double R, G, B; + RGB &operator+=(RGB const &other) + { + R += other.R, G += other.G, B += other.B; + return *this; + } + }; + + struct IspStatsRegion { + unsigned int counted; + unsigned int uncounted; + unsigned long long rSum; + unsigned long long gSum; + unsigned long long bSum; + }; + + struct AwbStatus { + double temperatureK; + double redGain; + double greenGain; + double blueGain; + }; + +private: + void calculateWBGains(const ipu3_uapi_stats_3a *stats); + void generateZones(std::vector &zones); + void generateAwbStats(const ipu3_uapi_stats_3a *stats); + void clearAwbStats(); + void awbGreyWorld(); + uint32_t estimateCCT(double red, double green, double blue); + + struct ipu3_uapi_grid_config awbGrid_; + + std::vector zones_; + IspStatsRegion awbStats_[kAwbStatsSizeX * kAwbStatsSizeY]; + AwbStatus asyncResults_; +}; + +} /* namespace ipa::ipu3::algorithms */ + +} /* namespace libcamera*/ +#endif /* __LIBCAMERA_IPU3_ALGORITHMS_AWB_H__ */ diff --git a/src/ipa/ipu3/algorithms/meson.build b/src/ipa/ipu3/algorithms/meson.build index 71eedfa4..fa004df4 100644 --- a/src/ipa/ipu3/algorithms/meson.build +++ b/src/ipa/ipu3/algorithms/meson.build @@ -2,5 +2,6 @@ ipu3_ipa_algorithms = files([ 'algorithm.cpp', + 'awb.cpp', 'tone_mapping.cpp', ]) diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp index 2468e94a..5fc358fa 100644 --- a/src/ipa/ipu3/ipu3.cpp +++ b/src/ipa/ipu3/ipu3.cpp @@ -30,9 +30,9 @@ #include "libcamera/internal/mapped_framebuffer.h" #include "algorithms/algorithm.h" +#include "algorithms/awb.h" #include "algorithms/tone_mapping.h" #include "ipu3_agc.h" -#include "ipu3_awb.h" #include "libipa/camera_sensor_helper.h" /** @@ -187,8 +187,6 @@ private: uint32_t minGain_; uint32_t maxGain_; - /* Interface to the AWB algorithm */ - std::unique_ptr awbAlgo_; /* Interface to the AEC/AGC algorithm */ std::unique_ptr agcAlgo_; /* Interface to the Camera Helper */ @@ -270,6 +268,7 @@ int IPAIPU3::init(const IPASettings &settings, *ipaControls = ControlInfoMap(std::move(controls), controls::controls); /* Construct our Algorithms */ + algorithms_.push_back(std::make_unique()); algorithms_.push_back(std::make_unique()); return 0; @@ -387,7 +386,6 @@ int IPAIPU3::configure(const IPAConfigInfo &configInfo) return ret; } - awbAlgo_ = std::make_unique(); agcAlgo_ = std::make_unique(); agcAlgo_->configure(context_, configInfo); @@ -466,8 +464,6 @@ void IPAIPU3::fillParams(unsigned int frame, ipu3_uapi_params *params) for (auto const &algo : algorithms_) algo->prepare(context_, params); - awbAlgo_->prepare(context_, params); - IPU3Action op; op.op = ActionParamFilled; @@ -490,8 +486,6 @@ void IPAIPU3::parseStatistics(unsigned int frame, exposure_ = context_.frameContext.agc.exposure; gain_ = camHelper_->gainCode(context_.frameContext.agc.gain); - awbAlgo_->process(context_, stats); - setControls(frame); /* \todo Use VBlank value calculated from each frame exposure. */ diff --git a/src/ipa/ipu3/ipu3_awb.cpp b/src/ipa/ipu3/ipu3_awb.cpp deleted file mode 100644 index 728162fe..00000000 --- a/src/ipa/ipu3/ipu3_awb.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2021, Ideas On Board - * - * ipu3_awb.cpp - AWB control algorithm - */ -#include "ipu3_awb.h" - -#include -#include - -#include - -namespace libcamera { - -namespace ipa::ipu3 { - -LOG_DEFINE_CATEGORY(IPU3Awb) - -static constexpr uint32_t kMinZonesCounted = 16; -static constexpr uint32_t kMinGreenLevelInZone = 32; - -/** - * \struct IspStatsRegion - * \brief RGB statistics for a given region - * - * The IspStatsRegion structure is intended to abstract the ISP specific - * statistics and use an agnostic algorithm to compute AWB. - * - * \var IspStatsRegion::counted - * \brief Number of pixels used to calculate the sums - * - * \var IspStatsRegion::uncounted - * \brief Remaining number of pixels in the region - * - * \var IspStatsRegion::rSum - * \brief Sum of the red values in the region - * - * \var IspStatsRegion::gSum - * \brief Sum of the green values in the region - * - * \var IspStatsRegion::bSum - * \brief Sum of the blue values in the region - */ - -/** - * \struct AwbStatus - * \brief AWB parameters calculated - * - * The AwbStatus structure is intended to store the AWB - * parameters calculated by the algorithm - * - * \var AwbStatus::temperatureK - * \brief Color temperature calculated - * - * \var AwbStatus::redGain - * \brief Gain calculated for the red channel - * - * \var AwbStatus::greenGain - * \brief Gain calculated for the green channel - * - * \var AwbStatus::blueGain - * \brief Gain calculated for the blue channel - */ - -/** - * \struct Ipu3AwbCell - * \brief Memory layout for each cell in AWB metadata - * - * The Ipu3AwbCell structure is used to get individual values - * such as red average or saturation ratio in a particular cell. - * - * \var Ipu3AwbCell::greenRedAvg - * \brief Green average for red lines in the cell - * - * \var Ipu3AwbCell::redAvg - * \brief Red average in the cell - * - * \var Ipu3AwbCell::blueAvg - * \brief blue average in the cell - * - * \var Ipu3AwbCell::greenBlueAvg - * \brief Green average for blue lines - * - * \var Ipu3AwbCell::satRatio - * \brief Saturation ratio in the cell - * - * \var Ipu3AwbCell::padding - * \brief array of unused bytes for padding - */ - -/* Default settings for Bayer noise reduction replicated from the Kernel */ -static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = { - .wb_gains = { 16, 16, 16, 16 }, - .wb_gains_thr = { 255, 255, 255, 255 }, - .thr_coeffs = { 1700, 0, 31, 31, 0, 16 }, - .thr_ctrl_shd = { 26, 26, 26, 26 }, - .opt_center = { -648, 0, -366, 0 }, - .lut = { - { 17, 23, 28, 32, 36, 39, 42, 45, - 48, 51, 53, 55, 58, 60, 62, 64, - 66, 68, 70, 72, 73, 75, 77, 78, - 80, 82, 83, 85, 86, 88, 89, 90 } }, - .bp_ctrl = { 20, 0, 1, 40, 0, 6, 0, 6, 0 }, - .dn_detect_ctrl = { 9, 3, 4, 0, 8, 0, 1, 1, 1, 1, 0 }, - .column_size = 1296, - .opt_center_sqr = { 419904, 133956 }, -}; - -/* Default color correction matrix defined as an identity matrix */ -static const struct ipu3_uapi_ccm_mat_config imguCssCcmDefault = { - 8191, 0, 0, 0, - 0, 8191, 0, 0, - 0, 0, 8191, 0 -}; - -IPU3Awb::IPU3Awb() - : Algorithm() -{ - asyncResults_.blueGain = 1.0; - asyncResults_.greenGain = 1.0; - asyncResults_.redGain = 1.0; - asyncResults_.temperatureK = 4500; - - zones_.reserve(kAwbStatsSizeX * kAwbStatsSizeY); -} - -IPU3Awb::~IPU3Awb() = default; - -/** - * The function estimates the correlated color temperature using - * from RGB color space input. - * In physics and color science, the Planckian locus or black body locus is - * the path or locus that the color of an incandescent black body would take - * in a particular chromaticity space as the blackbody temperature changes. - * - * If a narrow range of color temperatures is considered (those encapsulating - * daylight being the most practical case) one can approximate the Planckian - * locus in order to calculate the CCT in terms of chromaticity coordinates. - * - * More detailed information can be found in: - * https://en.wikipedia.org/wiki/Color_temperature#Approximation - */ -uint32_t IPU3Awb::estimateCCT(double red, double green, double blue) -{ - /* Convert the RGB values to CIE tristimulus values (XYZ) */ - double X = (-0.14282) * (red) + (1.54924) * (green) + (-0.95641) * (blue); - double Y = (-0.32466) * (red) + (1.57837) * (green) + (-0.73191) * (blue); - double Z = (-0.68202) * (red) + (0.77073) * (green) + (0.56332) * (blue); - - /* Calculate the normalized chromaticity values */ - double x = X / (X + Y + Z); - double y = Y / (X + Y + Z); - - /* Calculate CCT */ - double n = (x - 0.3320) / (0.1858 - y); - return 449 * n * n * n + 3525 * n * n + 6823.3 * n + 5520.33; -} - -/* Generate an RGB vector with the average values for each region */ -void IPU3Awb::generateZones(std::vector &zones) -{ - for (unsigned int i = 0; i < kAwbStatsSizeX * kAwbStatsSizeY; i++) { - RGB zone; - double counted = awbStats_[i].counted; - if (counted >= kMinZonesCounted) { - zone.G = awbStats_[i].gSum / counted; - if (zone.G >= kMinGreenLevelInZone) { - zone.R = awbStats_[i].rSum / counted; - zone.B = awbStats_[i].bSum / counted; - zones.push_back(zone); - } - } - } -} - -/* Translate the IPU3 statistics into the default statistics region array */ -void IPU3Awb::generateAwbStats(const ipu3_uapi_stats_3a *stats) -{ - uint32_t regionWidth = round(awbGrid_.width / static_cast(kAwbStatsSizeX)); - uint32_t regionHeight = round(awbGrid_.height / static_cast(kAwbStatsSizeY)); - - /* - * Generate a (kAwbStatsSizeX x kAwbStatsSizeY) array from the IPU3 grid which is - * (awbGrid_.width x awbGrid_.height). - */ - for (unsigned int j = 0; j < kAwbStatsSizeY * regionHeight; j++) { - for (unsigned int i = 0; i < kAwbStatsSizeX * regionWidth; i++) { - uint32_t cellPosition = j * awbGrid_.width + i; - uint32_t cellX = (cellPosition / regionWidth) % kAwbStatsSizeX; - uint32_t cellY = ((cellPosition / awbGrid_.width) / regionHeight) % kAwbStatsSizeY; - - uint32_t awbRegionPosition = cellY * kAwbStatsSizeX + cellX; - cellPosition *= 8; - - /* Cast the initial IPU3 structure to simplify the reading */ - Ipu3AwbCell *currentCell = reinterpret_cast(const_cast(&stats->awb_raw_buffer.meta_data[cellPosition])); - if (currentCell->satRatio == 0) { - /* The cell is not saturated, use the current cell */ - awbStats_[awbRegionPosition].counted++; - uint32_t greenValue = currentCell->greenRedAvg + currentCell->greenBlueAvg; - awbStats_[awbRegionPosition].gSum += greenValue / 2; - awbStats_[awbRegionPosition].rSum += currentCell->redAvg; - awbStats_[awbRegionPosition].bSum += currentCell->blueAvg; - } - } - } -} - -void IPU3Awb::clearAwbStats() -{ - for (unsigned int i = 0; i < kAwbStatsSizeX * kAwbStatsSizeY; i++) { - awbStats_[i].bSum = 0; - awbStats_[i].rSum = 0; - awbStats_[i].gSum = 0; - awbStats_[i].counted = 0; - awbStats_[i].uncounted = 0; - } -} - -void IPU3Awb::awbGreyWorld() -{ - LOG(IPU3Awb, Debug) << "Grey world AWB"; - /* - * Make a separate list of the derivatives for each of red and blue, so - * that we can sort them to exclude the extreme gains. We could - * consider some variations, such as normalising all the zones first, or - * doing an L2 average etc. - */ - std::vector &redDerivative(zones_); - std::vector blueDerivative(redDerivative); - std::sort(redDerivative.begin(), redDerivative.end(), - [](RGB const &a, RGB const &b) { - return a.G * b.R < b.G * a.R; - }); - std::sort(blueDerivative.begin(), blueDerivative.end(), - [](RGB const &a, RGB const &b) { - return a.G * b.B < b.G * a.B; - }); - - /* Average the middle half of the values. */ - int discard = redDerivative.size() / 4; - - RGB sumRed(0, 0, 0); - RGB sumBlue(0, 0, 0); - for (auto ri = redDerivative.begin() + discard, - bi = blueDerivative.begin() + discard; - ri != redDerivative.end() - discard; ri++, bi++) - sumRed += *ri, sumBlue += *bi; - - double redGain = sumRed.G / (sumRed.R + 1), - blueGain = sumBlue.G / (sumBlue.B + 1); - - /* Color temperature is not relevant in Grey world but still useful to estimate it :-) */ - asyncResults_.temperatureK = estimateCCT(sumRed.R, sumRed.G, sumBlue.B); - asyncResults_.redGain = redGain; - asyncResults_.greenGain = 1.0; - asyncResults_.blueGain = blueGain; -} - -void IPU3Awb::calculateWBGains(const ipu3_uapi_stats_3a *stats) -{ - ASSERT(stats->stats_3a_status.awb_en); - zones_.clear(); - clearAwbStats(); - generateAwbStats(stats); - generateZones(zones_); - LOG(IPU3Awb, Debug) << "Valid zones: " << zones_.size(); - if (zones_.size() > 10) { - awbGreyWorld(); - LOG(IPU3Awb, Debug) << "Gain found for red: " << asyncResults_.redGain - << " and for blue: " << asyncResults_.blueGain; - } -} - -void IPU3Awb::process(IPAContext &context, const ipu3_uapi_stats_3a *stats) -{ - calculateWBGains(stats); - - /* - * Gains are only recalculated if enough zones were detected. - * The results are cached, so if no results were calculated, we set the - * cached values from asyncResults_ here. - */ - context.frameContext.awb.gains.blue = asyncResults_.blueGain; - context.frameContext.awb.gains.green = asyncResults_.greenGain; - context.frameContext.awb.gains.red = asyncResults_.redGain; -} - -void IPU3Awb::prepare(IPAContext &context, ipu3_uapi_params *params) -{ - params->acc_param.awb.config.rgbs_thr_gr = 8191; - params->acc_param.awb.config.rgbs_thr_r = 8191; - params->acc_param.awb.config.rgbs_thr_gb = 8191; - params->acc_param.awb.config.rgbs_thr_b = IPU3_UAPI_AWB_RGBS_THR_B_INCL_SAT - | IPU3_UAPI_AWB_RGBS_THR_B_EN - | 8191; - - awbGrid_ = context.configuration.grid.bdsGrid; - - params->acc_param.awb.config.grid = context.configuration.grid.bdsGrid; - - /* - * Optical center is column start (respectively row start) of the - * region of interest minus its X center (respectively Y center). - * - * For the moment use BDS as a first approximation, but it should - * be calculated based on Shading (SHD) parameters. - */ - params->acc_param.bnr = imguCssBnrDefaults; - Size &bdsOutputSize = context.configuration.grid.bdsOutputSize; - params->acc_param.bnr.column_size = bdsOutputSize.width; - params->acc_param.bnr.opt_center.x_reset = awbGrid_.x_start - (bdsOutputSize.width / 2); - params->acc_param.bnr.opt_center.y_reset = awbGrid_.y_start - (bdsOutputSize.height / 2); - params->acc_param.bnr.opt_center_sqr.x_sqr_reset = params->acc_param.bnr.opt_center.x_reset - * params->acc_param.bnr.opt_center.x_reset; - params->acc_param.bnr.opt_center_sqr.y_sqr_reset = params->acc_param.bnr.opt_center.y_reset - * params->acc_param.bnr.opt_center.y_reset; - /* - * Green gains should not be touched and considered 1. - * Default is 16, so do not change it at all. - * 4096 is the value for a gain of 1.0 - */ - params->acc_param.bnr.wb_gains.gr = 16 * context.frameContext.awb.gains.green; - params->acc_param.bnr.wb_gains.r = 4096 * context.frameContext.awb.gains.red; - params->acc_param.bnr.wb_gains.b = 4096 * context.frameContext.awb.gains.blue; - params->acc_param.bnr.wb_gains.gb = 16 * context.frameContext.awb.gains.green; - - LOG(IPU3Awb, Debug) << "Color temperature estimated: " << asyncResults_.temperatureK; - - /* The CCM matrix may change when color temperature will be used */ - params->acc_param.ccm = imguCssCcmDefault; - - params->use.acc_awb = 1; - params->use.acc_bnr = 1; - params->use.acc_ccm = 1; -} - -} /* namespace ipa::ipu3 */ - -} /* namespace libcamera */ diff --git a/src/ipa/ipu3/ipu3_awb.h b/src/ipa/ipu3/ipu3_awb.h deleted file mode 100644 index 12703489..00000000 --- a/src/ipa/ipu3/ipu3_awb.h +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2021, Ideas On Board - * - * ipu3_awb.h - IPU3 AWB control algorithm - */ -#ifndef __LIBCAMERA_IPU3_AWB_H__ -#define __LIBCAMERA_IPU3_AWB_H__ - -#include - -#include - -#include - -#include "algorithms/algorithm.h" - -namespace libcamera { - -namespace ipa::ipu3 { - -/* Region size for the statistics generation algorithm */ -static constexpr uint32_t kAwbStatsSizeX = 16; -static constexpr uint32_t kAwbStatsSizeY = 12; - -class IPU3Awb : public Algorithm -{ -public: - IPU3Awb(); - ~IPU3Awb(); - - void prepare(IPAContext &context, ipu3_uapi_params *params) override; - void process(IPAContext &context, const ipu3_uapi_stats_3a *stats) override; - - struct Ipu3AwbCell { - unsigned char greenRedAvg; - unsigned char redAvg; - unsigned char blueAvg; - unsigned char greenBlueAvg; - unsigned char satRatio; - unsigned char padding[3]; - } __attribute__((packed)); - - /* \todo Make these three structs available to all the ISPs ? */ - struct RGB { - RGB(double _R = 0, double _G = 0, double _B = 0) - : R(_R), G(_G), B(_B) - { - } - double R, G, B; - RGB &operator+=(RGB const &other) - { - R += other.R, G += other.G, B += other.B; - return *this; - } - }; - - struct IspStatsRegion { - unsigned int counted; - unsigned int uncounted; - unsigned long long rSum; - unsigned long long gSum; - unsigned long long bSum; - }; - - struct AwbStatus { - double temperatureK; - double redGain; - double greenGain; - double blueGain; - }; - -private: - void calculateWBGains(const ipu3_uapi_stats_3a *stats); - void generateZones(std::vector &zones); - void generateAwbStats(const ipu3_uapi_stats_3a *stats); - void clearAwbStats(); - void awbGreyWorld(); - uint32_t estimateCCT(double red, double green, double blue); - - struct ipu3_uapi_grid_config awbGrid_; - - std::vector zones_; - IspStatsRegion awbStats_[kAwbStatsSizeX * kAwbStatsSizeY]; - AwbStatus asyncResults_; -}; - -} /* namespace ipa::ipu3 */ - -} /* namespace libcamera*/ -#endif /* __LIBCAMERA_IPU3_AWB_H__ */ diff --git a/src/ipa/ipu3/meson.build b/src/ipa/ipu3/meson.build index fcb27d68..d1126947 100644 --- a/src/ipa/ipu3/meson.build +++ b/src/ipa/ipu3/meson.build @@ -7,7 +7,6 @@ ipa_name = 'ipa_ipu3' ipu3_ipa_sources = files([ 'ipu3.cpp', 'ipu3_agc.cpp', - 'ipu3_awb.cpp', ]) ipu3_ipa_sources += ipu3_ipa_algorithms -- cgit v1.2.1