[libcamera-devel] [PATCH v5 04/10] ipa: Add common contrast based AF implementation

Daniel Semkowicz dse at thaumatec.com
Tue Mar 28 07:42:25 CEST 2023


Hi Jacopo,

On Mon, Mar 27, 2023 at 3:03 PM Jacopo Mondi
<jacopo.mondi at ideasonboard.com> wrote:
>
> Hi Daniel
>
> On Mon, Mar 27, 2023 at 02:29:45PM +0200, Daniel Semkowicz wrote:
> > Hi Jacopo,
> >
> > On Fri, Mar 24, 2023 at 4:30 PM Jacopo Mondi
> > <jacopo.mondi at ideasonboard.com> wrote:
> > >
> > > Hi Daniel
> > >
> > > On Fri, Mar 24, 2023 at 03:29:02PM +0100, Daniel Semkowicz via libcamera-devel wrote:
> > > > Create a new class with contrast based Auto Focus implementation
> > > > using hill climbing algorithm. This common implementation is independent
> > > > of platform specific code. This way, each platform can just implement
> > > > contrast calculation and run the AF control loop basing on this class.
> > > > This implementation is based on the code that was common for IPU3
> > > > and RPi AF algorithms.
> > > >
> > > > Signed-off-by: Daniel Semkowicz <dse at thaumatec.com>
> > > > ---
> > > >  .../libipa/algorithms/af_hill_climbing.cpp    | 379 ++++++++++++++++++
> > > >  src/ipa/libipa/algorithms/af_hill_climbing.h  |  91 +++++
> > > >  src/ipa/libipa/algorithms/meson.build         |   2 +
> > > >  3 files changed, 472 insertions(+)
> > > >  create mode 100644 src/ipa/libipa/algorithms/af_hill_climbing.cpp
> > > >  create mode 100644 src/ipa/libipa/algorithms/af_hill_climbing.h
> > > >
> > > > diff --git a/src/ipa/libipa/algorithms/af_hill_climbing.cpp b/src/ipa/libipa/algorithms/af_hill_climbing.cpp
> > > > new file mode 100644
> > > > index 00000000..6ee090eb
> > > > --- /dev/null
> > > > +++ b/src/ipa/libipa/algorithms/af_hill_climbing.cpp
> > > > @@ -0,0 +1,379 @@
> > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > > +/*
> > > > + * Copyright (C) 2021, Red Hat
> > > > + * Copyright (C) 2022, Ideas On Board
> > > > + * Copyright (C) 2023, Theobroma Systems
> > > > + *
> > > > + * af_hill_climbing.cpp - AF contrast based hill climbing common algorithm
> > > > + */
> > > > +
> > > > +#include "af_hill_climbing.h"
> > > > +
> > > > +#include "libcamera/internal/yaml_parser.h"
> > > > +
> > > > +/**
> > > > + * \file af_hill_climbing.h
> > > > + * \brief AF contrast based hill climbing common algorithm
> > > > + */
> > > > +
> > > > +namespace libcamera {
> > > > +
> > > > +namespace ipa::algorithms {
> > > > +
> > > > +LOG_DEFINE_CATEGORY(Af)
> > > > +
> > > > +/**
> > > > + * \class AfHillClimbing
> > > > + * \brief Contrast based hill climbing auto focus control algorithm
> > > > + * implementation
> > > > + *
> > > > + * Control part of the auto focus algorithm. It calculates the lens position
> > > > + * based on the contrast measure supplied by the platform-specific
> > > > + * implementation. This way it is independent from the platform.
> > > > + *
> > > > + * Platform layer that use this algorithm should call process() function
> > > > + * for each each frame and set the lens to the calculated position.
> > > > + *
> > > > + * Focus search is divided into two phases:
> > > > + * 1. coarse search,
> > > > + * 2. fine search.
> > > > + *
> > > > + * In the first phase, the lens is moved with bigger steps to quickly find
> > > > + * a rough position for the best focus. Then, based on the outcome of coarse
> > > > + * search, the second phase is executed. Lens is moved with smaller steps
> > > > + * in a limited range within the rough position to find the exact position
> > > > + * for best focus.
> > > > + *
> > > > + * Tuning file parameters:
> > > > + * - **coarse-search-step:** The value by which the lens position will change
> > > > + *   in one step in the *coarse search* phase. Unit is lens specific.
> > > > + * - **fine-search-step:** The value by which the lens position will change
> > > > + *   in one step in the *fine search* phase. Unit is lens specific.
> > > > + * - **fine-search-range:** Search range in the *fine search* phase, expressed
> > > > + *   as a percentage of the coarse search result. Valid values are
> > > > + *   in the [0, 100] interval. Value 5 means 5%. If coarse search stopped
> > > > + *   at value 200, the fine search range will be [190, 210].
> > > > + * - **max-variance-change:** ratio of contrast variance change in the
> > > > + *   *continuous mode* needed for triggering the focus change. When the variance
> > > > + *   change exceeds this value, focus search will be triggered. Valid values are
> > > > + *   in the [0.0, 1.0] interval.
> > > > + * .
> > > > + *
> > > > + * \todo Search range in the *fine search* phase should depend on the lens
> > > > + *   movement range rather than coarse search result.
> > > > + * \todo Implement setRange.
> > > > + * \todo Implement setSpeed.
> > > > + * \todo Implement setMeteringMode.
> > > > + * \todo Implement setWindows.
> > > > + * \todo Implement the AfPauseDeferred mode.
> > > > + */
> > > > +
> > > > +/**
> > > > + * \brief Initialize the AfHillClimbing with tuning data
> > > > + * \param[in] tuningData The tuning data for the algorithm
> > > > + *
> > > > + * This method should be called in the libcamera::ipa::Algorithm::init()
> > > > + * method of the platform layer.
> > > > + *
> > > > + * \return 0 if successful, an error code otherwise
> > > > + */
> > > > +int AfHillClimbing::init(const YamlObject &tuningData)
> > > > +{
> > > > +     coarseSearchStep_ = tuningData["coarse-search-step"].get<uint32_t>(30);
> > > > +     fineSearchStep_ = tuningData["fine-search-step"].get<uint32_t>(1);
> > > > +     fineRange_ = tuningData["fine-search-range"].get<uint32_t>(5);
> > > > +     fineRange_ /= 100;
> > > > +     maxChange_ = tuningData["max-variance-change"].get<double>(0.5);
> > > > +
> > > > +     LOG(Af, Debug) << "coarseSearchStep_: " << coarseSearchStep_
> > > > +                    << ", fineSearchStep_: " << fineSearchStep_
> > > > +                    << ", fineRange_: " << fineRange_
> > > > +                    << ", maxChange_: " << maxChange_;
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +/**
> > > > + * \brief Configure the AfHillClimbing with sensor and lens information
> > > > + * \param[in] minFocusPosition Minimum position supported by camera lens
> > > > + * \param[in] maxFocusPosition Maximum position supported by camera lens
> > > > + *
> > > > + * This method should be called in the libcamera::ipa::Algorithm::configure()
> > > > + * method of the platform layer.
> > > > + */
> > > > +void AfHillClimbing::configure(int32_t minFocusPosition,
> > > > +                            int32_t maxFocusPosition)
> > > > +{
> > > > +     minLensPosition_ = minFocusPosition;
> > > > +     maxLensPosition_ = maxFocusPosition;
> > > > +}
> > > > +
> > > > +/**
> > > > + * \brief Run the auto focus algorithm loop
> > > > + * \param[in] currentContrast New value of contrast measured for current frame
> > > > + *
> > > > + * This method should be called in the libcamera::ipa::Algorithm::process()
> > > > + * method of the platform layer for each frame.
> > > > + *
> > > > + * Contrast value supplied in the \p currentContrast parameter can be platform
> > > > + * specific. The only requirement is the contrast value must increase with
> > > > + * the increasing image focus. Contrast value must be highest when image is in
> > > > + * focus.
> > > > + *
> > > > + * \return New lens position calculated by the AF algorithm
> > > > + */
> > > > +int32_t AfHillClimbing::process(double currentContrast)
> > > > +{
> > > > +     currentContrast_ = currentContrast;
> > > > +
> > > > +     if (shouldSkipFrame())
> > > > +             return lensPosition_;
> > > > +
> > > > +     switch (mode_) {
> > > > +     case controls::AfModeManual:
> > > > +             /* Nothing to process. */
> > > > +             break;
> > > > +     case controls::AfModeAuto:
> > > > +             processAutoMode();
> > > > +             break;
> > > > +     case controls::AfModeContinuous:
> > > > +             processContinuousMode();
> > > > +             break;
> > > > +     }
> > > > +
> > > > +     return lensPosition_;
> > > > +}
> > > > +
> > > > +void AfHillClimbing::processAutoMode()
> > > > +{
> > > > +     if (state_ == controls::AfStateScanning) {
> > > > +             afCoarseScan();
> > > > +             afFineScan();
> > > > +     }
> > > > +}
> > > > +
> > > > +void AfHillClimbing::processContinuousMode()
> > > > +{
> > > > +     /* If we are in a paused state, we won't process the stats. */
> > > > +     if (pauseState_ == controls::AfPauseStatePaused)
> > > > +             return;
> > > > +
> > > > +     if (state_ == controls::AfStateScanning) {
> > > > +             afCoarseScan();
> > > > +             afFineScan();
> > > > +             return;
> > > > +     }
> > > > +
> > > > +     /*
> > > > +      * AF scan can be started at any moment in AfModeContinuous,
> > > > +      * except when the state is already AfStateScanning.
> > > > +      */
> > > > +     if (afIsOutOfFocus())
> > > > +             afReset();
> > > > +}
> > > > +
> > > > +/**
> > > > + * \brief Request AF to skip n frames
> > > > + * \param[in] n Number of frames to be skipped
> > > > + *
> > > > + * For the requested number of frames, the AF calculation will be skipped
> > > > + * and lens position will not change. The platform layer still needs to
> > > > + * call process() function for each frame during this time.
> > > > + * This function can be used by the platform layer if the hardware needs more
> > > > + * time for some operations.
> > > > + *
> > > > + * The number of the requested frames (\p n) will be applied only if \p n has
> > > > + * higher value than the number of frames already requested to be skipped.
> > > > + * For example, if *setFramesToSkip(5)* was already called for the current
> > > > + * frame, then calling *setFramesToSkip(3)* will not override the previous
> > > > + * request and 5 frames will be skipped.
> > >
> > > This still puzzles me a bit as it is not clear to me when the platform
> > > layer is supposed to set the number of frames to skip.
> > >
> > > Looking at the implementation it happens everytime the lens is moved.
> > > I guess this is fine for now, but this will need to be handled better
> > > by the CameraLens class modeling the lens movement delay.
> > >
> >
> > In RkISP it is also needed when setting AF Windows. ISP needs a one
> > additional frame to calculate the new statistics after setting window.
> > This is why I exposed this method as public.
> >
> > > > + */
> > > > +void AfHillClimbing::setFramesToSkip(uint32_t n)
> > > > +{
> > > > +     if (n > framesToSkip_)
> > > > +             framesToSkip_ = n;
> > > > +}
> > > > +
> > > > +void AfHillClimbing::setMode(controls::AfModeEnum mode)
> > > > +{
> > > > +     if (mode == mode_)
> > > > +             return;
> > > > +
> > > > +     LOG(Af, Debug) << "Switched AF mode from " << mode_ << " to " << mode;
> > > > +     mode_ = mode;
> > > > +
> > > > +     state_ = controls::AfStateIdle;
> > > > +     pauseState_ = controls::AfPauseStateRunning;
> > > > +
> > > > +     if (mode_ == controls::AfModeContinuous)
> > > > +             afReset();
> > > > +}
> > > > +
> > > > +void AfHillClimbing::setRange([[maybe_unused]] controls::AfRangeEnum range)
> > > > +{
> > > > +     LOG(Af, Error) << __FUNCTION__ << " not implemented!";
> > > > +}
> > > > +
> > > > +void AfHillClimbing::setSpeed([[maybe_unused]] controls::AfSpeedEnum speed)
> > > > +{
> > > > +     LOG(Af, Error) << __FUNCTION__ << " not implemented!";
> > > > +}
> > > > +
> > > > +void AfHillClimbing::setMeteringMode([[maybe_unused]] controls::AfMeteringEnum metering)
> > > > +{
> > > > +     LOG(Af, Error) << __FUNCTION__ << " not implemented!";
> > > > +}
> > > > +
> > > > +void AfHillClimbing::setWindows([[maybe_unused]] Span<const Rectangle> windows)
> > > > +{
> > > > +     LOG(Af, Error) << __FUNCTION__ << " not implemented!";
> > > > +}
> > > > +
> > >
> > > I'm still of the idea you should better not implement these functions
> > > at all. If anything tries to call into them will fail at compile time
> > > instead of getting a run-time error in the logs
> >
> > These functions are pure virtual, so you are forced by compiler
> > to implement them.
> >
>
> Ah! I removed the definition in the cpp file but I left the
> declaration in the header, so I guess it worked here because of the
> compiler generated one for me. Sorry for the overlook
>
> > >
> > > > +void AfHillClimbing::setTrigger(controls::AfTriggerEnum trigger)
> > > > +{
> > > > +     if (mode_ != controls::AfModeAuto) {
> > > > +             LOG(Af, Warning)
> > > > +                     << __FUNCTION__ << " not valid in mode " << mode_;
> > >
> > > We don't use __FUNCTION__ for the reason that as far as I know it's
> > > not a standard language keyword (while __func__ is iirc). I'm also not
> > > sure how it works with name mangling as I see gcc has a
> > > __PRETTY_FUNCTION__ specific extensions to beautify names.
> > >
> > > I would just
> > >                         << "setTrigger() not valid in mode " << mode_;
> > >
> > > > +             return;
> > > > +     }
> > > > +
> > > > +     LOG(Af, Debug) << "Trigger called with " << trigger;
> > > > +
> > > > +     if (trigger == controls::AfTriggerStart)
> > > > +             afReset();
> > > > +     else
> > > > +             state_ = controls::AfStateIdle;
> > >
> > > I would switch to be sure to handle all cases. A minor though
> > >
> > > > +}
> > > > +
> > > > +void AfHillClimbing::setPause(controls::AfPauseEnum pause)
> > > > +{
> > > > +     if (mode_ != controls::AfModeContinuous) {
> > > > +             LOG(Af, Warning)
> > > > +                     << __FUNCTION__ << " not valid in mode " << mode_;
> > >
> > > ditto
> > >
> > > > +             return;
> > > > +     }
> > > > +
> > > > +     switch (pause) {
> > > > +     case controls::AfPauseImmediate:
> > > > +             pauseState_ = controls::AfPauseStatePaused;
> > > > +             break;
> > > > +     case controls::AfPauseDeferred:
> > > > +             LOG(Af, Warning) << "AfPauseDeferred is not supported!";
> > > > +             break;
> > > > +     case controls::AfPauseResume:
> > > > +             pauseState_ = controls::AfPauseStateRunning;
> > > > +             break;
> > > > +     }
> > > > +}
> > > > +
> > > > +void AfHillClimbing::setLensPosition(float lensPosition)
> > > > +{
> > > > +     if (mode_ != controls::AfModeManual) {
> > > > +             LOG(Af, Warning)
> > > > +                     << __FUNCTION__ << " not valid in mode " << mode_;
> > > > +             return;
> > > > +     }
> > > > +
> > > > +     lensPosition_ = static_cast<int32_t>(lensPosition);
> > > > +
> > > > +     LOG(Af, Debug) << "Requesting lens position " << lensPosition_;
> > > > +}
> > > > +
> > > > +void AfHillClimbing::afCoarseScan()
> > > > +{
> > > > +     if (coarseCompleted_)
> > > > +             return;
> > > > +
> > > > +     if (afScan(coarseSearchStep_)) {
> > > > +             coarseCompleted_ = true;
> > > > +             maxContrast_ = 0;
> > > > +             const auto diff = static_cast<int32_t>(
> > > > +                     std::abs(lensPosition_) * fineRange_);
> > > > +             lensPosition_ = std::max(lensPosition_ - diff, minLensPosition_);
> > > > +             maxStep_ = std::min(lensPosition_ + diff, maxLensPosition_);
> > > > +     }
> > > > +}
> > > > +
> > > > +void AfHillClimbing::afFineScan()
> > > > +{
> > > > +     if (!coarseCompleted_)
> > > > +             return;
> > > > +
> > > > +     if (afScan(fineSearchStep_)) {
> > > > +             LOG(Af, Debug) << "AF found the best focus position!";
> > > > +             state_ = controls::AfStateFocused;
> > > > +     }
> > > > +}
> > > > +
> > > > +bool AfHillClimbing::afScan(uint32_t steps)
> > > > +{
> > > > +     if (lensPosition_ + static_cast<int32_t>(steps) > maxStep_) {
> > > > +             /* If the max step is reached, move lens to the position. */
> > > > +             lensPosition_ = bestPosition_;
> > > > +             return true;
> > > > +     } else {
> > >
> > > If you return in the if() clause , you can remove else { } and save
> > > one indendation level
> >
> > Good points! I will rework the code :)
> >
> > >
> > > > +             /*
> > > > +              * Find the maximum of the variance by estimating its
> > > > +              * derivative. If the direction changes, it means we have passed
> > > > +              * a maximum one step before.
> > > > +              */
> > > > +             if ((currentContrast_ - maxContrast_) >= -(maxContrast_ * 0.1)) {
> > > > +                     /*
> > > > +                      * Positive and zero derivative:
> > > > +                      * The variance is still increasing. The focus could be
> > > > +                      * increased for the next comparison. Also, the max
> > > > +                      * variance and previous focus value are updated.
> > > > +                      */
> > > > +                     bestPosition_ = lensPosition_;
> > > > +                     lensPosition_ += static_cast<int32_t>(steps);
> > > > +                     maxContrast_ = currentContrast_;
> > > > +             } else {
> > > > +                     /*
> > > > +                      * Negative derivative:
> > > > +                      * The variance starts to decrease which means the maximum
> > > > +                      * variance is found. Set focus step to previous good one
> > > > +                      * then return immediately.
> > > > +                      */
> > > > +                     lensPosition_ = bestPosition_;
> > > > +                     return true;
> > > > +             }
> > > > +     }
> > > > +
> > > > +     LOG(Af, Debug) << "Previous step is " << bestPosition_
> > > > +                    << ", Current step is " << lensPosition_;
> > > > +     return false;
> > > > +}
> > > > +
> > > > +void AfHillClimbing::afReset()
> > > > +{
> > > > +     LOG(Af, Debug) << "Reset AF parameters";
> > > > +     lensPosition_ = minLensPosition_;
> > > > +     maxStep_ = maxLensPosition_;
> > > > +     state_ = controls::AfStateScanning;
> > > > +     coarseCompleted_ = false;
> > > > +     maxContrast_ = 0.0;
> > > > +     setFramesToSkip(1);
> > > > +}
> > > > +
> > > > +bool AfHillClimbing::afIsOutOfFocus() const
> > > > +{
> > > > +     const double diff_var = std::abs(currentContrast_ - maxContrast_);
> > > > +     const double var_ratio = diff_var / maxContrast_;
> > > > +     LOG(Af, Debug) << "Variance change rate: " << var_ratio
> > > > +                    << ", Current lens step: " << lensPosition_;
> > > > +     return var_ratio > maxChange_;
> > > > +}
> > > > +
> > > > +bool AfHillClimbing::shouldSkipFrame()
> > > > +{
> > > > +     if (framesToSkip_ > 0) {
> > > > +             framesToSkip_--;
> > > > +             return true;
> > > > +     }
> > > > +
> > > > +     return false;
> > > > +}
> > > > +
> > > > +} /* namespace ipa::algorithms */
> > > > +
> > > > +} /* namespace libcamera */
> > > > diff --git a/src/ipa/libipa/algorithms/af_hill_climbing.h b/src/ipa/libipa/algorithms/af_hill_climbing.h
> > > > new file mode 100644
> > > > index 00000000..47d2bbec
> > > > --- /dev/null
> > > > +++ b/src/ipa/libipa/algorithms/af_hill_climbing.h
> > > > @@ -0,0 +1,91 @@
> > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > > +/*
> > > > + * Copyright (C) 2021, Red Hat
> > > > + * Copyright (C) 2022, Ideas On Board
> > > > + * Copyright (C) 2023, Theobroma Systems
> > > > + *
> > > > + * af_hill_climbing.h - AF contrast based hill climbing common algorithm
> > > > + */
> > > > +
> > > > +#pragma once
> > > > +
> > > > +#include <libcamera/base/log.h>
> > > > +
> > > > +#include "af.h"
> > > > +
> > > > +namespace libcamera {
> > > > +
> > > > +class YamlObject;
> > > > +
> > > > +namespace ipa::algorithms {
> > > > +
> > > > +LOG_DECLARE_CATEGORY(Af)
> > > > +
> > > > +class AfHillClimbing : public Af
> > >
> > > Isn't it better to declare the class final instead of the single
> > > functions ?
> >
> > It is not the same.
> > When used in a class definition, final specifies
> > that the class cannot be derived from.
>
> Yep
>
> > When used in a virtual function declaration or definition, final
> > specifier ensures that the function is virtual and specifies that it
> > may not be overridden by derived classes.
>
> I almost agree. final when applied to a virtual function ensures that
> it cannot be overriden in derived classes.
>
> The part I don't get in you comment is "final specifier ensures that
> the function is -virtual-" the rest I agree on.

When "final" is used in a function definition, it implies function
to be virtual. This is why I said it is a different for class and
function. But now I see, I probably misunderstood your intention
and your comment was more about architecture - what should be final.
Not that class final should replace function finals as I understood it.

>
> > It is technically possible to derive from AfHillClimbing to build
> > on it, even if it was meant to be used in composition style.
> > Do We want to force a composition approach or leave possibility to
> > derive from it?
>
> The thing that doesn't click for me is that you have declared as final
> the methods that implement the AF state machine, which is agnostic
> from the HillClimbing algorithm. Is this correct ?
>
>         void setMode(controls::AfModeEnum mode) final;
>         void setRange(controls::AfRangeEnum range) final;
>         void setSpeed(controls::AfSpeedEnum speed) final;
>         void setMeteringMode(controls::AfMeteringEnum metering) final;
>         void setWindows(Span<const Rectangle> windows) final;
>         void setTrigger(controls::AfTriggerEnum trigger) final;
>         void setPause(controls::AfPauseEnum pause) final;
>         void setLensPosition(float lensPosition) final;
>
>         void processAutoMode();
>         void processContinuousMode();
>         void afCoarseScan();
>         void afFineScan();
>         bool afScan(uint32_t steps);
>         void afReset();
>         [[nodiscard]] bool afIsOutOfFocus() const;
>         bool shouldSkipFrame();
>
> As I see it, the algorithm state machine method might later be broken
> out to a common AfBase class (or even making the Af class non-purely
> virtual) as they do not depend on the contrast-computation method.
>
> If one would like to derive from AfHillClimbing I guess it would be to
> tweak the algorithm's computation, not the state machine which adheres
> to the libcamera's controls definition. That's why I was suggesting
> that this fine-grained control on what can be overridden is probably a
> bit premature.

This is a valid point. Algorithms class hierarchy (and especially this
one) is in early stage. Then maybe I should drop the final specifiers
completely in this class? As it is hard to decide the future direction
at this point?

>
> >
> > Best regards
> > Daniel
> >
> > >
> > > class AfHillClimbing final : public Af
> > >
> > > > +{
> > > > +public:
> > > > +     int init(const YamlObject &tuningData);
> > > > +     void configure(int32_t minFocusPosition, int32_t maxFocusPosition);
> > > > +     int32_t process(double currentContrast);
> > > > +     void setFramesToSkip(uint32_t n);
> > > > +
> > > > +     controls::AfStateEnum state() final { return state_; }
> > > > +     controls::AfPauseStateEnum pauseState() final { return pauseState_; }
> > > > +
> > > > +private:
> > > > +     void setMode(controls::AfModeEnum mode) final;
> > > > +     void setRange(controls::AfRangeEnum range) final;
> > > > +     void setSpeed(controls::AfSpeedEnum speed) final;
> > > > +     void setMeteringMode(controls::AfMeteringEnum metering) final;
> > > > +     void setWindows(Span<const Rectangle> windows) final;
> > > > +     void setTrigger(controls::AfTriggerEnum trigger) final;
> > > > +     void setPause(controls::AfPauseEnum pause) final;
> > > > +     void setLensPosition(float lensPosition) final;
> > >
> > > These functions should have the 'override' specifier
> > >
> > > > +
> > > > +     void processAutoMode();
> > > > +     void processContinuousMode();
> > > > +     void afCoarseScan();
> > > > +     void afFineScan();
> > > > +     bool afScan(uint32_t steps);
> > > > +     void afReset();
> > > > +     [[nodiscard]] bool afIsOutOfFocus() const;
> > > > +     bool shouldSkipFrame();
> > > > +
> > > > +     controls::AfModeEnum mode_ = controls::AfModeManual;
> > > > +     controls::AfStateEnum state_ = controls::AfStateIdle;
> > > > +     controls::AfPauseStateEnum pauseState_ = controls::AfPauseStateRunning;
> > > > +
> > > > +     /* Current focus lens position. */
> > > > +     int32_t lensPosition_ = 0;
> > > > +     /* Local optimum focus lens position during scanning. */
> > > > +     int32_t bestPosition_ = 0;
> > > > +
> > > > +     /* Current AF statistic contrast. */
> > > > +     double currentContrast_ = 0;
> > > > +     /* It is used to determine the derivative during scanning */
> > > > +     double maxContrast_ = 0;
> > > > +     /* The designated maximum range of focus scanning. */
> > > > +     int32_t maxStep_ = 0;
> > > > +     /* If the coarse scan completes, it is set to true. */
> > > > +     bool coarseCompleted_ = false;
> > > > +
> > > > +     uint32_t framesToSkip_ = 0;
> > > > +
> > > > +     /* Position limits of the focus lens. */
> > > > +     int32_t minLensPosition_;
> > > > +     int32_t maxLensPosition_;
> > > > +
> > > > +     /* Minimum position step for searching appropriate focus. */
> > > > +     uint32_t coarseSearchStep_;
> > > > +     uint32_t fineSearchStep_;
> > > > +
> > > > +     /* Fine scan range 0 < fineRange_ < 1. */
> > > > +     double fineRange_;
> > > > +
> > > > +     /* Max ratio of variance change, 0.0 < maxChange_ < 1.0. */
> > > > +     double maxChange_;
> > > > +};
> > > > +
> > > > +} /* namespace ipa::algorithms */
> > > > +
> > > > +} /* namespace libcamera */
> > > > diff --git a/src/ipa/libipa/algorithms/meson.build b/src/ipa/libipa/algorithms/meson.build
> > > > index 3df4798f..20e437fc 100644
> > > > --- a/src/ipa/libipa/algorithms/meson.build
> > > > +++ b/src/ipa/libipa/algorithms/meson.build
> > > > @@ -2,8 +2,10 @@
> > > >
> > > >  libipa_algorithms_headers = files([
> > > >      'af.h',
> > > > +    'af_hill_climbing.h',
> > > >  ])
> > > >
> > > >  libipa_algorithms_sources = files([
> > > >      'af.cpp',
> > > > +    'af_hill_climbing.cpp',
> > > >  ])
> > > > --
> > > > 2.39.2
> > > >

Best regards
Daniel


More information about the libcamera-devel mailing list