[libcamera-devel,v4,04/12] utils: libtuning: modules: Add base LSC module
diff mbox series

Message ID 20221124113550.2182342-5-paul.elder@ideasonboard.com
State Accepted
Headers show
Series
  • utils: tuning: Add a new tuning infrastructure
Related show

Commit Message

Paul Elder Nov. 24, 2022, 11:35 a.m. UTC
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 <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

---
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

Patch
diff mbox series

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 <paul.elder@ideasonboard.com>
+
+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 <paul.elder@ideasonboard.com>
+
+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