[1/5] utils: libtuning: modules: Add skeletal AGC module
diff mbox series

Message ID 20240405145335.3010479-2-paul.elder@ideasonboard.com
State New
Headers show
Series
  • utils: tuning: Add AGC and CCM
Related show

Commit Message

Paul Elder April 5, 2024, 2:53 p.m. UTC
Add a skeletal AGC module just so that we can have some AGC tuning
values that we can use to test during development of AGC in the IPAs. As
rkisp1 is the main target, we only add support for rkisp1 for now.

The parameters are mostly copied from the hardcoded values in ctt,
except for the metering modes.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---
 .../tuning/libtuning/modules/agc/__init__.py  |  6 ++
 utils/tuning/libtuning/modules/agc/agc.py     | 24 +++++
 utils/tuning/libtuning/modules/agc/rkisp1.py  | 95 +++++++++++++++++++
 3 files changed, 125 insertions(+)
 create mode 100644 utils/tuning/libtuning/modules/agc/__init__.py
 create mode 100644 utils/tuning/libtuning/modules/agc/agc.py
 create mode 100644 utils/tuning/libtuning/modules/agc/rkisp1.py

Comments

Stefan Klug April 11, 2024, 2:19 p.m. UTC | #1
Hi Paul,

thank you for the patch.
Great to see the tuning tools progressing. 

On Fri, Apr 05, 2024 at 11:53:31PM +0900, Paul Elder wrote:
> Add a skeletal AGC module just so that we can have some AGC tuning
> values that we can use to test during development of AGC in the IPAs. As
> rkisp1 is the main target, we only add support for rkisp1 for now.
> 
> The parameters are mostly copied from the hardcoded values in ctt,
> except for the metering modes.
> 
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> ---
>  .../tuning/libtuning/modules/agc/__init__.py  |  6 ++
>  utils/tuning/libtuning/modules/agc/agc.py     | 24 +++++
>  utils/tuning/libtuning/modules/agc/rkisp1.py  | 95 +++++++++++++++++++
>  3 files changed, 125 insertions(+)
>  create mode 100644 utils/tuning/libtuning/modules/agc/__init__.py
>  create mode 100644 utils/tuning/libtuning/modules/agc/agc.py
>  create mode 100644 utils/tuning/libtuning/modules/agc/rkisp1.py
> 
> diff --git a/utils/tuning/libtuning/modules/agc/__init__.py b/utils/tuning/libtuning/modules/agc/__init__.py
> new file mode 100644
> index 00000000..4db9ca37
> --- /dev/null
> +++ b/utils/tuning/libtuning/modules/agc/__init__.py
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +#
> +# Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
> +
> +from libtuning.modules.agc.agc import AGC
> +from libtuning.modules.agc.rkisp1 import AGCRkISP1
> diff --git a/utils/tuning/libtuning/modules/agc/agc.py b/utils/tuning/libtuning/modules/agc/agc.py
> new file mode 100644
> index 00000000..5f3c4223
> --- /dev/null
> +++ b/utils/tuning/libtuning/modules/agc/agc.py
> @@ -0,0 +1,24 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +#
> +# Copyright (C) 2019, Raspberry Pi Ltd
> +# Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>

Do we have rules here? In other places I saw Ideas on Board beeing
mentioned which might be helpful in keeping the list short.

> +
> +from ..module import Module
> +
> +import libtuning as lt
> +import libtuning.utils as utils
> +
> +import numpy as np

Nit: I couldn't spot a usage of numpy and libtuning.utils. So they could be
removed for now. 

> +
> +
> +class AGC(Module):
> +    type = 'agc'
> +    hr_name = 'AGC (Base)'
> +    out_name = 'GenericAGC'
> +
> +    # \todo Add sector shapes and stuff just like lsc
> +    def __init__(self, *,
> +                 debug: list):
> +        super().__init__()
> +
> +        self.debug = debug
> diff --git a/utils/tuning/libtuning/modules/agc/rkisp1.py b/utils/tuning/libtuning/modules/agc/rkisp1.py
> new file mode 100644
> index 00000000..b683fecb
> --- /dev/null
> +++ b/utils/tuning/libtuning/modules/agc/rkisp1.py
> @@ -0,0 +1,95 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +#
> +# Copyright (C) 2019, Raspberry Pi Ltd
> +# Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
> +#
> +# rkisp1.py - AGC module for tuning rkisp1
> +
> +from .agc import AGC
> +
> +import libtuning as lt
> +import libtuning.utils as utils
> +
> +from numbers import Number
> +import numpy as np

