Show a patch.

GET /api/patches/18500/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 18500,
    "url": "https://patchwork.libcamera.org/api/patches/18500/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/18500/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20230331072341.24851-5-naush@raspberrypi.com>",
    "date": "2023-03-31T07:23:35",
    "name": "[libcamera-devel,v3,04/10] ipa: raspberrypi: alsc: Replace std::vectors by Array2D class",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "79f49e6dad2f4e89a92a00f046b797336a12f34f",
    "submitter": {
        "id": 34,
        "url": "https://patchwork.libcamera.org/api/people/34/?format=api",
        "name": "Naushir Patuck",
        "email": "naush@raspberrypi.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/18500/mbox/",
    "series": [
        {
            "id": 3830,
            "url": "https://patchwork.libcamera.org/api/series/3830/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3830",
            "date": "2023-03-31T07:23:31",
            "name": "Raspberry Pi: Generalised algorithms",
            "version": 3,
            "mbox": "https://patchwork.libcamera.org/series/3830/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/18500/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/18500/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 1D8E7C329C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 31 Mar 2023 07:40:55 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C41766276C;\n\tFri, 31 Mar 2023 09:40:54 +0200 (CEST)",
            "from mail-il1-x129.google.com (mail-il1-x129.google.com\n\t[IPv6:2607:f8b0:4864:20::129])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id DE8136275E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 31 Mar 2023 09:40:52 +0200 (CEST)",
            "by mail-il1-x129.google.com with SMTP id h14so9764537ilj.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 31 Mar 2023 00:40:52 -0700 (PDT)",
            "from localhost.localdomain ([93.93.133.154])\n\tby smtp.gmail.com with ESMTPSA id\n\ti17-20020a6bf411000000b0074555814e73sm421943iog.32.2023.03.31.00.40.49\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 31 Mar 2023 00:40:50 -0700 (PDT)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1680248454;\n\tbh=Xp3l2Cn+AaoAMI7S8X+v1R7g9YRdikS8Td9pAFvi98M=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=A6KNSWUWd+342GudSnFgp6KNjtQ4FtBhKqnPs6A+RPssC2kINugAuzuLn+9rhXImO\n\tQ8OEYBf7lM50SwBchSoCPUs0mIHqk0jWKTCvReYXpusKS9aCfl8DrQrqPLDLnS2Dx6\n\tQ6OS0fu8Ex95+q9eOfQyZOcxeEteckWjCyQCvZw0mZPoIKugLwDhtX8Ac0GtjyORCW\n\tCsqEtMtQJuip54jsEF+UJIYhZx4iXIos9jPRMoOIEotOb6U5/q5xK1aktnOTiVcT4S\n\tEmMMQgxmQaRD4+6eFnCfYiOeHIpNOhU8sPGaFewD+0DcRToX1XdyhdfsNK61F5ZKPk\n\twMwPWX7MA0F1A==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1680248451;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=3JdbajTs00HICJy9McNfdYmKn2OuE3fq0x4d14FeTTQ=;\n\tb=M93eLi1t3ntW300+lJEZmf6+nRofYkx0dkrEUn32GA7NEFO3w81d5nn8ebMF2L1hgM\n\tHoXHhwA9Y3lbdQ6Z9IhO1JWo3GMjd+8P26PH6tLeSKeFKTW9XBwsIV6Ae1yr0+H8s0az\n\twFzVo04nVfIwt6/UKTRRs5KK2vAnfCpcxSa6zDfgBKgZt7n5DCAcjj6omOT374J+m+aG\n\tKiv6UO8kN9i/q1LdYFjJJLGOzs+JqoPhZDBqnxDTHknIpTJ3Pq6VG04QhOlkRvGvYfoi\n\tujKROhVE6ZKiDm2tMYkyBnm7R2Z4xJIbeIgDPSzPhokcEKJiudRqTS2kD8v6/hBxZ8OD\n\tTuXg=="
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=raspberrypi.com\n\theader.i=@raspberrypi.com\n\theader.b=\"M93eLi1t\"; dkim-atps=neutral",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112; t=1680248451;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n\t:subject:date:message-id:reply-to;\n\tbh=3JdbajTs00HICJy9McNfdYmKn2OuE3fq0x4d14FeTTQ=;\n\tb=yf7+TbAOEkGFfkxUZSPuANfDkUDwZjZ33BjHGA+rgENV5tp+TbZ/arBZ+ODL+wlDU7\n\tJybUb2SbG//slXMharyFWNTx1SlkJqIIIomFIRqJ9KweRaqZ/Yzw+vpUxAZyAHHCFvpM\n\tTquuYj7dUzxxNoIroKdn6gkv6r0Wtc9Pmgs8sZHhK3YeTj/KmxEQKH/DDw1iTtsK1pJE\n\tQ5wyFHpfSZMKGhClGjftYkluk5Hjjqcv5IWpadvx2s8S3DuKPyOXAoWXXuXJRwqqjR1s\n\ty9rWIImGuoBQXLvIfpLunuXhOQhFwdww1Cjl9JwHIaJxMqq5hWoqjmpXCeDR262gcmvU\n\tTrfQ==",
        "X-Gm-Message-State": "AAQBX9drT4rgpQiHbEhpMBAIle3FSEpMkMxX2S6QJ4ZIDkr4aLo1memp\n\tziVUVEAlWAxXccgWN8AKyLH72GNTFzXz/EGx45SdYA==",
        "X-Google-Smtp-Source": "AKy350ZNJEHrk/cb6K+DPPPhnpH4Qi8rH6848a7gGlEN4rcnW5MZ8fbewtl9fSR1RurzBfO+cad3uA==",
        "X-Received": "by 2002:a92:c80c:0:b0:325:c3e9:f8b5 with SMTP id\n\tv12-20020a92c80c000000b00325c3e9f8b5mr19344919iln.12.1680248451035; \n\tFri, 31 Mar 2023 00:40:51 -0700 (PDT)",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Fri, 31 Mar 2023 08:23:35 +0100",
        "Message-Id": "<20230331072341.24851-5-naush@raspberrypi.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20230331072341.24851-1-naush@raspberrypi.com>",
        "References": "<20230331072341.24851-1-naush@raspberrypi.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v3 04/10] ipa: raspberrypi: alsc: Replace\n\tstd::vectors by Array2D class",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "From": "Naushir Patuck via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>",
        "Reply-To": "Naushir Patuck <naush@raspberrypi.com>",
        "Cc": "Jacopo Mondi <jacopo.mondi@ideasonboard.com>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "From: David Plowman <david.plowman@raspberrypi.com>\n\nThe Array2D class is a very thin wrapper round std::vector that can be\nused almost identically in the code, but it carries its 2D size with\nit so that we aren't passing it around all the time.\n\nAll the std::vectors that were X * Y in size (X and Y being the ALSC\ngrid size) have been replaced. The sparse matrices that are XY * 4 in\nsize have not been as they are somewhat different, are used\ndifferently, require more code changes, and actually make things more\nconfusing if everything looks like an Array2D but are not the same.\n\nThere should be no change in algorithm behaviour at all.\n\nSigned-off-by: David Plowman <david.plowman@raspberrypi.com>\nReviewed-by: Naushir Patuck <naush@raspberrypi.com>\nReviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n---\n src/ipa/raspberrypi/controller/rpi/alsc.cpp | 218 ++++++++++----------\n src/ipa/raspberrypi/controller/rpi/alsc.h   |  68 +++++-\n 2 files changed, 164 insertions(+), 122 deletions(-)",
    "diff": "diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.cpp b/src/ipa/raspberrypi/controller/rpi/alsc.cpp\nindex 51fe5d73f52d..524c48093590 100644\n--- a/src/ipa/raspberrypi/controller/rpi/alsc.cpp\n+++ b/src/ipa/raspberrypi/controller/rpi/alsc.cpp\n@@ -49,11 +49,10 @@ char const *Alsc::name() const\n \treturn NAME;\n }\n \n-static int generateLut(std::vector<double> &lut, const libcamera::YamlObject &params,\n-\t\t       const Size &size)\n+static int generateLut(Array2D<double> &lut, const libcamera::YamlObject &params)\n {\n \t/* These must be signed ints for the co-ordinate calculations below. */\n-\tint X = size.width, Y = size.height;\n+\tint X = lut.dimensions().width, Y = lut.dimensions().height;\n \tdouble cstrength = params[\"corner_strength\"].get<double>(2.0);\n \tif (cstrength <= 1.0) {\n \t\tLOG(RPiAlsc, Error) << \"corner_strength must be > 1.0\";\n@@ -82,9 +81,9 @@ static int generateLut(std::vector<double> &lut, const libcamera::YamlObject &pa\n \treturn 0;\n }\n \n-static int readLut(std::vector<double> &lut, const libcamera::YamlObject &params, const Size &size)\n+static int readLut(Array2D<double> &lut, const libcamera::YamlObject &params)\n {\n-\tif (params.size() != size.width * size.height) {\n+\tif (params.size() != lut.size()) {\n \t\tLOG(RPiAlsc, Error) << \"Invalid number of entries in LSC table\";\n \t\treturn -EINVAL;\n \t}\n@@ -128,7 +127,7 @@ static int readCalibrations(std::vector<AlscCalibration> &calibrations,\n \t\t\t}\n \n \t\t\tint num = 0;\n-\t\t\tcalibration.table.resize(size.width * size.height);\n+\t\t\tcalibration.table.resize(size);\n \t\t\tfor (const auto &elem : table.asList()) {\n \t\t\t\tvalue = elem.get<double>();\n \t\t\t\tif (!value)\n@@ -160,13 +159,13 @@ int Alsc::read(const libcamera::YamlObject &params)\n \tconfig_.luminanceStrength =\n \t\tparams[\"luminance_strength\"].get<double>(1.0);\n \n-\tconfig_.luminanceLut.resize(config_.tableSize.width * config_.tableSize.height, 1.0);\n+\tconfig_.luminanceLut.resize(config_.tableSize, 1.0);\n \tint ret = 0;\n \n \tif (params.contains(\"corner_strength\"))\n-\t\tret = generateLut(config_.luminanceLut, params, config_.tableSize);\n+\t\tret = generateLut(config_.luminanceLut, params);\n \telse if (params.contains(\"luminance_lut\"))\n-\t\tret = readLut(config_.luminanceLut, params[\"luminance_lut\"], config_.tableSize);\n+\t\tret = readLut(config_.luminanceLut, params[\"luminance_lut\"]);\n \telse\n \t\tLOG(RPiAlsc, Warning)\n \t\t\t<< \"no luminance table - assume unity everywhere\";\n@@ -191,16 +190,16 @@ int Alsc::read(const libcamera::YamlObject &params)\n \n static double getCt(Metadata *metadata, double defaultCt);\n static void getCalTable(double ct, std::vector<AlscCalibration> const &calibrations,\n-\t\t\tstd::vector<double> &calTable);\n-static void resampleCalTable(const std::vector<double> &calTableIn, CameraMode const &cameraMode,\n-\t\t\t     const Size &size, std::vector<double> &calTableOut);\n-static void compensateLambdasForCal(const std::vector<double> &calTable,\n-\t\t\t\t    const std::vector<double> &oldLambdas,\n-\t\t\t\t    std::vector<double> &newLambdas);\n-static void addLuminanceToTables(std::array<std::vector<double>, 3> &results,\n-\t\t\t\t const std::vector<double> &lambdaR, double lambdaG,\n-\t\t\t\t const std::vector<double> &lambdaB,\n-\t\t\t\t const std::vector<double> &luminanceLut,\n+\t\t\tArray2D<double> &calTable);\n+static void resampleCalTable(const Array2D<double> &calTableIn, CameraMode const &cameraMode,\n+\t\t\t     Array2D<double> &calTableOut);\n+static void compensateLambdasForCal(const Array2D<double> &calTable,\n+\t\t\t\t    const Array2D<double> &oldLambdas,\n+\t\t\t\t    Array2D<double> &newLambdas);\n+static void addLuminanceToTables(std::array<Array2D<double>, 3> &results,\n+\t\t\t\t const Array2D<double> &lambdaR, double lambdaG,\n+\t\t\t\t const Array2D<double> &lambdaB,\n+\t\t\t\t const Array2D<double> &luminanceLut,\n \t\t\t\t double luminanceStrength);\n \n void Alsc::initialise()\n@@ -212,22 +211,22 @@ void Alsc::initialise()\n \tconst size_t XY = config_.tableSize.width * config_.tableSize.height;\n \n \tfor (auto &r : syncResults_)\n-\t\tr.resize(XY);\n+\t\tr.resize(config_.tableSize);\n \tfor (auto &r : prevSyncResults_)\n-\t\tr.resize(XY);\n+\t\tr.resize(config_.tableSize);\n \tfor (auto &r : asyncResults_)\n-\t\tr.resize(XY);\n+\t\tr.resize(config_.tableSize);\n \n-\tluminanceTable_.resize(XY);\n-\tasyncLambdaR_.resize(XY);\n-\tasyncLambdaB_.resize(XY);\n+\tluminanceTable_.resize(config_.tableSize);\n+\tasyncLambdaR_.resize(config_.tableSize);\n+\tasyncLambdaB_.resize(config_.tableSize);\n \t/* The lambdas are initialised in the SwitchMode. */\n-\tlambdaR_.resize(XY);\n-\tlambdaB_.resize(XY);\n+\tlambdaR_.resize(config_.tableSize);\n+\tlambdaB_.resize(config_.tableSize);\n \n \t/* Temporaries for the computations, but sensible to allocate this up-front! */\n \tfor (auto &c : tmpC_)\n-\t\tc.resize(XY);\n+\t\tc.resize(config_.tableSize);\n \tfor (auto &m : tmpM_)\n \t\tm.resize(XY);\n }\n@@ -290,7 +289,7 @@ void Alsc::switchMode(CameraMode const &cameraMode,\n \t * We must resample the luminance table like we do the others, but it's\n \t * fixed so we can simply do it up front here.\n \t */\n-\tresampleCalTable(config_.luminanceLut, cameraMode_, config_.tableSize, luminanceTable_);\n+\tresampleCalTable(config_.luminanceLut, cameraMode_, luminanceTable_);\n \n \tif (resetTables) {\n \t\t/*\n@@ -302,11 +301,11 @@ void Alsc::switchMode(CameraMode const &cameraMode,\n \t\t */\n \t\tstd::fill(lambdaR_.begin(), lambdaR_.end(), 1.0);\n \t\tstd::fill(lambdaB_.begin(), lambdaB_.end(), 1.0);\n-\t\tstd::vector<double> &calTableR = tmpC_[0], &calTableB = tmpC_[1], &calTableTmp = tmpC_[2];\n+\t\tArray2D<double> &calTableR = tmpC_[0], &calTableB = tmpC_[1], &calTableTmp = tmpC_[2];\n \t\tgetCalTable(ct_, config_.calibrationsCr, calTableTmp);\n-\t\tresampleCalTable(calTableTmp, cameraMode_, config_.tableSize, calTableR);\n+\t\tresampleCalTable(calTableTmp, cameraMode_, calTableR);\n \t\tgetCalTable(ct_, config_.calibrationsCb, calTableTmp);\n-\t\tresampleCalTable(calTableTmp, cameraMode_, config_.tableSize, calTableB);\n+\t\tresampleCalTable(calTableTmp, cameraMode_, calTableB);\n \t\tcompensateLambdasForCal(calTableR, lambdaR_, asyncLambdaR_);\n \t\tcompensateLambdasForCal(calTableB, lambdaB_, asyncLambdaB_);\n \t\taddLuminanceToTables(syncResults_, asyncLambdaR_, 1.0, asyncLambdaB_,\n@@ -411,9 +410,9 @@ void Alsc::prepare(Metadata *imageMetadata)\n \t}\n \t/* Put output values into status metadata. */\n \tAlscStatus status;\n-\tstatus.r = prevSyncResults_[0];\n-\tstatus.g = prevSyncResults_[1];\n-\tstatus.b = prevSyncResults_[2];\n+\tstatus.r = prevSyncResults_[0].data();\n+\tstatus.g = prevSyncResults_[1].data();\n+\tstatus.b = prevSyncResults_[2].data();\n \timageMetadata->set(\"alsc.status\", status);\n }\n \n@@ -457,7 +456,7 @@ void Alsc::asyncFunc()\n }\n \n void getCalTable(double ct, std::vector<AlscCalibration> const &calibrations,\n-\t\t std::vector<double> &calTable)\n+\t\t Array2D<double> &calTable)\n {\n \tif (calibrations.empty()) {\n \t\tstd::fill(calTable.begin(), calTable.end(), 1.0);\n@@ -486,12 +485,12 @@ void getCalTable(double ct, std::vector<AlscCalibration> const &calibrations,\n \t}\n }\n \n-void resampleCalTable(const std::vector<double> &calTableIn,\n-\t\t      CameraMode const &cameraMode, const Size &size,\n-\t\t      std::vector<double> &calTableOut)\n+void resampleCalTable(const Array2D<double> &calTableIn,\n+\t\t      CameraMode const &cameraMode,\n+\t\t      Array2D<double> &calTableOut)\n {\n-\tint X = size.width;\n-\tint Y = size.height;\n+\tint X = calTableIn.dimensions().width;\n+\tint Y = calTableIn.dimensions().height;\n \n \t/*\n \t * Precalculate and cache the x sampling locations and phases to save\n@@ -529,9 +528,9 @@ void resampleCalTable(const std::vector<double> &calTableIn,\n \t\t\tyLo = Y - 1 - yLo;\n \t\t\tyHi = Y - 1 - yHi;\n \t\t}\n-\t\tdouble const *rowAbove = calTableIn.data() + X * yLo;\n-\t\tdouble const *rowBelow = calTableIn.data() + X * yHi;\n-\t\tdouble *out = calTableOut.data() + X * j;\n+\t\tdouble const *rowAbove = calTableIn.ptr() + X * yLo;\n+\t\tdouble const *rowBelow = calTableIn.ptr() + X * yHi;\n+\t\tdouble *out = calTableOut.ptr() + X * j;\n \t\tfor (int i = 0; i < X; i++) {\n \t\t\tdouble above = rowAbove[xLo[i]] * (1 - xf[i]) +\n \t\t\t\t       rowAbove[xHi[i]] * xf[i];\n@@ -543,8 +542,8 @@ void resampleCalTable(const std::vector<double> &calTableIn,\n }\n \n /* Calculate chrominance statistics (R/G and B/G) for each region. */\n-static void calculateCrCb(const RgbyRegions &awbRegion, std::vector<double> &cr,\n-\t\t\t  std::vector<double> &cb, uint32_t minCount, uint16_t minG)\n+static void calculateCrCb(const RgbyRegions &awbRegion, Array2D<double> &cr,\n+\t\t\t  Array2D<double> &cb, uint32_t minCount, uint16_t minG)\n {\n \tfor (unsigned int i = 0; i < cr.size(); i++) {\n \t\tauto s = awbRegion.get(i);\n@@ -559,16 +558,16 @@ static void calculateCrCb(const RgbyRegions &awbRegion, std::vector<double> &cr,\n \t}\n }\n \n-static void applyCalTable(const std::vector<double> &calTable, std::vector<double> &C)\n+static void applyCalTable(const Array2D<double> &calTable, Array2D<double> &C)\n {\n \tfor (unsigned int i = 0; i < C.size(); i++)\n \t\tif (C[i] != InsufficientData)\n \t\t\tC[i] *= calTable[i];\n }\n \n-void compensateLambdasForCal(const std::vector<double> &calTable,\n-\t\t\t     const std::vector<double> &oldLambdas,\n-\t\t\t     std::vector<double> &newLambdas)\n+void compensateLambdasForCal(const Array2D<double> &calTable,\n+\t\t\t     const Array2D<double> &oldLambdas,\n+\t\t\t     Array2D<double> &newLambdas)\n {\n \tdouble minNewLambda = std::numeric_limits<double>::max();\n \tfor (unsigned int i = 0; i < newLambdas.size(); i++) {\n@@ -579,9 +578,9 @@ void compensateLambdasForCal(const std::vector<double> &calTable,\n \t\tnewLambdas[i] /= minNewLambda;\n }\n \n-[[maybe_unused]] static void printCalTable(const std::vector<double> &C,\n-\t\t\t\t\t   const Size &size)\n+[[maybe_unused]] static void printCalTable(const Array2D<double> &C)\n {\n+\tconst Size &size = C.dimensions();\n \tprintf(\"table: [\\n\");\n \tfor (unsigned int j = 0; j < size.height; j++) {\n \t\tfor (unsigned int i = 0; i < size.width; i++) {\n@@ -607,11 +606,11 @@ static double computeWeight(double Ci, double Cj, double sigma)\n }\n \n /* Compute all weights. */\n-static void computeW(const std::vector<double> &C, double sigma,\n-\t\t     std::vector<std::array<double, 4>> &W, const Size &size)\n+static void computeW(const Array2D<double> &C, double sigma,\n+\t\t     std::vector<std::array<double, 4>> &W)\n {\n-\tsize_t XY = size.width * size.height;\n-\tsize_t X = size.width;\n+\tsize_t XY = C.size();\n+\tsize_t X = C.dimensions().width;\n \n \tfor (unsigned int i = 0; i < XY; i++) {\n \t\t/* Start with neighbour above and go clockwise. */\n@@ -623,13 +622,12 @@ static void computeW(const std::vector<double> &C, double sigma,\n }\n \n /* Compute M, the large but sparse matrix such that M * lambdas = 0. */\n-static void constructM(const std::vector<double> &C,\n+static void constructM(const Array2D<double> &C,\n \t\t       const std::vector<std::array<double, 4>> &W,\n-\t\t       std::vector<std::array<double, 4>> &M,\n-\t\t       const Size &size)\n+\t\t       std::vector<std::array<double, 4>> &M)\n {\n-\tsize_t XY = size.width * size.height;\n-\tsize_t X = size.width;\n+\tsize_t XY = C.size();\n+\tsize_t X = C.dimensions().width;\n \n \tdouble epsilon = 0.001;\n \tfor (unsigned int i = 0; i < XY; i++) {\n@@ -654,79 +652,78 @@ static void constructM(const std::vector<double> &C,\n  * need to test the i value to exclude them.\n  */\n static double computeLambdaBottom(int i, const std::vector<std::array<double, 4>> &M,\n-\t\t\t\t  std::vector<double> &lambda, const Size &size)\n+\t\t\t\t  Array2D<double> &lambda)\n {\n-\treturn M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + size.width] +\n+\treturn M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + lambda.dimensions().width] +\n \t       M[i][3] * lambda[i - 1];\n }\n static double computeLambdaBottomStart(int i, const std::vector<std::array<double, 4>> &M,\n-\t\t\t\t       std::vector<double> &lambda, const Size &size)\n+\t\t\t\t       Array2D<double> &lambda)\n {\n-\treturn M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + size.width];\n+\treturn M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + lambda.dimensions().width];\n }\n static double computeLambdaInterior(int i, const std::vector<std::array<double, 4>> &M,\n-\t\t\t\t    std::vector<double> &lambda, const Size &size)\n+\t\t\t\t    Array2D<double> &lambda)\n {\n-\treturn M[i][0] * lambda[i - size.width] + M[i][1] * lambda[i + 1] +\n-\t       M[i][2] * lambda[i + size.width] + M[i][3] * lambda[i - 1];\n+\treturn M[i][0] * lambda[i - lambda.dimensions().width] + M[i][1] * lambda[i + 1] +\n+\t       M[i][2] * lambda[i + lambda.dimensions().width] + M[i][3] * lambda[i - 1];\n }\n static double computeLambdaTop(int i, const std::vector<std::array<double, 4>> &M,\n-\t\t\t       std::vector<double> &lambda, const Size &size)\n+\t\t\t       Array2D<double> &lambda)\n {\n-\treturn M[i][0] * lambda[i - size.width] + M[i][1] * lambda[i + 1] +\n+\treturn M[i][0] * lambda[i - lambda.dimensions().width] + M[i][1] * lambda[i + 1] +\n \t       M[i][3] * lambda[i - 1];\n }\n static double computeLambdaTopEnd(int i, const std::vector<std::array<double, 4>> &M,\n-\t\t\t\t  std::vector<double> &lambda, const Size &size)\n+\t\t\t\t  Array2D<double> &lambda)\n {\n-\treturn M[i][0] * lambda[i - size.width] + M[i][3] * lambda[i - 1];\n+\treturn M[i][0] * lambda[i - lambda.dimensions().width] + M[i][3] * lambda[i - 1];\n }\n \n /* Gauss-Seidel iteration with over-relaxation. */\n static double gaussSeidel2Sor(const std::vector<std::array<double, 4>> &M, double omega,\n-\t\t\t      std::vector<double> &lambda, double lambdaBound,\n-\t\t\t      const Size &size)\n+\t\t\t      Array2D<double> &lambda, double lambdaBound)\n {\n-\tint XY = size.width * size.height;\n-\tint X = size.width;\n+\tint XY = lambda.size();\n+\tint X = lambda.dimensions().width;\n \tconst double min = 1 - lambdaBound, max = 1 + lambdaBound;\n-\tstd::vector<double> oldLambda = lambda;\n+\tArray2D<double> oldLambda = lambda;\n \tint i;\n-\tlambda[0] = computeLambdaBottomStart(0, M, lambda, size);\n+\tlambda[0] = computeLambdaBottomStart(0, M, lambda);\n \tlambda[0] = std::clamp(lambda[0], min, max);\n \tfor (i = 1; i < X; i++) {\n-\t\tlambda[i] = computeLambdaBottom(i, M, lambda, size);\n+\t\tlambda[i] = computeLambdaBottom(i, M, lambda);\n \t\tlambda[i] = std::clamp(lambda[i], min, max);\n \t}\n \tfor (; i < XY - X; i++) {\n-\t\tlambda[i] = computeLambdaInterior(i, M, lambda, size);\n+\t\tlambda[i] = computeLambdaInterior(i, M, lambda);\n \t\tlambda[i] = std::clamp(lambda[i], min, max);\n \t}\n \tfor (; i < XY - 1; i++) {\n-\t\tlambda[i] = computeLambdaTop(i, M, lambda, size);\n+\t\tlambda[i] = computeLambdaTop(i, M, lambda);\n \t\tlambda[i] = std::clamp(lambda[i], min, max);\n \t}\n-\tlambda[i] = computeLambdaTopEnd(i, M, lambda, size);\n+\tlambda[i] = computeLambdaTopEnd(i, M, lambda);\n \tlambda[i] = std::clamp(lambda[i], min, max);\n \t/*\n \t * Also solve the system from bottom to top, to help spread the updates\n \t * better.\n \t */\n-\tlambda[i] = computeLambdaTopEnd(i, M, lambda, size);\n+\tlambda[i] = computeLambdaTopEnd(i, M, lambda);\n \tlambda[i] = std::clamp(lambda[i], min, max);\n \tfor (i = XY - 2; i >= XY - X; i--) {\n-\t\tlambda[i] = computeLambdaTop(i, M, lambda, size);\n+\t\tlambda[i] = computeLambdaTop(i, M, lambda);\n \t\tlambda[i] = std::clamp(lambda[i], min, max);\n \t}\n \tfor (; i >= X; i--) {\n-\t\tlambda[i] = computeLambdaInterior(i, M, lambda, size);\n+\t\tlambda[i] = computeLambdaInterior(i, M, lambda);\n \t\tlambda[i] = std::clamp(lambda[i], min, max);\n \t}\n \tfor (; i >= 1; i--) {\n-\t\tlambda[i] = computeLambdaBottom(i, M, lambda, size);\n+\t\tlambda[i] = computeLambdaBottom(i, M, lambda);\n \t\tlambda[i] = std::clamp(lambda[i], min, max);\n \t}\n-\tlambda[0] = computeLambdaBottomStart(0, M, lambda, size);\n+\tlambda[0] = computeLambdaBottomStart(0, M, lambda);\n \tlambda[0] = std::clamp(lambda[0], min, max);\n \tdouble maxDiff = 0;\n \tfor (i = 0; i < XY; i++) {\n@@ -738,7 +735,7 @@ static double gaussSeidel2Sor(const std::vector<std::array<double, 4>> &M, doubl\n }\n \n /* Normalise the values so that the smallest value is 1. */\n-static void normalise(std::vector<double> &results)\n+static void normalise(Array2D<double> &results)\n {\n \tdouble minval = *std::min_element(results.begin(), results.end());\n \tstd::for_each(results.begin(), results.end(),\n@@ -746,7 +743,7 @@ static void normalise(std::vector<double> &results)\n }\n \n /* Rescale the values so that the average value is 1. */\n-static void reaverage(std::vector<double> &data)\n+static void reaverage(Array2D<double> &data)\n {\n \tdouble sum = std::accumulate(data.begin(), data.end(), 0.0);\n \tdouble ratio = 1 / (sum / data.size());\n@@ -754,17 +751,16 @@ static void reaverage(std::vector<double> &data)\n \t\t      [ratio](double val) { return val * ratio; });\n }\n \n-static void runMatrixIterations(const std::vector<double> &C,\n-\t\t\t\tstd::vector<double> &lambda,\n+static void runMatrixIterations(const Array2D<double> &C,\n+\t\t\t\tArray2D<double> &lambda,\n \t\t\t\tconst std::vector<std::array<double, 4>> &W,\n \t\t\t\tstd::vector<std::array<double, 4>> &M, double omega,\n-\t\t\t\tunsigned int nIter, double threshold, double lambdaBound,\n-\t\t\t\tconst Size &size)\n+\t\t\t\tunsigned int nIter, double threshold, double lambdaBound)\n {\n-\tconstructM(C, W, M, size);\n+\tconstructM(C, W, M);\n \tdouble lastMaxDiff = std::numeric_limits<double>::max();\n \tfor (unsigned int i = 0; i < nIter; i++) {\n-\t\tdouble maxDiff = fabs(gaussSeidel2Sor(M, omega, lambda, lambdaBound, size));\n+\t\tdouble maxDiff = fabs(gaussSeidel2Sor(M, omega, lambda, lambdaBound));\n \t\tif (maxDiff < threshold) {\n \t\t\tLOG(RPiAlsc, Debug)\n \t\t\t\t<< \"Stop after \" << i + 1 << \" iterations\";\n@@ -784,26 +780,26 @@ static void runMatrixIterations(const std::vector<double> &C,\n \treaverage(lambda);\n }\n \n-static void addLuminanceRb(std::vector<double> &result, const std::vector<double> &lambda,\n-\t\t\t   const std::vector<double> &luminanceLut,\n+static void addLuminanceRb(Array2D<double> &result, const Array2D<double> &lambda,\n+\t\t\t   const Array2D<double> &luminanceLut,\n \t\t\t   double luminanceStrength)\n {\n \tfor (unsigned int i = 0; i < result.size(); i++)\n \t\tresult[i] = lambda[i] * ((luminanceLut[i] - 1) * luminanceStrength + 1);\n }\n \n-static void addLuminanceG(std::vector<double> &result, double lambda,\n-\t\t\t  const std::vector<double> &luminanceLut,\n+static void addLuminanceG(Array2D<double> &result, double lambda,\n+\t\t\t  const Array2D<double> &luminanceLut,\n \t\t\t  double luminanceStrength)\n {\n \tfor (unsigned int i = 0; i < result.size(); i++)\n \t\tresult[i] = lambda * ((luminanceLut[i] - 1) * luminanceStrength + 1);\n }\n \n-void addLuminanceToTables(std::array<std::vector<double>, 3> &results,\n-\t\t\t  const std::vector<double> &lambdaR,\n-\t\t\t  double lambdaG, const std::vector<double> &lambdaB,\n-\t\t\t  const std::vector<double> &luminanceLut,\n+void addLuminanceToTables(std::array<Array2D<double>, 3> &results,\n+\t\t\t  const Array2D<double> &lambdaR,\n+\t\t\t  double lambdaG, const Array2D<double> &lambdaB,\n+\t\t\t  const Array2D<double> &luminanceLut,\n \t\t\t  double luminanceStrength)\n {\n \taddLuminanceRb(results[0], lambdaR, luminanceLut, luminanceStrength);\n@@ -815,8 +811,8 @@ void addLuminanceToTables(std::array<std::vector<double>, 3> &results,\n \n void Alsc::doAlsc()\n {\n-\tstd::vector<double> &cr = tmpC_[0], &cb = tmpC_[1], &calTableR = tmpC_[2],\n-\t\t\t    &calTableB = tmpC_[3], &calTableTmp = tmpC_[4];\n+\tArray2D<double> &cr = tmpC_[0], &cb = tmpC_[1], &calTableR = tmpC_[2],\n+\t\t\t&calTableB = tmpC_[3], &calTableTmp = tmpC_[4];\n \tstd::vector<std::array<double, 4>> &wr = tmpM_[0], &wb = tmpM_[1], &M = tmpM_[2];\n \n \t/*\n@@ -829,9 +825,9 @@ void Alsc::doAlsc()\n \t * case the camera mode is not full-frame.\n \t */\n \tgetCalTable(ct_, config_.calibrationsCr, calTableTmp);\n-\tresampleCalTable(calTableTmp, cameraMode_, config_.tableSize, calTableR);\n+\tresampleCalTable(calTableTmp, cameraMode_, calTableR);\n \tgetCalTable(ct_, config_.calibrationsCb, calTableTmp);\n-\tresampleCalTable(calTableTmp, cameraMode_, config_.tableSize, calTableB);\n+\tresampleCalTable(calTableTmp, cameraMode_, calTableB);\n \t/*\n \t * You could print out the cal tables for this image here, if you're\n \t * tuning the algorithm...\n@@ -841,13 +837,13 @@ void Alsc::doAlsc()\n \tapplyCalTable(calTableR, cr);\n \tapplyCalTable(calTableB, cb);\n \t/* Compute weights between zones. */\n-\tcomputeW(cr, config_.sigmaCr, wr, config_.tableSize);\n-\tcomputeW(cb, config_.sigmaCb, wb, config_.tableSize);\n+\tcomputeW(cr, config_.sigmaCr, wr);\n+\tcomputeW(cb, config_.sigmaCb, wb);\n \t/* Run Gauss-Seidel iterations over the resulting matrix, for R and B. */\n \trunMatrixIterations(cr, lambdaR_, wr, M, config_.omega, config_.nIter,\n-\t\t\t    config_.threshold, config_.lambdaBound, config_.tableSize);\n+\t\t\t    config_.threshold, config_.lambdaBound);\n \trunMatrixIterations(cb, lambdaB_, wb, M, config_.omega, config_.nIter,\n-\t\t\t    config_.threshold, config_.lambdaBound, config_.tableSize);\n+\t\t\t    config_.threshold, config_.lambdaBound);\n \t/*\n \t * Fold the calibrated gains into our final lambda values. (Note that on\n \t * the next run, we re-start with the lambda values that don't have the\ndiff --git a/src/ipa/raspberrypi/controller/rpi/alsc.h b/src/ipa/raspberrypi/controller/rpi/alsc.h\nindex 85e998db40e9..1ab61299c4cd 100644\n--- a/src/ipa/raspberrypi/controller/rpi/alsc.h\n+++ b/src/ipa/raspberrypi/controller/rpi/alsc.h\n@@ -22,9 +22,55 @@ namespace RPiController {\n \n /* Algorithm to generate automagic LSC (Lens Shading Correction) tables. */\n \n+/*\n+ * The Array2D class is a very thin wrapper round std::vector so that it can\n+ * be used in exactly the same way in the code but carries its correct width\n+ * and height (\"dimensions\") with it.\n+ */\n+\n+template<typename T>\n+class Array2D\n+{\n+public:\n+\tusing Size = libcamera::Size;\n+\n+\tconst Size &dimensions() const { return dimensions_; }\n+\n+\tsize_t size() const { return data_.size(); }\n+\n+\tconst std::vector<T> &data() const { return data_; }\n+\n+\tvoid resize(const Size &dims)\n+\t{\n+\t\tdimensions_ = dims;\n+\t\tdata_.resize(dims.width * dims.height);\n+\t}\n+\n+\tvoid resize(const Size &dims, const T &value)\n+\t{\n+\t\tresize(dims);\n+\t\tstd::fill(data_.begin(), data_.end(), value);\n+\t}\n+\n+\tT &operator[](int index) { return data_[index]; }\n+\n+\tconst T &operator[](int index) const { return data_[index]; }\n+\n+\tT *ptr() { return data_.data(); }\n+\n+\tconst T *ptr() const { return data_.data(); }\n+\n+\tauto begin() { return data_.begin(); }\n+\tauto end() { return data_.end(); }\n+\n+private:\n+\tSize dimensions_;\n+\tstd::vector<T> data_;\n+};\n+\n struct AlscCalibration {\n \tdouble ct;\n-\tstd::vector<double> table;\n+\tArray2D<double> table;\n };\n \n struct AlscConfig {\n@@ -40,7 +86,7 @@ struct AlscConfig {\n \tuint16_t minG;\n \tdouble omega;\n \tuint32_t nIter;\n-\tstd::vector<double> luminanceLut;\n+\tArray2D<double> luminanceLut;\n \tdouble luminanceStrength;\n \tstd::vector<AlscCalibration> calibrationsCr;\n \tstd::vector<AlscCalibration> calibrationsCb;\n@@ -67,7 +113,7 @@ private:\n \tAlscConfig config_;\n \tbool firstTime_;\n \tCameraMode cameraMode_;\n-\tstd::vector<double> luminanceTable_;\n+\tArray2D<double> luminanceTable_;\n \tstd::thread asyncThread_;\n \tvoid asyncFunc(); /* asynchronous thread function */\n \tstd::mutex mutex_;\n@@ -93,8 +139,8 @@ private:\n \tint frameCount_;\n \t/* counts up to startupFrames for Process function */\n \tint frameCount2_;\n-\tstd::array<std::vector<double>, 3> syncResults_;\n-\tstd::array<std::vector<double>, 3> prevSyncResults_;\n+\tstd::array<Array2D<double>, 3> syncResults_;\n+\tstd::array<Array2D<double>, 3> prevSyncResults_;\n \tvoid waitForAysncThread();\n \t/*\n \t * The following are for the asynchronous thread to use, though the main\n@@ -105,15 +151,15 @@ private:\n \tvoid fetchAsyncResults();\n \tdouble ct_;\n \tRgbyRegions statistics_;\n-\tstd::array<std::vector<double>, 3> asyncResults_;\n-\tstd::vector<double> asyncLambdaR_;\n-\tstd::vector<double> asyncLambdaB_;\n+\tstd::array<Array2D<double>, 3> asyncResults_;\n+\tArray2D<double> asyncLambdaR_;\n+\tArray2D<double> asyncLambdaB_;\n \tvoid doAlsc();\n-\tstd::vector<double> lambdaR_;\n-\tstd::vector<double> lambdaB_;\n+\tArray2D<double> lambdaR_;\n+\tArray2D<double> lambdaB_;\n \n \t/* Temporaries for the computations */\n-\tstd::array<std::vector<double>, 5> tmpC_;\n+\tstd::array<Array2D<double>, 5> tmpC_;\n \tstd::array<std::vector<std::array<double, 4>>, 3> tmpM_;\n };\n \n",
    "prefixes": [
        "libcamera-devel",
        "v3",
        "04/10"
    ]
}