[v2,23/25] libtuning: Make blacklevel optional
diff mbox series

Message ID 20240628104828.2928109-24-stefan.klug@ideasonboard.com
State New
Headers show
Series
  • Add ccm calibration to libtuning
Related show

Commit Message

Stefan Klug June 28, 2024, 10:47 a.m. UTC
Not every sensor provides blacklevel information through the kernel
interface. We should therefore not require that information to be in the
raw images for the tuning process. It can be provided via the tuning config.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
---
 utils/tuning/libtuning/image.py | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

Comments

Laurent Pinchart June 29, 2024, 1:06 a.m. UTC | #1
Hi Stefan,

Thank you for the patch.

On Fri, Jun 28, 2024 at 12:47:16PM +0200, Stefan Klug wrote:
> Not every sensor provides blacklevel information through the kernel
> interface.

Actually, unless I'm mistaken, none do.

Raspberry Pi is the only platform to report the black levels through the
libcamera API, and they come from the tuning file. There's a bit of a
circular dependency below :-)

The question is where black levels should come from. They are typically
the result of two phenomena:

- Dark currents and other physical effects that add charge to pixels in
  the absence of light. Those can depend on the integration time and the
  sensor die temperature, and their contribution to pixel values depend
  on the sensor gains.

- Pedestal added by the sensor to pixel values. They are typically
  fixed, sometimes programmable (in which case we could control them),
  and should be reported in datasheets (but documentation is not always
  available).

Offsetting the dark currents is the most difficult part. This can be
done in multiple ways, including

- Automatic BLC in the sensor based on dark lines. That's the easiest
  option, we only need to enable it and there's nothing else to do.

- Calculating black levels in the sensor based on dark lines, reporting
  them (through embedded data or I2C polling) to the host, and
  subtracting them in the ISP. This is more work, but does not require
  any tuning, so it's still not too bad.

- Reading the dark lines from the sensor, calculating the black levels
  in the ISP (or worst case in software) and subtracting them in the
  ISP. A bit more complicated than the previous option, and requires the
  ability to separate dark lines from visible image on the host side,
  which is not necessarily common.

- Calculating black levels dynamically from tuning data, based on the
  integration time, and temperature and the gains. This requires
  measurements and is the only option involving tuning.

The data pedestal needs to be subtracted regardless of how we handle the
dark currents, and is likely the biggest contribution to the overall
black level. If the value is fixed, it could be added to the sensor
database in libcamera. If it's programmable, then the driver should
report it (which would leave the question of what to set it to, but I
think that can be handled separately).

I think we can handle the dark currents separately, and later. For the
pedestal, let's add it to the sensor database to start with.

> We should therefore not require that information to be in the
> raw images for the tuning process. It can be provided via the tuning config.

I think that's not what it's for. The reason we need it for tuning is
that it should be subtracted from the raw image before we tune LSC
(assuming the ISP subtract the black level before it performs LSC).
Providing the value in the config file doesn't seem right.

> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> ---
>  utils/tuning/libtuning/image.py | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/utils/tuning/libtuning/image.py b/utils/tuning/libtuning/image.py
> index c8911a0ff125..0b9c78fcd9c2 100644
> --- a/utils/tuning/libtuning/image.py
> +++ b/utils/tuning/libtuning/image.py
> @@ -25,6 +25,8 @@ class Image:
>          self.color = -1
>          self.lux = -1
>          self.macbeth = None
> +        self.blacklevel = 0
> +        self.blacklevel_16 = 0
>  
>          try:
>              self._load_metadata_exif()
> @@ -74,8 +76,12 @@ class Image:
>          self.againQ8 = metadata[f'Exif.{photo}.ISOSpeedRatings'].value * 256 / 100
>          self.againQ8_norm = self.againQ8 / 256
>          self.camName = metadata['Exif.Image.Model'].value
> -        self.blacklevel = int(metadata[f'Exif.{subimage}.BlackLevel'].value[0])
> -        self.blacklevel_16 = self.blacklevel << (16 - self.sigbits)
> +        if f'Exif.{subimage}.BlackLevel' in metadata:
> +            self.blacklevel = int(metadata[f'Exif.{subimage}.BlackLevel'].value[0])
> +            self.blacklevel_16 = self.blacklevel << (16 - self.sigbits)
> +        else:
> +            logger.warning("Image doesn't contain blacklevel info")
> +
>  
>          # Channel order depending on bayer pattern
>          # The key is the order given by exif, where 0 is R, 1 is G, and 2 is B

Patch
diff mbox series

diff --git a/utils/tuning/libtuning/image.py b/utils/tuning/libtuning/image.py
index c8911a0ff125..0b9c78fcd9c2 100644
--- a/utils/tuning/libtuning/image.py
+++ b/utils/tuning/libtuning/image.py
@@ -25,6 +25,8 @@  class Image:
         self.color = -1
         self.lux = -1
         self.macbeth = None
+        self.blacklevel = 0
+        self.blacklevel_16 = 0
 
         try:
             self._load_metadata_exif()
@@ -74,8 +76,12 @@  class Image:
         self.againQ8 = metadata[f'Exif.{photo}.ISOSpeedRatings'].value * 256 / 100
         self.againQ8_norm = self.againQ8 / 256
         self.camName = metadata['Exif.Image.Model'].value
-        self.blacklevel = int(metadata[f'Exif.{subimage}.BlackLevel'].value[0])
-        self.blacklevel_16 = self.blacklevel << (16 - self.sigbits)
+        if f'Exif.{subimage}.BlackLevel' in metadata:
+            self.blacklevel = int(metadata[f'Exif.{subimage}.BlackLevel'].value[0])
+            self.blacklevel_16 = self.blacklevel << (16 - self.sigbits)
+        else:
+            logger.warning("Image doesn't contain blacklevel info")
+
 
         # Channel order depending on bayer pattern
         # The key is the order given by exif, where 0 is R, 1 is G, and 2 is B