same for these imports.

> +
> +
> +class AGCRkISP1(AGC):
> +    hr_name = 'AGC (RkISP1)'
> +    out_name = 'Agc'
> +    # \todo Not sure if this is useful. Probably will remove later.
> +    compatible = ['rkisp1']
> +
> +    def __init__(self, *,
> +                 hw_rev: lt.Param,
> +                 **kwargs):
> +        super().__init__(**kwargs)
> +
> +        # Basically everything besides V12 uses V10 so we'll just implement it
> +        # as an exception for V12
> +        self.hist_weights_grid_size = (5, 5)
> +        if hw_rev == 'V12':
> +            self.hist_weights_grid_size = (9, 9)
> +
> +    # We don't actually need anything from the config file
> +    def validate_config(self, config: dict) -> bool:
> +        return True
> +
> +    def _generate_metering_modes(self) -> dict:
> +        # \todo Implement support for V12

I think we should throw/error out in case of V12 even in this early
stage. These todos tend to stay longer than expected and we would then
endup with too small matrices and possibly quite unexpected behaviour.


Otherwise looks good to me.

Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> 

Cheers,
Stefan

> +        centre_weighted = [
> +            0, 0,  0, 0, 0,
> +            0, 6,  8, 6, 0,
> +            0, 8, 16, 8, 0,
> +            0, 6,  8, 6, 0,
> +            0, 0,  0, 0, 0
> +        ]
> +
> +        spot = [
> +            0, 0,  0, 0, 0,
> +            0, 2,  4, 2, 0,
> +            0, 4, 16, 4, 0,
> +            0, 2,  4, 2, 0,
> +            0, 0,  0, 0, 0
> +        ]
> +
> +        num_cells = self.hist_weights_grid_size[0] * self.hist_weights_grid_size[1]
> +        matrix = [1 for i in range(0, num_cells)]
> +
> +        return {
> +            'MeteringCentreWeighted': centre_weighted,
> +            'MeteringSpot': spot,
> +            'MeteringMatrix': matrix
> +        }
> +
> +    def _generate_exposure_modes(self) -> dict:
> +        normal = { 'shutter': [100, 10000, 30000, 60000, 120000],
> +                   'gain': [1.0, 2.0, 4.0, 6.0, 6.0] }
> +        short = { 'shutter': [100, 5000, 10000, 20000, 120000],
> +                  'gain': [1.0, 2.0, 4.0, 6.0, 6.0]}
> +
> +        return { 'ExposureNormal': normal, 'ExposureShort': short }
> +
> +    def _generate_constraint_modes(self) -> dict:
> +        normal = { 'lower': { 'qLo': 0.98, 'qHi': 1.0, 'yTarget': [ 0, 0.5, 1000, 0.5 ] } }
> +        highlight = {
> +            'lower': { 'qLo': 0.98, 'qHi': 1.0, 'yTarget': [ 0, 0.5, 1000, 0.5 ] },
> +            'upper': { 'qLo': 0.98, 'qHi': 1.0, 'yTarget': [ 0, 0.8, 1000, 0.5 ] }
> +        }
> +
> +        return { 'ConstraintNormal': normal, 'ConstraintHighlight': highlight }
> +
> +    def _generate_y_target(self) -> list:
> +        return [0, 0.16, 1000, 0.165, 10000, 0.17]
> +
> +    def process(self, config: dict, images: list, outputs: dict) -> dict:
> +        output = {}
> +
> +        output['AeMeteringMode'] = self._generate_metering_modes()
> +        output['AeExposureMode'] = self._generate_exposure_modes()
> +        output['AeConstraintMode'] = self._generate_constraint_modes()
> +        output['relativeLuminanceTarget'] = self._generate_y_target()
> +
> +        # \todo Debug functionality
> +
> +        return output
> -- 
> 2.39.2
>

Patch
diff mbox series

