From patchwork Thu Nov 10 17:31:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 17773 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 D921FBD16B for ; Thu, 10 Nov 2022 17:32:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3074A63086; Thu, 10 Nov 2022 18:32:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1668101541; bh=cDeERBi0lVPKemJBXXs6ncnPI/AH+uP5QuGku0ZDk+c=; 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=0wjm8iBYVvoBgedZVi0YtjGRnm4VSNotkV3/QryqOexMm/uqVWHV3SpKKfN0V6+du POz2V8M5gbugLyf+mlSG2FWHXrtvFW7dfzjLGJq4Wmth8/ogGG5bqCS3ADPDvURT/X XMnxwFfwY1naqZJX2i3F/7zVAn5At/pBLxQnDdaF8nqcwJk3G1HFrFZQTQgqGA2IeP +VGrWhoNAs+uVME0VMdfmjMidFz5d9undLBlAnDcUDrw77VqLni/CSQXU9cNGu9LxX mqPDx0o02DuQkGQP68ky5/1c24RqL/vC5in0VJpnSCU9V77f1tzYPGuKa1gD3NGMib bmWETIcDBIoHQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3C66463075 for ; Thu, 10 Nov 2022 18:32:20 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="FuPrsHOF"; 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 C792B32A; Thu, 10 Nov 2022 18:32:18 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1668101540; bh=cDeERBi0lVPKemJBXXs6ncnPI/AH+uP5QuGku0ZDk+c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FuPrsHOFGL3Svq6f5VWflILEIjmbbWdEMROtYdIuCRIWlmftC0Ja43d+rIkAjRwc9 WXaMj8Ou4q/a5+YoIdTVIF1VGLUVlXBgscWW4tO5RrDxC25cs0NRmeSsAStQdOBDNJ p+JooCiDdQ1ZLXAfnOqi8axErEZvqnAEgt3JfkTk= To: libcamera-devel@lists.libcamera.org Date: Fri, 11 Nov 2022 02:31:44 +0900 Message-Id: <20221110173154.488445-3-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 02/12] utils: tuning: libtuning: Implement math helpers 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" Implement math helpers for libtuning. This includes: - Average, a wrapper class for numpy averaging functions - Gradient, a class that represents gradients, for distributing and mapping - Smoothing, a wrapper class for cv2 smoothing functions Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v3: - Newly split from the first patch "utils: tuning: libtuning: Implement the core of libtuning" - See changelog from that patch --- utils/tuning/libtuning/average.py | 21 ++++++++ utils/tuning/libtuning/gradient.py | 75 +++++++++++++++++++++++++++++ utils/tuning/libtuning/smoothing.py | 24 +++++++++ 3 files changed, 120 insertions(+) create mode 100644 utils/tuning/libtuning/average.py create mode 100644 utils/tuning/libtuning/gradient.py create mode 100644 utils/tuning/libtuning/smoothing.py diff --git a/utils/tuning/libtuning/average.py b/utils/tuning/libtuning/average.py new file mode 100644 index 00000000..e28770d7 --- /dev/null +++ b/utils/tuning/libtuning/average.py @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2022, Paul Elder +# +# average.py - Wrapper for numpy averaging functions to enable duck-typing + +import numpy as np + + +# @brief Wrapper for np averaging functions so that they can be duck-typed +class Average(object): + def __init__(self): + pass + + def average(self, np_array): + raise NotImplementedError + + +class Mean(Average): + def average(self, np_array): + return np.mean(np_array) diff --git a/utils/tuning/libtuning/gradient.py b/utils/tuning/libtuning/gradient.py new file mode 100644 index 00000000..64a96369 --- /dev/null +++ b/utils/tuning/libtuning/gradient.py @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2022, Paul Elder +# +# gradient.py - Gradients that can be used to distribute or map numbers + +import libtuning as lt + +import math +from numbers import Number + + +# @brief Gradient for how to allocate pixels to sectors +# @description There are no parameters for the gradients as the domain is the +# number of pixels and the range is the number of sectors, and +# there is only one curve that has a startpoint and endpoint at +# (0, 0) and at (#pixels, #sectors). The exception is for curves +# that *do* have multiple solutions for only two points, such as +# gaussian, and curves of higher polynomial orders if we had them. +# +# \todo There will probably be a helper in the Gradient class, as I have a +# feeling that all the other curves (besides Linear and Gaussian) can be +# implemented in the same way. +class Gradient(object): + def __init__(self): + pass + + # @brief Distribute pixels into sectors (only in one dimension) + # @param domain Number of pixels + # @param sectors Number of sectors + # @return A list of number of pixels in each sector + def distribute(self, domain: list, sectors: list, ) -> list: + raise NotImplementedError + + # @brief Map a number on a curve + # @param domain Domain of the curve + # @param rang Range of the curve + # @param x Input on the domain of the curve + # @return y from the range of the curve + def map(self, domain: tuple, rang: tuple, x: Number) -> Number: + raise NotImplementedError + + +class Linear(Gradient): + # @param remainder Mode of handling remainder + def __init__(self, remainder: lt.Remainder = lt.Remainder.Float): + self.remainder = remainder + + def distribute(self, domain: list, sectors: list) -> list: + size = domain / sectors + rem = domain % sectors + + if rem == 0: + return [int(size)] * sectors + + size = math.ceil(size) + rem = domain % size + output_sectors = [int(size)] * (sectors - 1) + + if self.remainder == lt.Remainder.Float: + size = domain / sectors + output_sectors = [size] * sectors + elif self.remainder == lt.Remainder.DistributeFront: + output_sectors.append(int(rem)) + elif self.remainder == lt.Remainder.DistributeBack: + output_sectors.insert(0, int(rem)) + else: + raise ValueError + + return output_sectors + + def map(self, domain: tuple, rang: tuple, x: Number) -> Number: + m = (rang[1] - rang[0]) / (domain[1] - domain[0]) + b = rang[0] - m * domain[0] + return m * x + b diff --git a/utils/tuning/libtuning/smoothing.py b/utils/tuning/libtuning/smoothing.py new file mode 100644 index 00000000..b8a5a242 --- /dev/null +++ b/utils/tuning/libtuning/smoothing.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2022, Paul Elder +# +# smoothing.py - Wrapper for cv2 smoothing functions to enable duck-typing + +import cv2 + + +# @brief Wrapper for cv2 smoothing functions so that they can be duck-typed +class Smoothing(object): + def __init__(self): + pass + + def smoothing(self, src): + raise NotImplementedError + + +class MedianBlur(Smoothing): + def __init__(self, ksize): + self.ksize = ksize + + def smoothing(self, src): + return cv2.medianBlur(src.astype('float32'), self.ksize).astype('float64')