From patchwork Thu Nov 24 11:35:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 17870 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 2C103BDE6B for ; Thu, 24 Nov 2022 11:36:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EDE166331B; Thu, 24 Nov 2022 12:36:10 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1669289770; 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=2SSRQP+iTc3ZCvEHOr14WyTjJflUNosCb9g2lFxzRCMlALpl6DlRm9GRY4wBP8otX EMvBJUlvAutgb5/qlTwBeJWmA+4fCJveNidKalL/HNXSdzAFpy2z0mZ+3K1Ear0VZx yDzTHgPnXq/iozMtxu/iARipV91IUCeP4U5yxeV6jpcQC6S3o5KOmEOo8MWYJW2En8 IJopttRvWzpqggVjqpcZmgPg7/W0LOm2bLiMTAFxnmINlH5x1ByGqJlb1ZySDbwyae 16PDI1TOCN7cfYqQxEsSObG112VQ4MGXj0p0qGHeDjr8FXgxVD8w/6ClWP/+7JgBbD ULRZQDVLcL8ZA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4373F63321 for ; Thu, 24 Nov 2022 12:36:09 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="TMMGqd3l"; 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 B8EFD496; Thu, 24 Nov 2022 12:36:07 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1669289769; bh=3cdWwHM8Ia0AWP/tCkVhKltnyHW7TVzU9UusVKHhod0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TMMGqd3lfJK8tvgoVbAR4XGONNKdGq2bP8getL3UR+BMshtxpQ/Bjii0HPlNdNdvJ X1jWl48JdLSq1NJ2if6vEb0cowarb4/cMRegzh9VQLFCw232wW3PuEke46V5Awzx/R sTj4vuv1L7e9f0SDc73NMNMkmjb9eo1gkaZJNffc= To: libcamera-devel@lists.libcamera.org Date: Thu, 24 Nov 2022 20:35:42 +0900 Message-Id: <20221124113550.2182342-5-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20221124113550.2182342-1-paul.elder@ideasonboard.com> References: <20221124113550.2182342-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 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