[libcamera-devel,v2,04/11] utils: libtuning: modules: alsc: Add rkisp1 ALSC module
diff mbox series

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

Commit Message

Paul Elder Oct. 22, 2022, 6:23 a.m. UTC
Add an ALSC module for RkISP1.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
New in v2
---
 .../tuning/libtuning/modules/alsc/__init__.py |   1 +
 utils/tuning/libtuning/modules/alsc/rkisp1.py | 112 ++++++++++++++++++
 2 files changed, 113 insertions(+)
 create mode 100644 utils/tuning/libtuning/modules/alsc/rkisp1.py

Comments

Laurent Pinchart Nov. 8, 2022, 11:22 p.m. UTC | #1
Hi Paul,

Thank you for the patch.

On Sat, Oct 22, 2022 at 03:23:03PM +0900, Paul Elder via libcamera-devel wrote:
> Add an ALSC module for RkISP1.

s/ALSC/LSC/

Same through the code and in the commit message.

> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> 
> ---
> New in v2
> ---
>  .../tuning/libtuning/modules/alsc/__init__.py |   1 +
>  utils/tuning/libtuning/modules/alsc/rkisp1.py | 112 ++++++++++++++++++
>  2 files changed, 113 insertions(+)
>  create mode 100644 utils/tuning/libtuning/modules/alsc/rkisp1.py
> 
> diff --git a/utils/tuning/libtuning/modules/alsc/__init__.py b/utils/tuning/libtuning/modules/alsc/__init__.py
> index ef6300c2..932ce38e 100644
> --- a/utils/tuning/libtuning/modules/alsc/__init__.py
> +++ b/utils/tuning/libtuning/modules/alsc/__init__.py
> @@ -4,3 +4,4 @@
>  
>  from libtuning.modules.alsc.alsc import ALSC
>  from libtuning.modules.alsc.raspberrypi import ALSCRaspberryPi
> +from libtuning.modules.alsc.rkisp1 import ALSCRkISP1
> diff --git a/utils/tuning/libtuning/modules/alsc/rkisp1.py b/utils/tuning/libtuning/modules/alsc/rkisp1.py
> new file mode 100644
> index 00000000..d8f253e6
> --- /dev/null
> +++ b/utils/tuning/libtuning/modules/alsc/rkisp1.py
> @@ -0,0 +1,112 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +#
> +# Copyright (C) 2019, Raspberry Pi Ltd
> +# Copyright (C) 2022, Paul Elder <paul.elder@ideasonboard.com>
> +
> +from .alsc import ALSC
> +
> +import libtuning as lt
> +import libtuning.utils as utils
> +
> +from numbers import Number
> +import numpy as np
> +
> +
> +class ALSCRkISP1(ALSC):
> +    hr_name = 'ALSC (RkISP1)'
> +    out_name = 'LensShadingCorrection'
> +    # todo Not sure if this is useful. Probably will remove later.

s/todo/\todo/

I'll stop repeating this, please address it in all patches.

> +    compatible = ['rkisp1']
> +
> +    def __init__(self, *args, **kwargs):
> +        super().__init__(**kwargs)
> +
> +    # We don't actually need anything from the config file
> +    def _validate_config(self, config: dict) -> bool:
> +        return True
> +
> +    # @return Image color temperature, flattened array of red calibration table
> +    #         (containing {sector size} elements), flattened array of blue
> +    #         calibration table, flattened array of (red's) green calibration
> +    #         table, flattened array of (blue's) green calibration table
> +
> +    def _do_single_alsc(self, image: lt.Image):
> +        cgr, gr = self._lsc_single_channel(image.channels[lt.Color.GR], image)
> +        cgb, gb = self._lsc_single_channel(image.channels[lt.Color.GB], image)
> +
> +        # Should these ratio against the average of both greens or just each
> +        # green like we've done here?
> +        cr, _ = self._lsc_single_channel(image.channels[lt.Color.R], image, gr)
> +        cb, _ = self._lsc_single_channel(image.channels[lt.Color.B], image, gb)
> +
> +        return image.color, cr.flatten(), cb.flatten(), cgr.flatten(), cgb.flatten()
> +
> +    # @return List of dictionaries of color temperature, red table, red's green
> +    #         table, blue's green table, and blue table
> +
> +    def _do_all_alsc(self, images: list) -> list:
> +        output_list = []
> +        output_map_func = lt.gradient.Linear().map

