[{"id":22618,"web_url":"https://patchwork.libcamera.org/comment/22618/","msgid":"<CAEmqJPrPDzSG5J3ES9j+19CS7Cuu_+bVh1bz4GTHi-2pZrakbg@mail.gmail.com>","date":"2022-04-06T11:52:25","subject":"Re: [libcamera-devel] [PATCH v3] ipa: raspberrypi: alsc: Limit the\n\tcalculated lambda values","submitter":{"id":34,"url":"https://patchwork.libcamera.org/api/people/34/","name":"Naushir Patuck","email":"naush@raspberrypi.com"},"content":"Hi Laurent,\n\nOn Wed, 6 Apr 2022 at 11:11, Laurent Pinchart <\nlaurent.pinchart@ideasonboard.com> wrote:\n\n> From: Naushir Patuck <naush@raspberrypi.com>\n>\n> Under the right circumstances, the alsc calculations could spread the\n> colour\n> errors across the entire image as lambda remains unbound. This would cause\n> the\n> corrected image chroma values to slowly drift to incorrect values.\n>\n> This change adds a config parameter (alsc.lambda_bound) that provides an\n> upper\n> and lower bound to the lambda value at every stage of the calculation.\n> With this\n> change, we now adjust the lambda values so that the average across the\n> entire\n> grid is 1 instead of normalising to the minimum value.\n>\n> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> Reviewed-by: David Plowman <david.plowman@raspberrypi.com>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n> Changes since v2:\n>\n> - Fix typo\n> - Use Span and std::accumulate\n>\n> Naush, could you please test this to make sure I haven't messed up\n> anything ?\n>\n\nLooks good!\n\nTested-by: Naushir Patuck <naush@raspberrypi.com>\n\n\n>\n>  src/ipa/raspberrypi/controller/rpi/alsc.cpp | 58 +++++++++++++++------\n>  src/ipa/raspberrypi/controller/rpi/alsc.hpp |  1 +\n>  2 files changed, 44 insertions(+), 15 deletions(-)\n>\n> diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.cpp\n> b/src/ipa/raspberrypi/controller/rpi/alsc.cpp\n> index be3d1ae476cd..e575c14a92db 100644\n> --- a/src/ipa/raspberrypi/controller/rpi/alsc.cpp\n> +++ b/src/ipa/raspberrypi/controller/rpi/alsc.cpp\n> @@ -4,9 +4,12 @@\n>   *\n>   * alsc.cpp - ALSC (auto lens shading correction) control algorithm\n>   */\n> +\n>  #include <math.h>\n> +#include <numeric>\n>\n>  #include <libcamera/base/log.h>\n> +#include <libcamera/base/span.h>\n>\n>  #include \"../awb_status.h\"\n>  #include \"alsc.hpp\"\n> @@ -149,6 +152,7 @@ void Alsc::Read(boost::property_tree::ptree const\n> &params)\n>         read_calibrations(config_.calibrations_Cb, params,\n> \"calibrations_Cb\");\n>         config_.default_ct = params.get<double>(\"default_ct\", 4500.0);\n>         config_.threshold = params.get<double>(\"threshold\", 1e-3);\n> +       config_.lambda_bound = params.get<double>(\"lambda_bound\", 0.05);\n>  }\n>\n>  static double get_ct(Metadata *metadata, double default_ct);\n> @@ -610,30 +614,47 @@ static double compute_lambda_top_end(int i, double\n> const M[XY][4],\n>\n>  // Gauss-Seidel iteration with over-relaxation.\n>  static double gauss_seidel2_SOR(double const M[XY][4], double omega,\n> -                               double lambda[XY])\n> +                               double lambda[XY], double lambda_bound)\n>  {\n> +       const double min = 1 - lambda_bound, max = 1 + lambda_bound;\n>         double old_lambda[XY];\n>         int i;\n>         for (i = 0; i < XY; i++)\n>                 old_lambda[i] = lambda[i];\n>         lambda[0] = compute_lambda_bottom_start(0, M, lambda);\n> -       for (i = 1; i < X; i++)\n> +       lambda[0] = std::clamp(lambda[0], min, max);\n> +       for (i = 1; i < X; i++) {\n>                 lambda[i] = compute_lambda_bottom(i, M, lambda);\n> -       for (; i < XY - X; i++)\n> +               lambda[i] = std::clamp(lambda[i], min, max);\n> +       }\n> +       for (; i < XY - X; i++) {\n>                 lambda[i] = compute_lambda_interior(i, M, lambda);\n> -       for (; i < XY - 1; i++)\n> +               lambda[i] = std::clamp(lambda[i], min, max);\n> +       }\n> +       for (; i < XY - 1; i++) {\n>                 lambda[i] = compute_lambda_top(i, M, lambda);\n> +               lambda[i] = std::clamp(lambda[i], min, max);\n> +       }\n>         lambda[i] = compute_lambda_top_end(i, M, lambda);\n> +       lambda[i] = std::clamp(lambda[i], min, max);\n>         // Also solve the system from bottom to top, to help spread the\n> updates\n>         // better.\n>         lambda[i] = compute_lambda_top_end(i, M, lambda);\n> -       for (i = XY - 2; i >= XY - X; i--)\n> +       lambda[i] = std::clamp(lambda[i], min, max);\n> +       for (i = XY - 2; i >= XY - X; i--) {\n>                 lambda[i] = compute_lambda_top(i, M, lambda);\n> -       for (; i >= X; i--)\n> +               lambda[i] = std::clamp(lambda[i], min, max);\n> +       }\n> +       for (; i >= X; i--) {\n>                 lambda[i] = compute_lambda_interior(i, M, lambda);\n> -       for (; i >= 1; i--)\n> +               lambda[i] = std::clamp(lambda[i], min, max);\n> +       }\n> +       for (; i >= 1; i--) {\n>                 lambda[i] = compute_lambda_bottom(i, M, lambda);\n> +               lambda[i] = std::clamp(lambda[i], min, max);\n> +       }\n>         lambda[0] = compute_lambda_bottom_start(0, M, lambda);\n> +       lambda[0] = std::clamp(lambda[0], min, max);\n>         double max_diff = 0;\n>         for (i = 0; i < XY; i++) {\n>                 lambda[i] = old_lambda[i] + (lambda[i] - old_lambda[i]) *\n> omega;\n> @@ -653,15 +674,24 @@ static void normalise(double *ptr, size_t n)\n>                 ptr[i] /= minval;\n>  }\n>\n> +// Rescale the values so that the average value is 1.\n> +static void reaverage(Span<double> data)\n> +{\n> +       double sum = std::accumulate(data.begin(), data.end(), 0.0);\n> +       double ratio = 1 / (sum / data.size());\n> +       for (double &d : data)\n> +               d *= ratio;\n> +}\n> +\n>  static void run_matrix_iterations(double const C[XY], double lambda[XY],\n>                                   double const W[XY][4], double omega,\n> -                                 int n_iter, double threshold)\n> +                                 int n_iter, double threshold, double\n> lambda_bound)\n>  {\n>         double M[XY][4];\n>         construct_M(C, W, M);\n>         double last_max_diff = std::numeric_limits<double>::max();\n>         for (int i = 0; i < n_iter; i++) {\n> -               double max_diff = fabs(gauss_seidel2_SOR(M, omega,\n> lambda));\n> +               double max_diff = fabs(gauss_seidel2_SOR(M, omega, lambda,\n> lambda_bound));\n>                 if (max_diff < threshold) {\n>                         LOG(RPiAlsc, Debug)\n>                                 << \"Stop after \" << i + 1 << \" iterations\";\n> @@ -675,10 +705,8 @@ static void run_matrix_iterations(double const C[XY],\n> double lambda[XY],\n>                                 << last_max_diff << \" to \" << max_diff;\n>                 last_max_diff = max_diff;\n>         }\n> -       // We're going to normalise the lambdas so the smallest is 1. Not\n> sure\n> -       // this is really necessary as they get renormalised later, but I\n> -       // suppose it does stop these quantities from wandering off...\n> -       normalise(lambda, XY);\n> +       // We're going to normalise the lambdas so the total average is 1.\n> +       reaverage({ lambda, XY });\n>  }\n>\n>  static void add_luminance_rb(double result[XY], double const lambda[XY],\n> @@ -737,9 +765,9 @@ void Alsc::doAlsc()\n>         compute_W(Cb, config_.sigma_Cb, Wb);\n>         // Run Gauss-Seidel iterations over the resulting matrix, for R\n> and B.\n>         run_matrix_iterations(Cr, lambda_r_, Wr, config_.omega,\n> config_.n_iter,\n> -                             config_.threshold);\n> +                             config_.threshold, config_.lambda_bound);\n>         run_matrix_iterations(Cb, lambda_b_, Wb, config_.omega,\n> config_.n_iter,\n> -                             config_.threshold);\n> +                             config_.threshold, config_.lambda_bound);\n>         // Fold the calibrated gains into our final lambda values. (Note\n> that on\n>         // the next run, we re-start with the lambda values that don't\n> have the\n>         // calibration gains included.)\n> diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.hpp\n> b/src/ipa/raspberrypi/controller/rpi/alsc.hpp\n> index 9616b99ea7ca..d1dbe0d1d22d 100644\n> --- a/src/ipa/raspberrypi/controller/rpi/alsc.hpp\n> +++ b/src/ipa/raspberrypi/controller/rpi/alsc.hpp\n> @@ -41,6 +41,7 @@ struct AlscConfig {\n>         std::vector<AlscCalibration> calibrations_Cb;\n>         double default_ct; // colour temperature if no metadata found\n>         double threshold; // iteration termination threshold\n> +       double lambda_bound; // upper/lower bound for lambda from a value\n> of 1\n>  };\n>\n>  class Alsc : public Algorithm\n> --\n> Regards,\n>\n> Laurent Pinchart\n>\n>","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 AE009C3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  6 Apr 2022 11:52:44 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2558F65649;\n\tWed,  6 Apr 2022 13:52:44 +0200 (CEST)","from mail-lf1-x12f.google.com (mail-lf1-x12f.google.com\n\t[IPv6:2a00:1450:4864:20::12f])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7CABD6563F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  6 Apr 2022 13:52:42 +0200 (CEST)","by mail-lf1-x12f.google.com with SMTP id h7so3682493lfl.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 06 Apr 2022 04:52:42 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1649245964;\n\tbh=NX9NUzAfDv4yZRFScCkH71QK5FWQYWqWnaU0ACLFTrs=;\n\th=References:In-Reply-To:Date:To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=zqYvXsOasH4Jc8zVF0R+5ohBBuXKvQuhPznoi2G2UmP5wwpsHFIETSkF4ogbpPKMJ\n\tSYItuowYxdJ1cffYgQuc0+C8YzDjdzQuYfbIizEN9dPx8Dt8PzaSObuIozANyzWJIJ\n\teMeurgok5hKMGkDV3FFRKVZrFgmeTlP1FepdS38AWKTngtquVzjZuRhTF3jmf6PZw7\n\t0TfoLQAM53gG69FOEFyUDNaUpJwvXfIfGO402ME7+Dp94YvZ7aJZX++MexEQf+Xs4O\n\the3iocHV1Gxwe1kLpMehKkGj9Tsu8c3E61I4bZ8AkKqIwjIU8srNZu1dmQeTdSaTfU\n\tebUyOgAFogLHA==","v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=j9pea2nCpCRPUjp2uBxmU3gal1b4rLTtmVYSsAI+rLI=;\n\tb=L0vWUJDVYlt2yJyfsxOqEBQBPuYiVYHKuEB4JLxP/RJDyruSQVXPoNQqhHKw3TWZXK\n\tJuxvOAyjhIvIHEQQSkY84OBGi6LGMnVkfKx6rOJulHuNGKSmqN/Mgc2tAxaz1Nx39WJF\n\tz6XKOlugFYu75PLtmONP380IYpqavBFQIdBzZ7lap1yGqk+0zI9nR9rTiQblu95YsfFU\n\t6yTWT7kHoYv6cZN7GUYJ39Wl2h/OBYfVMyfysxaGLGCg9pZNfWgvkmzctTxG6DM9jrFP\n\thh7QOsMwX9WitbqQ0cw/hhAiiDKroYwuxWp3CF2L9NRRVli3jFZ2QUPJyd+vhmtNHYRk\n\teo1g=="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=raspberrypi.com\n\theader.i=@raspberrypi.com\n\theader.b=\"L0vWUJDV\"; dkim-atps=neutral","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=j9pea2nCpCRPUjp2uBxmU3gal1b4rLTtmVYSsAI+rLI=;\n\tb=K3jCuHZ5ZyEeIZNdOyu+i7iIneEvwipxZYd5lFyVtvomtDmXgwi8p1mzjAKi4shERf\n\tW49vRQHvjl+XCSdGxwbSuwpXZ1f3w7POmaFXv6ENClP+0+1vR3O1T+eJJ14JDN+nKE9W\n\tluURdP1EnZTH7b+ICkIBELH9aafKHOV2VEkFnAym/gvls8OKDBkjdLc1IJyPEFSST7Ba\n\t0KmrZ4anLZhvo+y0Hb8fX2UtNTn8gCiMrpwoAkhCxrgVTwuZtsAg7qcjb2+FKZLfswXA\n\tHNufB2feJqTVYdH+6x+640GAPdkbogcDnGbuOqCP8HSZpvAvmbkAxUTXplPTOFpEF3mp\n\tGxcw==","X-Gm-Message-State":"AOAM533K6K0e9biNVT/wFxgSfng+5exRC1OcU1FbwVfBKwDKfkU8PEL3\n\tiIxvQzcSPgJMiUcCyVYTW6SRa1p+ERaQV1gl2e5WfA==","X-Google-Smtp-Source":"ABdhPJzFLOxZDkVN1gBPkNvdj2i1fIdki9KjNNu5I3SLtJB22X0A5k9UmkgXnahs0Z9jRyJAqBEFVylz+KPeI1loezI=","X-Received":"by 2002:a05:6512:230c:b0:44a:2282:8e38 with SMTP id\n\to12-20020a056512230c00b0044a22828e38mr5776231lfu.315.1649245961814;\n\tWed, 06 Apr 2022 04:52:41 -0700 (PDT)","MIME-Version":"1.0","References":"<20220406101100.23502-1-laurent.pinchart@ideasonboard.com>","In-Reply-To":"<20220406101100.23502-1-laurent.pinchart@ideasonboard.com>","Date":"Wed, 6 Apr 2022 12:52:25 +0100","Message-ID":"<CAEmqJPrPDzSG5J3ES9j+19CS7Cuu_+bVh1bz4GTHi-2pZrakbg@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"multipart/alternative; boundary=\"000000000000b397df05dbfaff1b\"","Subject":"Re: [libcamera-devel] [PATCH v3] ipa: raspberrypi: alsc: Limit the\n\tcalculated lambda values","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]