From patchwork Sat Oct 22 06:23:01 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 17653 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 059D0C0DA4 for ; Sat, 22 Oct 2022 06:23:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B571362ED0; Sat, 22 Oct 2022 08:23:28 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1666419808; bh=sjsrLUhf4WlqMjHRF76GfAgAS7z1irWu2Vo1Lwc5b0c=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=4Zs0X7MFECamDD10UpDBnKmzy0nvp8vQUCOaH8sAEpP0WIggPD5gjGn0BWAdoKaqi eoXYRVTboj1U/JI34k5YZYVmWdZk0H7/j0V1jwXeuo+zsHAqKX2i/hIiuln5auRvDB B1d1PvmDxXqsA1yO8HjiThBG3vxYY1aNOuD6AGKYpeoatVMa5SFTuQhYZCcaOb4x5n 7nRbTthqK+0TbYmTO4OmOcJ4n6atPznmAnvkGB/t+tWBLdynKx6Bsor1HeIx8whV7H BJ9bHFN9xwHIdI8dbYBVoAMnCEQAFzyo+eqHT98rl+eAiQn9MSTFegwMeiJGwRF2Nx Ij8Tg5l3XqeOQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C2A6F62EC3 for ; Sat, 22 Oct 2022 08:23:24 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hN1386eW"; dkim-atps=neutral Received: from pyrite.rasen.tech (h175-177-042-159.catv02.itscom.jp [175.177.42.159]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 67F25106; Sat, 22 Oct 2022 08:23:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1666419804; bh=sjsrLUhf4WlqMjHRF76GfAgAS7z1irWu2Vo1Lwc5b0c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hN1386eWkTea7lEWuBiSD9BaBIkzSBZMF7otHylqSXKTow1b2Gx2ArtRpiB16O2YY k3ifk6eglDu5awg6tmnX57ESfapS6WGmiBFf9z1FAmDt6ikBeS9h/dE8d/sQfgwrAm 3j1HA9j+ZY6fSk23qvNAiOgZscTo86k8R2Pyyhrs= To: libcamera-devel@lists.libcamera.org Date: Sat, 22 Oct 2022 15:23:01 +0900 Message-Id: <20221022062310.2545463-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221022062310.2545463-1-paul.elder@ideasonboard.com> References: <20221022062310.2545463-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 02/11] utils: libtuning: modules: Add ALSC module X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Paul Elder via libcamera-devel From: Paul Elder Reply-To: Paul Elder Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add an ALSC module to libtuning's collection of modules. It is based on raspberrypi's ctt's ALSC, but customizable for different lens shading table sizes, among other things. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v2: - fix python errors - fix style - add SPDX and copyright - don't call super().validateConfig() - fix sector splitting - i forgot that we have to half the image size for each color channel - make the base ALSC module into an abstract module, which can be easily used by other platforms to derive their own ALSC modules (see rkisp1's module later in this series; i think it's quite nice) --- .../tuning/libtuning/modules/alsc/__init__.py | 5 ++ utils/tuning/libtuning/modules/alsc/alsc.py | 78 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 utils/tuning/libtuning/modules/alsc/__init__.py create mode 100644 utils/tuning/libtuning/modules/alsc/alsc.py diff --git a/utils/tuning/libtuning/modules/alsc/__init__.py b/utils/tuning/libtuning/modules/alsc/__init__.py new file mode 100644 index 00000000..a8f28923 --- /dev/null +++ b/utils/tuning/libtuning/modules/alsc/__init__.py @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2022, Paul Elder + +from libtuning.modules.alsc.alsc import ALSC diff --git a/utils/tuning/libtuning/modules/alsc/alsc.py b/utils/tuning/libtuning/modules/alsc/alsc.py new file mode 100644 index 00000000..b708b28d --- /dev/null +++ b/utils/tuning/libtuning/modules/alsc/alsc.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (C) 2019, Raspberry Pi Ltd +# Copyright (C) 2022, Paul Elder + +from ..module import Module + +import libtuning as lt +import libtuning.utils as utils + +import numpy as np + + +class ALSC(Module): + type = 'alsc' + hr_name = 'ALSC (Base)' + out_name = 'GenericALSC' + + def __init__(self, *, + debug: list, + sector_shape: tuple, + sector_x_gradient: lt.Gradient, + sector_y_gradient: lt.Gradient, + sector_average_function: lt.Average, + smoothing_function: lt.Smoothing): + super().__init__() + + if (sector_x_gradient is lt.gradient.Linear and sector_x_remainder is None): + raise ValueError('sector_x_remainder must be specified if sector_x_gradient is Linear') + + if (sector_y_gradient is lt.gradient.Linear and sector_y_remainder is None): + raise ValueError('sector_y_remainder must be specified if sector_y_gradient is Linear') + + self.debug = debug + + self.sector_shape = sector_shape + self.sector_x_gradient = sector_x_gradient + self.sector_y_gradient = sector_y_gradient + self.sector_average_function = sector_average_function + + self.smoothing_function = smoothing_function + + def _enumerate_alsc_images(self, images): + for image in images: + if image.alsc_only: + yield image + + def _get_grid(self, channel, img_w, img_h): + # List of number of pixels in each sector + sectors_x = self.sector_x_gradient.distribute(img_w / 2, self.sector_shape[0]) + sectors_y = self.sector_y_gradient.distribute(img_h / 2, self.sector_shape[1]) + + grid = [] + + r = 0 + for y in sectors_y: + c = 0 + for x in sectors_x: + grid.append(self.sector_average_function.average(channel[r:r + y, c:c + x])) + c += x + r += y + + return np.array(grid) + + def _lsc_single_channel(self, channel: np.array, + image: lt.Image, green_grid: np.array = None): + grid = self._get_grid(channel, image.w, image.h) + grid -= image.blacklevel_16 + if green_grid is None: + table = np.reshape(1 / grid, self.sector_shape[::-1]) + else: + table = np.reshape(green_grid / grid, self.sector_shape[::-1]) + table = self.smoothing_function.smoothing(table) + + if green_grid is None: + table = table / np.min(table) + + return table, grid