Isn't this meant to come from the gradient passed to the constructor ?

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> +        output_map_domain = (1, 3.999)
> +        output_map_range = (1024, 4095)
> +
> +        # List of colour temperatures
> +        list_col = []
> +        # Associated calibration tables
> +        list_cr = []
> +        list_cb = []
> +        list_cgr = []
> +        list_cgb = []
> +        for image in self._enumerate_alsc_images(images):
> +            col, cr, cb, cgr, cgb = self._do_single_alsc(image)
> +            list_col.append(col)
> +            list_cr.append(cr)
> +            list_cb.append(cb)
> +            list_cgr.append(cgr)
> +            list_cgb.append(cgb)
> +
> +        # Convert to numpy array for data manipulation
> +        list_col = np.array(list_col)
> +        list_cr = np.array(list_cr)
> +        list_cb = np.array(list_cb)
> +        list_cgr = np.array(list_cgr)
> +        list_cgb = np.array(list_cgb)
> +
> +        for color_temperature in sorted(set(list_col)):
> +            # Average tables for the same colour temperature
> +            indices = np.where(list_col == color_temperature)
> +            color_temperature = int(color_temperature)
> +
> +            tables = []
> +            for lis in [list_cr, list_cgr, list_cgb, list_cb]:
> +                table = np.mean(lis[indices], axis=0)
> +                table = output_map_func(output_map_domain, output_map_range, table)
> +                table = np.round(table).astype('int32').tolist()
> +                tables.append(table)
> +
> +            entry = {
> +                'ct': color_temperature,
> +                'r': tables[0],
> +                'gr': tables[1],
> +                'gb': tables[2],
> +                'b': tables[3],
> +            }
> +
> +            output_list.append(entry)
> +
> +        return output_list
> +
> +    def _process(self, args, config: dict, images: list, outputs: dict) -> dict:
> +        output = {}
> +
> +        # todo This should actually come from self.sector_{x,y}_gradient
> +        size_gradient = lt.gradient.Linear(lt.Remainder.Float)
> +        output['x-size'] = size_gradient.distribute(0.5, 8)
> +        output['y-size'] = size_gradient.distribute(0.5, 8)
> +
> +        output['sets'] = self._do_all_alsc(images)
> +
> +        # todo Validate images from greyscale camera and force grescale mode
> +        # todo Debug functionality
> +
> +        return output
Paul Elder Nov. 10, 2022, 7:08 a.m. UTC | #2
On Wed, Nov 09, 2022 at 01:22:50AM +0200, Laurent Pinchart wrote:
> Hi Paul,
> 
> Thank you for the patch.
> 
> On Sat, Oct 22, 2022 at 03:23:03PM +0900, Paul Elder via libcamera-devel wrote:
> > Add an ALSC module for RkISP1.
> 
> s/ALSC/LSC/
> 
> Same through the code and in the commit message.
> 
> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> > 
> > ---
> > New in v2
> > ---
> >  .../tuning/libtuning/modules/alsc/__init__.py |   1 +
> >  utils/tuning/libtuning/modules/alsc/rkisp1.py | 112 ++++++++++++++++++
> >  2 files changed, 113 insertions(+)
> >  create mode 100644 utils/tuning/libtuning/modules/alsc/rkisp1.py
> > 
> > diff --git a/utils/tuning/libtuning/modules/alsc/__init__.py b/utils/tuning/libtuning/modules/alsc/__init__.py
> > index ef6300c2..932ce38e 100644
> > --- a/utils/tuning/libtuning/modules/alsc/__init__.py
> > +++ b/utils/tuning/libtuning/modules/alsc/__init__.py
> > @@ -4,3 +4,4 @@
> >  
> >  from libtuning.modules.alsc.alsc import ALSC
> >  from libtuning.modules.alsc.raspberrypi import ALSCRaspberryPi
> > +from libtuning.modules.alsc.rkisp1 import ALSCRkISP1
> > diff --git a/utils/tuning/libtuning/modules/alsc/rkisp1.py b/utils/tuning/libtuning/modules/alsc/rkisp1.py
> > new file mode 100644
> > index 00000000..d8f253e6
> > --- /dev/null
> > +++ b/utils/tuning/libtuning/modules/alsc/rkisp1.py
> > @@ -0,0 +1,112 @@
> > +# SPDX-License-Identifier: BSD-2-Clause
> > +#
> > +# Copyright (C) 2019, Raspberry Pi Ltd
> > +# Copyright (C) 2022, Paul Elder <paul.elder@ideasonboard.com>
> > +
> > +from .alsc import ALSC
> > +
> > +import libtuning as lt
> > +import libtuning.utils as utils
> > +
> > +from numbers import Number
> > +import numpy as np
> > +
> > +
> > +class ALSCRkISP1(ALSC):
> > +    hr_name = 'ALSC (RkISP1)'
> > +    out_name = 'LensShadingCorrection'
> > +    # todo Not sure if this is useful. Probably will remove later.
> 
> s/todo/\todo/
> 
> I'll stop repeating this, please address it in all patches.
> 
> > +    compatible = ['rkisp1']
> > +
> > +    def __init__(self, *args, **kwargs):
> > +        super().__init__(**kwargs)
> > +
> > +    # We don't actually need anything from the config file
> > +    def _validate_config(self, config: dict) -> bool:
> > +        return True
> > +
> > +    # @return Image color temperature, flattened array of red calibration table
> > +    #         (containing {sector size} elements), flattened array of blue
> > +    #         calibration table, flattened array of (red's) green calibration
> > +    #         table, flattened array of (blue's) green calibration table
> > +
> > +    def _do_single_alsc(self, image: lt.Image):
> > +        cgr, gr = self._lsc_single_channel(image.channels[lt.Color.GR], image)
> > +        cgb, gb = self._lsc_single_channel(image.channels[lt.Color.GB], image)
> > +
> > +        # Should these ratio against the average of both greens or just each
> > +        # green like we've done here?
> > +        cr, _ = self._lsc_single_channel(image.channels[lt.Color.R], image, gr)
> > +        cb, _ = self._lsc_single_channel(image.channels[lt.Color.B], image, gb)
> > +
> > +        return image.color, cr.flatten(), cb.flatten(), cgr.flatten(), cgb.flatten()
> > +
> > +    # @return List of dictionaries of color temperature, red table, red's green
> > +    #         table, blue's green table, and blue table
> > +
> > +    def _do_all_alsc(self, images: list) -> list:
> > +        output_list = []
> > +        output_map_func = lt.gradient.Linear().map
> 
> Isn't this meant to come from the gradient passed to the constructor ?

