ipa: mali-c55: Add Agc algorithm
Add a new algorithm and associated infrastructure for Agc. The tuning files for uncalibrated sensors is extended to enable the algorithm. Acked-by: Nayden Kanchev <nayden.kanchev@arm.com> Co-developed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
parent
93a33e7a0f
commit
e8cae247e8
6 changed files with 577 additions and 2 deletions
410
src/ipa/mali-c55/algorithms/agc.cpp
Normal file
410
src/ipa/mali-c55/algorithms/agc.cpp
Normal file
|
@ -0,0 +1,410 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024, Ideas On Board Oy
|
||||||
|
*
|
||||||
|
* agc.cpp - AGC/AEC mean-based control algorithm
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "agc.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <libcamera/base/log.h>
|
||||||
|
#include <libcamera/base/utils.h>
|
||||||
|
|
||||||
|
#include <libcamera/control_ids.h>
|
||||||
|
#include <libcamera/property_ids.h>
|
||||||
|
|
||||||
|
#include "libipa/colours.h"
|
||||||
|
#include "libipa/fixedpoint.h"
|
||||||
|
|
||||||
|
namespace libcamera {
|
||||||
|
|
||||||
|
using namespace std::literals::chrono_literals;
|
||||||
|
|
||||||
|
namespace ipa::mali_c55::algorithms {
|
||||||
|
|
||||||
|
LOG_DEFINE_CATEGORY(MaliC55Agc)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Number of histogram bins. This is only true for the specific configuration we
|
||||||
|
* set to the ISP; 4 separate histograms of 256 bins each. If that configuration
|
||||||
|
* ever changes then this constant will need updating.
|
||||||
|
*/
|
||||||
|
static constexpr unsigned int kNumHistogramBins = 256;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Mali-C55 ISP has a digital gain block which allows setting gain in Q5.8
|
||||||
|
* format, a range of 0.0 to (very nearly) 32.0. We clamp from 1.0 to the actual
|
||||||
|
* max value which is 8191 * 2^-8.
|
||||||
|
*/
|
||||||
|
static constexpr double kMinDigitalGain = 1.0;
|
||||||
|
static constexpr double kMaxDigitalGain = 31.99609375;
|
||||||
|
|
||||||
|
uint32_t AgcStatistics::decodeBinValue(uint16_t binVal)
|
||||||
|
{
|
||||||
|
int exponent = (binVal & 0xf000) >> 12;
|
||||||
|
int mantissa = binVal & 0xfff;
|
||||||
|
|
||||||
|
if (!exponent)
|
||||||
|
return mantissa * 2;
|
||||||
|
else
|
||||||
|
return (mantissa + 4096) * std::pow(2, exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We configure the ISP to give us 4 histograms of 256 bins each, with
|
||||||
|
* a single histogram per colour channel (R/Gr/Gb/B). The memory space
|
||||||
|
* containing the data is a single block containing all 4 histograms
|
||||||
|
* with the position of each colour's histogram within it dependent on
|
||||||
|
* the bayer pattern of the data input to the ISP.
|
||||||
|
*
|
||||||
|
* NOTE: The validity of this function depends on the parameters we have
|
||||||
|
* configured. With different skip/offset x, y values not all of the
|
||||||
|
* colour channels would be populated, and they may not be in the same
|
||||||
|
* planes as calculated here.
|
||||||
|
*/
|
||||||
|
int AgcStatistics::setBayerOrderIndices(BayerFormat::Order bayerOrder)
|
||||||
|
{
|
||||||
|
switch (bayerOrder) {
|
||||||
|
case BayerFormat::Order::RGGB:
|
||||||
|
rIndex_ = 0;
|
||||||
|
grIndex_ = 1;
|
||||||
|
gbIndex_ = 2;
|
||||||
|
bIndex_ = 3;
|
||||||
|
break;
|
||||||
|
case BayerFormat::Order::GRBG:
|
||||||
|
grIndex_ = 0;
|
||||||
|
rIndex_ = 1;
|
||||||
|
bIndex_ = 2;
|
||||||
|
gbIndex_ = 3;
|
||||||
|
break;
|
||||||
|
case BayerFormat::Order::GBRG:
|
||||||
|
gbIndex_ = 0;
|
||||||
|
bIndex_ = 1;
|
||||||
|
rIndex_ = 2;
|
||||||
|
grIndex_ = 3;
|
||||||
|
break;
|
||||||
|
case BayerFormat::Order::BGGR:
|
||||||
|
bIndex_ = 0;
|
||||||
|
gbIndex_ = 1;
|
||||||
|
grIndex_ = 2;
|
||||||
|
rIndex_ = 3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(MaliC55Agc, Error)
|
||||||
|
<< "Invalid bayer format " << bayerOrder;
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AgcStatistics::parseStatistics(const mali_c55_stats_buffer *stats)
|
||||||
|
{
|
||||||
|
uint32_t r[256], g[256], b[256], y[256];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to decode the bin values for each histogram from their 16-bit
|
||||||
|
* compressed values to a 32-bit value. We also take the average of the
|
||||||
|
* Gr/Gb values into a single green histogram.
|
||||||
|
*/
|
||||||
|
for (unsigned int i = 0; i < 256; i++) {
|
||||||
|
r[i] = decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * rIndex_)]);
|
||||||
|
g[i] = (decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * grIndex_)]) +
|
||||||
|
decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * gbIndex_)])) / 2;
|
||||||
|
b[i] = decodeBinValue(stats->ae_1024bin_hist.bins[i + (256 * bIndex_)]);
|
||||||
|
|
||||||
|
y[i] = rec601LuminanceFromRGB({ { static_cast<double>(r[i]),
|
||||||
|
static_cast<double>(g[i]),
|
||||||
|
static_cast<double>(b[i]) } });
|
||||||
|
}
|
||||||
|
|
||||||
|
rHist = Histogram(Span<uint32_t>(r, kNumHistogramBins));
|
||||||
|
gHist = Histogram(Span<uint32_t>(g, kNumHistogramBins));
|
||||||
|
bHist = Histogram(Span<uint32_t>(b, kNumHistogramBins));
|
||||||
|
yHist = Histogram(Span<uint32_t>(y, kNumHistogramBins));
|
||||||
|
}
|
||||||
|
|
||||||
|
Agc::Agc()
|
||||||
|
: AgcMeanLuminance()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int Agc::init(IPAContext &context, const YamlObject &tuningData)
|
||||||
|
{
|
||||||
|
int ret = parseTuningData(tuningData);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
context.ctrlMap[&controls::AeEnable] = ControlInfo(false, true);
|
||||||
|
context.ctrlMap[&controls::DigitalGain] = ControlInfo(
|
||||||
|
static_cast<float>(kMinDigitalGain),
|
||||||
|
static_cast<float>(kMaxDigitalGain),
|
||||||
|
static_cast<float>(kMinDigitalGain)
|
||||||
|
);
|
||||||
|
context.ctrlMap.merge(controls());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Agc::configure(IPAContext &context,
|
||||||
|
[[maybe_unused]] const IPACameraSensorInfo &configInfo)
|
||||||
|
{
|
||||||
|
int ret = statistics_.setBayerOrderIndices(context.configuration.sensor.bayerOrder);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Defaults; we use whatever the sensor's default exposure is and the
|
||||||
|
* minimum analogue gain. AEGC is _active_ by default.
|
||||||
|
*/
|
||||||
|
context.activeState.agc.autoEnabled = true;
|
||||||
|
context.activeState.agc.automatic.sensorGain = context.configuration.agc.minAnalogueGain;
|
||||||
|
context.activeState.agc.automatic.exposure = context.configuration.agc.defaultExposure;
|
||||||
|
context.activeState.agc.automatic.ispGain = kMinDigitalGain;
|
||||||
|
context.activeState.agc.manual.sensorGain = context.configuration.agc.minAnalogueGain;
|
||||||
|
context.activeState.agc.manual.exposure = context.configuration.agc.defaultExposure;
|
||||||
|
context.activeState.agc.manual.ispGain = kMinDigitalGain;
|
||||||
|
context.activeState.agc.constraintMode = constraintModes().begin()->first;
|
||||||
|
context.activeState.agc.exposureMode = exposureModeHelpers().begin()->first;
|
||||||
|
|
||||||
|
/* \todo Run this again when FrameDurationLimits is passed in */
|
||||||
|
setLimits(context.configuration.agc.minShutterSpeed,
|
||||||
|
context.configuration.agc.maxShutterSpeed,
|
||||||
|
context.configuration.agc.minAnalogueGain,
|
||||||
|
context.configuration.agc.maxAnalogueGain);
|
||||||
|
|
||||||
|
resetFrameCount();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Agc::queueRequest(IPAContext &context, const uint32_t frame,
|
||||||
|
[[maybe_unused]] IPAFrameContext &frameContext,
|
||||||
|
const ControlList &controls)
|
||||||
|
{
|
||||||
|
auto &agc = context.activeState.agc;
|
||||||
|
|
||||||
|
const auto &constraintMode = controls.get(controls::AeConstraintMode);
|
||||||
|
agc.constraintMode = constraintMode.value_or(agc.constraintMode);
|
||||||
|
|
||||||
|
const auto &exposureMode = controls.get(controls::AeExposureMode);
|
||||||
|
agc.exposureMode = exposureMode.value_or(agc.exposureMode);
|
||||||
|
|
||||||
|
const auto &agcEnable = controls.get(controls::AeEnable);
|
||||||
|
if (agcEnable && *agcEnable != agc.autoEnabled) {
|
||||||
|
agc.autoEnabled = *agcEnable;
|
||||||
|
|
||||||
|
LOG(MaliC55Agc, Info)
|
||||||
|
<< (agc.autoEnabled ? "Enabling" : "Disabling")
|
||||||
|
<< " AGC";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the automatic exposure and gain is enabled we have no further work
|
||||||
|
* to do here...
|
||||||
|
*/
|
||||||
|
if (agc.autoEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ...otherwise we need to look for exposure and gain controls and use
|
||||||
|
* those to set the activeState.
|
||||||
|
*/
|
||||||
|
const auto &exposure = controls.get(controls::ExposureTime);
|
||||||
|
if (exposure) {
|
||||||
|
agc.manual.exposure = *exposure * 1.0us / context.configuration.sensor.lineDuration;
|
||||||
|
|
||||||
|
LOG(MaliC55Agc, Debug)
|
||||||
|
<< "Exposure set to " << agc.manual.exposure
|
||||||
|
<< " on request sequence " << frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &analogueGain = controls.get(controls::AnalogueGain);
|
||||||
|
if (analogueGain) {
|
||||||
|
agc.manual.sensorGain = *analogueGain;
|
||||||
|
|
||||||
|
LOG(MaliC55Agc, Debug)
|
||||||
|
<< "Analogue gain set to " << agc.manual.sensorGain
|
||||||
|
<< " on request sequence " << frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &digitalGain = controls.get(controls::DigitalGain);
|
||||||
|
if (digitalGain) {
|
||||||
|
agc.manual.ispGain = *digitalGain;
|
||||||
|
|
||||||
|
LOG(MaliC55Agc, Debug)
|
||||||
|
<< "Digital gain set to " << agc.manual.ispGain
|
||||||
|
<< " on request sequence " << frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Agc::fillGainParamBlock(IPAContext &context, IPAFrameContext &frameContext,
|
||||||
|
mali_c55_params_block block)
|
||||||
|
{
|
||||||
|
IPAActiveState &activeState = context.activeState;
|
||||||
|
double gain;
|
||||||
|
|
||||||
|
if (activeState.agc.autoEnabled)
|
||||||
|
gain = activeState.agc.automatic.ispGain;
|
||||||
|
else
|
||||||
|
gain = activeState.agc.manual.ispGain;
|
||||||
|
|
||||||
|
block.header->type = MALI_C55_PARAM_BLOCK_DIGITAL_GAIN;
|
||||||
|
block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
|
||||||
|
block.header->size = sizeof(struct mali_c55_params_digital_gain);
|
||||||
|
|
||||||
|
block.digital_gain->gain = floatingToFixedPoint<5, 8, uint16_t, double>(gain);
|
||||||
|
frameContext.agc.ispGain = gain;
|
||||||
|
|
||||||
|
return block.header->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Agc::fillParamsBuffer(mali_c55_params_block block,
|
||||||
|
enum mali_c55_param_block_type type)
|
||||||
|
{
|
||||||
|
block.header->type = type;
|
||||||
|
block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
|
||||||
|
block.header->size = sizeof(struct mali_c55_params_aexp_hist);
|
||||||
|
|
||||||
|
/* Collect every 3rd pixel horizontally */
|
||||||
|
block.aexp_hist->skip_x = 1;
|
||||||
|
/* Start from first column */
|
||||||
|
block.aexp_hist->offset_x = 0;
|
||||||
|
/* Collect every pixel vertically */
|
||||||
|
block.aexp_hist->skip_y = 0;
|
||||||
|
/* Start from the first row */
|
||||||
|
block.aexp_hist->offset_y = 0;
|
||||||
|
/* 1x scaling (i.e. none) */
|
||||||
|
block.aexp_hist->scale_bottom = 0;
|
||||||
|
block.aexp_hist->scale_top = 0;
|
||||||
|
/* Collect all Bayer planes into 4 separate histograms */
|
||||||
|
block.aexp_hist->plane_mode = 1;
|
||||||
|
/* Tap the data immediately after the digital gain block */
|
||||||
|
block.aexp_hist->tap_point = MALI_C55_AEXP_HIST_TAP_FS;
|
||||||
|
|
||||||
|
return block.header->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Agc::fillWeightsArrayBuffer(mali_c55_params_block block,
|
||||||
|
enum mali_c55_param_block_type type)
|
||||||
|
{
|
||||||
|
block.header->type = type;
|
||||||
|
block.header->flags = MALI_C55_PARAM_BLOCK_FL_NONE;
|
||||||
|
block.header->size = sizeof(struct mali_c55_params_aexp_weights);
|
||||||
|
|
||||||
|
/* We use every zone - a 15x15 grid */
|
||||||
|
block.aexp_weights->nodes_used_horiz = 15;
|
||||||
|
block.aexp_weights->nodes_used_vert = 15;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We uniformly weight the zones to 1 - this results in the collected
|
||||||
|
* histograms containing a true pixel count, which we can then use to
|
||||||
|
* approximate colour channel averages for the image.
|
||||||
|
*/
|
||||||
|
Span<uint8_t> weights{
|
||||||
|
block.aexp_weights->zone_weights,
|
||||||
|
MALI_C55_MAX_ZONES
|
||||||
|
};
|
||||||
|
std::fill(weights.begin(), weights.end(), 1);
|
||||||
|
|
||||||
|
return block.header->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Agc::prepare(IPAContext &context, const uint32_t frame,
|
||||||
|
IPAFrameContext &frameContext, mali_c55_params_buffer *params)
|
||||||
|
{
|
||||||
|
mali_c55_params_block block;
|
||||||
|
|
||||||
|
block.data = ¶ms->data[params->total_size];
|
||||||
|
params->total_size += fillGainParamBlock(context, frameContext, block);
|
||||||
|
|
||||||
|
if (frame > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
block.data = ¶ms->data[params->total_size];
|
||||||
|
params->total_size += fillParamsBuffer(block,
|
||||||
|
MALI_C55_PARAM_BLOCK_AEXP_HIST);
|
||||||
|
|
||||||
|
block.data = ¶ms->data[params->total_size];
|
||||||
|
params->total_size += fillWeightsArrayBuffer(block,
|
||||||
|
MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS);
|
||||||
|
|
||||||
|
block.data = ¶ms->data[params->total_size];
|
||||||
|
params->total_size += fillParamsBuffer(block,
|
||||||
|
MALI_C55_PARAM_BLOCK_AEXP_IHIST);
|
||||||
|
|
||||||
|
block.data = ¶ms->data[params->total_size];
|
||||||
|
params->total_size += fillWeightsArrayBuffer(block,
|
||||||
|
MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
double Agc::estimateLuminance(const double gain) const
|
||||||
|
{
|
||||||
|
double rAvg = statistics_.rHist.interQuantileMean(0, 1) * gain;
|
||||||
|
double gAvg = statistics_.gHist.interQuantileMean(0, 1) * gain;
|
||||||
|
double bAvg = statistics_.bHist.interQuantileMean(0, 1) * gain;
|
||||||
|
double yAvg = rec601LuminanceFromRGB({ { rAvg, gAvg, bAvg } });
|
||||||
|
|
||||||
|
return yAvg / kNumHistogramBins;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Agc::process(IPAContext &context,
|
||||||
|
[[maybe_unused]] const uint32_t frame,
|
||||||
|
IPAFrameContext &frameContext,
|
||||||
|
const mali_c55_stats_buffer *stats,
|
||||||
|
[[maybe_unused]] ControlList &metadata)
|
||||||
|
{
|
||||||
|
IPASessionConfiguration &configuration = context.configuration;
|
||||||
|
IPAActiveState &activeState = context.activeState;
|
||||||
|
|
||||||
|
if (!stats) {
|
||||||
|
LOG(MaliC55Agc, Error) << "No statistics buffer passed to Agc";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
statistics_.parseStatistics(stats);
|
||||||
|
context.activeState.agc.temperatureK = estimateCCT({ { statistics_.rHist.interQuantileMean(0, 1),
|
||||||
|
statistics_.gHist.interQuantileMean(0, 1),
|
||||||
|
statistics_.bHist.interQuantileMean(0, 1) } });
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Agc algorithm needs to know the effective exposure value that was
|
||||||
|
* applied to the sensor when the statistics were collected.
|
||||||
|
*/
|
||||||
|
uint32_t exposure = frameContext.agc.exposure;
|
||||||
|
double analogueGain = frameContext.agc.sensorGain;
|
||||||
|
double digitalGain = frameContext.agc.ispGain;
|
||||||
|
double totalGain = analogueGain * digitalGain;
|
||||||
|
utils::Duration currentShutter = exposure * configuration.sensor.lineDuration;
|
||||||
|
utils::Duration effectiveExposureValue = currentShutter * totalGain;
|
||||||
|
|
||||||
|
utils::Duration shutterTime;
|
||||||
|
double aGain, dGain;
|
||||||
|
std::tie(shutterTime, aGain, dGain) =
|
||||||
|
calculateNewEv(activeState.agc.constraintMode,
|
||||||
|
activeState.agc.exposureMode, statistics_.yHist,
|
||||||
|
effectiveExposureValue);
|
||||||
|
|
||||||
|
dGain = std::clamp(dGain, kMinDigitalGain, kMaxDigitalGain);
|
||||||
|
|
||||||
|
LOG(MaliC55Agc, Debug)
|
||||||
|
<< "Divided up shutter, analogue gain and digital gain are "
|
||||||
|
<< shutterTime << ", " << aGain << " and " << dGain;
|
||||||
|
|
||||||
|
activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration;
|
||||||
|
activeState.agc.automatic.sensorGain = aGain;
|
||||||
|
activeState.agc.automatic.ispGain = dGain;
|
||||||
|
|
||||||
|
metadata.set(controls::ExposureTime, currentShutter.get<std::micro>());
|
||||||
|
metadata.set(controls::AnalogueGain, frameContext.agc.sensorGain);
|
||||||
|
metadata.set(controls::DigitalGain, frameContext.agc.ispGain);
|
||||||
|
metadata.set(controls::ColourTemperature, context.activeState.agc.temperatureK);
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_IPA_ALGORITHM(Agc, "Agc")
|
||||||
|
|
||||||
|
} /* namespace ipa::mali_c55::algorithms */
|
||||||
|
|
||||||
|
} /* namespace libcamera */
|
81
src/ipa/mali-c55/algorithms/agc.h
Normal file
81
src/ipa/mali-c55/algorithms/agc.h
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023, Ideas on Board Oy
|
||||||
|
*
|
||||||
|
* agc.h - Mali C55 AGC/AEC mean-based control algorithm
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libcamera/base/utils.h>
|
||||||
|
|
||||||
|
#include "libcamera/internal/bayer_format.h"
|
||||||
|
|
||||||
|
#include "libipa/agc_mean_luminance.h"
|
||||||
|
#include "libipa/histogram.h"
|
||||||
|
|
||||||
|
#include "algorithm.h"
|
||||||
|
#include "ipa_context.h"
|
||||||
|
|
||||||
|
namespace libcamera {
|
||||||
|
|
||||||
|
namespace ipa::mali_c55::algorithms {
|
||||||
|
|
||||||
|
class AgcStatistics
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AgcStatistics()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int setBayerOrderIndices(BayerFormat::Order bayerOrder);
|
||||||
|
uint32_t decodeBinValue(uint16_t binVal);
|
||||||
|
void parseStatistics(const mali_c55_stats_buffer *stats);
|
||||||
|
|
||||||
|
Histogram rHist;
|
||||||
|
Histogram gHist;
|
||||||
|
Histogram bHist;
|
||||||
|
Histogram yHist;
|
||||||
|
private:
|
||||||
|
unsigned int rIndex_;
|
||||||
|
unsigned int grIndex_;
|
||||||
|
unsigned int gbIndex_;
|
||||||
|
unsigned int bIndex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Agc : public Algorithm, public AgcMeanLuminance
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Agc();
|
||||||
|
~Agc() = default;
|
||||||
|
|
||||||
|
int init(IPAContext &context, const YamlObject &tuningData) override;
|
||||||
|
int configure(IPAContext &context,
|
||||||
|
const IPACameraSensorInfo &configInfo) override;
|
||||||
|
void queueRequest(IPAContext &context, const uint32_t frame,
|
||||||
|
IPAFrameContext &frameContext,
|
||||||
|
const ControlList &controls) override;
|
||||||
|
void prepare(IPAContext &context, const uint32_t frame,
|
||||||
|
IPAFrameContext &frameContext,
|
||||||
|
mali_c55_params_buffer *params) override;
|
||||||
|
void process(IPAContext &context, const uint32_t frame,
|
||||||
|
IPAFrameContext &frameContext,
|
||||||
|
const mali_c55_stats_buffer *stats,
|
||||||
|
ControlList &metadata) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
double estimateLuminance(const double gain) const override;
|
||||||
|
size_t fillGainParamBlock(IPAContext &context,
|
||||||
|
IPAFrameContext &frameContext,
|
||||||
|
mali_c55_params_block block);
|
||||||
|
size_t fillParamsBuffer(mali_c55_params_block block,
|
||||||
|
enum mali_c55_param_block_type type);
|
||||||
|
size_t fillWeightsArrayBuffer(mali_c55_params_block block,
|
||||||
|
enum mali_c55_param_block_type type);
|
||||||
|
|
||||||
|
AgcStatistics statistics_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace ipa::mali_c55::algorithms */
|
||||||
|
|
||||||
|
} /* namespace libcamera */
|
|
@ -1,4 +1,5 @@
|
||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
mali_c55_ipa_algorithms = files([
|
mali_c55_ipa_algorithms = files([
|
||||||
|
'agc.cpp',
|
||||||
])
|
])
|
||||||
|
|
|
@ -3,4 +3,5 @@
|
||||||
---
|
---
|
||||||
version: 1
|
version: 1
|
||||||
algorithms:
|
algorithms:
|
||||||
|
- Agc:
|
||||||
...
|
...
|
||||||
|
|
|
@ -7,8 +7,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libcamera/base/utils.h>
|
||||||
#include <libcamera/controls.h>
|
#include <libcamera/controls.h>
|
||||||
|
|
||||||
|
#include "libcamera/internal/bayer_format.h"
|
||||||
|
|
||||||
#include <libipa/fc_queue.h>
|
#include <libipa/fc_queue.h>
|
||||||
|
|
||||||
namespace libcamera {
|
namespace libcamera {
|
||||||
|
@ -16,15 +19,44 @@ namespace libcamera {
|
||||||
namespace ipa::mali_c55 {
|
namespace ipa::mali_c55 {
|
||||||
|
|
||||||
struct IPASessionConfiguration {
|
struct IPASessionConfiguration {
|
||||||
|
struct {
|
||||||
|
utils::Duration minShutterSpeed;
|
||||||
|
utils::Duration maxShutterSpeed;
|
||||||
|
uint32_t defaultExposure;
|
||||||
|
double minAnalogueGain;
|
||||||
|
double maxAnalogueGain;
|
||||||
|
} agc;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
BayerFormat::Order bayerOrder;
|
||||||
|
utils::Duration lineDuration;
|
||||||
|
} sensor;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IPAActiveState {
|
struct IPAActiveState {
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
uint32_t exposure;
|
||||||
|
double sensorGain;
|
||||||
|
double ispGain;
|
||||||
|
} automatic;
|
||||||
|
struct {
|
||||||
|
uint32_t exposure;
|
||||||
|
double sensorGain;
|
||||||
|
double ispGain;
|
||||||
|
} manual;
|
||||||
|
bool autoEnabled;
|
||||||
|
uint32_t constraintMode;
|
||||||
|
uint32_t exposureMode;
|
||||||
|
uint32_t temperatureK;
|
||||||
|
} agc;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IPAFrameContext : public FrameContext {
|
struct IPAFrameContext : public FrameContext {
|
||||||
struct {
|
struct {
|
||||||
uint32_t exposure;
|
uint32_t exposure;
|
||||||
double sensorGain;
|
double sensorGain;
|
||||||
|
double ispGain;
|
||||||
} agc;
|
} agc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ namespace libcamera {
|
||||||
|
|
||||||
LOG_DEFINE_CATEGORY(IPAMaliC55)
|
LOG_DEFINE_CATEGORY(IPAMaliC55)
|
||||||
|
|
||||||
|
using namespace std::literals::chrono_literals;
|
||||||
|
|
||||||
namespace ipa::mali_c55 {
|
namespace ipa::mali_c55 {
|
||||||
|
|
||||||
/* Maximum number of frame contexts to be held */
|
/* Maximum number of frame contexts to be held */
|
||||||
|
@ -60,6 +62,9 @@ protected:
|
||||||
std::string logPrefix() const override;
|
std::string logPrefix() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void updateSessionConfiguration(const IPACameraSensorInfo &info,
|
||||||
|
const ControlInfoMap &sensorControls,
|
||||||
|
BayerFormat::Order bayerOrder);
|
||||||
void updateControls(const IPACameraSensorInfo &sensorInfo,
|
void updateControls(const IPACameraSensorInfo &sensorInfo,
|
||||||
const ControlInfoMap &sensorControls,
|
const ControlInfoMap &sensorControls,
|
||||||
ControlInfoMap *ipaControls);
|
ControlInfoMap *ipaControls);
|
||||||
|
@ -131,7 +136,21 @@ int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig
|
||||||
|
|
||||||
void IPAMaliC55::setControls()
|
void IPAMaliC55::setControls()
|
||||||
{
|
{
|
||||||
|
IPAActiveState &activeState = context_.activeState;
|
||||||
|
uint32_t exposure;
|
||||||
|
uint32_t gain;
|
||||||
|
|
||||||
|
if (activeState.agc.autoEnabled) {
|
||||||
|
exposure = activeState.agc.automatic.exposure;
|
||||||
|
gain = camHelper_->gainCode(activeState.agc.automatic.sensorGain);
|
||||||
|
} else {
|
||||||
|
exposure = activeState.agc.manual.exposure;
|
||||||
|
gain = camHelper_->gainCode(activeState.agc.manual.sensorGain);
|
||||||
|
}
|
||||||
|
|
||||||
ControlList ctrls(sensorControls_);
|
ControlList ctrls(sensorControls_);
|
||||||
|
ctrls.set(V4L2_CID_EXPOSURE, static_cast<int32_t>(exposure));
|
||||||
|
ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast<int32_t>(gain));
|
||||||
|
|
||||||
setSensorControls.emit(ctrls);
|
setSensorControls.emit(ctrls);
|
||||||
}
|
}
|
||||||
|
@ -146,6 +165,36 @@ void IPAMaliC55::stop()
|
||||||
context_.frameContexts.clear();
|
context_.frameContexts.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IPAMaliC55::updateSessionConfiguration(const IPACameraSensorInfo &info,
|
||||||
|
const ControlInfoMap &sensorControls,
|
||||||
|
BayerFormat::Order bayerOrder)
|
||||||
|
{
|
||||||
|
context_.configuration.sensor.bayerOrder = bayerOrder;
|
||||||
|
|
||||||
|
const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;
|
||||||
|
int32_t minExposure = v4l2Exposure.min().get<int32_t>();
|
||||||
|
int32_t maxExposure = v4l2Exposure.max().get<int32_t>();
|
||||||
|
int32_t defExposure = v4l2Exposure.def().get<int32_t>();
|
||||||
|
|
||||||
|
const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;
|
||||||
|
int32_t minGain = v4l2Gain.min().get<int32_t>();
|
||||||
|
int32_t maxGain = v4l2Gain.max().get<int32_t>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the AGC computes the new exposure values for a frame, it needs
|
||||||
|
* to know the limits for shutter speed and analogue gain.
|
||||||
|
* As it depends on the sensor, update it with the controls.
|
||||||
|
*
|
||||||
|
* \todo take VBLANK into account for maximum shutter speed
|
||||||
|
*/
|
||||||
|
context_.configuration.sensor.lineDuration = info.minLineLength * 1.0s / info.pixelRate;
|
||||||
|
context_.configuration.agc.minShutterSpeed = minExposure * context_.configuration.sensor.lineDuration;
|
||||||
|
context_.configuration.agc.maxShutterSpeed = maxExposure * context_.configuration.sensor.lineDuration;
|
||||||
|
context_.configuration.agc.defaultExposure = defExposure;
|
||||||
|
context_.configuration.agc.minAnalogueGain = camHelper_->gain(minGain);
|
||||||
|
context_.configuration.agc.maxAnalogueGain = camHelper_->gain(maxGain);
|
||||||
|
}
|
||||||
|
|
||||||
void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo,
|
void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo,
|
||||||
const ControlInfoMap &sensorControls,
|
const ControlInfoMap &sensorControls,
|
||||||
ControlInfoMap *ipaControls)
|
ControlInfoMap *ipaControls)
|
||||||
|
@ -207,8 +256,7 @@ void IPAMaliC55::updateControls(const IPACameraSensorInfo &sensorInfo,
|
||||||
*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
|
*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
|
||||||
}
|
}
|
||||||
|
|
||||||
int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig,
|
int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,
|
||||||
[[maybe_unused]] uint8_t bayerOrder,
|
|
||||||
ControlInfoMap *ipaControls)
|
ControlInfoMap *ipaControls)
|
||||||
{
|
{
|
||||||
sensorControls_ = ipaConfig.sensorControls;
|
sensorControls_ = ipaConfig.sensorControls;
|
||||||
|
@ -220,6 +268,8 @@ int IPAMaliC55::configure(const IPAConfigInfo &ipaConfig,
|
||||||
|
|
||||||
const IPACameraSensorInfo &info = ipaConfig.sensorInfo;
|
const IPACameraSensorInfo &info = ipaConfig.sensorInfo;
|
||||||
|
|
||||||
|
updateSessionConfiguration(info, ipaConfig.sensorControls,
|
||||||
|
static_cast<BayerFormat::Order>(bayerOrder));
|
||||||
updateControls(info, ipaConfig.sensorControls, ipaControls);
|
updateControls(info, ipaConfig.sensorControls, ipaControls);
|
||||||
|
|
||||||
for (auto const &a : algorithms()) {
|
for (auto const &a : algorithms()) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue