[{"id":30756,"web_url":"https://patchwork.libcamera.org/comment/30756/","msgid":"<87sev9vljd.fsf@redhat.com>","date":"2024-08-12T14:54:46","subject":"Re: [PATCH v3 22/23] libcamera: software_isp: Move exposure+gain to\n\tan algorithm module","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Milan Zamazal <mzamazal@redhat.com> writes:\n\n> This is the last step to fully convert software ISP to Algorithm-based\n> processing.\n>\n> The newly introduced frameContext.sensor properties are set, and the\n> updated code moved, before calling Algorithm::process() to have the\n> values up-to-date in stats processing.\n>\n> Resolves software ISP TODO #10.\n>\n> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n> ---\n>  src/ipa/simple/algorithms/agc.cpp     | 139 ++++++++++++++++++++++\n>  src/ipa/simple/algorithms/agc.h       |  36 ++++++\n>  src/ipa/simple/algorithms/meson.build |   1 +\n>  src/ipa/simple/data/uncalibrated.yaml |   1 +\n>  src/ipa/simple/ipa_context.cpp        |  11 ++\n>  src/ipa/simple/ipa_context.h          |  17 +++\n>  src/ipa/simple/soft_simple.cpp        | 159 +++++---------------------\n>  src/libcamera/software_isp/TODO       |  10 --\n>  8 files changed, 235 insertions(+), 139 deletions(-)\n>  create mode 100644 src/ipa/simple/algorithms/agc.cpp\n>  create mode 100644 src/ipa/simple/algorithms/agc.h\n>\n> diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp\n> new file mode 100644\n> index 00000000..a80022ff\n> --- /dev/null\n> +++ b/src/ipa/simple/algorithms/agc.cpp\n> @@ -0,0 +1,139 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024, Red Hat Inc.\n> + *\n> + * Exposure and gain\n> + */\n> +\n> +#include \"agc.h\"\n> +\n> +#include <stdint.h>\n> +\n> +#include <libcamera/base/log.h>\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(IPASoftExposure)\n> +\n> +namespace ipa::soft::algorithms {\n> +\n> +/*\n> + * The number of bins to use for the optimal exposure calculations.\n> + */\n> +static constexpr unsigned int kExposureBinsCount = 5;\n> +\n> +/*\n> + * The exposure is optimal when the mean sample value of the histogram is\n> + * in the middle of the range.\n> + */\n> +static constexpr float kExposureOptimal = kExposureBinsCount / 2.0;\n> +\n> +/*\n> + * The below value implements the hysteresis for the exposure adjustment.\n> + * It is small enough to have the exposure close to the optimal, and is big\n> + * enough to prevent the exposure from wobbling around the optimal value.\n> + */\n> +static constexpr float kExposureSatisfactory = 0.2;\n> +\n> +Agc::Agc()\n> +{\n> +}\n> +\n> +void Agc::updateExposure(IPAContext &context, double exposureMSV)\n> +{\n> +\t/*\n> +\t * kExpDenominator of 10 gives ~10% increment/decrement;\n> +\t * kExpDenominator of 5 - about ~20%\n> +\t */\n> +\tstatic constexpr uint8_t kExpDenominator = 10;\n> +\tstatic constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;\n> +\tstatic constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;\n> +\n> +\tdouble next;\n> +\tint32_t &exposure = context.activeState.agc.exposure;\n> +\tdouble &again = context.activeState.agc.again;\n> +\n> +\tif (exposureMSV < kExposureOptimal - kExposureSatisfactory) {\n> +\t\tnext = exposure * kExpNumeratorUp / kExpDenominator;\n> +\t\tif (next - exposure < 1)\n> +\t\t\texposure += 1;\n> +\t\telse\n> +\t\t\texposure = next;\n> +\t\tif (exposure >= context.configuration.agc.exposureMax) {\n> +\t\t\tnext = again * kExpNumeratorUp / kExpDenominator;\n> +\t\t\tif (next - again < context.configuration.agc.againMinStep)\n> +\t\t\t\tagain += context.configuration.agc.againMinStep;\n> +\t\t\telse\n> +\t\t\t\tagain = next;\n> +\t\t}\n> +\t}\n> +\n> +\tif (exposureMSV > kExposureOptimal + kExposureSatisfactory) {\n> +\t\tif (exposure == context.configuration.agc.exposureMax &&\n> +\t\t    again > context.configuration.agc.againMin) {\n> +\t\t\tnext = again * kExpNumeratorDown / kExpDenominator;\n> +\t\t\tif (again - next < context.configuration.agc.againMinStep)\n> +\t\t\t\tagain -= context.configuration.agc.againMinStep;\n> +\t\t\telse\n> +\t\t\t\tagain = next;\n> +\t\t} else {\n> +\t\t\tnext = exposure * kExpNumeratorDown / kExpDenominator;\n> +\t\t\tif (exposure - next < 1)\n> +\t\t\t\texposure -= 1;\n> +\t\t\telse\n> +\t\t\t\texposure = next;\n> +\t\t}\n> +\t}\n> +\n> +\texposure = std::clamp(exposure, context.configuration.agc.exposureMin,\n> +\t\t\t      context.configuration.agc.exposureMax);\n> +\tagain = std::clamp(again, context.configuration.agc.againMin,\n> +\t\t\t   context.configuration.agc.againMax);\n> +\n> +\tLOG(IPASoftExposure, Debug)\n> +\t\t<< \"exposureMSV \" << exposureMSV\n> +\t\t<< \" exp \" << exposure << \" again \" << again;\n> +}\n> +\n> +void Agc::process(IPAContext &context,\n> +\t\t  [[maybe_unused]] const uint32_t frame,\n> +\t\t  [[maybe_unused]] IPAFrameContext &frameContext,\n> +\t\t  const SwIspStats *stats,\n> +\t\t  [[maybe_unused]] ControlList &metadata)\n> +{\n> +\t/*\n> +\t * Calculate Mean Sample Value (MSV) according to formula from:\n> +\t * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf\n> +\t */\n> +\tconst auto &histogram = stats->yHistogram;\n> +\tconst unsigned int blackLevelHistIdx =\n> +\t\tcontext.configuration.black.level * SwIspStats::kYHistogramSize;\n\nThis should use context.activeState.  I'll fix it in the next update.\n\n(context.configuration.black.level should be assigned from the camera\nhelper, which optionally provides black level now.  I'm working on a\ncorresponding patch, which will be posted separately as it is not\nrefactoring.)\n\n> +\tconst unsigned int histogramSize =\n> +\t\tSwIspStats::kYHistogramSize - blackLevelHistIdx;\n> +\tconst unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount;\n> +\tconst unsigned int yHistValsPerBinMod =\n> +\t\thistogramSize / (histogramSize % kExposureBinsCount + 1);\n> +\tint exposureBins[kExposureBinsCount] = {};\n> +\tunsigned int denom = 0;\n> +\tunsigned int num = 0;\n> +\n> +\tfor (unsigned int i = 0; i < histogramSize; i++) {\n> +\t\tunsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;\n> +\t\texposureBins[idx] += histogram[blackLevelHistIdx + i];\n> +\t}\n> +\n> +\tfor (unsigned int i = 0; i < kExposureBinsCount; i++) {\n> +\t\tLOG(IPASoftExposure, Debug) << i << \": \" << exposureBins[i];\n> +\t\tdenom += exposureBins[i];\n> +\t\tnum += exposureBins[i] * (i + 1);\n> +\t}\n> +\n> +\tfloat exposureMSV = (denom == 0 ? 0 : static_cast<float>(num) / denom);\n> +\tupdateExposure(context, exposureMSV);\n> +}\n> +\n> +REGISTER_IPA_ALGORITHM(Agc, \"Agc\")\n> +\n> +} /* namespace ipa::soft::algorithms */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h\n> new file mode 100644\n> index 00000000..a4aa656c\n> --- /dev/null\n> +++ b/src/ipa/simple/algorithms/agc.h\n> @@ -0,0 +1,36 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024, Red Hat Inc.\n> + *\n> + * Exposure and gain\n> + */\n> +\n> +#pragma once\n> +\n> +#include \"libcamera/internal/software_isp/swisp_stats.h\"\n> +\n> +#include \"algorithm.h\"\n> +#include \"ipa_context.h\"\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa::soft::algorithms {\n> +\n> +class Agc : public Algorithm\n> +{\n> +public:\n> +\tAgc();\n> +\t~Agc() = default;\n> +\n> +\tvoid process(IPAContext &context, const uint32_t frame,\n> +\t\t     IPAFrameContext &frameContext,\n> +\t\t     const SwIspStats *stats,\n> +\t\t     ControlList &metadata) override;\n> +\n> +private:\n> +\tvoid updateExposure(IPAContext &context, double exposureMSV);\n> +};\n> +\n> +} /* namespace ipa::soft::algorithms */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/simple/algorithms/meson.build b/src/ipa/simple/algorithms/meson.build\n> index 324c7b4e..0b178e17 100644\n> --- a/src/ipa/simple/algorithms/meson.build\n> +++ b/src/ipa/simple/algorithms/meson.build\n> @@ -1,6 +1,7 @@\n>  # SPDX-License-Identifier: CC0-1.0\n>  \n>  soft_simple_ipa_algorithms = files([\n> +    'agc.cpp',\n>      'blc.cpp',\n>      'colors.cpp',\n>  ])\n> diff --git a/src/ipa/simple/data/uncalibrated.yaml b/src/ipa/simple/data/uncalibrated.yaml\n> index 7e5149e5..7489c81f 100644\n> --- a/src/ipa/simple/data/uncalibrated.yaml\n> +++ b/src/ipa/simple/data/uncalibrated.yaml\n> @@ -5,4 +5,5 @@ version: 1\n>  algorithms:\n>    - BlackLevel:\n>    - Colors:\n> +  - Agc:\n>  ...\n> diff --git a/src/ipa/simple/ipa_context.cpp b/src/ipa/simple/ipa_context.cpp\n> index 5fa492cb..3f94bbeb 100644\n> --- a/src/ipa/simple/ipa_context.cpp\n> +++ b/src/ipa/simple/ipa_context.cpp\n> @@ -77,6 +77,17 @@ namespace libcamera::ipa::soft {\n>   * \\brief Gain of blue color\n>   */\n>  \n> +/**\n> + * \\var IPAActiveState::agc\n> + * \\brief Context for the AGC algorithm\n> + *\n> + * \\var IPAActiveState::agc.exposure\n> + * \\brief Current exposure value\n> + *\n> + * \\var IPAActiveState::agc.again\n> + * \\brief Current analog gain value\n> + */\n> +\n>  /**\n>   * \\var IPAActiveState::gamma\n>   * \\brief Context for gamma in the Colors algorithm\n> diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h\n> index 552d6cde..40a2efc2 100644\n> --- a/src/ipa/simple/ipa_context.h\n> +++ b/src/ipa/simple/ipa_context.h\n> @@ -10,6 +10,8 @@\n>  \n>  #include <array>\n>  \n> +#include <libcamera/controls.h>\n> +\n>  #include <libipa/fc_queue.h>\n>  \n>  namespace libcamera {\n> @@ -18,6 +20,13 @@ namespace ipa::soft {\n>  \n>  struct IPASessionConfiguration {\n>  \tfloat gamma;\n> +\tstruct {\n> +\t\tint32_t exposureMin, exposureMax;\n> +\t\tdouble againMin, againMax, againMinStep;\n> +\t} agc;\n> +\tstruct {\n> +\t\tdouble level;\n> +\t} black;\n>  };\n>  \n>  struct IPAActiveState {\n> @@ -29,6 +38,10 @@ struct IPAActiveState {\n>  \t\tdouble green;\n>  \t\tdouble blue;\n>  \t} gains;\n> +\tstruct {\n> +\t\tint32_t exposure;\n> +\t\tdouble again;\n> +\t} agc;\n>  \tstatic constexpr unsigned int kGammaLookupSize = 1024;\n>  \tstruct {\n>  \t\tstd::array<double, kGammaLookupSize> gammaTable;\n> @@ -37,6 +50,10 @@ struct IPAActiveState {\n>  };\n>  \n>  struct IPAFrameContext : public FrameContext {\n> +\tstruct {\n> +\t\tuint32_t exposure;\n> +\t\tdouble gain;\n> +\t} sensor;\n>  };\n>  \n>  struct IPAContext {\n> diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp\n> index cf2d628d..547d0fcf 100644\n> --- a/src/ipa/simple/soft_simple.cpp\n> +++ b/src/ipa/simple/soft_simple.cpp\n> @@ -33,23 +33,6 @@ LOG_DEFINE_CATEGORY(IPASoft)\n>  \n>  namespace ipa::soft {\n>  \n> -/*\n> - * The number of bins to use for the optimal exposure calculations.\n> - */\n> -static constexpr unsigned int kExposureBinsCount = 5;\n> -\n> -/*\n> - * The exposure is optimal when the mean sample value of the histogram is\n> - * in the middle of the range.\n> - */\n> -static constexpr float kExposureOptimal = kExposureBinsCount / 2.0;\n> -\n> -/*\n> - * The below value implements the hysteresis for the exposure adjustment.\n> - * It is small enough to have the exposure close to the optimal, and is big\n> - * enough to prevent the exposure from wobbling around the optimal value.\n> - */\n> -static constexpr float kExposureSatisfactory = 0.2;\n>  /* Maximum number of frame contexts to be held */\n>  static constexpr uint32_t kMaxFrameContexts = 16;\n>  \n> @@ -57,8 +40,7 @@ class IPASoftSimple : public ipa::soft::IPASoftInterface, public Module\n>  {\n>  public:\n>  \tIPASoftSimple()\n> -\t\t: params_(nullptr), stats_(nullptr),\n> -\t\t  context_({ {}, {}, { kMaxFrameContexts } })\n> +\t\t: context_({ {}, {}, { kMaxFrameContexts } })\n>  \t{\n>  \t}\n>  \n> @@ -91,11 +73,6 @@ private:\n>  \n>  \t/* Local parameter storage */\n>  \tstruct IPAContext context_;\n> -\n> -\tint32_t exposureMin_, exposureMax_;\n> -\tint32_t exposure_;\n> -\tdouble againMin_, againMax_, againMinStep_;\n> -\tdouble again_;\n>  };\n>  \n>  IPASoftSimple::~IPASoftSimple()\n> @@ -206,20 +183,23 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo)\n>  \tconst ControlInfo &exposureInfo = sensorInfoMap_.find(V4L2_CID_EXPOSURE)->second;\n>  \tconst ControlInfo &gainInfo = sensorInfoMap_.find(V4L2_CID_ANALOGUE_GAIN)->second;\n>  \n> -\texposureMin_ = exposureInfo.min().get<int32_t>();\n> -\texposureMax_ = exposureInfo.max().get<int32_t>();\n> -\tif (!exposureMin_) {\n> +\tcontext_.configuration.agc.exposureMin = exposureInfo.min().get<int32_t>();\n> +\tcontext_.configuration.agc.exposureMax = exposureInfo.max().get<int32_t>();\n> +\tif (!context_.configuration.agc.exposureMin) {\n>  \t\tLOG(IPASoft, Warning) << \"Minimum exposure is zero, that can't be linear\";\n> -\t\texposureMin_ = 1;\n> +\t\tcontext_.configuration.agc.exposureMin = 1;\n>  \t}\n>  \n>  \tint32_t againMin = gainInfo.min().get<int32_t>();\n>  \tint32_t againMax = gainInfo.max().get<int32_t>();\n>  \n>  \tif (camHelper_) {\n> -\t\tagainMin_ = camHelper_->gain(againMin);\n> -\t\tagainMax_ = camHelper_->gain(againMax);\n> -\t\tagainMinStep_ = (againMax_ - againMin_) / 100.0;\n> +\t\tcontext_.configuration.agc.againMin = camHelper_->gain(againMin);\n> +\t\tcontext_.configuration.agc.againMax = camHelper_->gain(againMax);\n> +\t\tcontext_.configuration.agc.againMinStep =\n> +\t\t\t(context_.configuration.agc.againMax -\n> +\t\t\t context_.configuration.agc.againMin) /\n> +\t\t\t100.0;\n>  \t} else {\n>  \t\t/*\n>  \t\t * The camera sensor gain (g) is usually not equal to the value written\n> @@ -231,13 +211,14 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo)\n>  \t\t * the AGC algorithm (abrupt near one edge, and very small near the\n>  \t\t * other) we limit the range of the gain values used.\n>  \t\t */\n> -\t\tagainMax_ = againMax;\n> +\t\tcontext_.configuration.agc.againMax = againMax;\n>  \t\tif (!againMin) {\n>  \t\t\tLOG(IPASoft, Warning)\n>  \t\t\t\t<< \"Minimum gain is zero, that can't be linear\";\n> -\t\t\tagainMin_ = std::min(100, againMin / 2 + againMax / 2);\n> +\t\t\tcontext_.configuration.agc.againMin =\n> +\t\t\t\tstd::min(100, againMin / 2 + againMax / 2);\n>  \t\t}\n> -\t\tagainMinStep_ = 1.0;\n> +\t\tcontext_.configuration.agc.againMinStep = 1.0;\n>  \t}\n>  \n>  \tfor (auto const &algo : algorithms()) {\n> @@ -246,9 +227,12 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo)\n>  \t\t\treturn ret;\n>  \t}\n>  \n> -\tLOG(IPASoft, Info) << \"Exposure \" << exposureMin_ << \"-\" << exposureMax_\n> -\t\t\t   << \", gain \" << againMin_ << \"-\" << againMax_\n> -\t\t\t   << \" (\" << againMinStep_ << \")\";\n> +\tLOG(IPASoft, Info)\n> +\t\t<< \"Exposure \" << context_.configuration.agc.exposureMin << \"-\"\n> +\t\t<< context_.configuration.agc.exposureMax\n> +\t\t<< \", gain \" << context_.configuration.agc.againMin << \"-\"\n> +\t\t<< context_.configuration.agc.againMax\n> +\t\t<< \" (\" << context_.configuration.agc.againMinStep << \")\";\n>  \n>  \treturn 0;\n>  }\n> @@ -284,6 +268,12 @@ void IPASoftSimple::processStats(\n>  \tconst ControlList &sensorControls)\n>  {\n>  \tIPAFrameContext &frameContext = context_.frameContexts.get(frame);\n> +\n> +\tframeContext.sensor.exposure =\n> +\t\tsensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n> +\tint32_t again = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();\n> +\tframeContext.sensor.gain = camHelper_ ? camHelper_->gain(again) : again;\n> +\n>  \t/*\n>  \t * Software ISP currently does not produce any metadata. Use an empty\n>  \t * ControlList for now.\n> @@ -294,37 +284,6 @@ void IPASoftSimple::processStats(\n>  \tfor (auto const &algo : algorithms())\n>  \t\talgo->process(context_, frame, frameContext, stats_, metadata);\n>  \n> -\t/* \\todo Switch to the libipa/algorithm.h API someday. */\n> -\n> -\t/*\n> -\t * Calculate Mean Sample Value (MSV) according to formula from:\n> -\t * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf\n> -\t */\n> -\tconst uint8_t blackLevel = context_.activeState.black.level;\n> -\tconst unsigned int blackLevelHistIdx =\n> -\t\tblackLevel / (256 / SwIspStats::kYHistogramSize);\n> -\tconst unsigned int histogramSize =\n> -\t\tSwIspStats::kYHistogramSize - blackLevelHistIdx;\n> -\tconst unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount;\n> -\tconst unsigned int yHistValsPerBinMod =\n> -\t\thistogramSize / (histogramSize % kExposureBinsCount + 1);\n> -\tint exposureBins[kExposureBinsCount] = {};\n> -\tunsigned int denom = 0;\n> -\tunsigned int num = 0;\n> -\n> -\tfor (unsigned int i = 0; i < histogramSize; i++) {\n> -\t\tunsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;\n> -\t\texposureBins[idx] += stats_->yHistogram[blackLevelHistIdx + i];\n> -\t}\n> -\n> -\tfor (unsigned int i = 0; i < kExposureBinsCount; i++) {\n> -\t\tLOG(IPASoft, Debug) << i << \": \" << exposureBins[i];\n> -\t\tdenom += exposureBins[i];\n> -\t\tnum += exposureBins[i] * (i + 1);\n> -\t}\n> -\n> -\tfloat exposureMSV = static_cast<float>(num) / denom;\n> -\n>  \t/* Sanity check */\n>  \tif (!sensorControls.contains(V4L2_CID_EXPOSURE) ||\n>  \t    !sensorControls.contains(V4L2_CID_ANALOGUE_GAIN)) {\n> @@ -332,72 +291,14 @@ void IPASoftSimple::processStats(\n>  \t\treturn;\n>  \t}\n>  \n> -\texposure_ = sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();\n> -\tint32_t again = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();\n> -\tagain_ = camHelper_ ? camHelper_->gain(again) : again;\n> -\n> -\tupdateExposure(exposureMSV);\n> -\n>  \tControlList ctrls(sensorInfoMap_);\n>  \n> -\tctrls.set(V4L2_CID_EXPOSURE, exposure_);\n> +\tauto &againNew = context_.activeState.agc.again;\n> +\tctrls.set(V4L2_CID_EXPOSURE, context_.activeState.agc.exposure);\n>  \tctrls.set(V4L2_CID_ANALOGUE_GAIN,\n> -\t\t  static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(again_) : again_));\n> +\t\t  static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(againNew) : againNew));\n>  \n>  \tsetSensorControls.emit(ctrls);\n> -\n> -\tLOG(IPASoft, Debug) << \"exposureMSV \" << exposureMSV\n> -\t\t\t    << \" exp \" << exposure_ << \" again \" << again_\n> -\t\t\t    << \" gain R/B \" << context_.activeState.gains.red\n> -\t\t\t    << \"/\" << context_.activeState.gains.blue\n> -\t\t\t    << \" black level \" << static_cast<unsigned int>(blackLevel);\n> -}\n> -\n> -void IPASoftSimple::updateExposure(double exposureMSV)\n> -{\n> -\t/*\n> -\t * kExpDenominator of 10 gives ~10% increment/decrement;\n> -\t * kExpDenominator of 5 - about ~20%\n> -\t */\n> -\tstatic constexpr uint8_t kExpDenominator = 10;\n> -\tstatic constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;\n> -\tstatic constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;\n> -\n> -\tdouble next;\n> -\n> -\tif (exposureMSV < kExposureOptimal - kExposureSatisfactory) {\n> -\t\tnext = exposure_ * kExpNumeratorUp / kExpDenominator;\n> -\t\tif (next - exposure_ < 1)\n> -\t\t\texposure_ += 1;\n> -\t\telse\n> -\t\t\texposure_ = next;\n> -\t\tif (exposure_ >= exposureMax_) {\n> -\t\t\tnext = again_ * kExpNumeratorUp / kExpDenominator;\n> -\t\t\tif (next - again_ < againMinStep_)\n> -\t\t\t\tagain_ += againMinStep_;\n> -\t\t\telse\n> -\t\t\t\tagain_ = next;\n> -\t\t}\n> -\t}\n> -\n> -\tif (exposureMSV > kExposureOptimal + kExposureSatisfactory) {\n> -\t\tif (exposure_ == exposureMax_ && again_ > againMin_) {\n> -\t\t\tnext = again_ * kExpNumeratorDown / kExpDenominator;\n> -\t\t\tif (again_ - next < againMinStep_)\n> -\t\t\t\tagain_ -= againMinStep_;\n> -\t\t\telse\n> -\t\t\t\tagain_ = next;\n> -\t\t} else {\n> -\t\t\tnext = exposure_ * kExpNumeratorDown / kExpDenominator;\n> -\t\t\tif (exposure_ - next < 1)\n> -\t\t\t\texposure_ -= 1;\n> -\t\t\telse\n> -\t\t\t\texposure_ = next;\n> -\t\t}\n> -\t}\n> -\n> -\texposure_ = std::clamp(exposure_, exposureMin_, exposureMax_);\n> -\tagain_ = std::clamp(again_, againMin_, againMax_);\n>  }\n>  \n>  std::string IPASoftSimple::logPrefix() const\n> diff --git a/src/libcamera/software_isp/TODO b/src/libcamera/software_isp/TODO\n> index 6b1cb57a..42828895 100644\n> --- a/src/libcamera/software_isp/TODO\n> +++ b/src/libcamera/software_isp/TODO\n> @@ -218,16 +218,6 @@ Yes, because, well... all the other IPAs were doing that...\n>  \n>  ---\n>  \n> -10. Switch to libipa/algorithm.h API in processStats\n> -\n> ->> void IPASoftSimple::processStats(const ControlList &sensorControls)\n> ->>\n> -> Do you envision switching to the libipa/algorithm.h API at some point ?\n> -\n> -At some point, yes.\n> -\n> ----\n> -\n>  13. Improve black level and colour gains application\n>  \n>  I think the black level should eventually be moved before debayering, and","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id BC629BE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 12 Aug 2024 14:54:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 779FA633BA;\n\tMon, 12 Aug 2024 16:54:54 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D1F4863369\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 12 Aug 2024 16:54:52 +0200 (CEST)","from mail-wm1-f70.google.com (mail-wm1-f70.google.com\n\t[209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-691-J1i2ibc2Mne0K5uTUH064g-1; Mon, 12 Aug 2024 10:54:50 -0400","by mail-wm1-f70.google.com with SMTP id\n\t5b1f17b1804b1-427b7a2052bso52254525e9.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 12 Aug 2024 07:54:50 -0700 (PDT)","from nuthatch (ip-77-48-47-2.net.vodafone.cz. [77.48.47.2])\n\tby smtp.gmail.com with ESMTPSA id\n\tffacd0b85a97d-36e4e51ebcdsm7757711f8f.78.2024.08.12.07.54.46\n\tfor <libcamera-devel@lists.libcamera.org>\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tMon, 12 Aug 2024 07:54:46 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"QmNnCzIL\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1723474491;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=PDf416EoQCONxaVyEOcRF+R/MR54RpcxB8JfZnL1q4o=;\n\tb=QmNnCzILcQhBr/pScWT2KjaJqKAmZsgP9W/JcgBs6smCjiOJl1VAZP58v0RUGvXRDBiYtb\n\tmNy+TchHdM4vL+vUFwyqNl/kXjyhhIv96Fyj0H9UD0SOHu8NqDd6A++KE7Rsl8UYvnf6CP\n\tGAVIu2vzgOBCTjEtb1eC1tYgCYfZzbA=","X-MC-Unique":"J1i2ibc2Mne0K5uTUH064g-1","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1723474489; x=1724079289;\n\th=mime-version:user-agent:message-id:date:references:in-reply-to\n\t:subject:to:from:x-gm-message-state:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=PDf416EoQCONxaVyEOcRF+R/MR54RpcxB8JfZnL1q4o=;\n\tb=RLxqfd+gBaOSEbiyWiRhPNOrx9dREqzYsewXBOg5aHjLmv1ylFbU5ouZZRHUzxq5qz\n\tQ5Pai5CvT/m5ndGzsmdpTh0ExgP1fA9dWL7C2eUNWTIZFsuGA9ZxazShEolZ+Zkuv7Z+\n\tco7VwG1yd8eYaoq1bnykHxpzGMuRPNpmKEghX1XIb2NDgO6H520zhZJseVCcN5gcBoa8\n\tjH6qtuYa5CPohSdxKQnjFrw7Phe8x7mMaDqoI5nK2vvx+4VpfWT4r+3jIJyvd5n91Enu\n\tE2aVxG5NY5DAx+aASBgX8O3AG+wNyWRVdCGFE83ntxVER+b+OWvh/sAwR/qbpcxOGK9W\n\taCDQ==","X-Gm-Message-State":"AOJu0YwNWwKAIbi9S1bOC3aCLGlauDZL3OpTfRn0nllSdO5sGbLJuWZm\n\t9Xkj3+X2fHzrefg7YgMFq1fPIsWrcs52XqbB51GULJvG0FzKGyFaVjmJgLbMMp01BgcbYn3mxfG\n\ttItb1R6EqgZai6+OaI9umrFgfYh9UvBbg9jrdHGAiYI9Y/kuXY6WsBpWhB+s1mH/u693Zqk4UUz\n\taD/fqXaCu4dqn1u66fQn7SG15U6bra13BjzHT+zqFSlPaXr8wWAD8kI1E=","X-Received":["by 2002:adf:ed4e:0:b0:368:4d4e:407c with SMTP id\n\tffacd0b85a97d-3716ccfa84bmr464510f8f.33.1723474488393; \n\tMon, 12 Aug 2024 07:54:48 -0700 (PDT)","by 2002:adf:ed4e:0:b0:368:4d4e:407c with SMTP id\n\tffacd0b85a97d-3716ccfa84bmr464482f8f.33.1723474487526; \n\tMon, 12 Aug 2024 07:54:47 -0700 (PDT)"],"X-Google-Smtp-Source":"AGHT+IHeXmvB0Dzb77oUdfNP7CK20feA8p4RCwUBHZBmXm7o5SwQEio7uVNfz1GTm4E2rewNLTNdLA==","From":"Milan Zamazal <mzamazal@redhat.com>","To":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH v3 22/23] libcamera: software_isp: Move exposure+gain to\n\tan algorithm module","In-Reply-To":"<20240717085444.289997-23-mzamazal@redhat.com> (Milan Zamazal's\n\tmessage of \"Wed, 17 Jul 2024 10:54:43 +0200\")","References":"<20240717085444.289997-1-mzamazal@redhat.com>\n\t<20240717085444.289997-23-mzamazal@redhat.com>","Date":"Mon, 12 Aug 2024 16:54:46 +0200","Message-ID":"<87sev9vljd.fsf@redhat.com>","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]