No, this is a different linear function for mapping the output of lsc [1,
3.999] to the values that the rkisp1's lsc registers want [1024, 4095].

> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>


Thanks,

Paul

> 
> > +        output_map_domain = (1, 3.999)
> > +        output_map_range = (1024, 4095)
> > +
> > +        # List of colour temperatures
> > +        list_col = []
> > +        # Associated calibration tables
> > +        list_cr = []
> > +        list_cb = []
> > +        list_cgr = []
> > +        list_cgb = []
> > +        for image in self._enumerate_alsc_images(images):
> > +            col, cr, cb, cgr, cgb = self._do_single_alsc(image)
> > +            list_col.append(col)
> > +            list_cr.append(cr)
> > +            list_cb.append(cb)
> > +            list_cgr.append(cgr)
> > +            list_cgb.append(cgb)
> > +
> > +        # Convert to numpy array for data manipulation
> > +        list_col = np.array(list_col)
> > +        list_cr = np.array(list_cr)
> > +        list_cb = np.array(list_cb)
> > +        list_cgr = np.array(list_cgr)
> > +        list_cgb = np.array(list_cgb)
> > +
> > +        for color_temperature in sorted(set(list_col)):
> > +            # Average tables for the same colour temperature
> > +            indices = np.where(list_col == color_temperature)
> > +            color_temperature = int(color_temperature)
> > +
> > +            tables = []
> > +            for lis in [list_cr, list_cgr, list_cgb, list_cb]:
> > +                table = np.mean(lis[indices], axis=0)
> > +                table = output_map_func(output_map_domain, output_map_range, table)
> > +                table = np.round(table).astype('int32').tolist()
> > +                tables.append(table)
> > +
> > +            entry = {
> > +                'ct': color_temperature,
> > +                'r': tables[0],
> > +                'gr': tables[1],
> > +                'gb': tables[2],
> > +                'b': tables[3],
> > +            }
> > +
> > +            output_list.append(entry)
> > +
> > +        return output_list
> > +
> > +    def _process(self, args, config: dict, images: list, outputs: dict) -> dict:
> > +        output = {}
> > +
> > +        # todo This should actually come from self.sector_{x,y}_gradient
> > +        size_gradient = lt.gradient.Linear(lt.Remainder.Float)
> > +        output['x-size'] = size_gradient.distribute(0.5, 8)
> > +        output['y-size'] = size_gradient.distribute(0.5, 8)
> > +
> > +        output['sets'] = self._do_all_alsc(images)
> > +
> > +        # todo Validate images from greyscale camera and force grescale mode
> > +        # todo Debug functionality
> > +
> > +        return output

Patch
diff mbox series

