ipa: raspberrypi: Use the generic statistics structure in the algorithms
Repurpose the StatisticsPtr type from being a shared_ptr<bcm2835_isp_stats> to shared_ptr<RPiController::Statistics>. This removes any hardware specific header files and structures from the algorithms source code. Add a new function in the Raspberry Pi IPA to populate the generic statistics structure from the values provided by the hardware in the bcm2835_isp_stats structure. Update the Lux, AWB, AGC, ALSC, Contrast, and Focus algorithms to use the generic statistics structure appropriately in their calculations. Additionally, remove references to any hardware specific headers and defines in these source files. Signed-off-by: Naushir Patuck <naush@raspberrypi.com> Reviewed-by: David Plowman <david.plowman@raspberrypi.com> Tested-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
parent
e8dd0fdc83
commit
6d60f264d1
15 changed files with 121 additions and 87 deletions
|
@ -78,14 +78,14 @@ private:
|
||||||
bool parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp);
|
bool parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp);
|
||||||
void putAGCStatistics(StatisticsPtr stats);
|
void putAGCStatistics(StatisticsPtr stats);
|
||||||
|
|
||||||
uint32_t aeHistLinear_[128];
|
Histogram aeHistLinear_;
|
||||||
uint32_t aeHistAverage_;
|
uint32_t aeHistAverage_;
|
||||||
bool aeHistValid_;
|
bool aeHistValid_;
|
||||||
};
|
};
|
||||||
|
|
||||||
CamHelperImx708::CamHelperImx708()
|
CamHelperImx708::CamHelperImx708()
|
||||||
: CamHelper(std::make_unique<MdParserSmia>(registerList), frameIntegrationDiff),
|
: CamHelper(std::make_unique<MdParserSmia>(registerList), frameIntegrationDiff),
|
||||||
aeHistLinear_{ 0 }, aeHistAverage_(0), aeHistValid_(false)
|
aeHistLinear_{}, aeHistAverage_(0), aeHistValid_(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,9 +264,11 @@ bool CamHelperImx708::parsePdafData(const uint8_t *ptr, size_t len,
|
||||||
|
|
||||||
bool CamHelperImx708::parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp)
|
bool CamHelperImx708::parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp)
|
||||||
{
|
{
|
||||||
static const uint32_t ISP_PIPELINE_BITS = 13;
|
static constexpr unsigned int PipelineBits = Statistics::NormalisationFactorPow2;
|
||||||
|
|
||||||
uint64_t count = 0, sum = 0;
|
uint64_t count = 0, sum = 0;
|
||||||
size_t step = bpp >> 1; /* bytes per histogram bin */
|
size_t step = bpp >> 1; /* bytes per histogram bin */
|
||||||
|
uint32_t hist[128];
|
||||||
|
|
||||||
if (len < 144 * step)
|
if (len < 144 * step)
|
||||||
return false;
|
return false;
|
||||||
|
@ -280,12 +282,12 @@ bool CamHelperImx708::parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp)
|
||||||
if (ptr[3] != 0x55)
|
if (ptr[3] != 0x55)
|
||||||
return false;
|
return false;
|
||||||
uint32_t c = (ptr[0] << 14) + (ptr[1] << 6) + (ptr[2] >> 2);
|
uint32_t c = (ptr[0] << 14) + (ptr[1] << 6) + (ptr[2] >> 2);
|
||||||
aeHistLinear_[i] = c >> 2; /* pixels to quads */
|
hist[i] = c >> 2; /* pixels to quads */
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
count += c;
|
count += c;
|
||||||
sum += c *
|
sum += c *
|
||||||
(i * (1u << (ISP_PIPELINE_BITS - 7)) +
|
(i * (1u << (PipelineBits - 7)) +
|
||||||
(1u << (ISP_PIPELINE_BITS - 8)));
|
(1u << (PipelineBits - 8)));
|
||||||
}
|
}
|
||||||
ptr += step;
|
ptr += step;
|
||||||
}
|
}
|
||||||
|
@ -301,15 +303,16 @@ bool CamHelperImx708::parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp)
|
||||||
uint32_t c = (ptr[0] << 14) + (ptr[1] << 6) + (ptr[2] >> 2);
|
uint32_t c = (ptr[0] << 14) + (ptr[1] << 6) + (ptr[2] >> 2);
|
||||||
count += c;
|
count += c;
|
||||||
sum += c *
|
sum += c *
|
||||||
((3u << ISP_PIPELINE_BITS) >> (17 - i));
|
((3u << PipelineBits) >> (17 - i));
|
||||||
ptr += step;
|
ptr += step;
|
||||||
}
|
}
|
||||||
if ((unsigned)((ptr[0] << 12) + (ptr[1] << 4) + (ptr[2] >> 4)) !=
|
if ((unsigned)((ptr[0] << 12) + (ptr[1] << 4) + (ptr[2] >> 4)) !=
|
||||||
aeHistLinear_[1]) {
|
hist[1]) {
|
||||||
LOG(IPARPI, Error) << "Lin/Log histogram mismatch";
|
LOG(IPARPI, Error) << "Lin/Log histogram mismatch";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aeHistLinear_ = Histogram(hist, 128);
|
||||||
aeHistAverage_ = count ? (sum / count) : 0;
|
aeHistAverage_ = count ? (sum / count) : 0;
|
||||||
|
|
||||||
return count != 0;
|
return count != 0;
|
||||||
|
@ -329,13 +332,12 @@ void CamHelperImx708::putAGCStatistics(StatisticsPtr stats)
|
||||||
* scaled by a fiddle-factor so that a conventional (non-HDR) y_target
|
* scaled by a fiddle-factor so that a conventional (non-HDR) y_target
|
||||||
* of e.g. 0.17 will map to a suitable level for HDR.
|
* of e.g. 0.17 will map to a suitable level for HDR.
|
||||||
*/
|
*/
|
||||||
memcpy(stats->hist[0].g_hist, aeHistLinear_, sizeof(stats->hist[0].g_hist));
|
stats->yHist = aeHistLinear_;
|
||||||
|
|
||||||
constexpr unsigned int HdrHeadroomFactor = 4;
|
constexpr unsigned int HdrHeadroomFactor = 4;
|
||||||
uint64_t v = HdrHeadroomFactor * aeHistAverage_;
|
uint64_t v = HdrHeadroomFactor * aeHistAverage_;
|
||||||
for (int i = 0; i < AGC_REGIONS; i++) {
|
for (auto ®ion : stats->agcRegions) {
|
||||||
struct bcm2835_isp_stats_region &r = stats->agc_stats[i];
|
region.val.rSum = region.val.gSum = region.val.bSum = region.counted * v;
|
||||||
r.r_sum = r.b_sum = r.g_sum = r.counted * v;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,19 +15,17 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <linux/bcm2835-isp.h>
|
|
||||||
|
|
||||||
#include "libcamera/internal/yaml_parser.h"
|
#include "libcamera/internal/yaml_parser.h"
|
||||||
|
|
||||||
#include "camera_mode.h"
|
#include "camera_mode.h"
|
||||||
#include "device_status.h"
|
#include "device_status.h"
|
||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
|
#include "statistics.h"
|
||||||
|
|
||||||
namespace RPiController {
|
namespace RPiController {
|
||||||
|
|
||||||
class Algorithm;
|
class Algorithm;
|
||||||
typedef std::unique_ptr<Algorithm> AlgorithmPtr;
|
typedef std::unique_ptr<Algorithm> AlgorithmPtr;
|
||||||
typedef std::shared_ptr<bcm2835_isp_stats> StatisticsPtr;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Controller holds a pointer to some global_metadata, which is how
|
* The Controller holds a pointer to some global_metadata, which is how
|
||||||
|
|
|
@ -352,14 +352,12 @@ bool Af::getPhase(PdafData const &data, double &phase, double &conf) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double Af::getContrast(struct bcm2835_isp_stats_focus const focus_stats[FOCUS_REGIONS]) const
|
double Af::getContrast(const FocusRegions &focusStats) const
|
||||||
{
|
{
|
||||||
uint32_t sumWc = 0;
|
uint32_t sumWc = 0;
|
||||||
|
|
||||||
for (unsigned i = 0; i < FOCUS_REGIONS; ++i) {
|
for (unsigned i = 0; i < focusStats.numRegions(); ++i)
|
||||||
unsigned w = contrastWeights_[i];
|
sumWc += contrastWeights_[i] * focusStats.get(i).val;
|
||||||
sumWc += w * (focus_stats[i].contrast_val[1][1] >> 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (sumWeights_ == 0) ? 0.0 : (double)sumWc / (double)sumWeights_;
|
return (sumWeights_ == 0) ? 0.0 : (double)sumWc / (double)sumWeights_;
|
||||||
}
|
}
|
||||||
|
@ -666,7 +664,7 @@ void Af::prepare(Metadata *imageMetadata)
|
||||||
void Af::process(StatisticsPtr &stats, [[maybe_unused]] Metadata *imageMetadata)
|
void Af::process(StatisticsPtr &stats, [[maybe_unused]] Metadata *imageMetadata)
|
||||||
{
|
{
|
||||||
(void)imageMetadata;
|
(void)imageMetadata;
|
||||||
prevContrast_ = getContrast(stats->focus_stats);
|
prevContrast_ = getContrast(stats->focusRegions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Controls */
|
/* Controls */
|
||||||
|
|
|
@ -11,6 +11,12 @@
|
||||||
#include "../pdaf_data.h"
|
#include "../pdaf_data.h"
|
||||||
#include "../pwl.h"
|
#include "../pwl.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* \todo FOCUS_REGIONS is taken from bcm2835-isp.h, but should be made as a
|
||||||
|
* generic RegionStats structure.
|
||||||
|
*/
|
||||||
|
#define FOCUS_REGIONS 12
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This algorithm implements a hybrid of CDAF and PDAF, favouring PDAF.
|
* This algorithm implements a hybrid of CDAF and PDAF, favouring PDAF.
|
||||||
*
|
*
|
||||||
|
@ -117,7 +123,7 @@ private:
|
||||||
|
|
||||||
void computeWeights();
|
void computeWeights();
|
||||||
bool getPhase(PdafData const &data, double &phase, double &conf) const;
|
bool getPhase(PdafData const &data, double &phase, double &conf) const;
|
||||||
double getContrast(struct bcm2835_isp_stats_focus const focus_stats[FOCUS_REGIONS]) const;
|
double getContrast(const FocusRegions &focusStats) const;
|
||||||
void doPDAF(double phase, double conf);
|
void doPDAF(double phase, double conf);
|
||||||
bool earlyTerminationByPhase(double phase);
|
bool earlyTerminationByPhase(double phase);
|
||||||
double findPeak(unsigned index) const;
|
double findPeak(unsigned index) const;
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include <linux/bcm2835-isp.h>
|
|
||||||
|
|
||||||
#include <libcamera/base/log.h>
|
#include <libcamera/base/log.h>
|
||||||
|
|
||||||
#include "../awb_status.h"
|
#include "../awb_status.h"
|
||||||
|
@ -451,7 +449,7 @@ void Agc::process(StatisticsPtr &stats, Metadata *imageMetadata)
|
||||||
fetchCurrentExposure(imageMetadata);
|
fetchCurrentExposure(imageMetadata);
|
||||||
/* Compute the total gain we require relative to the current exposure. */
|
/* Compute the total gain we require relative to the current exposure. */
|
||||||
double gain, targetY;
|
double gain, targetY;
|
||||||
computeGain(stats.get(), imageMetadata, gain, targetY);
|
computeGain(stats, imageMetadata, gain, targetY);
|
||||||
/* Now compute the target (final) exposure which we think we want. */
|
/* Now compute the target (final) exposure which we think we want. */
|
||||||
computeTargetExposure(gain);
|
computeTargetExposure(gain);
|
||||||
/*
|
/*
|
||||||
|
@ -585,24 +583,23 @@ void Agc::fetchAwbStatus(Metadata *imageMetadata)
|
||||||
LOG(RPiAgc, Debug) << "No AWB status found";
|
LOG(RPiAgc, Debug) << "No AWB status found";
|
||||||
}
|
}
|
||||||
|
|
||||||
static double computeInitialY(bcm2835_isp_stats *stats, AwbStatus const &awb,
|
static double computeInitialY(StatisticsPtr &stats, AwbStatus const &awb,
|
||||||
double weights[], double gain)
|
double weights[], double gain)
|
||||||
{
|
{
|
||||||
bcm2835_isp_stats_region *regions = stats->agc_stats;
|
|
||||||
/*
|
/*
|
||||||
* Note how the calculation below means that equal weights give you
|
* Note how the calculation below means that equal weights give you
|
||||||
* "average" metering (i.e. all pixels equally important).
|
* "average" metering (i.e. all pixels equally important).
|
||||||
*/
|
*/
|
||||||
double rSum = 0, gSum = 0, bSum = 0, pixelSum = 0;
|
double rSum = 0, gSum = 0, bSum = 0, pixelSum = 0;
|
||||||
for (unsigned int i = 0; i < AgcStatsSize; i++) {
|
for (unsigned int i = 0; i < stats->agcRegions.numRegions(); i++) {
|
||||||
double counted = regions[i].counted;
|
auto ®ion = stats->agcRegions.get(i);
|
||||||
double rAcc = std::min(regions[i].r_sum * gain, ((1 << PipelineBits) - 1) * counted);
|
double rAcc = std::min<double>(region.val.rSum * gain, ((1 << PipelineBits) - 1) * region.counted);
|
||||||
double gAcc = std::min(regions[i].g_sum * gain, ((1 << PipelineBits) - 1) * counted);
|
double gAcc = std::min<double>(region.val.gSum * gain, ((1 << PipelineBits) - 1) * region.counted);
|
||||||
double bAcc = std::min(regions[i].b_sum * gain, ((1 << PipelineBits) - 1) * counted);
|
double bAcc = std::min<double>(region.val.bSum * gain, ((1 << PipelineBits) - 1) * region.counted);
|
||||||
rSum += rAcc * weights[i];
|
rSum += rAcc * weights[i];
|
||||||
gSum += gAcc * weights[i];
|
gSum += gAcc * weights[i];
|
||||||
bSum += bAcc * weights[i];
|
bSum += bAcc * weights[i];
|
||||||
pixelSum += counted * weights[i];
|
pixelSum += region.counted * weights[i];
|
||||||
}
|
}
|
||||||
if (pixelSum == 0.0) {
|
if (pixelSum == 0.0) {
|
||||||
LOG(RPiAgc, Warning) << "computeInitialY: pixelSum is zero";
|
LOG(RPiAgc, Warning) << "computeInitialY: pixelSum is zero";
|
||||||
|
@ -624,23 +621,23 @@ static double computeInitialY(bcm2835_isp_stats *stats, AwbStatus const &awb,
|
||||||
|
|
||||||
static constexpr double EvGainYTargetLimit = 0.9;
|
static constexpr double EvGainYTargetLimit = 0.9;
|
||||||
|
|
||||||
static double constraintComputeGain(AgcConstraint &c, Histogram &h, double lux,
|
static double constraintComputeGain(AgcConstraint &c, const Histogram &h, double lux,
|
||||||
double evGain, double &targetY)
|
double evGain, double &targetY)
|
||||||
{
|
{
|
||||||
targetY = c.yTarget.eval(c.yTarget.domain().clip(lux));
|
targetY = c.yTarget.eval(c.yTarget.domain().clip(lux));
|
||||||
targetY = std::min(EvGainYTargetLimit, targetY * evGain);
|
targetY = std::min(EvGainYTargetLimit, targetY * evGain);
|
||||||
double iqm = h.interQuantileMean(c.qLo, c.qHi);
|
double iqm = h.interQuantileMean(c.qLo, c.qHi);
|
||||||
return (targetY * NUM_HISTOGRAM_BINS) / iqm;
|
return (targetY * h.bins()) / iqm;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Agc::computeGain(bcm2835_isp_stats *statistics, Metadata *imageMetadata,
|
void Agc::computeGain(StatisticsPtr &statistics, Metadata *imageMetadata,
|
||||||
double &gain, double &targetY)
|
double &gain, double &targetY)
|
||||||
{
|
{
|
||||||
struct LuxStatus lux = {};
|
struct LuxStatus lux = {};
|
||||||
lux.lux = 400; /* default lux level to 400 in case no metadata found */
|
lux.lux = 400; /* default lux level to 400 in case no metadata found */
|
||||||
if (imageMetadata->get("lux.status", lux) != 0)
|
if (imageMetadata->get("lux.status", lux) != 0)
|
||||||
LOG(RPiAgc, Warning) << "No lux level found";
|
LOG(RPiAgc, Warning) << "No lux level found";
|
||||||
Histogram h(statistics->hist[0].g_hist, NUM_HISTOGRAM_BINS);
|
const Histogram &h = statistics->yHist;
|
||||||
double evGain = status_.ev * config_.baseEv;
|
double evGain = status_.ev * config_.baseEv;
|
||||||
/*
|
/*
|
||||||
* The initial gain and target_Y come from some of the regions. After
|
* The initial gain and target_Y come from some of the regions. After
|
||||||
|
|
|
@ -96,7 +96,7 @@ private:
|
||||||
void housekeepConfig();
|
void housekeepConfig();
|
||||||
void fetchCurrentExposure(Metadata *imageMetadata);
|
void fetchCurrentExposure(Metadata *imageMetadata);
|
||||||
void fetchAwbStatus(Metadata *imageMetadata);
|
void fetchAwbStatus(Metadata *imageMetadata);
|
||||||
void computeGain(bcm2835_isp_stats *statistics, Metadata *imageMetadata,
|
void computeGain(StatisticsPtr &statistics, Metadata *imageMetadata,
|
||||||
double &gain, double &targetY);
|
double &gain, double &targetY);
|
||||||
void computeTargetExposure(double gain);
|
void computeTargetExposure(double gain);
|
||||||
bool applyDigitalGain(double gain, double targetY);
|
bool applyDigitalGain(double gain, double targetY);
|
||||||
|
|
|
@ -310,19 +310,21 @@ double getCt(Metadata *metadata, double defaultCt)
|
||||||
return awbStatus.temperatureK;
|
return awbStatus.temperatureK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void copyStats(bcm2835_isp_stats_region regions[XY], StatisticsPtr &stats,
|
static void copyStats(RgbyRegions ®ions, StatisticsPtr &stats,
|
||||||
AlscStatus const &status)
|
AlscStatus const &status)
|
||||||
{
|
{
|
||||||
bcm2835_isp_stats_region *inputRegions = stats->awb_stats;
|
if (!regions.numRegions())
|
||||||
|
regions.init(stats->awbRegions.size());
|
||||||
|
|
||||||
double *rTable = (double *)status.r;
|
double *rTable = (double *)status.r;
|
||||||
double *gTable = (double *)status.g;
|
double *gTable = (double *)status.g;
|
||||||
double *bTable = (double *)status.b;
|
double *bTable = (double *)status.b;
|
||||||
for (int i = 0; i < XY; i++) {
|
for (unsigned int i = 0; i < stats->awbRegions.numRegions(); i++) {
|
||||||
regions[i].r_sum = inputRegions[i].r_sum / rTable[i];
|
auto r = stats->awbRegions.get(i);
|
||||||
regions[i].g_sum = inputRegions[i].g_sum / gTable[i];
|
r.val.rSum = static_cast<uint64_t>(r.val.rSum / rTable[i]);
|
||||||
regions[i].b_sum = inputRegions[i].b_sum / bTable[i];
|
r.val.gSum = static_cast<uint64_t>(r.val.gSum / gTable[i]);
|
||||||
regions[i].counted = inputRegions[i].counted;
|
r.val.bSum = static_cast<uint64_t>(r.val.bSum / bTable[i]);
|
||||||
/* (don't care about the uncounted value) */
|
regions.set(i, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,19 +514,19 @@ void resampleCalTable(double const calTableIn[XY],
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate chrominance statistics (R/G and B/G) for each region. */
|
/* Calculate chrominance statistics (R/G and B/G) for each region. */
|
||||||
static_assert(XY == AWB_REGIONS, "ALSC/AWB statistics region mismatch");
|
static void calculateCrCb(const RgbyRegions &awbRegion, double cr[XY],
|
||||||
static void calculateCrCb(bcm2835_isp_stats_region *awbRegion, double cr[XY],
|
|
||||||
double cb[XY], uint32_t minCount, uint16_t minG)
|
double cb[XY], uint32_t minCount, uint16_t minG)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < XY; i++) {
|
for (int i = 0; i < XY; i++) {
|
||||||
bcm2835_isp_stats_region &zone = awbRegion[i];
|
auto s = awbRegion.get(i);
|
||||||
if (zone.counted <= minCount ||
|
|
||||||
zone.g_sum / zone.counted <= minG) {
|
if (s.counted <= minCount || s.val.gSum / s.counted <= minG) {
|
||||||
cr[i] = cb[i] = InsufficientData;
|
cr[i] = cb[i] = InsufficientData;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cr[i] = zone.r_sum / (double)zone.g_sum;
|
|
||||||
cb[i] = zone.b_sum / (double)zone.g_sum;
|
cr[i] = s.val.rSum / (double)s.val.gSum;
|
||||||
|
cb[i] = s.val.bSum / (double)s.val.gSum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include "../algorithm.h"
|
#include "../algorithm.h"
|
||||||
#include "../alsc_status.h"
|
#include "../alsc_status.h"
|
||||||
|
#include "../statistics.h"
|
||||||
|
|
||||||
namespace RPiController {
|
namespace RPiController {
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ private:
|
||||||
/* copy out the results from the async thread so that it can be restarted */
|
/* copy out the results from the async thread so that it can be restarted */
|
||||||
void fetchAsyncResults();
|
void fetchAsyncResults();
|
||||||
double ct_;
|
double ct_;
|
||||||
bcm2835_isp_stats_region statistics_[AlscCellsY * AlscCellsX];
|
RgbyRegions statistics_;
|
||||||
double asyncResults_[3][AlscCellsY][AlscCellsX];
|
double asyncResults_[3][AlscCellsY][AlscCellsX];
|
||||||
double asyncLambdaR_[AlscCellsX * AlscCellsY];
|
double asyncLambdaR_[AlscCellsX * AlscCellsY];
|
||||||
double asyncLambdaB_[AlscCellsX * AlscCellsY];
|
double asyncLambdaB_[AlscCellsX * AlscCellsY];
|
||||||
|
|
|
@ -21,9 +21,6 @@ LOG_DEFINE_CATEGORY(RPiAwb)
|
||||||
|
|
||||||
#define NAME "rpi.awb"
|
#define NAME "rpi.awb"
|
||||||
|
|
||||||
static constexpr unsigned int AwbStatsSizeX = DEFAULT_AWB_REGIONS_X;
|
|
||||||
static constexpr unsigned int AwbStatsSizeY = DEFAULT_AWB_REGIONS_Y;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* todo - the locking in this algorithm needs some tidying up as has been done
|
* todo - the locking in this algorithm needs some tidying up as has been done
|
||||||
* elsewhere (ALSC and AGC).
|
* elsewhere (ALSC and AGC).
|
||||||
|
@ -401,17 +398,16 @@ void Awb::asyncFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
static void generateStats(std::vector<Awb::RGB> &zones,
|
static void generateStats(std::vector<Awb::RGB> &zones,
|
||||||
bcm2835_isp_stats_region *stats, double minPixels,
|
RgbyRegions &stats, double minPixels,
|
||||||
double minG)
|
double minG)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < AwbStatsSizeX * AwbStatsSizeY; i++) {
|
for (auto const ®ion : stats) {
|
||||||
Awb::RGB zone;
|
Awb::RGB zone;
|
||||||
double counted = stats[i].counted;
|
if (region.counted >= minPixels) {
|
||||||
if (counted >= minPixels) {
|
zone.G = region.val.gSum / region.counted;
|
||||||
zone.G = stats[i].g_sum / counted;
|
|
||||||
if (zone.G >= minG) {
|
if (zone.G >= minG) {
|
||||||
zone.R = stats[i].r_sum / counted;
|
zone.R = region.val.rSum / region.counted;
|
||||||
zone.B = stats[i].b_sum / counted;
|
zone.B = region.val.bSum / region.counted;
|
||||||
zones.push_back(zone);
|
zones.push_back(zone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -425,7 +421,7 @@ void Awb::prepareStats()
|
||||||
* LSC has already been applied to the stats in this pipeline, so stop
|
* LSC has already been applied to the stats in this pipeline, so stop
|
||||||
* any LSC compensation. We also ignore config_.fast in this version.
|
* any LSC compensation. We also ignore config_.fast in this version.
|
||||||
*/
|
*/
|
||||||
generateStats(zones_, statistics_->awb_stats, config_.minPixels,
|
generateStats(zones_, statistics_->awbRegions, config_.minPixels,
|
||||||
config_.minG);
|
config_.minG);
|
||||||
/*
|
/*
|
||||||
* apply sensitivities, so values appear to come from our "canonical"
|
* apply sensitivities, so values appear to come from our "canonical"
|
||||||
|
@ -641,7 +637,7 @@ void Awb::awbBayes()
|
||||||
* valid... not entirely sure about this.
|
* valid... not entirely sure about this.
|
||||||
*/
|
*/
|
||||||
Pwl prior = interpolatePrior();
|
Pwl prior = interpolatePrior();
|
||||||
prior *= zones_.size() / (double)(AwbStatsSizeX * AwbStatsSizeY);
|
prior *= zones_.size() / (double)(statistics_->awbRegions.numRegions());
|
||||||
prior.map([](double x, double y) {
|
prior.map([](double x, double y) {
|
||||||
LOG(RPiAwb, Debug) << "(" << x << "," << y << ")";
|
LOG(RPiAwb, Debug) << "(" << x << "," << y << ")";
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "../awb_algorithm.h"
|
#include "../awb_algorithm.h"
|
||||||
#include "../pwl.h"
|
#include "../pwl.h"
|
||||||
#include "../awb_status.h"
|
#include "../awb_status.h"
|
||||||
|
#include "../statistics.h"
|
||||||
|
|
||||||
namespace RPiController {
|
namespace RPiController {
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ Pwl computeStretchCurve(Histogram const &histogram,
|
||||||
* bit.
|
* bit.
|
||||||
*/
|
*/
|
||||||
double histLo = histogram.quantile(config.loHistogram) *
|
double histLo = histogram.quantile(config.loHistogram) *
|
||||||
(65536 / NUM_HISTOGRAM_BINS);
|
(65536 / histogram.bins());
|
||||||
double levelLo = config.loLevel * 65536;
|
double levelLo = config.loLevel * 65536;
|
||||||
LOG(RPiContrast, Debug)
|
LOG(RPiContrast, Debug)
|
||||||
<< "Move histogram point " << histLo << " to " << levelLo;
|
<< "Move histogram point " << histLo << " to " << levelLo;
|
||||||
|
@ -119,7 +119,7 @@ Pwl computeStretchCurve(Histogram const &histogram,
|
||||||
* Keep the mid-point (median) in the same place, though, to limit the
|
* Keep the mid-point (median) in the same place, though, to limit the
|
||||||
* apparent amount of global brightness shift.
|
* apparent amount of global brightness shift.
|
||||||
*/
|
*/
|
||||||
double mid = histogram.quantile(0.5) * (65536 / NUM_HISTOGRAM_BINS);
|
double mid = histogram.quantile(0.5) * (65536 / histogram.bins());
|
||||||
enhance.append(mid, mid);
|
enhance.append(mid, mid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -127,7 +127,7 @@ Pwl computeStretchCurve(Histogram const &histogram,
|
||||||
* there up.
|
* there up.
|
||||||
*/
|
*/
|
||||||
double histHi = histogram.quantile(config.hiHistogram) *
|
double histHi = histogram.quantile(config.hiHistogram) *
|
||||||
(65536 / NUM_HISTOGRAM_BINS);
|
(65536 / histogram.bins());
|
||||||
double levelHi = config.hiLevel * 65536;
|
double levelHi = config.hiLevel * 65536;
|
||||||
LOG(RPiContrast, Debug)
|
LOG(RPiContrast, Debug)
|
||||||
<< "Move histogram point " << histHi << " to " << levelHi;
|
<< "Move histogram point " << histHi << " to " << levelHi;
|
||||||
|
@ -158,7 +158,7 @@ Pwl applyManualContrast(Pwl const &gammaCurve, double brightness,
|
||||||
void Contrast::process(StatisticsPtr &stats,
|
void Contrast::process(StatisticsPtr &stats,
|
||||||
[[maybe_unused]] Metadata *imageMetadata)
|
[[maybe_unused]] Metadata *imageMetadata)
|
||||||
{
|
{
|
||||||
Histogram histogram(stats->hist[0].g_hist, NUM_HISTOGRAM_BINS);
|
Histogram &histogram = stats->yHist;
|
||||||
/*
|
/*
|
||||||
* We look at the histogram and adjust the gamma curve in the following
|
* We look at the histogram and adjust the gamma curve in the following
|
||||||
* ways: 1. Adjust the gamma curve so as to pull the start of the
|
* ways: 1. Adjust the gamma curve so as to pull the start of the
|
||||||
|
|
|
@ -31,10 +31,9 @@ char const *Focus::name() const
|
||||||
void Focus::process(StatisticsPtr &stats, Metadata *imageMetadata)
|
void Focus::process(StatisticsPtr &stats, Metadata *imageMetadata)
|
||||||
{
|
{
|
||||||
FocusStatus status;
|
FocusStatus status;
|
||||||
unsigned int i;
|
for (unsigned int i = 0; i < stats->focusRegions.numRegions(); i++)
|
||||||
for (i = 0; i < FOCUS_REGIONS; i++)
|
status.focusMeasures[i] = stats->focusRegions.get(i).val;
|
||||||
status.focusMeasures[i] = stats->focus_stats[i].contrast_val[1][1] / 1000;
|
status.num = stats->focusRegions.numRegions();
|
||||||
status.num = i;
|
|
||||||
imageMetadata->set("focus.status", status);
|
imageMetadata->set("focus.status", status);
|
||||||
|
|
||||||
LOG(RPiFocus, Debug)
|
LOG(RPiFocus, Debug)
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
*/
|
*/
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include <linux/bcm2835-isp.h>
|
|
||||||
|
|
||||||
#include <libcamera/base/log.h>
|
#include <libcamera/base/log.h>
|
||||||
|
|
||||||
#include "../device_status.h"
|
#include "../device_status.h"
|
||||||
|
@ -83,20 +81,12 @@ void Lux::process(StatisticsPtr &stats, Metadata *imageMetadata)
|
||||||
if (imageMetadata->get("device.status", deviceStatus) == 0) {
|
if (imageMetadata->get("device.status", deviceStatus) == 0) {
|
||||||
double currentGain = deviceStatus.analogueGain;
|
double currentGain = deviceStatus.analogueGain;
|
||||||
double currentAperture = deviceStatus.aperture.value_or(currentAperture_);
|
double currentAperture = deviceStatus.aperture.value_or(currentAperture_);
|
||||||
uint64_t sum = 0;
|
double currentY = stats->yHist.interQuantileMean(0, 1);
|
||||||
uint32_t num = 0;
|
|
||||||
uint32_t *bin = stats->hist[0].g_hist;
|
|
||||||
const int numBins = sizeof(stats->hist[0].g_hist) /
|
|
||||||
sizeof(stats->hist[0].g_hist[0]);
|
|
||||||
for (int i = 0; i < numBins; i++)
|
|
||||||
sum += bin[i] * (uint64_t)i, num += bin[i];
|
|
||||||
/* add .5 to reflect the mid-points of bins */
|
|
||||||
double currentY = sum / (double)num + .5;
|
|
||||||
double gainRatio = referenceGain_ / currentGain;
|
double gainRatio = referenceGain_ / currentGain;
|
||||||
double shutterSpeedRatio =
|
double shutterSpeedRatio =
|
||||||
referenceShutterSpeed_ / deviceStatus.shutterSpeed;
|
referenceShutterSpeed_ / deviceStatus.shutterSpeed;
|
||||||
double apertureRatio = referenceAperture_ / currentAperture;
|
double apertureRatio = referenceAperture_ / currentAperture;
|
||||||
double yRatio = currentY * (65536 / numBins) / referenceY_;
|
double yRatio = currentY * (65536 / stats->yHist.bins()) / referenceY_;
|
||||||
double estimatedLux = shutterSpeedRatio * gainRatio *
|
double estimatedLux = shutterSpeedRatio * gainRatio *
|
||||||
apertureRatio * apertureRatio *
|
apertureRatio * apertureRatio *
|
||||||
yRatio * referenceLux_;
|
yRatio * referenceLux_;
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
#include "sharpen_algorithm.h"
|
#include "sharpen_algorithm.h"
|
||||||
#include "sharpen_status.h"
|
#include "sharpen_status.h"
|
||||||
|
#include "statistics.h"
|
||||||
|
|
||||||
namespace libcamera {
|
namespace libcamera {
|
||||||
|
|
||||||
|
@ -152,6 +153,7 @@ private:
|
||||||
void prepareISP(const ISPConfig &data);
|
void prepareISP(const ISPConfig &data);
|
||||||
void reportMetadata(unsigned int ipaContext);
|
void reportMetadata(unsigned int ipaContext);
|
||||||
void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext);
|
void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext);
|
||||||
|
RPiController::StatisticsPtr fillStatistics(bcm2835_isp_stats *stats) const;
|
||||||
void processStats(unsigned int bufferId, unsigned int ipaContext);
|
void processStats(unsigned int bufferId, unsigned int ipaContext);
|
||||||
void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration);
|
void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration);
|
||||||
void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
|
void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
|
||||||
|
@ -1364,6 +1366,46 @@ void IPARPi::fillDeviceStatus(const ControlList &sensorControls, unsigned int ip
|
||||||
rpiMetadata_[ipaContext].set("device.status", deviceStatus);
|
rpiMetadata_[ipaContext].set("device.status", deviceStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RPiController::StatisticsPtr IPARPi::fillStatistics(bcm2835_isp_stats *stats) const
|
||||||
|
{
|
||||||
|
using namespace RPiController;
|
||||||
|
|
||||||
|
unsigned int i;
|
||||||
|
StatisticsPtr statistics =
|
||||||
|
std::make_unique<Statistics>(Statistics::AgcStatsPos::PreWb, Statistics::ColourStatsPos::PostLsc);
|
||||||
|
|
||||||
|
/* RGB histograms are not used, so do not populate them. */
|
||||||
|
statistics->yHist = RPiController::Histogram(stats->hist[0].g_hist, NUM_HISTOGRAM_BINS);
|
||||||
|
|
||||||
|
statistics->awbRegions.init({ DEFAULT_AWB_REGIONS_X, DEFAULT_AWB_REGIONS_Y });
|
||||||
|
for (i = 0; i < statistics->awbRegions.numRegions(); i++)
|
||||||
|
statistics->awbRegions.set(i, { { stats->awb_stats[i].r_sum,
|
||||||
|
stats->awb_stats[i].g_sum,
|
||||||
|
stats->awb_stats[i].b_sum },
|
||||||
|
stats->awb_stats[i].counted,
|
||||||
|
stats->awb_stats[i].notcounted });
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are only ever 15 regions computed by the firmware due to zoning,
|
||||||
|
* but the HW defines AGC_REGIONS == 16!
|
||||||
|
*/
|
||||||
|
statistics->agcRegions.init(15);
|
||||||
|
for (i = 0; i < statistics->agcRegions.numRegions(); i++)
|
||||||
|
statistics->agcRegions.set(i, { { stats->agc_stats[i].r_sum,
|
||||||
|
stats->agc_stats[i].g_sum,
|
||||||
|
stats->agc_stats[i].b_sum },
|
||||||
|
stats->agc_stats[i].counted,
|
||||||
|
stats->awb_stats[i].notcounted });
|
||||||
|
|
||||||
|
statistics->focusRegions.init({ 4, 3 });
|
||||||
|
for (i = 0; i < statistics->focusRegions.numRegions(); i++)
|
||||||
|
statistics->focusRegions.set(i, { stats->focus_stats[i].contrast_val[1][1] / 1000,
|
||||||
|
stats->focus_stats[i].contrast_val_num[1][1],
|
||||||
|
stats->focus_stats[i].contrast_val_num[1][0] });
|
||||||
|
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
|
||||||
void IPARPi::processStats(unsigned int bufferId, unsigned int ipaContext)
|
void IPARPi::processStats(unsigned int bufferId, unsigned int ipaContext)
|
||||||
{
|
{
|
||||||
RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
|
RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext];
|
||||||
|
@ -1376,7 +1418,7 @@ void IPARPi::processStats(unsigned int bufferId, unsigned int ipaContext)
|
||||||
|
|
||||||
Span<uint8_t> mem = it->second.planes()[0];
|
Span<uint8_t> mem = it->second.planes()[0];
|
||||||
bcm2835_isp_stats *stats = reinterpret_cast<bcm2835_isp_stats *>(mem.data());
|
bcm2835_isp_stats *stats = reinterpret_cast<bcm2835_isp_stats *>(mem.data());
|
||||||
RPiController::StatisticsPtr statistics = std::make_shared<bcm2835_isp_stats>(*stats);
|
RPiController::StatisticsPtr statistics = fillStatistics(stats);
|
||||||
helper_->process(statistics, rpiMetadata);
|
helper_->process(statistics, rpiMetadata);
|
||||||
controller_.process(statistics, &rpiMetadata);
|
controller_.process(statistics, &rpiMetadata);
|
||||||
|
|
||||||
|
|
|
@ -73,4 +73,6 @@ struct Statistics {
|
||||||
FocusRegions focusRegions;
|
FocusRegions focusRegions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using StatisticsPtr = std::shared_ptr<Statistics>;
|
||||||
|
|
||||||
} /* namespace RPiController */
|
} /* namespace RPiController */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue