summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ipa/raspberrypi/cam_helper.cpp12
-rw-r--r--src/ipa/raspberrypi/cam_helper.h6
-rw-r--r--src/ipa/raspberrypi/cam_helper_imx219.cpp3
-rw-r--r--src/ipa/raspberrypi/cam_helper_imx296.cpp10
-rw-r--r--src/ipa/raspberrypi/cam_helper_imx477.cpp7
-rw-r--r--src/ipa/raspberrypi/cam_helper_imx519.cpp7
-rw-r--r--src/ipa/raspberrypi/raspberrypi.cpp8
7 files changed, 30 insertions, 23 deletions
diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp
index 42251ba2..fd3527b9 100644
--- a/src/ipa/raspberrypi/cam_helper.cpp
+++ b/src/ipa/raspberrypi/cam_helper.cpp
@@ -61,16 +61,16 @@ void CamHelper::process([[maybe_unused]] StatisticsPtr &stats,
{
}
-uint32_t CamHelper::exposureLines(const Duration exposure) const
+uint32_t CamHelper::exposureLines(const Duration exposure, const Duration lineLength) const
{
assert(initialized_);
- return exposure / mode_.minLineLength;
+ return exposure / lineLength;
}
-Duration CamHelper::exposure(uint32_t exposureLines) const
+Duration CamHelper::exposure(uint32_t exposureLines, const Duration lineLength) const
{
assert(initialized_);
- return exposureLines * mode_.minLineLength;
+ return exposureLines * lineLength;
}
uint32_t CamHelper::getVBlanking(Duration &exposure,
@@ -78,7 +78,7 @@ uint32_t CamHelper::getVBlanking(Duration &exposure,
Duration maxFrameDuration) const
{
uint32_t frameLengthMin, frameLengthMax, vblank;
- uint32_t exposureLines = CamHelper::exposureLines(exposure);
+ uint32_t exposureLines = CamHelper::exposureLines(exposure, mode_.minLineLength);
assert(initialized_);
@@ -94,7 +94,7 @@ uint32_t CamHelper::getVBlanking(Duration &exposure,
* re-calculate if it has been clipped.
*/
exposureLines = std::min(frameLengthMax - frameIntegrationDiff_, exposureLines);
- exposure = CamHelper::exposure(exposureLines);
+ exposure = CamHelper::exposure(exposureLines, mode_.minLineLength);
/* Limit the vblank to the range allowed by the frame length limits. */
vblank = std::clamp(exposureLines + frameIntegrationDiff_,
diff --git a/src/ipa/raspberrypi/cam_helper.h b/src/ipa/raspberrypi/cam_helper.h
index 70d62719..9b5e6026 100644
--- a/src/ipa/raspberrypi/cam_helper.h
+++ b/src/ipa/raspberrypi/cam_helper.h
@@ -78,8 +78,10 @@ public:
virtual void prepare(libcamera::Span<const uint8_t> buffer,
Metadata &metadata);
virtual void process(StatisticsPtr &stats, Metadata &metadata);
- virtual uint32_t exposureLines(libcamera::utils::Duration exposure) const;
- virtual libcamera::utils::Duration exposure(uint32_t exposureLines) const;
+ virtual uint32_t exposureLines(const libcamera::utils::Duration exposure,
+ const libcamera::utils::Duration lineLength) const;
+ virtual libcamera::utils::Duration exposure(uint32_t exposureLines,
+ const libcamera::utils::Duration lineLength) const;
virtual uint32_t getVBlanking(libcamera::utils::Duration &exposure,
libcamera::utils::Duration minFrameDuration,
libcamera::utils::Duration maxFrameDuration) const;
diff --git a/src/ipa/raspberrypi/cam_helper_imx219.cpp b/src/ipa/raspberrypi/cam_helper_imx219.cpp
index 7ded07a2..98a3b319 100644
--- a/src/ipa/raspberrypi/cam_helper_imx219.cpp
+++ b/src/ipa/raspberrypi/cam_helper_imx219.cpp
@@ -94,7 +94,8 @@ void CamHelperImx219::populateMetadata(const MdParser::RegisterMap &registers,
{
DeviceStatus deviceStatus;
- deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg));
+ deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg),
+ mode_.minLineLength);
deviceStatus.analogueGain = gain(registers.at(gainReg));
deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg);
diff --git a/src/ipa/raspberrypi/cam_helper_imx296.cpp b/src/ipa/raspberrypi/cam_helper_imx296.cpp
index 66d21e36..d86ff387 100644
--- a/src/ipa/raspberrypi/cam_helper_imx296.cpp
+++ b/src/ipa/raspberrypi/cam_helper_imx296.cpp
@@ -21,8 +21,8 @@ public:
CamHelperImx296();
uint32_t gainCode(double gain) const override;
double gain(uint32_t gainCode) const override;
- uint32_t exposureLines(Duration exposure) const override;
- Duration exposure(uint32_t exposureLines) const override;
+ uint32_t exposureLines(const Duration exposure, const Duration lineLength) const override;
+ Duration exposure(uint32_t exposureLines, const Duration lineLength) const override;
void getDelays(int &exposureDelay, int &gainDelay, int &vblankDelay) const override;
private:
@@ -53,12 +53,14 @@ double CamHelperImx296::gain(uint32_t gainCode) const
return std::pow(10.0, gainCode / 200.0);
}
-uint32_t CamHelperImx296::exposureLines(Duration exposure) const
+uint32_t CamHelperImx296::exposureLines(const Duration exposure,
+ [[maybe_unused]] const Duration lineLength) const
{
return std::max<uint32_t>(minExposureLines, (exposure - 14.26us) / timePerLine);
}
-Duration CamHelperImx296::exposure(uint32_t exposureLines) const
+Duration CamHelperImx296::exposure(uint32_t exposureLines,
+ [[maybe_unused]] const Duration lineLength) const
{
return std::max<uint32_t>(minExposureLines, exposureLines) * timePerLine + 14.26us;
}
diff --git a/src/ipa/raspberrypi/cam_helper_imx477.cpp b/src/ipa/raspberrypi/cam_helper_imx477.cpp
index aa306d66..71529bdd 100644
--- a/src/ipa/raspberrypi/cam_helper_imx477.cpp
+++ b/src/ipa/raspberrypi/cam_helper_imx477.cpp
@@ -144,9 +144,9 @@ uint32_t CamHelperImx477::getVBlanking(Duration &exposure,
if (shift) {
/* Account for any rounding in the scaled frame length value. */
frameLength <<= shift;
- exposureLines = CamHelperImx477::exposureLines(exposure);
+ exposureLines = CamHelperImx477::exposureLines(exposure, mode_.minLineLength);
exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff);
- exposure = CamHelperImx477::exposure(exposureLines);
+ exposure = CamHelperImx477::exposure(exposureLines, mode_.minLineLength);
}
return frameLength - mode_.height;
@@ -170,7 +170,8 @@ void CamHelperImx477::populateMetadata(const MdParser::RegisterMap &registers,
{
DeviceStatus deviceStatus;
- deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg));
+ deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg),
+ mode_.minLineLength);
deviceStatus.analogueGain = gain(registers.at(gainHiReg) * 256 + registers.at(gainLoReg));
deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg);
deviceStatus.sensorTemperature = std::clamp<int8_t>(registers.at(temperatureReg), -20, 80);
diff --git a/src/ipa/raspberrypi/cam_helper_imx519.cpp b/src/ipa/raspberrypi/cam_helper_imx519.cpp
index 54e104e7..2c120dad 100644
--- a/src/ipa/raspberrypi/cam_helper_imx519.cpp
+++ b/src/ipa/raspberrypi/cam_helper_imx519.cpp
@@ -144,9 +144,9 @@ uint32_t CamHelperImx519::getVBlanking(Duration &exposure,
if (shift) {
/* Account for any rounding in the scaled frame length value. */
frameLength <<= shift;
- exposureLines = CamHelperImx519::exposureLines(exposure);
+ exposureLines = CamHelperImx519::exposureLines(exposure, mode_.minLineLength);
exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff);
- exposure = CamHelperImx519::exposure(exposureLines);
+ exposure = CamHelperImx519::exposure(exposureLines, mode_.minLineLength);
}
return frameLength - mode_.height;
@@ -170,7 +170,8 @@ void CamHelperImx519::populateMetadata(const MdParser::RegisterMap &registers,
{
DeviceStatus deviceStatus;
- deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg));
+ deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg),
+ mode_.minLineLength);
deviceStatus.analogueGain = gain(registers.at(gainHiReg) * 256 + registers.at(gainLoReg));
deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg);
diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
index 5db5b86d..560b61bd 100644
--- a/src/ipa/raspberrypi/raspberrypi.cpp
+++ b/src/ipa/raspberrypi/raspberrypi.cpp
@@ -477,7 +477,7 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo,
const uint32_t exposureMin = sensorCtrls_.at(V4L2_CID_EXPOSURE).min().get<int32_t>();
ctrlMap[&controls::ExposureTime] =
- ControlInfo(static_cast<int32_t>(helper_->exposure(exposureMin).get<std::micro>()),
+ ControlInfo(static_cast<int32_t>(helper_->exposure(exposureMin, mode_.minLineLength).get<std::micro>()),
static_cast<int32_t>(maxShutter.get<std::micro>()));
result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls);
@@ -550,7 +550,7 @@ void IPARPi::reportMetadata()
deviceStatus->shutterSpeed.get<std::micro>());
libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogueGain);
libcameraMetadata_.set(controls::FrameDuration,
- helper_->exposure(deviceStatus->frameLength).get<std::micro>());
+ helper_->exposure(deviceStatus->frameLength, mode_.minLineLength).get<std::micro>());
if (deviceStatus->sensorTemperature)
libcameraMetadata_.set(controls::SensorTemperature, *deviceStatus->sensorTemperature);
}
@@ -1105,7 +1105,7 @@ void IPARPi::fillDeviceStatus(const ControlList &sensorControls)
int32_t gainCode = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
int32_t vblank = sensorControls.get(V4L2_CID_VBLANK).get<int32_t>();
- deviceStatus.shutterSpeed = helper_->exposure(exposureLines);
+ deviceStatus.shutterSpeed = helper_->exposure(exposureLines, mode_.minLineLength);
deviceStatus.analogueGain = helper_->gain(gainCode);
deviceStatus.frameLength = mode_.height + vblank;
@@ -1197,7 +1197,7 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)
/* getVBlanking might clip exposure time to the fps limits. */
Duration exposure = agcStatus->shutterTime;
int32_t vblanking = helper_->getVBlanking(exposure, minFrameDuration_, maxFrameDuration_);
- int32_t exposureLines = helper_->exposureLines(exposure);
+ int32_t exposureLines = helper_->exposureLines(exposure, mode_.minLineLength);
LOG(IPARPI, Debug) << "Applying AGC Exposure: " << exposure
<< " (Shutter lines: " << exposureLines << ", AGC requested "
/a> 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
#!/usr/bin/env python3
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (C) 2019, Raspberry Pi (Trading) Limited
#
# ctt.py - camera tuning tool

import os
import sys
from ctt_image_load import *
from ctt_ccm import *
from ctt_awb import *
from ctt_alsc import *
from ctt_lux import *
from ctt_noise import *
from ctt_geq import *
from ctt_pretty_print_json import *
import random
import json
import re

"""
This file houses the camera object, which is used to perform the calibrations.
The camera object houses all the calibration images as attributes in two lists:
    - imgs (macbeth charts)
    - imgs_alsc (alsc correction images)
Various calibrations are methods of the camera object, and the output is stored
in a dictionary called self.json.
Once all the caibration has been completed, the Camera.json is written into a
json file.
The camera object initialises its json dictionary by reading from a pre-written
blank json file. This has been done to avoid reproducing the entire json file
in the code here, thereby avoiding unecessary clutter.
"""


"""
Get the colour and lux values from the strings of each inidvidual image
"""
def get_col_lux(string):
    """
    Extract colour and lux values from filename
    """
    col = re.search(r'([0-9]+)[kK](\.(jpg|jpeg|brcm|dng)|_.*\.(jpg|jpeg|brcm|dng))$', string)
    lux = re.search(r'([0-9]+)[lL](\.(jpg|jpeg|brcm|dng)|_.*\.(jpg|jpeg|brcm|dng))$', string)
    try:
        col = col.group(1)
    except AttributeError:
        """
        Catch error if images labelled incorrectly and pass reasonable defaults
        """
        return None, None
    try:
        lux = lux.group(1)
    except AttributeError:
        """
        Catch error if images labelled incorrectly and pass reasonable defaults
        Still returns colour if that has been found.
        """
        return col, None
    return int(col), int(lux)


"""
Camera object that is the backbone of the tuning tool.
Input is the desired path of the output json.
"""
class Camera:
    def __init__(self, jfile):
        self.path = os.path.dirname(os.path.expanduser(__file__)) + '/'
        if self.path == '/':
            self.path = ''
        self.imgs = []
        self.imgs_alsc = []
        self.log = 'Log created : ' + time.asctime(time.localtime(time.time()))
        self.log_separator = '\n'+'-'*70+'\n'
        self.jf = jfile
        """
        initial json dict populated by uncalibrated values
        """
        self.json = {
            "rpi.black_level": {
                "black_level": 4096
            },
            "rpi.dpc": {
            },
            "rpi.lux": {
                "reference_shutter_speed": 10000,
                "reference_gain": 1,
                "reference_aperture": 1.0
            },
            "rpi.noise": {
            },
            "rpi.geq": {
            },
            "rpi.sdn": {
            },
            "rpi.awb": {
                "priors": [
                    {"lux": 0, "prior": [2000, 1.0, 3000, 0.0, 13000, 0.0]},
                    {"lux": 800, "prior": [2000, 0.0, 6000, 2.0, 13000, 2.0]},
                    {"lux": 1500, "prior": [2000, 0.0, 4000, 1.0, 6000, 6.0, 6500, 7.0, 7000, 1.0, 13000, 1.0]}
                ],
                "modes": {
                    "auto": {"lo": 2500, "hi": 8000},
                    "incandescent": {"lo": 2500, "hi": 3000},
                    "tungsten": {"lo": 3000, "hi": 3500},
                    "fluorescent": {"lo": 4000, "hi": 4700},
                    "indoor": {"lo": 3000, "hi": 5000},
                    "daylight": {"lo": 5500, "hi": 6500},
                    "cloudy": {"lo": 7000, "hi": 8600}
                },
                "bayes": 1
            },
            "rpi.agc": {
                "metering_modes": {
                    "centre-weighted": {
                        "weights": [3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0]
                    },
                    "spot": {
                        "weights": [2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                    },
                    "matrix": {
                        "weights": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
                    }
                },
                "exposure_modes": {
                    "normal": {
                        "shutter": [100, 10000, 30000, 60000, 120000],
                        "gain": [1.0, 2.0, 4.0, 6.0, 6.0]
                    },
                    "sport": {
                        "shutter": [100, 5000, 10000, 20000, 120000],
                        "gain": [1.0, 2.0, 4.0, 6.0, 6.0]
                    }
                },
                "constraint_modes": {
                    "normal": [
                        {"bound": "LOWER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.5, 1000, 0.5]}
                    ],
                    "highlight": [
                        {"bound": "LOWER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.5, 1000, 0.5]},
                        {"bound": "UPPER", "q_lo": 0.98, "q_hi": 1.0, "y_target": [0, 0.8, 1000, 0.8]}
                    ]
                },
                "y_target": [0, 0.16, 1000, 0.165, 10000, 0.17]
            },
            "rpi.alsc": {
                'omega': 1.3,
                'n_iter': 100,
                'luminance_strength': 0.7,
            },
            "rpi.contrast": {
                "ce_enable": 1,
                "gamma_curve": [
                    0,     0,
                    1024,  5040,
                    2048,  9338,
                    3072,  12356,
                    4096,  15312,
                    5120,  18051,
                    6144,  20790,
                    7168,  23193,
                    8192,  25744,
                    9216,  27942,
                    10240, 30035,
                    11264, 32005,
                    12288, 33975,
                    13312, 35815,
                    14336, 37600,
                    15360, 39168,
                    16384, 40642,
                    18432, 43379,
                    20480, 45749,
                    22528, 47753,
                    24576, 49621,
                    26624, 51253,
                    28672, 52698,
                    30720, 53796,
                    32768, 54876,
                    36864, 57012,
                    40960, 58656,
                    45056, 59954,
                    49152, 61183,
                    53248, 62355,
                    57344, 63419,
                    61440, 64476,
                    65535, 65535
                ]
            },
            "rpi.ccm": {
            },
            "rpi.sharpen": {
            }
        }

    """
    Perform colour correction calibrations by comparing macbeth patch colours
    to standard macbeth chart colours.
    """
    def ccm_cal(self, do_alsc_colour):
        if 'rpi.ccm' in self.disable:
            return 1
        print('\nStarting CCM calibration')
        self.log_new_sec('CCM')
        """
        if image is greyscale then CCm makes no sense
        """
        if self.grey:
            print('\nERROR: Can\'t do CCM on greyscale image!')
            self.log += '\nERROR: Cannot perform CCM calibration '
            self.log += 'on greyscale image!\nCCM aborted!'
            del self.json['rpi.ccm']
            return 0
        a = time.time()
        """
        Check if alsc tables have been generated, if not then do ccm without
        alsc
        """
        if ("rpi.alsc" not in self.disable) and do_alsc_colour:
            """
            case where ALSC colour has been done, so no errors should be
            expected...
            """
            try:
                cal_cr_list = self.json['rpi.alsc']['calibrations_Cr']
                cal_cb_list = self.json['rpi.alsc']['calibrations_Cb']
                self.log += '\nALSC tables found successfully'
            except KeyError:
                cal_cr_list, cal_cb_list = None, None
                print('WARNING! No ALSC tables found for CCM!')
                print('Performing CCM calibrations without ALSC correction...')
                self.log += '\nWARNING: No ALSC tables found.\nCCM calibration '
                self.log += 'performed without ALSC correction...'
        else:
            """
            case where config options result in CCM done without ALSC colour tables
            """
            cal_cr_list, cal_cb_list = None, None
            self.log += '\nWARNING: No ALSC tables found.\nCCM calibration '
            self.log += 'performed without ALSC correction...'

        """
        Do CCM calibration