diff --git a/utils/tuning/libtuning/modules/alsc/__init__.py b/utils/tuning/libtuning/modules/alsc/__init__.py
index ef6300c2..932ce38e 100644
--- a/utils/tuning/libtuning/modules/alsc/__init__.py
+++ b/utils/tuning/libtuning/modules/alsc/__init__.py
@@ -4,3 +4,4 @@ 
 
 from libtuning.modules.alsc.alsc import ALSC
 from libtuning.modules.alsc.raspberrypi import ALSCRaspberryPi
+from libtuning.modules.alsc.rkisp1 import ALSCRkISP1
diff --git a/utils/tuning/libtuning/modules/alsc/rkisp1.py b/utils/tuning/libtuning/modules/alsc/rkisp1.py
new file mode 100644
index 00000000..d8f253e6
--- /dev/null
+++ b/utils/tuning/libtuning/modules/alsc/rkisp1.py
@@ -0,0 +1,112 @@ 
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (C) 2019, Raspberry Pi Ltd
+# Copyright (C) 2022, Paul Elder <paul.elder@ideasonboard.com>
+
+from .alsc import ALSC
+
+import libtuning as lt
+import libtuning.utils as utils
+
+from numbers import Number
+import numpy as np
+
+
+class ALSCRkISP1(ALSC):
+    hr_name = 'ALSC (RkISP1)'
+    out_name = 'LensShadingCorrection'
+    # todo Not sure if this is useful. Probably will remove later.
+    compatible = ['rkisp1']
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(**kwargs)
+
+    # We don't actually need anything from the config file
+    def _validate_config(self, config: dict) -> bool:
+        return True
+
+    # @return Image color temperature, flattened array of red calibration table
+    #         (containing {sector size} elements), flattened array of blue
+    #         calibration table, flattened array of (red's) green calibration
+    #         table, flattened array of (blue's) green calibration table
+
+    def _do_single_alsc(self, image: lt.Image):
+        cgr, gr = self._lsc_single_channel(image.channels[lt.Color.GR], image)
+        cgb, gb = self._lsc_single_channel(image.channels[lt.Color.GB], image)
+
+        # Should these ratio against the average of both greens or just each
+        # green like we've done here?
+        cr, _ = self._lsc_single_channel(image.channels[lt.Color.R], image, gr)
+        cb, _ = self._lsc_single_channel(image.channels[lt.Color.B], image, gb)
+
+        return image.color, cr.flatten(), cb.flatten(), cgr.flatten(), cgb.flatten()
+
+    # @return List of dictionaries of color temperature, red table, red's green
+    #         table, blue's green table, and blue table
+
+    def _do_all_alsc(self, images: list) -> list:
+        output_list = []
+        output_map_func = lt.gradient.Linear().map
+        output_map_domain = (1, 3.999)
+        output_map_range = (1024, 4095)
+
+        # List of colour temperatures
+        list_col = []
+        # Associated calibration tables
+        list_cr = []
+        list_cb = []
+        list_cgr = []
+        list_cgb = []
+        for image in self._enumerate_alsc_images(images):
+            col, cr, cb, cgr, cgb = self._do_single_alsc(image)
+            list_col.append(col)
+            list_cr.append(cr)
+            list_cb.append(cb)
+            list_cgr.append(cgr)
+            list_cgb.append(cgb)
+
+        # Convert to numpy array for data manipulation
+        list_col = np.array(list_col)
+        list_cr = np.array(list_cr)
+        list_cb = np.array(list_cb)
+        list_cgr = np.array(list_cgr)
+        list_cgb = np.array(list_cgb)
+
+        for color_temperature in sorted(set(list_col)):
+            # Average tables for the same colour temperature
+            indices = np.where(list_col == color_temperature)
+            color_temperature = int(color_temperature)
+
+            tables = []
+            for lis in [list_cr, list_cgr, list_cgb, list_cb]:
+                table = np.mean(lis[indices], axis=0)
+                table = output_map_func(output_map_domain, output_map_range, table)
+                table = np.round(table).astype('int32').tolist()
+                tables.append(table)
+
+            entry = {
+                'ct': color_temperature,
+                'r': tables[0],
+                'gr': tables[1],
+                'gb': tables[2],
+                'b': tables[3],
+            }
+
+            output_list.append(entry)
+
+        return output_list
+
+    def _process(self, args, config: dict, images: list, outputs: dict) -> dict:
+        output = {}
+
+        # todo This should actually come from self.sector_{x,y}_gradient
+        size_gradient = lt.gradient.Linear(lt.Remainder.Float)
+        output['x-size'] = size_gradient.distribute(0.5, 8)
+        output['y-size'] = size_gradient.distribute(0.5, 8)
+
+        output['sets'] = self._do_all_alsc(images)
+
+        # todo Validate images from greyscale camera and force grescale mode
+        # todo Debug functionality
+
+        return output