<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 &params)<br>
> +static int generateLut(std::vector<double> &lut, const libcamera::YamlObject &params,<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 &params)<br>
>       return 0;<br>
>  }<br>
><br>
> -static int readLut(double *lut, const libcamera::YamlObject &params)<br>
> +static int readLut(std::vector<double> &lut, const libcamera::YamlObject &params, 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 &params)<br>
><br>
>  static int readCalibrations(std::vector<AlscCalibration> &calibrations,<br>
>                           const libcamera::YamlObject &params,<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 &params)<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 &params)<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 &params)<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 &regions, 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>