From patchwork Thu Nov 10 17:31:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 17775 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 4EF33BD16B for ; Thu, 10 Nov 2022 17:32:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 12A5C63092; Thu, 10 Nov 2022 18:32:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1668101545; bh=3cdWwHM8Ia0AWP/tCkVhKltnyHW7TVzU9UusVKHhod0=; 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=IWGbo4PI90/VUmAD/tWqPs8u9PkJGWG3lBbsLOaplqfU1Rr0jPFXugHmds1dgvw33 r4gk+CVJsMKy7FTj3Jl0BOAZcpT+8OT8yOT/E5ZxY+XKvHvgTpvC6Y7+8pbXN0FwgV t9MXhXbJXhn6hslxbZJCu8GqAlieuaf9qZpwy8E3Sw7kKf9WRB0SCrjcdqMXqBxWJK Vjz5oPc5xkJZdnLNfjHKWNKNj07iyVMnk3h3t4GJej/knnx5X5N7CdXmmOLpP+j2iQ n3ZrSuCJBXXOGhILYBA2VAZPCTwjDLPBon0locAmy5gPaQw8fat8+/KsoLq8Nn5jzq krZYxZFsoq/EQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3F58D63085 for ; Thu, 10 Nov 2022 18:32:24 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="oMEhoPib"; dkim-atps=neutral Received: from pyrite.tail37cf.ts.net (h175-177-042-159.catv02.itscom.jp [175.177.42.159]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7B5884D4; Thu, 10 Nov 2022 18:32:22 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1668101544; bh=3cdWwHM8Ia0AWP/tCkVhKltnyHW7TVzU9UusVKHhod0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=oMEhoPibxoYOx/Yk2gA1MXqBoWneDxg/ioWHg9aRtvnzYwbufElg/2Yn8H9MDVsIf 9iv6HZQYM4+LuPGUXZlThX+4qrrmeTJn6fDrzh5NopSncisAGNyYcLiX+CgpYh2OId DH+OBgOFVzXsKy2UhIocrTx0iIXRP1s0K1V+B0Vo= To: libcamera-devel@lists.libcamera.org Date: Fri, 11 Nov 2022 02:31:46 +0900 Message-Id: <20221110173154.488445-5-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20221110173154.488445-1-paul.elder@ideasonboard.com> References: <20221110173154.488445-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 04/12] utils: libtuning: modules: Add base LSC 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 a base LSC 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. It alone is insufficient as a module, but it provides utilities that are useful for and which will simplify implementing LSC modules. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v3: - rename ALSC to LSC - update changelog to more accurately reflect the redesign (splitting core LSC from Raspberry Pi ALSC and rkisp1 LSC) 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/lsc/__init__.py | 5 ++ utils/tuning/libtuning/modules/lsc/lsc.py | 72 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 utils/tuning/libtuning/modules/lsc/__init__.py create mode 100644 utils/tuning/libtuning/modules/lsc/lsc.py diff --git a/utils/tuning/libtuning/modules/lsc/__init__.py b/utils/tuning/libtuning/modules/lsc/__init__.py new file mode 100644 index 00000000..47d9b846 --- /dev/null +++ b/utils/tuning/libtuning/modules/lsc/__init__.py @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2022, Paul Elder + +from libtuning.modules.lsc.lsc import LSC diff --git a/utils/tuning/libtuning/modules/lsc/lsc.py b/utils/tuning/libtuning/modules/lsc/lsc.py new file mode 100644 index 00000000..344a07a3 --- /dev/null +++ b/utils/tuning/libtuning/modules/lsc/lsc.py @@ -0,0 +1,72 @@ +# 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 LSC(Module): + type = 'lsc' + hr_name = 'LSC (Base)' + out_name = 'GenericLSC' + + 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__() + + 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_lsc_images(self, images): + for image in images: + if image.lsc_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