Show a patch.

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

{
    "id": 21300,
    "url": "https://patchwork.libcamera.org/api/patches/21300/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/21300/",
    "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": "<20240920133941.90629-6-stefan.klug@ideasonboard.com>",
    "date": "2024-09-20T13:39:20",
    "name": "[v3,5/9] ipa: rkisp1: Use interpolator in lsc",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "296c4ab096e24de49a834e2d5306d124e52e1909",
    "submitter": {
        "id": 184,
        "url": "https://patchwork.libcamera.org/api/people/184/?format=api",
        "name": "Stefan Klug",
        "email": "stefan.klug@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/21300/mbox/",
    "series": [
        {
            "id": 4609,
            "url": "https://patchwork.libcamera.org/api/series/4609/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4609",
            "date": "2024-09-20T13:39:15",
            "name": "Implement polynomial lsc support",
            "version": 3,
            "mbox": "https://patchwork.libcamera.org/series/4609/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/21300/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/21300/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 668DFC32D6\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 20 Sep 2024 13:40:07 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A582C63502;\n\tFri, 20 Sep 2024 15:40:06 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4912A634F5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Sep 2024 15:40:00 +0200 (CEST)",
            "from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:8ade:938d:48b1:cede])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 4CAB7C62;\n\tFri, 20 Sep 2024 15:38:36 +0200 (CEST)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"JakXYiga\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1726839516;\n\tbh=fltO5fWWoE7ngxCRHIGb+I6vpcCv3x6w1DV77Fxtpdo=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=JakXYigahThuIQW/WFv2zk8gjjTP9QqF+PF2Xs+9urgbW7Gx6/3LC2H+t2KL5qP+h\n\tZcvm3HHwDAou6cn+Znptb991r6L8f5Pb3iZBwT/iCblh+/9rzL25nWqtV2SGqLzitS\n\tnKuZOFcErodzJUZTknqbSl4m+h66CyAAeJPhqjm4=",
        "From": "Stefan Klug <stefan.klug@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Stefan Klug <stefan.klug@ideasonboard.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tPaul Elder <paul.elder@ideasonboard.com>",
        "Subject": "[PATCH v3 5/9] ipa: rkisp1: Use interpolator in lsc",
        "Date": "Fri, 20 Sep 2024 15:39:20 +0200",
        "Message-ID": "<20240920133941.90629-6-stefan.klug@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.43.0",
        "In-Reply-To": "<20240920133941.90629-1-stefan.klug@ideasonboard.com>",
        "References": "<20240920133941.90629-1-stefan.klug@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "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>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Now, that the generic interpolator is available, use it to do the\ninterpolation of the lens shading tables. This makes the algorithm\neasier to read and remove some duplicate code.\n\nSigned-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n\n---\n\nChanges in v3:\n- Collected tags\n- Fixed a comment\n---\n src/ipa/rkisp1/algorithms/lsc.cpp | 166 +++++++++---------------------\n src/ipa/rkisp1/algorithms/lsc.h   |  13 +--\n 2 files changed, 57 insertions(+), 122 deletions(-)",
    "diff": "diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp\nindex 5f3a0388075b..fe84062bbc70 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.cpp\n+++ b/src/ipa/rkisp1/algorithms/lsc.cpp\n@@ -24,6 +24,36 @@\n \n namespace libcamera {\n \n+namespace ipa {\n+\n+constexpr int kColourTemperatureChangeThreshhold = 10;\n+\n+template<typename T>\n+void interpolateVector(const std::vector<T> &a, const std::vector<T> &b,\n+\t\t       std::vector<T> &dest, double lambda)\n+{\n+\tassert(a.size() == b.size());\n+\tdest.resize(a.size());\n+\tfor (size_t i = 0; i < a.size(); i++) {\n+\t\tdest[i] = a[i] * (1.0 - lambda) + b[i] * lambda;\n+\t}\n+}\n+\n+template<>\n+void Interpolator<rkisp1::algorithms::LensShadingCorrection::Components>::\n+\tinterpolate(const rkisp1::algorithms::LensShadingCorrection::Components &a,\n+\t\t    const rkisp1::algorithms::LensShadingCorrection::Components &b,\n+\t\t    rkisp1::algorithms::LensShadingCorrection::Components &dest,\n+\t\t    double lambda)\n+{\n+\tinterpolateVector(a.r, b.r, dest.r, lambda);\n+\tinterpolateVector(a.gr, b.gr, dest.gr, lambda);\n+\tinterpolateVector(a.gb, b.gb, dest.gb, lambda);\n+\tinterpolateVector(a.b, b.b, dest.b, lambda);\n+}\n+\n+} /* namespace ipa */\n+\n namespace ipa::rkisp1::algorithms {\n \n /**\n@@ -90,8 +120,9 @@ static std::vector<uint16_t> parseTable(const YamlObject &tuningData,\n }\n \n LensShadingCorrection::LensShadingCorrection()\n-\t: lastCt_({ 0, 0 })\n+\t: lastAppliedCt_(0), lastAppliedQuantizedCt_(0)\n {\n+\tsets_.setQuantization(kColourTemperatureChangeThreshhold);\n }\n \n /**\n@@ -115,17 +146,18 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context,\n \t}\n \n \tconst auto &sets = yamlSets.asList();\n+\tstd::map<unsigned int, Components> lscData;\n \tfor (const auto &yamlSet : sets) {\n \t\tuint32_t ct = yamlSet[\"ct\"].get<uint32_t>(0);\n \n-\t\tif (sets_.count(ct)) {\n+\t\tif (lscData.count(ct)) {\n \t\t\tLOG(RkISP1Lsc, Error)\n \t\t\t\t<< \"Multiple sets found for color temperature \"\n \t\t\t\t<< ct;\n \t\t\treturn -EINVAL;\n \t\t}\n \n-\t\tComponents &set = sets_[ct];\n+\t\tComponents &set = lscData[ct];\n \n \t\tset.ct = ct;\n \t\tset.r = parseTable(yamlSet, \"r\");\n@@ -142,11 +174,13 @@ int LensShadingCorrection::init([[maybe_unused]] IPAContext &context,\n \t\t}\n \t}\n \n-\tif (sets_.empty()) {\n+\tif (lscData.empty()) {\n \t\tLOG(RkISP1Lsc, Error) << \"Failed to load any sets\";\n \t\treturn -EINVAL;\n \t}\n \n+\tsets_.setData(std::move(lscData));\n+\n \treturn 0;\n }\n \n@@ -202,134 +236,34 @@ void LensShadingCorrection::copyTable(rkisp1_cif_isp_lsc_config &config,\n \tstd::copy(set.b.begin(), set.b.end(), &config.b_data_tbl[0][0]);\n }\n \n-/*\n- * Interpolate LSC parameters based on color temperature value.\n- */\n-void LensShadingCorrection::interpolateTable(rkisp1_cif_isp_lsc_config &config,\n-\t\t\t\t\t     const Components &set0,\n-\t\t\t\t\t     const Components &set1,\n-\t\t\t\t\t     const uint32_t ct)\n-{\n-\tdouble coeff0 = (set1.ct - ct) / static_cast<double>(set1.ct - set0.ct);\n-\tdouble coeff1 = (ct - set0.ct) / static_cast<double>(set1.ct - set0.ct);\n-\n-\tfor (unsigned int i = 0; i < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; ++i) {\n-\t\tfor (unsigned int j = 0; j < RKISP1_CIF_ISP_LSC_SAMPLES_MAX; ++j) {\n-\t\t\tunsigned int sample = i * RKISP1_CIF_ISP_LSC_SAMPLES_MAX + j;\n-\n-\t\t\tconfig.r_data_tbl[i][j] =\n-\t\t\t\tset0.r[sample] * coeff0 +\n-\t\t\t\tset1.r[sample] * coeff1;\n-\n-\t\t\tconfig.gr_data_tbl[i][j] =\n-\t\t\t\tset0.gr[sample] * coeff0 +\n-\t\t\t\tset1.gr[sample] * coeff1;\n-\n-\t\t\tconfig.gb_data_tbl[i][j] =\n-\t\t\t\tset0.gb[sample] * coeff0 +\n-\t\t\t\tset1.gb[sample] * coeff1;\n-\n-\t\t\tconfig.b_data_tbl[i][j] =\n-\t\t\t\tset0.b[sample] * coeff0 +\n-\t\t\t\tset1.b[sample] * coeff1;\n-\t\t}\n-\t}\n-}\n-\n /**\n  * \\copydoc libcamera::ipa::Algorithm::prepare\n  */\n void LensShadingCorrection::prepare(IPAContext &context,\n-\t\t\t\t    const uint32_t frame,\n+\t\t\t\t    [[maybe_unused]] const uint32_t frame,\n \t\t\t\t    [[maybe_unused]] IPAFrameContext &frameContext,\n \t\t\t\t    RkISP1Params *params)\n {\n-\t/*\n-\t * If there is only one set, the configuration has already been done\n-\t * for first frame.\n-\t */\n-\tif (sets_.size() == 1 && frame > 0)\n-\t\treturn;\n-\n-\t/*\n-\t * If there is only one set, pick it. We can ignore lastCt_, as it will\n-\t * never be relevant.\n-\t */\n-\tif (sets_.size() == 1) {\n-\t\tauto config = params->block<BlockType::Lsc>();\n-\t\tconfig.setEnabled(true);\n-\n-\t\tsetParameters(*config);\n-\t\tcopyTable(*config, sets_.cbegin()->second);\n-\t\treturn;\n-\t}\n-\n \tuint32_t ct = context.activeState.awb.temperatureK;\n-\tct = std::clamp(ct, sets_.cbegin()->first, sets_.crbegin()->first);\n-\n-\t/*\n-\t * If the original is the same, then it means the same adjustment would\n-\t * be made. If the adjusted is the same, then it means that it's the\n-\t * same as what was actually applied. Thus in these cases we can skip\n-\t * reprogramming the LSC.\n-\t *\n-\t * original == adjusted can only happen if an interpolation\n-\t * happened, or if original has an exact entry in sets_. This means\n-\t * that if original != adjusted, then original was adjusted to\n-\t * the nearest available entry in sets_, resulting in adjusted.\n-\t * Clearly, any ct value that is in between original and adjusted\n-\t * will be adjusted to the same adjusted value, so we can skip\n-\t * reprogramming the LSC table.\n-\t *\n-\t * We also skip updating the original value, as the last one had a\n-\t * larger bound and thus a larger range of ct values that will be\n-\t * adjusted to the same adjusted.\n-\t */\n-\tif ((lastCt_.original <= ct && ct <= lastCt_.adjusted) ||\n-\t    (lastCt_.adjusted <= ct && ct <= lastCt_.original))\n+\tif (std::abs(static_cast<int>(ct) - static_cast<int>(lastAppliedCt_)) <\n+\t    kColourTemperatureChangeThreshhold)\n+\t\treturn;\n+\tunsigned int quantizedCt;\n+\tconst Components &set = sets_.getInterpolated(ct, &quantizedCt);\n+\tif (lastAppliedQuantizedCt_ == quantizedCt)\n \t\treturn;\n \n \tauto config = params->block<BlockType::Lsc>();\n \tconfig.setEnabled(true);\n \tsetParameters(*config);\n+\tcopyTable(*config, set);\n \n-\t/*\n-\t * The color temperature matches exactly one of the available LSC tables.\n-\t */\n-\tif (sets_.count(ct)) {\n-\t\tcopyTable(*config, sets_[ct]);\n-\t\tlastCt_ = { ct, ct };\n-\t\treturn;\n-\t}\n+\tlastAppliedCt_ = ct;\n+\tlastAppliedQuantizedCt_ = quantizedCt;\n \n-\t/* No shortcuts left; we need to round or interpolate */\n-\tauto iter = sets_.upper_bound(ct);\n-\tconst Components &set1 = iter->second;\n-\tconst Components &set0 = (--iter)->second;\n-\tuint32_t ct0 = set0.ct;\n-\tuint32_t ct1 = set1.ct;\n-\tuint32_t diff0 = ct - ct0;\n-\tuint32_t diff1 = ct1 - ct;\n-\tstatic constexpr double kThreshold = 0.1;\n-\tfloat threshold = kThreshold * (ct1 - ct0);\n-\n-\tif (diff0 < threshold || diff1 < threshold) {\n-\t\tconst Components &set = diff0 < diff1 ? set0 : set1;\n-\t\tLOG(RkISP1Lsc, Debug) << \"using LSC table for \" << set.ct;\n-\t\tcopyTable(*config, set);\n-\t\tlastCt_ = { ct, set.ct };\n-\t\treturn;\n-\t}\n-\n-\t/*\n-\t * ct is not within 10% of the difference between the neighbouring\n-\t * color temperatures, so we need to interpolate.\n-\t */\n \tLOG(RkISP1Lsc, Debug)\n-\t\t<< \"ct is \" << ct << \", interpolating between \"\n-\t\t<< ct0 << \" and \" << ct1;\n-\tinterpolateTable(*config, set0, set1, ct);\n-\tlastCt_ = { ct, ct };\n+\t\t<< \"ct is \" << ct << \", quantized to \"\n+\t\t<< quantizedCt;\n }\n \n REGISTER_IPA_ALGORITHM(LensShadingCorrection, \"LensShadingCorrection\")\ndiff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h\nindex a9c7a230e0fc..5a0824e36dd5 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.h\n+++ b/src/ipa/rkisp1/algorithms/lsc.h\n@@ -9,6 +9,8 @@\n \n #include <map>\n \n+#include \"libipa/interpolator.h\"\n+\n #include \"algorithm.h\"\n \n namespace libcamera {\n@@ -27,7 +29,6 @@ public:\n \t\t     IPAFrameContext &frameContext,\n \t\t     RkISP1Params *params) override;\n \n-private:\n \tstruct Components {\n \t\tuint32_t ct;\n \t\tstd::vector<uint16_t> r;\n@@ -36,23 +37,23 @@ private:\n \t\tstd::vector<uint16_t> b;\n \t};\n \n+private:\n \tvoid setParameters(rkisp1_cif_isp_lsc_config &config);\n \tvoid copyTable(rkisp1_cif_isp_lsc_config &config, const Components &set0);\n \tvoid interpolateTable(rkisp1_cif_isp_lsc_config &config,\n \t\t\t      const Components &set0, const Components &set1,\n \t\t\t      const uint32_t ct);\n \n-\tstd::map<uint32_t, Components> sets_;\n+\tipa::Interpolator<Components> sets_;\n \tstd::vector<double> xSize_;\n \tstd::vector<double> ySize_;\n \tuint16_t xGrad_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE];\n \tuint16_t yGrad_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE];\n \tuint16_t xSizes_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE];\n \tuint16_t ySizes_[RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE];\n-\tstruct {\n-\t\tuint32_t original;\n-\t\tuint32_t adjusted;\n-\t} lastCt_;\n+\n+\tunsigned int lastAppliedCt_;\n+\tunsigned int lastAppliedQuantizedCt_;\n };\n \n } /* namespace ipa::rkisp1::algorithms */\n",
    "prefixes": [
        "v3",
        "5/9"
    ]
}