diff --git a/utils/tuning/libtuning/modules/agc/__init__.py b/utils/tuning/libtuning/modules/agc/__init__.py
new file mode 100644
index 00000000..4db9ca37
--- /dev/null
+++ b/utils/tuning/libtuning/modules/agc/__init__.py
@@ -0,0 +1,6 @@ 
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+
+from libtuning.modules.agc.agc import AGC
+from libtuning.modules.agc.rkisp1 import AGCRkISP1
diff --git a/utils/tuning/libtuning/modules/agc/agc.py b/utils/tuning/libtuning/modules/agc/agc.py
new file mode 100644
index 00000000..5f3c4223
--- /dev/null
+++ b/utils/tuning/libtuning/modules/agc/agc.py
@@ -0,0 +1,24 @@ 
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (C) 2019, Raspberry Pi Ltd
+# Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+
+from ..module import Module
+
+import libtuning as lt
+import libtuning.utils as utils
+
+import numpy as np
+
+
+class AGC(Module):
+    type = 'agc'
+    hr_name = 'AGC (Base)'
+    out_name = 'GenericAGC'
+
+    # \todo Add sector shapes and stuff just like lsc
+    def __init__(self, *,
+                 debug: list):
+        super().__init__()
+
+        self.debug = debug
diff --git a/utils/tuning/libtuning/modules/agc/rkisp1.py b/utils/tuning/libtuning/modules/agc/rkisp1.py
new file mode 100644
index 00000000..b683fecb
--- /dev/null
+++ b/utils/tuning/libtuning/modules/agc/rkisp1.py
@@ -0,0 +1,95 @@ 
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (C) 2019, Raspberry Pi Ltd
+# Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>
+#
+# rkisp1.py - AGC module for tuning rkisp1
+
+from .agc import AGC
+
+import libtuning as lt
+import libtuning.utils as utils
+
+from numbers import Number
+import numpy as np
+
+
+class AGCRkISP1(AGC):
+    hr_name = 'AGC (RkISP1)'
+    out_name = 'Agc'
+    # \todo Not sure if this is useful. Probably will remove later.
+    compatible = ['rkisp1']
+
+    def __init__(self, *,
+                 hw_rev: lt.Param,
+                 **kwargs):
+        super().__init__(**kwargs)
+
+        # Basically everything besides V12 uses V10 so we'll just implement it
+        # as an exception for V12
+        self.hist_weights_grid_size = (5, 5)
+        if hw_rev == 'V12':
+            self.hist_weights_grid_size = (9, 9)
+
+    # We don't actually need anything from the config file
+    def validate_config(self, config: dict) -> bool:
+        return True
+
+    def _generate_metering_modes(self) -> dict:
+        # \todo Implement support for V12
+        centre_weighted = [
+            0, 0,  0, 0, 0,
+            0, 6,  8, 6, 0,
+            0, 8, 16, 8, 0,
+            0, 6,  8, 6, 0,
+            0, 0,  0, 0, 0
+        ]
+
+        spot = [
+            0, 0,  0, 0, 0,
+            0, 2,  4, 2, 0,
+            0, 4, 16, 4, 0,
+            0, 2,  4, 2, 0,
+            0, 0,  0, 0, 0
+        ]
+
+        num_cells = self.hist_weights_grid_size[0] * self.hist_weights_grid_size[1]
+        matrix = [1 for i in range(0, num_cells)]
+
+        return {
+            'MeteringCentreWeighted': centre_weighted,
+            'MeteringSpot': spot,
+            'MeteringMatrix': matrix
+        }
+
+    def _generate_exposure_modes(self) -> dict:
+        normal = { 'shutter': [100, 10000, 30000, 60000, 120000],
+                   'gain': [1.0, 2.0, 4.0, 6.0, 6.0] }
+        short = { 'shutter': [100, 5000, 10000, 20000, 120000],
+                  'gain': [1.0, 2.0, 4.0, 6.0, 6.0]}
+
+        return { 'ExposureNormal': normal, 'ExposureShort': short }
+
+    def _generate_constraint_modes(self) -> dict:
+        normal = { 'lower': { 'qLo': 0.98, 'qHi': 1.0, 'yTarget': [ 0, 0.5, 1000, 0.5 ] } }
+        highlight = {
+            'lower': { 'qLo': 0.98, 'qHi': 1.0, 'yTarget': [ 0, 0.5, 1000, 0.5 ] },
+            'upper': { 'qLo': 0.98, 'qHi': 1.0, 'yTarget': [ 0, 0.8, 1000, 0.5 ] }
+        }
+
+        return { 'ConstraintNormal': normal, 'ConstraintHighlight': highlight }
+
+    def _generate_y_target(self) -> list:
+        return [0, 0.16, 1000, 0.165, 10000, 0.17]
+
+    def process(self, config: dict, images: list, outputs: dict) -> dict:
+        output = {}
+
+        output['AeMeteringMode'] = self._generate_metering_modes()
+        output['AeExposureMode'] = self._generate_exposure_modes()
+        output['AeConstraintMode'] = self._generate_constraint_modes()
+        output['relativeLuminanceTarget'] = self._generate_y_target()
+
+        # \todo Debug functionality
+
+        return output