<div dir="ltr"><div dir="ltr">Hi Jacopo,<div><br></div><div>Thank you for your review!</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, 24 Mar 2023 at 08:30, Jacopo Mondi <<a href="mailto:jacopo.mondi@ideasonboard.com" target="_blank">jacopo.mondi@ideasonboard.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Wed, Mar 22, 2023 at 01:06:05PM +0000, Naushir Patuck via libcamera-devel wrote:<br>
> Remove any hard-coded assumptions about the target hardware platform<br>
> from the ALSC algorithm. Instead, use the "target" string provided by<br>
> the camera tuning config and generalised statistics structures to<br>
> determing parameters such as grid and region sizes.<br>
><br>
> The ALSC calculations use run-time allocated arrays/vectors on every<br>
> frame. Allocating these might add a non-trivial run-time penalty.<br>
> Replace these dynamic allocations with a set of reusable pre-allocated<br>
> vectors during the init phase.<br>
><br>
> Signed-off-by: Naushir Patuck <<a href="mailto:naush@raspberrypi.com" target="_blank">naush@raspberrypi.com</a>><br>
> Signed-off-by: David Plowman <<a href="mailto:david.plowman@raspberrypi.com" target="_blank">david.plowman@raspberrypi.com</a>><br>
> ---<br>
> src/ipa/raspberrypi/controller/alsc_status.h | 13 +-<br>
> src/ipa/raspberrypi/controller/rpi/alsc.cpp | 341 +++++++++++--------<br>
> src/ipa/raspberrypi/controller/rpi/alsc.h | 29 +-<br>
> src/ipa/raspberrypi/raspberrypi.cpp | 9 +-<br>
> 4 files changed, 224 insertions(+), 168 deletions(-)<br>
><br>
> diff --git a/src/ipa/raspberrypi/controller/alsc_status.h b/src/ipa/raspberrypi/controller/alsc_status.h<br>
> index e5aa7e37c330..49a9f4a0cb5a 100644<br>
> --- a/src/ipa/raspberrypi/controller/alsc_status.h<br>
> +++ b/src/ipa/raspberrypi/controller/alsc_status.h<br>
> @@ -6,16 +6,17 @@<br>
> */<br>
> #pragma once<br>
><br>
> +#include <vector><br>
> +<br>
> /*<br>
> * The ALSC algorithm should post the following structure into the image's<br>
> * "alsc.status" metadata.<br>
> */<br>
><br>
> -constexpr unsigned int AlscCellsX = 16;<br>
> -constexpr unsigned int AlscCellsY = 12;<br>
> -<br>
> struct AlscStatus {<br>
> - double r[AlscCellsY][AlscCellsX];<br>
> - double g[AlscCellsY][AlscCellsX];<br>
> - double b[AlscCellsY][AlscCellsX];<br>
> + std::vector<double> r;<br>
> + std::vector<double> g;<br>
> + std::vector<double> b;<br>
> + unsigned int rows;<br>
> + unsigned int cols;<br>
> };<br>
> diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.cpp b/src/ipa/raspberrypi/controller/rpi/alsc.cpp<br>
> index eb4e2f9496e1..51fe5d73f52d 100644<br>
> --- a/src/ipa/raspberrypi/controller/rpi/alsc.cpp<br>
> +++ b/src/ipa/raspberrypi/controller/rpi/alsc.cpp<br>
> @@ -5,6 +5,7 @@<br>
> * alsc.cpp - ALSC (auto lens shading correction) control algorithm<br>
> */<br>
><br>
> +#include <algorithm><br>
> #include <functional><br>
> #include <math.h><br>
> #include <numeric><br>
> @@ -24,9 +25,6 @@ LOG_DEFINE_CATEGORY(RPiAlsc)<br>
><br>
> #define NAME "rpi.alsc"<br>
><br>
> -static const int X = AlscCellsX;<br>
> -static const int Y = AlscCellsY;<br>
> -static const int XY = X * Y;<br>
> static const double InsufficientData = -1.0;<br>
><br>
> Alsc::Alsc(Controller *controller)<br>
> @@ -51,8 +49,11 @@ char const *Alsc::name() const<br>
> return NAME;<br>
> }<br>
><br>
> -static int generateLut(double *lut, const libcamera::YamlObject ¶ms)<br>
> +static int generateLut(std::vector<double> &lut, const libcamera::YamlObject ¶ms,<br>
> + const Size &size)<br>
> {<br>
> + /* These must be signed ints for the co-ordinate calculations below. */<br>
> + int X = size.width, Y = size.height;<br>
> double cstrength = params["corner_strength"].get<double>(2.0);<br>
> if (cstrength <= 1.0) {<br>
> LOG(RPiAlsc, Error) << "corner_strength must be > 1.0";<br>
> @@ -81,9 +82,9 @@ static int generateLut(double *lut, const libcamera::YamlObject ¶ms)<br>
> return 0;<br>
> }<br>
><br>
> -static int readLut(double *lut, const libcamera::YamlObject ¶ms)<br>
> +static int readLut(std::vector<double> &lut, const libcamera::YamlObject ¶ms, const Size &size)<br>
> {<br>
> - if (params.size() != XY) {<br>
> + if (params.size() != size.width * size.height) {<br>
> LOG(RPiAlsc, Error) << "Invalid number of entries in LSC table";<br>
> return -EINVAL;<br>
> }<br>
> @@ -101,7 +102,7 @@ static int readLut(double *lut, const libcamera::YamlObject ¶ms)<br>
><br>
> static int readCalibrations(std::vector<AlscCalibration> &calibrations,<br>
> const libcamera::YamlObject ¶ms,<br>
> - std::string const &name)<br>
> + std::string const &name, const Size &size)<br>
> {<br>
> if (params.contains(name)) {<br>
> double lastCt = 0;<br>
> @@ -119,7 +120,7 @@ static int readCalibrations(std::vector<AlscCalibration> &calibrations,<br>
> calibration.ct = lastCt = ct;<br>
><br>
> const libcamera::YamlObject &table = p["table"];<br>
> - if (table.size() != XY) {<br>
> + if (table.size() != size.width * size.height) {<br>
> LOG(RPiAlsc, Error)<br>
> << "Incorrect number of values for ct "<br>
> << ct << " in " << name;<br>
> @@ -127,6 +128,7 @@ static int readCalibrations(std::vector<AlscCalibration> &calibrations,<br>
> }<br>
><br>
> int num = 0;<br>
> + calibration.table.resize(size.width * size.height);<br>
> for (const auto &elem : table.asList()) {<br>
> value = elem.get<double>();<br>
> if (!value)<br>
> @@ -134,7 +136,7 @@ static int readCalibrations(std::vector<AlscCalibration> &calibrations,<br>
> calibration.table[num++] = *value;<br>
> }<br>
><br>
> - calibrations.push_back(calibration);<br>
> + calibrations.push_back(std::move(calibration));<br>
> LOG(RPiAlsc, Debug)<br>
> << "Read " << name << " calibration for ct " << ct;<br>
> }<br>
> @@ -144,6 +146,7 @@ static int readCalibrations(std::vector<AlscCalibration> &calibrations,<br>
><br>
> int Alsc::read(const libcamera::YamlObject ¶ms)<br>
> {<br>
> + config_.tableSize = getHardwareConfig().awbRegions;<br>
> config_.framePeriod = params["frame_period"].get<uint16_t>(12);<br>
> config_.startupFrames = params["startup_frames"].get<uint16_t>(10);<br>
> config_.speed = params["speed"].get<double>(0.05);<br>
> @@ -153,28 +156,29 @@ int Alsc::read(const libcamera::YamlObject ¶ms)<br>
> config_.minCount = params["min_count"].get<double>(10.0);<br>
> config_.minG = params["min_G"].get<uint16_t>(50);<br>
> config_.omega = params["omega"].get<double>(1.3);<br>
> - config_.nIter = params["n_iter"].get<uint32_t>(X + Y);<br>
> + config_.nIter = params["n_iter"].get<uint32_t>(config_.tableSize.width + config_.tableSize.height);<br>
> config_.luminanceStrength =<br>
> params["luminance_strength"].get<double>(1.0);<br>
> - for (int i = 0; i < XY; i++)<br>
> - config_.luminanceLut[i] = 1.0;<br>
><br>
> + config_.luminanceLut.resize(config_.tableSize.width * config_.tableSize.height, 1.0);<br>
> int ret = 0;<br>
><br>
> if (params.contains("corner_strength"))<br>
> - ret = generateLut(config_.luminanceLut, params);<br>
> + ret = generateLut(config_.luminanceLut, params, config_.tableSize);<br>
> else if (params.contains("luminance_lut"))<br>
> - ret = readLut(config_.luminanceLut, params["luminance_lut"]);<br>
> + ret = readLut(config_.luminanceLut, params["luminance_lut"], config_.tableSize);<br>
> else<br>
> LOG(RPiAlsc, Warning)<br>
> << "no luminance table - assume unity everywhere";<br>
> if (ret)<br>
> return ret;<br>
><br>
> - ret = readCalibrations(config_.calibrationsCr, params, "calibrations_Cr");<br>
> + ret = readCalibrations(config_.calibrationsCr, params, "calibrations_Cr",<br>
> + config_.tableSize);<br>
> if (ret)<br>
> return ret;<br>
> - ret = readCalibrations(config_.calibrationsCb, params, "calibrations_Cb");<br>
> + ret = readCalibrations(config_.calibrationsCb, params, "calibrations_Cb",<br>
> + config_.tableSize);<br>
> if (ret)<br>
> return ret;<br>
><br>
> @@ -187,13 +191,16 @@ int Alsc::read(const libcamera::YamlObject ¶ms)<br>
><br>
> static double getCt(Metadata *metadata, double defaultCt);<br>
> static void getCalTable(double ct, std::vector<AlscCalibration> const &calibrations,<br>
> - double calTable[XY]);<br>
> -static void resampleCalTable(double const calTableIn[XY], CameraMode const &cameraMode,<br>
> - double calTableOut[XY]);<br>
> -static void compensateLambdasForCal(double const calTable[XY], double const oldLambdas[XY],<br>
> - double newLambdas[XY]);<br>
> -static void addLuminanceToTables(double results[3][Y][X], double const lambdaR[XY], double lambdaG,<br>
> - double const lambdaB[XY], double const luminanceLut[XY],<br>
> + std::vector<double> &calTable);<br>
> +static void resampleCalTable(const std::vector<double> &calTableIn, CameraMode const &cameraMode,<br>
> + const Size &size, std::vector<double> &calTableOut);<br>
> +static void compensateLambdasForCal(const std::vector<double> &calTable,<br>
> + const std::vector<double> &oldLambdas,<br>
> + std::vector<double> &newLambdas);<br>
> +static void addLuminanceToTables(std::array<std::vector<double>, 3> &results,<br>
> + const std::vector<double> &lambdaR, double lambdaG,<br>
> + const std::vector<double> &lambdaB,<br>
> + const std::vector<double> &luminanceLut,<br>
> double luminanceStrength);<br>
><br>
> void Alsc::initialise()<br>
> @@ -201,7 +208,28 @@ void Alsc::initialise()<br>
> frameCount2_ = frameCount_ = framePhase_ = 0;<br>
> firstTime_ = true;<br>
> ct_ = config_.defaultCt;<br>
> +<br>
> + const size_t XY = config_.tableSize.width * config_.tableSize.height;<br>
> +<br>
> + for (auto &r : syncResults_)<br>
> + r.resize(XY);<br>
> + for (auto &r : prevSyncResults_)<br>
> + r.resize(XY);<br>
> + for (auto &r : asyncResults_)<br>
> + r.resize(XY);<br>
> +<br>
> + luminanceTable_.resize(XY);<br>
> + asyncLambdaR_.resize(XY);<br>
> + asyncLambdaB_.resize(XY);<br>
> /* The lambdas are initialised in the SwitchMode. */<br>
> + lambdaR_.resize(XY);<br>
> + lambdaB_.resize(XY);<br>
> +<br>
> + /* Temporaries for the computations, but sensible to allocate this up-front! */<br>
> + for (auto &c : tmpC_)<br>
> + c.resize(XY);<br>
> + for (auto &m : tmpM_)<br>
> + m.resize(XY);<br>
> }<br>
><br>
> void Alsc::waitForAysncThread()<br>
> @@ -262,7 +290,7 @@ void Alsc::switchMode(CameraMode const &cameraMode,<br>
> * We must resample the luminance table like we do the others, but it's<br>
> * fixed so we can simply do it up front here.<br>
> */<br>
> - resampleCalTable(config_.luminanceLut, cameraMode_, luminanceTable_);<br>
> + resampleCalTable(config_.luminanceLut, cameraMode_, config_.tableSize, luminanceTable_);<br>
><br>
> if (resetTables) {<br>
> /*<br>
> @@ -272,18 +300,18 @@ void Alsc::switchMode(CameraMode const &cameraMode,<br>
> * the lambdas, but the rest of this code then echoes the code in<br>
> * doAlsc, without the adaptive algorithm.<br>
> */<br>
> - for (int i = 0; i < XY; i++)<br>
> - lambdaR_[i] = lambdaB_[i] = 1.0;<br>
> - double calTableR[XY], calTableB[XY], calTableTmp[XY];<br>
> + std::fill(lambdaR_.begin(), lambdaR_.end(), 1.0);<br>
> + std::fill(lambdaB_.begin(), lambdaB_.end(), 1.0);<br>
> + std::vector<double> &calTableR = tmpC_[0], &calTableB = tmpC_[1], &calTableTmp = tmpC_[2];<br>
> getCalTable(ct_, config_.calibrationsCr, calTableTmp);<br>
> - resampleCalTable(calTableTmp, cameraMode_, calTableR);<br>
> + resampleCalTable(calTableTmp, cameraMode_, config_.tableSize, calTableR);<br>
> getCalTable(ct_, config_.calibrationsCb, calTableTmp);<br>
> - resampleCalTable(calTableTmp, cameraMode_, calTableB);<br>
> + resampleCalTable(calTableTmp, cameraMode_, config_.tableSize, calTableB);<br>
> compensateLambdasForCal(calTableR, lambdaR_, asyncLambdaR_);<br>
> compensateLambdasForCal(calTableB, lambdaB_, asyncLambdaB_);<br>
> addLuminanceToTables(syncResults_, asyncLambdaR_, 1.0, asyncLambdaB_,<br>
> luminanceTable_, config_.luminanceStrength);<br>
> - memcpy(prevSyncResults_, syncResults_, sizeof(prevSyncResults_));<br>
> + prevSyncResults_ = syncResults_;<br>
> framePhase_ = config_.framePeriod; /* run the algo again asap */<br>
> firstTime_ = false;<br>
> }<br>
> @@ -294,7 +322,7 @@ void Alsc::fetchAsyncResults()<br>
> LOG(RPiAlsc, Debug) << "Fetch ALSC results";<br>
> asyncFinished_ = false;<br>
> asyncStarted_ = false;<br>
> - memcpy(syncResults_, asyncResults_, sizeof(syncResults_));<br>
> + syncResults_ = asyncResults_;<br>
> }<br>
><br>
> double getCt(Metadata *metadata, double defaultCt)<br>
> @@ -316,9 +344,9 @@ static void copyStats(RgbyRegions ®ions, StatisticsPtr &stats,<br>
> if (!regions.numRegions())<br>
> regions.init(stats->awbRegions.size());<br>
><br>
> - double *rTable = (double *)status.r;<br>
> - double *gTable = (double *)status.g;<br>
> - double *bTable = (double *)status.b;<br>
> + const std::vector<double> &rTable = status.r;<br>
> + const std::vector<double> &gTable = status.g;<br>
> + const std::vector<double> &bTable = status.b;<br>
> for (unsigned int i = 0; i < stats->awbRegions.numRegions(); i++) {<br>
> auto r = stats->awbRegions.get(i);<br>
> r.val.rSum = static_cast<uint64_t>(r.val.rSum / rTable[i]);<br>
> @@ -344,12 +372,9 @@ void Alsc::restartAsync(StatisticsPtr &stats, Metadata *imageMetadata)<br>
> if (imageMetadata->get("alsc.status", alscStatus) != 0) {<br>
> LOG(RPiAlsc, Warning)<br>
> << "No ALSC status found for applied gains!";<br>
> - for (int y = 0; y < Y; y++)<br>
> - for (int x = 0; x < X; x++) {<br>
> - alscStatus.r[y][x] = 1.0;<br>
> - alscStatus.g[y][x] = 1.0;<br>
> - alscStatus.b[y][x] = 1.0;<br>
> - }<br>
> + alscStatus.r.resize(config_.tableSize.width * config_.tableSize.height, 1.0);<br>
> + alscStatus.g.resize(config_.tableSize.width * config_.tableSize.height, 1.0);<br>
> + alscStatus.b.resize(config_.tableSize.width * config_.tableSize.height, 1.0);<br>
> }<br>
> copyStats(statistics_, stats, alscStatus);<br>
> framePhase_ = 0;<br>
> @@ -380,15 +405,15 @@ void Alsc::prepare(Metadata *imageMetadata)<br>
> fetchAsyncResults();<br>
> }<br>
> /* Apply IIR filter to results and program into the pipeline. */<br>
> - double *ptr = (double *)syncResults_,<br>
> - *pptr = (double *)prevSyncResults_;<br>
> - for (unsigned int i = 0; i < sizeof(syncResults_) / sizeof(double); i++)<br>
> - pptr[i] = speed * ptr[i] + (1.0 - speed) * pptr[i];<br>
> + for (unsigned int j = 0; j < syncResults_.size(); j++) {<br>
> + for (unsigned int i = 0; i < syncResults_[j].size(); i++)<br>
> + prevSyncResults_[j][i] = speed * syncResults_[j][i] + (1.0 - speed) * prevSyncResults_[j][i];<br>
> + }<br>
> /* Put output values into status metadata. */<br>
> AlscStatus status;<br>
> - memcpy(status.r, prevSyncResults_[0], sizeof(status.r));<br>
> - memcpy(status.g, prevSyncResults_[1], sizeof(status.g));<br>
> - memcpy(status.b, prevSyncResults_[2], sizeof(status.b));<br>
> + status.r = prevSyncResults_[0];<br>
> + status.g = prevSyncResults_[1];<br>
> + status.b = prevSyncResults_[2];<br>
> imageMetadata->set("alsc.status", status);<br>
> }<br>
><br>
> @@ -432,18 +457,17 @@ void Alsc::asyncFunc()<br>
> }<br>
><br>
> void getCalTable(double ct, std::vector<AlscCalibration> const &calibrations,<br>
> - double calTable[XY])<br>
> + std::vector<double> &calTable)<br>
> {<br>
> if (calibrations.empty()) {<br>
> - for (int i = 0; i < XY; i++)<br>
> - calTable[i] = 1.0;<br>
> + std::fill(calTable.begin(), calTable.end(), 1.0);<br>
> LOG(RPiAlsc, Debug) << "no calibrations found";<br>
> } else if (ct <= calibrations.front().ct) {<br>
> - memcpy(calTable, calibrations.front().table, XY * sizeof(double));<br>
> + calTable = calibrations.front().table;<br>
> LOG(RPiAlsc, Debug) << "using calibration for "<br>
> << calibrations.front().ct;<br>
> } else if (ct >= calibrations.back().ct) {<br>
> - memcpy(calTable, calibrations.back().table, XY * sizeof(double));<br>
> + calTable = calibrations.back().table;<br>
> LOG(RPiAlsc, Debug) << "using calibration for "<br>
> << calibrations.back().ct;<br>
> } else {<br>
> @@ -454,7 +478,7 @@ void getCalTable(double ct, std::vector<AlscCalibration> const &calibrations,<br>
> LOG(RPiAlsc, Debug)<br>
> << "ct is " << ct << ", interpolating between "<br>
> << ct0 << " and " << ct1;<br>
> - for (int i = 0; i < XY; i++)<br>
> + for (unsigned int i = 0; i < calTable.size(); i++)<br>
> calTable[i] =<br>
> (calibrations[idx].table[i] * (ct1 - ct) +<br>
> calibrations[idx + 1].table[i] * (ct - ct0)) /<br>
> @@ -462,9 +486,13 @@ void getCalTable(double ct, std::vector<AlscCalibration> const &calibrations,<br>
> }<br>
> }<br>
><br>
> -void resampleCalTable(double const calTableIn[XY],<br>
> - CameraMode const &cameraMode, double calTableOut[XY])<br>
> +void resampleCalTable(const std::vector<double> &calTableIn,<br>
> + CameraMode const &cameraMode, const Size &size,<br>
> + std::vector<double> &calTableOut)<br>
> {<br>
> + int X = size.width;<br>
> + int Y = size.height;<br>
> +<br>
> /*<br>
> * Precalculate and cache the x sampling locations and phases to save<br>
> * recomputing them on every row.<br>
> @@ -501,23 +529,24 @@ void resampleCalTable(double const calTableIn[XY],<br>
> yLo = Y - 1 - yLo;<br>
> yHi = Y - 1 - yHi;<br>
> }<br>
> - double const *rowAbove = calTableIn + X * yLo;<br>
> - double const *rowBelow = calTableIn + X * yHi;<br>
> + double const *rowAbove = calTableIn.data() + X * yLo;<br>
> + double const *rowBelow = calTableIn.data() + X * yHi;<br>
> + double *out = calTableOut.data() + X * j;<br>
> for (int i = 0; i < X; i++) {<br>
> double above = rowAbove[xLo[i]] * (1 - xf[i]) +<br>
> rowAbove[xHi[i]] * xf[i];<br>
> double below = rowBelow[xLo[i]] * (1 - xf[i]) +<br>
> rowBelow[xHi[i]] * xf[i];<br>
> - *(calTableOut++) = above * (1 - yf) + below * yf;<br>
> + *(out++) = above * (1 - yf) + below * yf;<br>
> }<br>
> }<br>
> }<br>
><br>
> /* Calculate chrominance statistics (R/G and B/G) for each region. */<br>
> -static void calculateCrCb(const RgbyRegions &awbRegion, double cr[XY],<br>
> - double cb[XY], uint32_t minCount, uint16_t minG)<br>
> +static void calculateCrCb(const RgbyRegions &awbRegion, std::vector<double> &cr,<br>
> + std::vector<double> &cb, uint32_t minCount, uint16_t minG)<br>
> {<br>
> - for (int i = 0; i < XY; i++) {<br>
> + for (unsigned int i = 0; i < cr.size(); i++) {<br>
> auto s = awbRegion.get(i);<br>
><br>
> if (s.counted <= minCount || s.val.gSum / s.counted <= minG) {<br>
> @@ -530,33 +559,34 @@ static void calculateCrCb(const RgbyRegions &awbRegion, double cr[XY],<br>
> }<br>
> }<br>
><br>
> -static void applyCalTable(double const calTable[XY], double C[XY])<br>
> +static void applyCalTable(const std::vector<double> &calTable, std::vector<double> &C)<br>
> {<br>
> - for (int i = 0; i < XY; i++)<br>
> + for (unsigned int i = 0; i < C.size(); i++)<br>
> if (C[i] != InsufficientData)<br>
> C[i] *= calTable[i];<br>
> }<br>
><br>
> -void compensateLambdasForCal(double const calTable[XY],<br>
> - double const oldLambdas[XY],<br>
> - double newLambdas[XY])<br>
> +void compensateLambdasForCal(const std::vector<double> &calTable,<br>
> + const std::vector<double> &oldLambdas,<br>
> + std::vector<double> &newLambdas)<br>
> {<br>
> double minNewLambda = std::numeric_limits<double>::max();<br>
> - for (int i = 0; i < XY; i++) {<br>
> + for (unsigned int i = 0; i < newLambdas.size(); i++) {<br>
> newLambdas[i] = oldLambdas[i] * calTable[i];<br>
> minNewLambda = std::min(minNewLambda, newLambdas[i]);<br>
> }<br>
> - for (int i = 0; i < XY; i++)<br>
> + for (unsigned int i = 0; i < newLambdas.size(); i++)<br>
> newLambdas[i] /= minNewLambda;<br>
> }<br>
><br>
> -[[maybe_unused]] static void printCalTable(double const C[XY])<br>
> +[[maybe_unused]] static void printCalTable(const std::vector<double> &C,<br>
> + const Size &size)<br>
> {<br>
> printf("table: [\n");<br>
> - for (int j = 0; j < Y; j++) {<br>
> - for (int i = 0; i < X; i++) {<br>
> - printf("%5.3f", 1.0 / C[j * X + i]);<br>
> - if (i != X - 1 || j != Y - 1)<br>
> + for (unsigned int j = 0; j < size.height; j++) {<br>
> + for (unsigned int i = 0; i < size.width; i++) {<br>
> + printf("%5.3f", 1.0 / C[j * size.width + i]);<br>
> + if (i != size.width - 1 || j != size.height - 1)<br>
> printf(",");<br>
> }<br>
> printf("\n");<br>
> @@ -577,9 +607,13 @@ static double computeWeight(double Ci, double Cj, double sigma)<br>
> }<br>
><br>
> /* Compute all weights. */<br>
> -static void computeW(double const C[XY], double sigma, double W[XY][4])<br>
> +static void computeW(const std::vector<double> &C, double sigma,<br>
> + std::vector<std::array<double, 4>> &W, const Size &size)<br>
> {<br>
> - for (int i = 0; i < XY; i++) {<br>
> + size_t XY = size.width * size.height;<br>
> + size_t X = size.width;<br>
> +<br>
> + for (unsigned int i = 0; i < XY; i++) {<br>
> /* Start with neighbour above and go clockwise. */<br>
> W[i][0] = i >= X ? computeWeight(C[i], C[i - X], sigma) : 0;<br>
> W[i][1] = i % X < X - 1 ? computeWeight(C[i], C[i + 1], sigma) : 0;<br>
> @@ -589,11 +623,16 @@ static void computeW(double const C[XY], double sigma, double W[XY][4])<br>
> }<br>
><br>
> /* Compute M, the large but sparse matrix such that M * lambdas = 0. */<br>
> -static void constructM(double const C[XY], double const W[XY][4],<br>
> - double M[XY][4])<br>
> +static void constructM(const std::vector<double> &C,<br>
> + const std::vector<std::array<double, 4>> &W,<br>
> + std::vector<std::array<double, 4>> &M,<br>
> + const Size &size)<br>
> {<br>
> + size_t XY = size.width * size.height;<br>
> + size_t X = size.width;<br>
> +<br>
> double epsilon = 0.001;<br>
> - for (int i = 0; i < XY; i++) {<br>
> + for (unsigned int i = 0; i < XY; i++) {<br>
> /*<br>
> * Note how, if C[i] == INSUFFICIENT_DATA, the weights will all<br>
> * be zero so the equation is still set up correctly.<br>
> @@ -614,79 +653,80 @@ static void constructM(double const C[XY], double const W[XY][4],<br>
> * left/right neighbours are zero down the left/right edges, so we don't need<br>
> * need to test the i value to exclude them.<br>
> */<br>
> -static double computeLambdaBottom(int i, double const M[XY][4],<br>
> - double lambda[XY])<br>
> +static double computeLambdaBottom(int i, const std::vector<std::array<double, 4>> &M,<br>
> + std::vector<double> &lambda, const Size &size)<br>
> {<br>
> - return M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + X] +<br>
> + return M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + size.width] +<br>
> M[i][3] * lambda[i - 1];<br>
> }<br>
> -static double computeLambdaBottomStart(int i, double const M[XY][4],<br>
> - double lambda[XY])<br>
> +static double computeLambdaBottomStart(int i, const std::vector<std::array<double, 4>> &M,<br>
> + std::vector<double> &lambda, const Size &size)<br>
> {<br>
> - return M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + X];<br>
> + return M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + size.width];<br>
> }<br>
> -static double computeLambdaInterior(int i, double const M[XY][4],<br>
> - double lambda[XY])<br>
> +static double computeLambdaInterior(int i, const std::vector<std::array<double, 4>> &M,<br>
> + std::vector<double> &lambda, const Size &size)<br>
> {<br>
> - return M[i][0] * lambda[i - X] + M[i][1] * lambda[i + 1] +<br>
> - M[i][2] * lambda[i + X] + M[i][3] * lambda[i - 1];<br>
> + return M[i][0] * lambda[i - size.width] + M[i][1] * lambda[i + 1] +<br>
> + M[i][2] * lambda[i + size.width] + M[i][3] * lambda[i - 1];<br>
> }<br>
> -static double computeLambdaTop(int i, double const M[XY][4],<br>
> - double lambda[XY])<br>
> +static double computeLambdaTop(int i, const std::vector<std::array<double, 4>> &M,<br>
> + std::vector<double> &lambda, const Size &size)<br>
> {<br>
> - return M[i][0] * lambda[i - X] + M[i][1] * lambda[i + 1] +<br>
> + return M[i][0] * lambda[i - size.width] + M[i][1] * lambda[i + 1] +<br>
> M[i][3] * lambda[i - 1];<br>
> }<br>
> -static double computeLambdaTopEnd(int i, double const M[XY][4],<br>
> - double lambda[XY])<br>
> +static double computeLambdaTopEnd(int i, const std::vector<std::array<double, 4>> &M,<br>
> + std::vector<double> &lambda, const Size &size)<br>
> {<br>
> - return M[i][0] * lambda[i - X] + M[i][3] * lambda[i - 1];<br>
> + return M[i][0] * lambda[i - size.width] + M[i][3] * lambda[i - 1];<br>
> }<br>
><br>
> /* Gauss-Seidel iteration with over-relaxation. */<br>
> -static double gaussSeidel2Sor(double const M[XY][4], double omega,<br>
> - double lambda[XY], double lambdaBound)<br>
> +static double gaussSeidel2Sor(const std::vector<std::array<double, 4>> &M, double omega,<br>
> + std::vector<double> &lambda, double lambdaBound,<br>
> + const Size &size)<br>
> {<br>
> + int XY = size.width * size.height;<br>
> + int X = size.width;<br>
> const double min = 1 - lambdaBound, max = 1 + lambdaBound;<br>
> - double oldLambda[XY];<br>
> + std::vector<double> oldLambda = lambda;<br>
> int i;<br>
> - for (i = 0; i < XY; i++)<br>
> - oldLambda[i] = lambda[i];<br>
> - lambda[0] = computeLambdaBottomStart(0, M, lambda);<br>
> + lambda[0] = computeLambdaBottomStart(0, M, lambda, size);<br>
> lambda[0] = std::clamp(lambda[0], min, max);<br>
> for (i = 1; i < X; i++) {<br>
> - lambda[i] = computeLambdaBottom(i, M, lambda);<br>
> + lambda[i] = computeLambdaBottom(i, M, lambda, size);<br>
> lambda[i] = std::clamp(lambda[i], min, max);<br>
> }<br>
> for (; i < XY - X; i++) {<br>
> - lambda[i] = computeLambdaInterior(i, M, lambda);<br>
> + lambda[i] = computeLambdaInterior(i, M, lambda, size);<br>
> lambda[i] = std::clamp(lambda[i], min, max);<br>
> }<br>
> for (; i < XY - 1; i++) {<br>
> - lambda[i] = computeLambdaTop(i, M, lambda);<br>
> + lambda[i] = computeLambdaTop(i, M, lambda, size);<br>
> lambda[i] = std::clamp(lambda[i], min, max);<br>
> }<br>
> - lambda[i] = computeLambdaTopEnd(i, M, lambda);<br>
> + lambda[i] = computeLambdaTopEnd(i, M, lambda, size);<br>
> lambda[i] = std::clamp(lambda[i], min, max);<br>
> /*<br>
> * Also solve the system from bottom to top, to help spread the updates<br>
> * better.<br>
> */<br>
> - lambda[i] = computeLambdaTopEnd(i, M, lambda);<br>
> + lambda[i] = computeLambdaTopEnd(i, M, lambda, size);<br>
> lambda[i] = std::clamp(lambda[i], min, max);<br>
> for (i = XY - 2; i >= XY - X; i--) {<br>
> - lambda[i] = computeLambdaTop(i, M, lambda);<br>
> + lambda[i] = computeLambdaTop(i, M, lambda, size);<br>
> lambda[i] = std::clamp(lambda[i], min, max);<br>
> }<br>
> for (; i >= X; i--) {<br>
> - lambda[i] = computeLambdaInterior(i, M, lambda);<br>
> + lambda[i] = computeLambdaInterior(i, M, lambda, size);<br>
> lambda[i] = std::clamp(lambda[i], min, max);<br>
> }<br>
> for (; i >= 1; i--) {<br>
> - lambda[i] = computeLambdaBottom(i, M, lambda);<br>
> + lambda[i] = computeLambdaBottom(i, M, lambda, size);<br>
> lambda[i] = std::clamp(lambda[i], min, max);<br>
> }<br>
> - lambda[0] = computeLambdaBottomStart(0, M, lambda);<br>
> + lambda[0] = computeLambdaBottomStart(0, M, lambda, size);<br>
> lambda[0] = std::clamp(lambda[0], min, max);<br>
> double maxDiff = 0;<br>
> for (i = 0; i < XY; i++) {<br>
> @@ -698,33 +738,33 @@ static double gaussSeidel2Sor(double const M[XY][4], double omega,<br>
> }<br>
><br>
> /* Normalise the values so that the smallest value is 1. */<br>
> -static void normalise(double *ptr, size_t n)<br>
> +static void normalise(std::vector<double> &results)<br>
> {<br>
> - double minval = ptr[0];<br>
> - for (size_t i = 1; i < n; i++)<br>
> - minval = std::min(minval, ptr[i]);<br>
> - for (size_t i = 0; i < n; i++)<br>
> - ptr[i] /= minval;<br>
> + double minval = *std::min_element(results.begin(), results.end());<br>
> + std::for_each(results.begin(), results.end(),<br>
> + [minval](double val) { return val / minval; });<br>
> }<br>
><br>
> /* Rescale the values so that the average value is 1. */<br>
> -static void reaverage(Span<double> data)<br>
> +static void reaverage(std::vector<double> &data)<br>
> {<br>
> double sum = std::accumulate(data.begin(), data.end(), 0.0);<br>
> double ratio = 1 / (sum / data.size());<br>
> - for (double &d : data)<br>
> - d *= ratio;<br>
> + std::for_each(data.begin(), data.end(),<br>
> + [ratio](double val) { return val * ratio; });<br>
> }<br>
><br>
> -static void runMatrixIterations(double const C[XY], double lambda[XY],<br>
> - double const W[XY][4], double omega,<br>
> - int nIter, double threshold, double lambdaBound)<br>
> +static void runMatrixIterations(const std::vector<double> &C,<br>
> + std::vector<double> &lambda,<br>
> + const std::vector<std::array<double, 4>> &W,<br>
> + std::vector<std::array<double, 4>> &M, double omega,<br>
> + unsigned int nIter, double threshold, double lambdaBound,<br>
> + const Size &size)<br>
> {<br>
> - double M[XY][4];<br>
> - constructM(C, W, M);<br>
> + constructM(C, W, M, size);<br>
> double lastMaxDiff = std::numeric_limits<double>::max();<br>
> - for (int i = 0; i < nIter; i++) {<br>
> - double maxDiff = fabs(gaussSeidel2Sor(M, omega, lambda, lambdaBound));<br>
> + for (unsigned int i = 0; i < nIter; i++) {<br>
> + double maxDiff = fabs(gaussSeidel2Sor(M, omega, lambda, lambdaBound, size));<br>
> if (maxDiff < threshold) {<br>
> LOG(RPiAlsc, Debug)<br>
> << "Stop after " << i + 1 << " iterations";<br>
> @@ -741,39 +781,44 @@ static void runMatrixIterations(double const C[XY], double lambda[XY],<br>
> lastMaxDiff = maxDiff;<br>
> }<br>
> /* We're going to normalise the lambdas so the total average is 1. */<br>
> - reaverage({ lambda, XY });<br>
> + reaverage(lambda);<br>
> }<br>
><br>
> -static void addLuminanceRb(double result[XY], double const lambda[XY],<br>
> - double const luminanceLut[XY],<br>
> +static void addLuminanceRb(std::vector<double> &result, const std::vector<double> &lambda,<br>
> + const std::vector<double> &luminanceLut,<br>
> double luminanceStrength)<br>
> {<br>
> - for (int i = 0; i < XY; i++)<br>
> + for (unsigned int i = 0; i < result.size(); i++)<br>
> result[i] = lambda[i] * ((luminanceLut[i] - 1) * luminanceStrength + 1);<br>
> }<br>
><br>
> -static void addLuminanceG(double result[XY], double lambda,<br>
> - double const luminanceLut[XY],<br>
> +static void addLuminanceG(std::vector<double> &result, double lambda,<br>
> + const std::vector<double> &luminanceLut,<br>
> double luminanceStrength)<br>
> {<br>
> - for (int i = 0; i < XY; i++)<br>
> + for (unsigned int i = 0; i < result.size(); i++)<br>
> result[i] = lambda * ((luminanceLut[i] - 1) * luminanceStrength + 1);<br>
> }<br>
><br>
> -void addLuminanceToTables(double results[3][Y][X], double const lambdaR[XY],<br>
> - double lambdaG, double const lambdaB[XY],<br>
> - double const luminanceLut[XY],<br>
> +void addLuminanceToTables(std::array<std::vector<double>, 3> &results,<br>
> + const std::vector<double> &lambdaR,<br>
> + double lambdaG, const std::vector<double> &lambdaB,<br>
> + const std::vector<double> &luminanceLut,<br>
> double luminanceStrength)<br>
> {<br>
> - addLuminanceRb((double *)results[0], lambdaR, luminanceLut, luminanceStrength);<br>
> - addLuminanceG((double *)results[1], lambdaG, luminanceLut, luminanceStrength);<br>
> - addLuminanceRb((double *)results[2], lambdaB, luminanceLut, luminanceStrength);<br>
> - normalise((double *)results, 3 * XY);<br>
> + addLuminanceRb(results[0], lambdaR, luminanceLut, luminanceStrength);<br>
> + addLuminanceG(results[1], lambdaG, luminanceLut, luminanceStrength);<br>
> + addLuminanceRb(results[2], lambdaB, luminanceLut, luminanceStrength);<br>
> + for (auto &r : results)<br>
> + normalise(r);<br>
> }<br>
><br>
> void Alsc::doAlsc()<br>
> {<br>
> - double cr[XY], cb[XY], wr[XY][4], wb[XY][4], calTableR[XY], calTableB[XY], calTableTmp[XY];<br>
> + std::vector<double> &cr = tmpC_[0], &cb = tmpC_[1], &calTableR = tmpC_[2],<br>
> + &calTableB = tmpC_[3], &calTableTmp = tmpC_[4];<br>
> + std::vector<std::array<double, 4>> &wr = tmpM_[0], &wb = tmpM_[1], &M = tmpM_[2];<br>
> +<br>
> /*<br>
> * Calculate our R/B ("Cr"/"Cb") colour statistics, and assess which are<br>
> * usable.<br>
> @@ -784,9 +829,9 @@ void Alsc::doAlsc()<br>
> * case the camera mode is not full-frame.<br>
> */<br>
> getCalTable(ct_, config_.calibrationsCr, calTableTmp);<br>
> - resampleCalTable(calTableTmp, cameraMode_, calTableR);<br>
> + resampleCalTable(calTableTmp, cameraMode_, config_.tableSize, calTableR);<br>
> getCalTable(ct_, config_.calibrationsCb, calTableTmp);<br>
> - resampleCalTable(calTableTmp, cameraMode_, calTableB);<br>
> + resampleCalTable(calTableTmp, cameraMode_, config_.tableSize, calTableB);<br>
> /*<br>
> * You could print out the cal tables for this image here, if you're<br>
> * tuning the algorithm...<br>
> @@ -796,13 +841,13 @@ void Alsc::doAlsc()<br>
> applyCalTable(calTableR, cr);<br>
> applyCalTable(calTableB, cb);<br>
> /* Compute weights between zones. */<br>
> - computeW(cr, config_.sigmaCr, wr);<br>
> - computeW(cb, config_.sigmaCb, wb);<br>
> + computeW(cr, config_.sigmaCr, wr, config_.tableSize);<br>
> + computeW(cb, config_.sigmaCb, wb, config_.tableSize);<br>
> /* Run Gauss-Seidel iterations over the resulting matrix, for R and B. */<br>
> - runMatrixIterations(cr, lambdaR_, wr, config_.omega, config_.nIter,<br>
> - config_.threshold, config_.lambdaBound);<br>
> - runMatrixIterations(cb, lambdaB_, wb, config_.omega, config_.nIter,<br>
> - config_.threshold, config_.lambdaBound);<br>
> + runMatrixIterations(cr, lambdaR_, wr, M, config_.omega, config_.nIter,<br>
> + config_.threshold, config_.lambdaBound, config_.tableSize);<br>
> + runMatrixIterations(cb, lambdaB_, wb, M, config_.omega, config_.nIter,<br>
> + config_.threshold, config_.lambdaBound, config_.tableSize);<br>
> /*<br>
> * Fold the calibrated gains into our final lambda values. (Note that on<br>
> * the next run, we re-start with the lambda values that don't have the<br>
> diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.h b/src/ipa/raspberrypi/controller/rpi/alsc.h<br>
> index 9167c9ffa2e3..85e998db40e9 100644<br>
> --- a/src/ipa/raspberrypi/controller/rpi/alsc.h<br>
> +++ b/src/ipa/raspberrypi/controller/rpi/alsc.h<br>
> @@ -6,9 +6,13 @@<br>
> */<br>
> #pragma once<br>
><br>
> +#include <array><br>
> #include <mutex><br>
> #include <condition_variable><br>
> #include <thread><br>
> +#include <vector><br>
> +<br>
> +#include <libcamera/geometry.h><br>
><br>
> #include "../algorithm.h"<br>
> #include "../alsc_status.h"<br>
> @@ -20,7 +24,7 @@ namespace RPiController {<br>
><br>
> struct AlscCalibration {<br>
> double ct;<br>
> - double table[AlscCellsX * AlscCellsY];<br>
> + std::vector<double> table;<br>
> };<br>
><br>
> struct AlscConfig {<br>
> @@ -36,13 +40,14 @@ struct AlscConfig {<br>
> uint16_t minG;<br>
> double omega;<br>
> uint32_t nIter;<br>
> - double luminanceLut[AlscCellsX * AlscCellsY];<br>
> + std::vector<double> luminanceLut;<br>
> double luminanceStrength;<br>
> std::vector<AlscCalibration> calibrationsCr;<br>
> std::vector<AlscCalibration> calibrationsCb;<br>
> double defaultCt; /* colour temperature if no metadata found */<br>
> double threshold; /* iteration termination threshold */<br>
> double lambdaBound; /* upper/lower bound for lambda from a value of 1 */<br>
> + libcamera::Size tableSize;<br>
> };<br>
><br>
> class Alsc : public Algorithm<br>
> @@ -62,7 +67,7 @@ private:<br>
> AlscConfig config_;<br>
> bool firstTime_;<br>
> CameraMode cameraMode_;<br>
> - double luminanceTable_[AlscCellsX * AlscCellsY];<br>
> + std::vector<double> luminanceTable_;<br>
> std::thread asyncThread_;<br>
> void asyncFunc(); /* asynchronous thread function */<br>
> std::mutex mutex_;<br>
> @@ -88,8 +93,8 @@ private:<br>
> int frameCount_;<br>
> /* counts up to startupFrames for Process function */<br>
> int frameCount2_;<br>
> - double syncResults_[3][AlscCellsY][AlscCellsX];<br>
> - double prevSyncResults_[3][AlscCellsY][AlscCellsX];<br>
> + std::array<std::vector<double>, 3> syncResults_;<br>
> + std::array<std::vector<double>, 3> prevSyncResults_;<br>
> void waitForAysncThread();<br>
> /*<br>
> * The following are for the asynchronous thread to use, though the main<br>
> @@ -100,12 +105,16 @@ private:<br>
> void fetchAsyncResults();<br>
> double ct_;<br>
> RgbyRegions statistics_;<br>
> - double asyncResults_[3][AlscCellsY][AlscCellsX];<br>
> - double asyncLambdaR_[AlscCellsX * AlscCellsY];<br>
> - double asyncLambdaB_[AlscCellsX * AlscCellsY];<br>
> + std::array<std::vector<double>, 3> asyncResults_;<br>
> + std::vector<double> asyncLambdaR_;<br>
> + std::vector<double> asyncLambdaB_;<br>
> void doAlsc();<br>
> - double lambdaR_[AlscCellsX * AlscCellsY];<br>
> - double lambdaB_[AlscCellsX * AlscCellsY];<br>
> + std::vector<double> lambdaR_;<br>
> + std::vector<double> lambdaB_;<br>
> +<br>
> + /* Temporaries for the computations */<br>
> + std::array<std::vector<double>, 5> tmpC_;<br>
> + std::array<std::vector<std::array<double, 4>>, 3> tmpM_;<br>
> };<br>
><br>
> } /* namespace RPiController */<br>
> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp<br>
> index b64cb96e2dde..0fa79bb4af41 100644<br>
> --- a/src/ipa/raspberrypi/raspberrypi.cpp<br>
> +++ b/src/ipa/raspberrypi/raspberrypi.cpp<br>
> @@ -13,6 +13,7 @@<br>
> #include <math.h><br>
> #include <stdint.h><br>
> #include <string.h><br>
> +#include <vector><br>
<br>
Should this be moved after <sys/mman.h> ?<br></blockquote><div><br></div><div>Ack.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> #include <sys/mman.h><br>
><br>
> #include <linux/bcm2835-isp.h><br>
> @@ -174,7 +175,7 @@ private:<br>
> void applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls);<br>
> void applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls);<br>
> void applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls);<br>
> - void resampleTable(uint16_t dest[], double const src[12][16], int destW, int destH);<br>
> + void resampleTable(uint16_t dest[], const std::vector<double> &src, int destW, int destH);<br>
><br>
> std::map<unsigned int, MappedFrameBuffer> buffers_;<br>
><br>
> @@ -1768,7 +1769,7 @@ void IPARPi::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls)<br>
> * Resamples a 16x12 table with central sampling to destW x destH with corner<br>
<br>
The 16x12 size is mentioned here </blockquote><div><br></div><div>The raspberrypi.cpp file is platform specific as it translates the platform<br>independent params from the algorithms into HW pipeline specific params. As<br>such, I think it's ok to reference the actual grid values in this file. Ditto<br>for the reference below.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> * sampling.<br>
> */<br>
> -void IPARPi::resampleTable(uint16_t dest[], double const src[12][16],<br>
> +void IPARPi::resampleTable(uint16_t dest[], const std::vector<double> &src,<br>
> int destW, int destH)<br>
> {<br>
> /*<br>
> @@ -1793,8 +1794,8 @@ void IPARPi::resampleTable(uint16_t dest[], double const src[12][16],<br>
> double yf = y - yLo;<br>
> int yHi = yLo < 11 ? yLo + 1 : 11;<br>
> yLo = yLo > 0 ? yLo : 0;<br>
> - double const *rowAbove = src[yLo];<br>
> - double const *rowBelow = src[yHi];<br>
> + double const *rowAbove = src.data() + yLo * 16;<br>
> + double const *rowBelow = src.data() + yHi * 16;<br>
<br>
As well as assumed here. Also the previous index was yLo and the new<br>
one (yLo * 16). Is it ok ?<br></blockquote><div><br></div><div>Yes this is fine. The platform independent algorithm code writes out a flat 1D<br>array of 16*12 values. This bit of code here interprets that as a 2D table<br>again.<br></div><div><br></div><div>Regards,</div><div>Naush</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> for (int i = 0; i < destW; i++) {<br>
> double above = rowAbove[xLo[i]] * (1 - xf[i]) + rowAbove[xHi[i]] * xf[i];<br>
> double below = rowBelow[xLo[i]] * (1 - xf[i]) + rowBelow[xHi[i]] * xf[i];<br>
> --<br>
> 2.34.1<br>
><br>
</blockquote></div></div>