Files
external_libcamera/src/ipa/raspberrypi/cam_helper_imx708.cpp
T
Naushir Patuck 6d60f264d1 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>
2023-02-09 13:11:30 +00:00

353 lines
11 KiB
C++

/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (C) 2022, Raspberry Pi Ltd
*
* cam_helper_imx708.cpp - camera helper for imx708 sensor
*/
#include <cmath>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <libcamera/base/log.h>
#include "controller/pdaf_data.h"
#include "cam_helper.h"
#include "md_parser.h"
using namespace RPiController;
using namespace libcamera;
using libcamera::utils::Duration;
namespace libcamera {
LOG_DECLARE_CATEGORY(IPARPI)
}
/*
* We care about two gain registers and a pair of exposure registers. Their
* I2C addresses from the Sony imx708 datasheet:
*/
constexpr uint32_t expHiReg = 0x0202;
constexpr uint32_t expLoReg = 0x0203;
constexpr uint32_t gainHiReg = 0x0204;
constexpr uint32_t gainLoReg = 0x0205;
constexpr uint32_t frameLengthHiReg = 0x0340;
constexpr uint32_t frameLengthLoReg = 0x0341;
constexpr uint32_t lineLengthHiReg = 0x0342;
constexpr uint32_t lineLengthLoReg = 0x0343;
constexpr uint32_t temperatureReg = 0x013a;
constexpr std::initializer_list<uint32_t> registerList =
{ expHiReg, expLoReg, gainHiReg, gainLoReg, lineLengthHiReg,
lineLengthLoReg, frameLengthHiReg, frameLengthLoReg, temperatureReg };
class CamHelperImx708 : public CamHelper
{
public:
CamHelperImx708();
uint32_t gainCode(double gain) const override;
double gain(uint32_t gain_code) const override;
void prepare(libcamera::Span<const uint8_t> buffer, Metadata &metadata) override;
void process(StatisticsPtr &stats, Metadata &metadata) override;
std::pair<uint32_t, uint32_t> getBlanking(Duration &exposure, Duration minFrameDuration,
Duration maxFrameDuration) const override;
void getDelays(int &exposureDelay, int &gainDelay,
int &vblankDelay, int &hblankDelay) const override;
bool sensorEmbeddedDataPresent() const override;
double getModeSensitivity(const CameraMode &mode) const override;
unsigned int hideFramesModeSwitch() const override { return 1; } // seems to be required for HDR
private:
/*
* Smallest difference between the frame length and integration time,
* in units of lines.
*/
static constexpr int frameIntegrationDiff = 22;
/* Maximum frame length allowable for long exposure calculations. */
static constexpr int frameLengthMax = 0xffdc;
/* Largest long exposure scale factor given as a left shift on the frame length. */
static constexpr int longExposureShiftMax = 7;
void populateMetadata(const MdParser::RegisterMap &registers,
Metadata &metadata) const override;
static bool parsePdafData(const uint8_t *ptr, size_t len, unsigned bpp,
PdafData &pdaf);
bool parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp);
void putAGCStatistics(StatisticsPtr stats);
Histogram aeHistLinear_;
uint32_t aeHistAverage_;
bool aeHistValid_;
};
CamHelperImx708::CamHelperImx708()
: CamHelper(std::make_unique<MdParserSmia>(registerList), frameIntegrationDiff),
aeHistLinear_{}, aeHistAverage_(0), aeHistValid_(false)
{
}
uint32_t CamHelperImx708::gainCode(double gain) const
{
return static_cast<uint32_t>(1024 - 1024 / gain);
}
double CamHelperImx708::gain(uint32_t gain_code) const
{
return 1024.0 / (1024 - gain_code);
}
void CamHelperImx708::prepare(libcamera::Span<const uint8_t> buffer, Metadata &metadata)
{
MdParser::RegisterMap registers;
DeviceStatus deviceStatus;
LOG(IPARPI, Debug) << "Embedded buffer size: " << buffer.size();
if (metadata.get("device.status", deviceStatus)) {
LOG(IPARPI, Error) << "DeviceStatus not found from DelayedControls";
return;
}
parseEmbeddedData(buffer, metadata);
/*
* Parse PDAF data, which we expect to occupy the third scanline
* of embedded data. As PDAF is quite sensor-specific, it's parsed here.
*/
size_t bytesPerLine = (mode_.width * mode_.bitdepth) >> 3;
if (buffer.size() > 2 * bytesPerLine) {
PdafData pdaf;
if (parsePdafData(&buffer[2 * bytesPerLine],
buffer.size() - 2 * bytesPerLine,
mode_.bitdepth, pdaf))
metadata.set("pdaf.data", pdaf);
}
/* Parse AE-HIST data where present */
if (buffer.size() > 3 * bytesPerLine) {
aeHistValid_ = parseAEHist(&buffer[3 * bytesPerLine],
buffer.size() - 3 * bytesPerLine,
mode_.bitdepth);
}
/*
* The DeviceStatus struct is first populated with values obtained from
* DelayedControls. If this reports frame length is > frameLengthMax,
* it means we are using a long exposure mode. Since the long exposure
* scale factor is not returned back through embedded data, we must rely
* on the existing exposure lines and frame length values returned by
* DelayedControls.
*
* Otherwise, all values are updated with what is reported in the
* embedded data.
*/
if (deviceStatus.frameLength > frameLengthMax) {
DeviceStatus parsedDeviceStatus;
metadata.get("device.status", parsedDeviceStatus);
parsedDeviceStatus.shutterSpeed = deviceStatus.shutterSpeed;
parsedDeviceStatus.frameLength = deviceStatus.frameLength;
metadata.set("device.status", parsedDeviceStatus);
LOG(IPARPI, Debug) << "Metadata updated for long exposure: "
<< parsedDeviceStatus;
}
}
void CamHelperImx708::process(StatisticsPtr &stats, [[maybe_unused]] Metadata &metadata)
{
if (aeHistValid_)
putAGCStatistics(stats);
}
std::pair<uint32_t, uint32_t> CamHelperImx708::getBlanking(Duration &exposure,
Duration minFrameDuration,
Duration maxFrameDuration) const
{
uint32_t frameLength, exposureLines;
unsigned int shift = 0;
auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration,
maxFrameDuration);
frameLength = mode_.height + vblank;
Duration lineLength = hblankToLineLength(hblank);
/*
* Check if the frame length calculated needs to be setup for long
* exposure mode. This will require us to use a long exposure scale
* factor provided by a shift operation in the sensor.
*/
while (frameLength > frameLengthMax) {
if (++shift > longExposureShiftMax) {
shift = longExposureShiftMax;
frameLength = frameLengthMax;
break;
}
frameLength >>= 1;
}
if (shift) {
/* Account for any rounding in the scaled frame length value. */
frameLength <<= shift;
exposureLines = CamHelper::exposureLines(exposure, lineLength);
exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff);
exposure = CamHelper::exposure(exposureLines, lineLength);
}
return { frameLength - mode_.height, hblank };
}
void CamHelperImx708::getDelays(int &exposureDelay, int &gainDelay,
int &vblankDelay, int &hblankDelay) const
{
exposureDelay = 2;
gainDelay = 2;
vblankDelay = 3;
hblankDelay = 3;
}
bool CamHelperImx708::sensorEmbeddedDataPresent() const
{
return true;
}
double CamHelperImx708::getModeSensitivity(const CameraMode &mode) const
{
/* In binned modes, sensitivity increases by a factor of 2 */
return (mode.width > 2304) ? 1.0 : 2.0;
}
void CamHelperImx708::populateMetadata(const MdParser::RegisterMap &registers,
Metadata &metadata) const
{
DeviceStatus deviceStatus;
deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 +
registers.at(lineLengthLoReg));
deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg),
deviceStatus.lineLength);
deviceStatus.analogueGain = gain(registers.at(gainHiReg) * 256 + registers.at(gainLoReg));
deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg);
deviceStatus.sensorTemperature = std::clamp<int8_t>(registers.at(temperatureReg), -20, 80);
metadata.set("device.status", deviceStatus);
}
bool CamHelperImx708::parsePdafData(const uint8_t *ptr, size_t len,
unsigned bpp, PdafData &pdaf)
{
size_t step = bpp >> 1; /* bytes per PDAF grid entry */
if (bpp < 10 || bpp > 12 || len < 194 * step || ptr[0] != 0 || ptr[1] >= 0x40) {
LOG(IPARPI, Error) << "PDAF data in unsupported format";
return false;
}
ptr += 2 * step;
for (unsigned i = 0; i < PDAF_DATA_ROWS; ++i) {
for (unsigned j = 0; j < PDAF_DATA_COLS; ++j) {
unsigned c = (ptr[0] << 3) | (ptr[1] >> 5);
int p = (((ptr[1] & 0x0F) - (ptr[1] & 0x10)) << 6) | (ptr[2] >> 2);
pdaf.conf[i][j] = c;
pdaf.phase[i][j] = c ? p : 0;
ptr += step;
}
}
return true;
}
bool CamHelperImx708::parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp)
{
static constexpr unsigned int PipelineBits = Statistics::NormalisationFactorPow2;
uint64_t count = 0, sum = 0;
size_t step = bpp >> 1; /* bytes per histogram bin */
uint32_t hist[128];
if (len < 144 * step)
return false;
/*
* Read the 128 bin linear histogram, which by default covers
* the full range of the HDR shortest exposure (small values are
* expected to dominate, so pixel-value resolution will be poor).
*/
for (unsigned i = 0; i < 128; ++i) {
if (ptr[3] != 0x55)
return false;
uint32_t c = (ptr[0] << 14) + (ptr[1] << 6) + (ptr[2] >> 2);
hist[i] = c >> 2; /* pixels to quads */
if (i != 0) {
count += c;
sum += c *
(i * (1u << (PipelineBits - 7)) +
(1u << (PipelineBits - 8)));
}
ptr += step;
}
/*
* Now use the first 9 bins of the log histogram (these should be
* subdivisions of the smallest linear bin), to get a more accurate
* average value. Don't assume that AEHIST1_AVERAGE is present.
*/
for (unsigned i = 0; i < 9; ++i) {
if (ptr[3] != 0x55)
return false;
uint32_t c = (ptr[0] << 14) + (ptr[1] << 6) + (ptr[2] >> 2);
count += c;
sum += c *
((3u << PipelineBits) >> (17 - i));
ptr += step;
}
if ((unsigned)((ptr[0] << 12) + (ptr[1] << 4) + (ptr[2] >> 4)) !=
hist[1]) {
LOG(IPARPI, Error) << "Lin/Log histogram mismatch";
return false;
}
aeHistLinear_ = Histogram(hist, 128);
aeHistAverage_ = count ? (sum / count) : 0;
return count != 0;
}
void CamHelperImx708::putAGCStatistics(StatisticsPtr stats)
{
/*
* For HDR mode, copy sensor's AE/AGC statistics over ISP's, so the
* AGC algorithm sees a linear response to exposure and gain changes.
*
* Histogram: Just copy the "raw" histogram over the tone-mapped one,
* although they have different distributions (raw values are lower).
* Tuning should either ignore it, or constrain for highlights only.
*
* Average: Overwrite all regional averages with a global raw average,
* 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.
*/
stats->yHist = aeHistLinear_;
constexpr unsigned int HdrHeadroomFactor = 4;
uint64_t v = HdrHeadroomFactor * aeHistAverage_;
for (auto &region : stats->agcRegions) {
region.val.rSum = region.val.gSum = region.val.bSum = region.counted * v;
}
}
static CamHelper *create()
{
return new CamHelperImx708();
}
static RegisterCamHelper reg("imx708", &create);
static RegisterCamHelper regWide("imx708_wide", &create);
static RegisterCamHelper regNoIr("imx708_noir", &create);
static RegisterCamHelper regWideNoIr("imx708_wide_noir", &create);