ipa: raspberrypi: Introduce IpaBase class
Create a new IpaBase class that handles general purpose housekeeping duties for the Raspberry Pi IPA. The implementation of the new class is essentially pulled from the existing ipa/rpi/vc4/raspberrypi.cpp file with a minimal amount of refactoring. Create a derived IpaVc4 class from IpaBase that handles the VC4 pipeline specific tasks of the IPA. Again, code for this class implementation is taken from the existing ipa/rpi/vc4/raspberrypi.cpp with a minimal amount of refactoring. The goal of this change is to allow third parties to implement their own IPA running on the Raspberry Pi without duplicating all of the IPA housekeeping tasks. Signed-off-by: Naushir Patuck <naush@raspberrypi.com> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
parent
cea3de4226
commit
d903fdbe31
6 changed files with 1076 additions and 952 deletions
File diff suppressed because it is too large
Load diff
122
src/ipa/rpi/common/ipa_base.h
Normal file
122
src/ipa/rpi/common/ipa_base.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Copyright (C) 2023, Raspberry Pi Ltd
|
||||
*
|
||||
* ipa_base.h - Raspberry Pi IPA base class
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libcamera/base/utils.h>
|
||||
#include <libcamera/controls.h>
|
||||
|
||||
#include <libcamera/ipa/raspberrypi_ipa_interface.h>
|
||||
|
||||
#include "libcamera/internal/mapped_framebuffer.h"
|
||||
|
||||
#include "cam_helper/cam_helper.h"
|
||||
#include "controller/agc_status.h"
|
||||
#include "controller/camera_mode.h"
|
||||
#include "controller/controller.h"
|
||||
#include "controller/metadata.h"
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
namespace ipa::RPi {
|
||||
|
||||
class IpaBase : public IPARPiInterface
|
||||
{
|
||||
public:
|
||||
IpaBase();
|
||||
~IpaBase();
|
||||
|
||||
int32_t init(const IPASettings &settings, const InitParams ¶ms, InitResult *result) override;
|
||||
int32_t configure(const IPACameraSensorInfo &sensorInfo, const ConfigParams ¶ms,
|
||||
ConfigResult *result) override;
|
||||
|
||||
void start(const ControlList &controls, StartResult *result) override;
|
||||
void stop() override {}
|
||||
|
||||
void mapBuffers(const std::vector<IPABuffer> &buffers) override;
|
||||
void unmapBuffers(const std::vector<unsigned int> &ids) override;
|
||||
|
||||
void prepareIsp(const PrepareParams ¶ms) override;
|
||||
void processStats(const ProcessParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
/* Raspberry Pi controller specific defines. */
|
||||
std::unique_ptr<RPiController::CamHelper> helper_;
|
||||
RPiController::Controller controller_;
|
||||
|
||||
ControlInfoMap sensorCtrls_;
|
||||
ControlInfoMap lensCtrls_;
|
||||
|
||||
/* Camera sensor params. */
|
||||
CameraMode mode_;
|
||||
|
||||
/* Track the frame length times over FrameLengthsQueueSize frames. */
|
||||
std::deque<utils::Duration> frameLengths_;
|
||||
utils::Duration lastTimeout_;
|
||||
|
||||
private:
|
||||
/* Number of metadata objects available in the context list. */
|
||||
static constexpr unsigned int numMetadataContexts = 16;
|
||||
|
||||
virtual int32_t platformInit(const InitParams ¶ms, InitResult *result) = 0;
|
||||
virtual int32_t platformConfigure(const ConfigParams ¶ms, ConfigResult *result) = 0;
|
||||
|
||||
virtual void platformPrepareIsp(const PrepareParams ¶ms,
|
||||
RPiController::Metadata &rpiMetadata) = 0;
|
||||
virtual RPiController::StatisticsPtr platformProcessStats(Span<uint8_t> mem) = 0;
|
||||
|
||||
void setMode(const IPACameraSensorInfo &sensorInfo);
|
||||
void setCameraTimeoutValue();
|
||||
bool validateSensorControls();
|
||||
bool validateLensControls();
|
||||
void applyControls(const ControlList &controls);
|
||||
virtual void handleControls(const ControlList &controls) = 0;
|
||||
void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext);
|
||||
void reportMetadata(unsigned int ipaContext);
|
||||
void applyFrameDurations(utils::Duration minFrameDuration, utils::Duration maxFrameDuration);
|
||||
void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
|
||||
|
||||
std::map<unsigned int, MappedFrameBuffer> buffers_;
|
||||
|
||||
bool lensPresent_;
|
||||
ControlList libcameraMetadata_;
|
||||
|
||||
std::array<RPiController::Metadata, numMetadataContexts> rpiMetadata_;
|
||||
|
||||
/*
|
||||
* We count frames to decide if the frame must be hidden (e.g. from
|
||||
* display) or mistrusted (i.e. not given to the control algos).
|
||||
*/
|
||||
uint64_t frameCount_;
|
||||
|
||||
/* How many frames we should avoid running control algos on. */
|
||||
unsigned int mistrustCount_;
|
||||
|
||||
/* Number of frames that need to be dropped on startup. */
|
||||
unsigned int dropFrameCount_;
|
||||
|
||||
/* Frame timestamp for the last run of the controller. */
|
||||
uint64_t lastRunTimestamp_;
|
||||
|
||||
/* Do we run a Controller::process() for this frame? */
|
||||
bool processPending_;
|
||||
|
||||
/* Distinguish the first camera start from others. */
|
||||
bool firstStart_;
|
||||
|
||||
/* Frame duration (1/fps) limits. */
|
||||
utils::Duration minFrameDuration_;
|
||||
utils::Duration maxFrameDuration_;
|
||||
};
|
||||
|
||||
} /* namespace ipa::RPi */
|
||||
|
||||
} /* namespace libcamera */
|
17
src/ipa/rpi/common/meson.build
Normal file
17
src/ipa/rpi/common/meson.build
Normal file
|
@ -0,0 +1,17 @@
|
|||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
rpi_ipa_common_sources = files([
|
||||
'ipa_base.cpp',
|
||||
])
|
||||
|
||||
rpi_ipa_common_includes = [
|
||||
include_directories('..'),
|
||||
]
|
||||
|
||||
rpi_ipa_common_deps = [
|
||||
libcamera_private,
|
||||
]
|
||||
|
||||
rpi_ipa_common_lib = static_library('rpi_ipa_common', rpi_ipa_common_sources,
|
||||
include_directories : rpi_ipa_common_includes,
|
||||
dependencies : rpi_ipa_common_deps)
|
|
@ -1,6 +1,7 @@
|
|||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
subdir('cam_helper')
|
||||
subdir('common')
|
||||
subdir('controller')
|
||||
|
||||
foreach pipeline : pipelines
|
||||
|
|
|
@ -9,6 +9,7 @@ vc4_ipa_deps = [
|
|||
|
||||
vc4_ipa_libs = [
|
||||
rpi_ipa_cam_helper_lib,
|
||||
rpi_ipa_common_lib,
|
||||
rpi_ipa_controller_lib
|
||||
]
|
||||
|
||||
|
@ -18,7 +19,7 @@ vc4_ipa_includes = [
|
|||
]
|
||||
|
||||
vc4_ipa_sources = files([
|
||||
'raspberrypi.cpp',
|
||||
'vc4.cpp',
|
||||
])
|
||||
|
||||
vc4_ipa_includes += include_directories('..')
|
||||
|
|
540
src/ipa/rpi/vc4/vc4.cpp
Normal file
540
src/ipa/rpi/vc4/vc4.cpp
Normal file
|
@ -0,0 +1,540 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Copyright (C) 2019-2021, Raspberry Pi Ltd
|
||||
*
|
||||
* rpi.cpp - Raspberry Pi VC4/BCM2835 ISP IPA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <linux/bcm2835-isp.h>
|
||||
|
||||
#include <libcamera/base/log.h>
|
||||
#include <libcamera/ipa/ipa_module_info.h>
|
||||
|
||||
#include "common/ipa_base.h"
|
||||
#include "controller/af_status.h"
|
||||
#include "controller/agc_algorithm.h"
|
||||
#include "controller/alsc_status.h"
|
||||
#include "controller/awb_status.h"
|
||||
#include "controller/black_level_status.h"
|
||||
#include "controller/ccm_status.h"
|
||||
#include "controller/contrast_status.h"
|
||||
#include "controller/denoise_algorithm.h"
|
||||
#include "controller/denoise_status.h"
|
||||
#include "controller/dpc_status.h"
|
||||
#include "controller/geq_status.h"
|
||||
#include "controller/lux_status.h"
|
||||
#include "controller/noise_status.h"
|
||||
#include "controller/sharpen_status.h"
|
||||
|
||||
namespace libcamera {
|
||||
|
||||
LOG_DECLARE_CATEGORY(IPARPI)
|
||||
|
||||
namespace ipa::RPi {
|
||||
|
||||
class IpaVc4 final : public IpaBase
|
||||
{
|
||||
public:
|
||||
IpaVc4()
|
||||
: IpaBase(), lsTable_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~IpaVc4()
|
||||
{
|
||||
if (lsTable_)
|
||||
munmap(lsTable_, MaxLsGridSize);
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t platformInit(const InitParams ¶ms, InitResult *result) override;
|
||||
int32_t platformConfigure(const ConfigParams ¶ms, ConfigResult *result) override;
|
||||
|
||||
void platformPrepareIsp(const PrepareParams ¶ms, RPiController::Metadata &rpiMetadata) override;
|
||||
RPiController::StatisticsPtr platformProcessStats(Span<uint8_t> mem) override;
|
||||
|
||||
void handleControls(const ControlList &controls) override;
|
||||
bool validateIspControls();
|
||||
|
||||
void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);
|
||||
void applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls);
|
||||
void applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls);
|
||||
void applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls);
|
||||
void applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls);
|
||||
void applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls);
|
||||
void applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls);
|
||||
void applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls);
|
||||
void applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls);
|
||||
void applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls);
|
||||
void applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls);
|
||||
void resampleTable(uint16_t dest[], const std::vector<double> &src, int destW, int destH);
|
||||
|
||||
/* VC4 ISP controls. */
|
||||
ControlInfoMap ispCtrls_;
|
||||
|
||||
/* LS table allocation passed in from the pipeline handler. */
|
||||
SharedFD lsTableHandle_;
|
||||
void *lsTable_;
|
||||
};
|
||||
|
||||
int32_t IpaVc4::platformInit([[maybe_unused]] const InitParams ¶ms, [[maybe_unused]] InitResult *result)
|
||||
{
|
||||
const std::string &target = controller_.getTarget();
|
||||
|
||||
if (target != "bcm2835") {
|
||||
LOG(IPARPI, Error)
|
||||
<< "Tuning data file target returned \"" << target << "\""
|
||||
<< ", expected \"bcm2835\"";
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t IpaVc4::platformConfigure(const ConfigParams ¶ms, [[maybe_unused]] ConfigResult *result)
|
||||
{
|
||||
ispCtrls_ = params.ispControls;
|
||||
if (!validateIspControls()) {
|
||||
LOG(IPARPI, Error) << "ISP control validation failed.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Store the lens shading table pointer and handle if available. */
|
||||
if (params.lsTableHandle.isValid()) {
|
||||
/* Remove any previous table, if there was one. */
|
||||
if (lsTable_) {
|
||||
munmap(lsTable_, MaxLsGridSize);
|
||||
lsTable_ = nullptr;
|
||||
}
|
||||
|
||||
/* Map the LS table buffer into user space. */
|
||||
lsTableHandle_ = std::move(params.lsTableHandle);
|
||||
if (lsTableHandle_.isValid()) {
|
||||
lsTable_ = mmap(nullptr, MaxLsGridSize, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, lsTableHandle_.get(), 0);
|
||||
|
||||
if (lsTable_ == MAP_FAILED) {
|
||||
LOG(IPARPI, Error) << "dmaHeap mmap failure for LS table.";
|
||||
lsTable_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IpaVc4::platformPrepareIsp([[maybe_unused]] const PrepareParams ¶ms,
|
||||
RPiController::Metadata &rpiMetadata)
|
||||
{
|
||||
ControlList ctrls(ispCtrls_);
|
||||
|
||||
/* Lock the metadata buffer to avoid constant locks/unlocks. */
|
||||
std::unique_lock<RPiController::Metadata> lock(rpiMetadata);
|
||||
|
||||
AwbStatus *awbStatus = rpiMetadata.getLocked<AwbStatus>("awb.status");
|
||||
if (awbStatus)
|
||||
applyAWB(awbStatus, ctrls);
|
||||
|
||||
CcmStatus *ccmStatus = rpiMetadata.getLocked<CcmStatus>("ccm.status");
|
||||
if (ccmStatus)
|
||||
applyCCM(ccmStatus, ctrls);
|
||||
|
||||
AgcStatus *dgStatus = rpiMetadata.getLocked<AgcStatus>("agc.status");
|
||||
if (dgStatus)
|
||||
applyDG(dgStatus, ctrls);
|
||||
|
||||
AlscStatus *lsStatus = rpiMetadata.getLocked<AlscStatus>("alsc.status");
|
||||
if (lsStatus)
|
||||
applyLS(lsStatus, ctrls);
|
||||
|
||||
ContrastStatus *contrastStatus = rpiMetadata.getLocked<ContrastStatus>("contrast.status");
|
||||
if (contrastStatus)
|
||||
applyGamma(contrastStatus, ctrls);
|
||||
|
||||
BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked<BlackLevelStatus>("black_level.status");
|
||||
if (blackLevelStatus)
|
||||
applyBlackLevel(blackLevelStatus, ctrls);
|
||||
|
||||
GeqStatus *geqStatus = rpiMetadata.getLocked<GeqStatus>("geq.status");
|
||||
if (geqStatus)
|
||||
applyGEQ(geqStatus, ctrls);
|
||||
|
||||
DenoiseStatus *denoiseStatus = rpiMetadata.getLocked<DenoiseStatus>("denoise.status");
|
||||
if (denoiseStatus)
|
||||
applyDenoise(denoiseStatus, ctrls);
|
||||
|
||||
SharpenStatus *sharpenStatus = rpiMetadata.getLocked<SharpenStatus>("sharpen.status");
|
||||
if (sharpenStatus)
|
||||
applySharpen(sharpenStatus, ctrls);
|
||||
|
||||
DpcStatus *dpcStatus = rpiMetadata.getLocked<DpcStatus>("dpc.status");
|
||||
if (dpcStatus)
|
||||
applyDPC(dpcStatus, ctrls);
|
||||
|
||||
const AfStatus *afStatus = rpiMetadata.getLocked<AfStatus>("af.status");
|
||||
if (afStatus) {
|
||||
ControlList lensctrls(lensCtrls_);
|
||||
applyAF(afStatus, lensctrls);
|
||||
if (!lensctrls.empty())
|
||||
setLensControls.emit(lensctrls);
|
||||
}
|
||||
|
||||
if (!ctrls.empty())
|
||||
setIspControls.emit(ctrls);
|
||||
}
|
||||
|
||||
RPiController::StatisticsPtr IpaVc4::platformProcessStats(Span<uint8_t> mem)
|
||||
{
|
||||
using namespace RPiController;
|
||||
|
||||
const bcm2835_isp_stats *stats = reinterpret_cast<bcm2835_isp_stats *>(mem.data());
|
||||
StatisticsPtr statistics = std::make_unique<Statistics>(Statistics::AgcStatsPos::PreWb,
|
||||
Statistics::ColourStatsPos::PostLsc);
|
||||
const Controller::HardwareConfig &hw = controller_.getHardwareConfig();
|
||||
unsigned int i;
|
||||
|
||||
/* RGB histograms are not used, so do not populate them. */
|
||||
statistics->yHist = RPiController::Histogram(stats->hist[0].g_hist,
|
||||
hw.numHistogramBins);
|
||||
|
||||
/* All region sums are based on a 16-bit normalised pipeline bit-depth. */
|
||||
unsigned int scale = Statistics::NormalisationFactorPow2 - hw.pipelineWidth;
|
||||
|
||||
statistics->awbRegions.init(hw.awbRegions);
|
||||
for (i = 0; i < statistics->awbRegions.numRegions(); i++)
|
||||
statistics->awbRegions.set(i, { { stats->awb_stats[i].r_sum << scale,
|
||||
stats->awb_stats[i].g_sum << scale,
|
||||
stats->awb_stats[i].b_sum << scale },
|
||||
stats->awb_stats[i].counted,
|
||||
stats->awb_stats[i].notcounted });
|
||||
|
||||
statistics->agcRegions.init(hw.agcRegions);
|
||||
for (i = 0; i < statistics->agcRegions.numRegions(); i++)
|
||||
statistics->agcRegions.set(i, { { stats->agc_stats[i].r_sum << scale,
|
||||
stats->agc_stats[i].g_sum << scale,
|
||||
stats->agc_stats[i].b_sum << scale },
|
||||
stats->agc_stats[i].counted,
|
||||
stats->awb_stats[i].notcounted });
|
||||
|
||||
statistics->focusRegions.init(hw.focusRegions);
|
||||
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 IpaVc4::handleControls([[maybe_unused]] const ControlList &controls)
|
||||
{
|
||||
/* No controls require any special updates to the hardware configuration. */
|
||||
}
|
||||
|
||||
bool IpaVc4::validateIspControls()
|
||||
{
|
||||
static const uint32_t ctrls[] = {
|
||||
V4L2_CID_RED_BALANCE,
|
||||
V4L2_CID_BLUE_BALANCE,
|
||||
V4L2_CID_DIGITAL_GAIN,
|
||||
V4L2_CID_USER_BCM2835_ISP_CC_MATRIX,
|
||||
V4L2_CID_USER_BCM2835_ISP_GAMMA,
|
||||
V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL,
|
||||
V4L2_CID_USER_BCM2835_ISP_GEQ,
|
||||
V4L2_CID_USER_BCM2835_ISP_DENOISE,
|
||||
V4L2_CID_USER_BCM2835_ISP_SHARPEN,
|
||||
V4L2_CID_USER_BCM2835_ISP_DPC,
|
||||
V4L2_CID_USER_BCM2835_ISP_LENS_SHADING,
|
||||
V4L2_CID_USER_BCM2835_ISP_CDN,
|
||||
};
|
||||
|
||||
for (auto c : ctrls) {
|
||||
if (ispCtrls_.find(c) == ispCtrls_.end()) {
|
||||
LOG(IPARPI, Error) << "Unable to find ISP control "
|
||||
<< utils::hex(c);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IpaVc4::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls)
|
||||
{
|
||||
LOG(IPARPI, Debug) << "Applying WB R: " << awbStatus->gainR << " B: "
|
||||
<< awbStatus->gainB;
|
||||
|
||||
ctrls.set(V4L2_CID_RED_BALANCE,
|
||||
static_cast<int32_t>(awbStatus->gainR * 1000));
|
||||
ctrls.set(V4L2_CID_BLUE_BALANCE,
|
||||
static_cast<int32_t>(awbStatus->gainB * 1000));
|
||||
}
|
||||
|
||||
void IpaVc4::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)
|
||||
{
|
||||
ctrls.set(V4L2_CID_DIGITAL_GAIN,
|
||||
static_cast<int32_t>(dgStatus->digitalGain * 1000));
|
||||
}
|
||||
|
||||
void IpaVc4::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls)
|
||||
{
|
||||
bcm2835_isp_custom_ccm ccm;
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
ccm.ccm.ccm[i / 3][i % 3].den = 1000;
|
||||
ccm.ccm.ccm[i / 3][i % 3].num = 1000 * ccmStatus->matrix[i];
|
||||
}
|
||||
|
||||
ccm.enabled = 1;
|
||||
ccm.ccm.offsets[0] = ccm.ccm.offsets[1] = ccm.ccm.offsets[2] = 0;
|
||||
|
||||
ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ccm),
|
||||
sizeof(ccm) });
|
||||
ctrls.set(V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, c);
|
||||
}
|
||||
|
||||
void IpaVc4::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls)
|
||||
{
|
||||
bcm2835_isp_black_level blackLevel;
|
||||
|
||||
blackLevel.enabled = 1;
|
||||
blackLevel.black_level_r = blackLevelStatus->blackLevelR;
|
||||
blackLevel.black_level_g = blackLevelStatus->blackLevelG;
|
||||
blackLevel.black_level_b = blackLevelStatus->blackLevelB;
|
||||
|
||||
ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&blackLevel),
|
||||
sizeof(blackLevel) });
|
||||
ctrls.set(V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, c);
|
||||
}
|
||||
|
||||
void IpaVc4::applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls)
|
||||
{
|
||||
const unsigned int numGammaPoints = controller_.getHardwareConfig().numGammaPoints;
|
||||
struct bcm2835_isp_gamma gamma;
|
||||
|
||||
for (unsigned int i = 0; i < numGammaPoints - 1; i++) {
|
||||
int x = i < 16 ? i * 1024
|
||||
: (i < 24 ? (i - 16) * 2048 + 16384
|
||||
: (i - 24) * 4096 + 32768);
|
||||
gamma.x[i] = x;
|
||||
gamma.y[i] = std::min<uint16_t>(65535, contrastStatus->gammaCurve.eval(x));
|
||||
}
|
||||
|
||||
gamma.x[numGammaPoints - 1] = 65535;
|
||||
gamma.y[numGammaPoints - 1] = 65535;
|
||||
gamma.enabled = 1;
|
||||
|
||||
ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&gamma),
|
||||
sizeof(gamma) });
|
||||
ctrls.set(V4L2_CID_USER_BCM2835_ISP_GAMMA, c);
|
||||
}
|
||||
|
||||
void IpaVc4::applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls)
|
||||
{
|
||||
bcm2835_isp_geq geq;
|
||||
|
||||
geq.enabled = 1;
|
||||
geq.offset = geqStatus->offset;
|
||||
geq.slope.den = 1000;
|
||||
geq.slope.num = 1000 * geqStatus->slope;
|
||||
|
||||
ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&geq),
|
||||
sizeof(geq) });
|
||||
ctrls.set(V4L2_CID_USER_BCM2835_ISP_GEQ, c);
|
||||
}
|
||||
|
||||
void IpaVc4::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls)
|
||||
{
|
||||
using RPiController::DenoiseMode;
|
||||
|
||||
bcm2835_isp_denoise denoise;
|
||||
DenoiseMode mode = static_cast<DenoiseMode>(denoiseStatus->mode);
|
||||
|
||||
denoise.enabled = mode != DenoiseMode::Off;
|
||||
denoise.constant = denoiseStatus->noiseConstant;
|
||||
denoise.slope.num = 1000 * denoiseStatus->noiseSlope;
|
||||
denoise.slope.den = 1000;
|
||||
denoise.strength.num = 1000 * denoiseStatus->strength;
|
||||
denoise.strength.den = 1000;
|
||||
|
||||
/* Set the CDN mode to match the SDN operating mode. */
|
||||
bcm2835_isp_cdn cdn;
|
||||
switch (mode) {
|
||||
case DenoiseMode::ColourFast:
|
||||
cdn.enabled = 1;
|
||||
cdn.mode = CDN_MODE_FAST;
|
||||
break;
|
||||
case DenoiseMode::ColourHighQuality:
|
||||
cdn.enabled = 1;
|
||||
cdn.mode = CDN_MODE_HIGH_QUALITY;
|
||||
break;
|
||||
default:
|
||||
cdn.enabled = 0;
|
||||
}
|
||||
|
||||
ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&denoise),
|
||||
sizeof(denoise) });
|
||||
ctrls.set(V4L2_CID_USER_BCM2835_ISP_DENOISE, c);
|
||||
|
||||
c = ControlValue(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&cdn),
|
||||
sizeof(cdn) });
|
||||
ctrls.set(V4L2_CID_USER_BCM2835_ISP_CDN, c);
|
||||
}
|
||||
|
||||
void IpaVc4::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls)
|
||||
{
|
||||
bcm2835_isp_sharpen sharpen;
|
||||
|
||||
sharpen.enabled = 1;
|
||||
sharpen.threshold.num = 1000 * sharpenStatus->threshold;
|
||||
sharpen.threshold.den = 1000;
|
||||
sharpen.strength.num = 1000 * sharpenStatus->strength;
|
||||
sharpen.strength.den = 1000;
|
||||
sharpen.limit.num = 1000 * sharpenStatus->limit;
|
||||
sharpen.limit.den = 1000;
|
||||
|
||||
ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&sharpen),
|
||||
sizeof(sharpen) });
|
||||
ctrls.set(V4L2_CID_USER_BCM2835_ISP_SHARPEN, c);
|
||||
}
|
||||
|
||||
void IpaVc4::applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls)
|
||||
{
|
||||
bcm2835_isp_dpc dpc;
|
||||
|
||||
dpc.enabled = 1;
|
||||
dpc.strength = dpcStatus->strength;
|
||||
|
||||
ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&dpc),
|
||||
sizeof(dpc) });
|
||||
ctrls.set(V4L2_CID_USER_BCM2835_ISP_DPC, c);
|
||||
}
|
||||
|
||||
void IpaVc4::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls)
|
||||
{
|
||||
/*
|
||||
* Program lens shading tables into pipeline.
|
||||
* Choose smallest cell size that won't exceed 63x48 cells.
|
||||
*/
|
||||
const int cellSizes[] = { 16, 32, 64, 128, 256 };
|
||||
unsigned int numCells = std::size(cellSizes);
|
||||
unsigned int i, w, h, cellSize;
|
||||
for (i = 0; i < numCells; i++) {
|
||||
cellSize = cellSizes[i];
|
||||
w = (mode_.width + cellSize - 1) / cellSize;
|
||||
h = (mode_.height + cellSize - 1) / cellSize;
|
||||
if (w < 64 && h <= 48)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == numCells) {
|
||||
LOG(IPARPI, Error) << "Cannot find cell size";
|
||||
return;
|
||||
}
|
||||
|
||||
/* We're going to supply corner sampled tables, 16 bit samples. */
|
||||
w++, h++;
|
||||
bcm2835_isp_lens_shading ls = {
|
||||
.enabled = 1,
|
||||
.grid_cell_size = cellSize,
|
||||
.grid_width = w,
|
||||
.grid_stride = w,
|
||||
.grid_height = h,
|
||||
/* .dmabuf will be filled in by pipeline handler. */
|
||||
.dmabuf = 0,
|
||||
.ref_transform = 0,
|
||||
.corner_sampled = 1,
|
||||
.gain_format = GAIN_FORMAT_U4P10
|
||||
};
|
||||
|
||||
if (!lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) {
|
||||
LOG(IPARPI, Error) << "Do not have a correctly allocate lens shading table!";
|
||||
return;
|
||||
}
|
||||
|
||||
if (lsStatus) {
|
||||
/* Format will be u4.10 */
|
||||
uint16_t *grid = static_cast<uint16_t *>(lsTable_);
|
||||
|
||||
resampleTable(grid, lsStatus->r, w, h);
|
||||
resampleTable(grid + w * h, lsStatus->g, w, h);
|
||||
memcpy(grid + 2 * w * h, grid + w * h, w * h * sizeof(uint16_t));
|
||||
resampleTable(grid + 3 * w * h, lsStatus->b, w, h);
|
||||
}
|
||||
|
||||
ControlValue c(Span<const uint8_t>{ reinterpret_cast<uint8_t *>(&ls),
|
||||
sizeof(ls) });
|
||||
ctrls.set(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, c);
|
||||
}
|
||||
|
||||
void IpaVc4::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls)
|
||||
{
|
||||
if (afStatus->lensSetting) {
|
||||
ControlValue v(afStatus->lensSetting.value());
|
||||
lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, v);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Resamples a 16x12 table with central sampling to destW x destH with corner
|
||||
* sampling.
|
||||
*/
|
||||
void IpaVc4::resampleTable(uint16_t dest[], const std::vector<double> &src,
|
||||
int destW, int destH)
|
||||
{
|
||||
/*
|
||||
* Precalculate and cache the x sampling locations and phases to
|
||||
* save recomputing them on every row.
|
||||
*/
|
||||
assert(destW > 1 && destH > 1 && destW <= 64);
|
||||
int xLo[64], xHi[64];
|
||||
double xf[64];
|
||||
double x = -0.5, xInc = 16.0 / (destW - 1);
|
||||
for (int i = 0; i < destW; i++, x += xInc) {
|
||||
xLo[i] = floor(x);
|
||||
xf[i] = x - xLo[i];
|
||||
xHi[i] = xLo[i] < 15 ? xLo[i] + 1 : 15;
|
||||
xLo[i] = xLo[i] > 0 ? xLo[i] : 0;
|
||||
}
|
||||
|
||||
/* Now march over the output table generating the new values. */
|
||||
double y = -0.5, yInc = 12.0 / (destH - 1);
|
||||
for (int j = 0; j < destH; j++, y += yInc) {
|
||||
int yLo = floor(y);
|
||||
double yf = y - yLo;
|
||||
int yHi = yLo < 11 ? yLo + 1 : 11;
|
||||
yLo = yLo > 0 ? yLo : 0;
|
||||
double const *rowAbove = src.data() + yLo * 16;
|
||||
double const *rowBelow = src.data() + yHi * 16;
|
||||
for (int i = 0; i < destW; i++) {
|
||||
double above = rowAbove[xLo[i]] * (1 - xf[i]) + rowAbove[xHi[i]] * xf[i];
|
||||
double below = rowBelow[xLo[i]] * (1 - xf[i]) + rowBelow[xHi[i]] * xf[i];
|
||||
int result = floor(1024 * (above * (1 - yf) + below * yf) + .5);
|
||||
*(dest++) = result > 16383 ? 16383 : result; /* want u4.10 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace ipa::RPi */
|
||||
|
||||
/*
|
||||
* External IPA module interface
|
||||
*/
|
||||
extern "C" {
|
||||
const struct IPAModuleInfo ipaModuleInfo = {
|
||||
IPA_MODULE_API_VERSION,
|
||||
1,
|
||||
"PipelineHandlerRPi",
|
||||
"rpi/vc4",
|
||||
};
|
||||
|
||||
IPAInterface *ipaCreate()
|
||||
{
|
||||
return new ipa::RPi::IpaVc4();
|
||||
}
|
||||
|
||||
} /* extern "C" */
|
||||
|
||||
} /* namespace libcamera */
|
Loading…
Add table
Add a link
Reference in a new issue