ipa: rpi: controller: AutoFocus bidirectional scanning

To reduce unnecessary lens movements, allow the CDAF-based
search procedure to start from either end of the range;
or if not near an end, from the current lens position.

This sometimes requires a second coarse scan, if the first
one started in the middle and did not find peak contrast.

Shorten the fine scan from 5 steps to 3 steps; allow fine scan
to be omitted altogether when "step_fine": 0 in the tuning file.

Move updateLensPosition() out of startProgrammedScan() to avoid
calling it more than once per iteration.

Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
Nick Hollinghurst 2025-06-20 13:42:28 +01:00 committed by Kieran Bingham
parent 686f88707c
commit ea5f451c56
2 changed files with 67 additions and 39 deletions

View file

@ -200,6 +200,7 @@ Af::Af(Controller *controller)
sceneChangeCount_(0), sceneChangeCount_(0),
scanMaxContrast_(0.0), scanMaxContrast_(0.0),
scanMinContrast_(1.0e9), scanMinContrast_(1.0e9),
scanStep_(0.0),
scanData_(), scanData_(),
reportState_(AfState::Idle) reportState_(AfState::Idle)
{ {
@ -251,13 +252,14 @@ void Af::switchMode(CameraMode const &cameraMode, [[maybe_unused]] Metadata *met
<< statsRegion_.height; << statsRegion_.height;
invalidateWeights(); invalidateWeights();
if (scanState_ >= ScanState::Coarse && scanState_ < ScanState::Settle) { if (scanState_ >= ScanState::Coarse1 && scanState_ < ScanState::Settle) {
/* /*
* If a scan was in progress, re-start it, as CDAF statistics * If a scan was in progress, re-start it, as CDAF statistics
* may have changed. Though if the application is just about * may have changed. Though if the application is just about
* to take a still picture, this will not help... * to take a still picture, this will not help...
*/ */
startProgrammedScan(); startProgrammedScan();
updateLensPosition();
} }
skipCount_ = cfg_.skipFrames; skipCount_ = cfg_.skipFrames;
} }
@ -543,31 +545,42 @@ void Af::doScan(double contrast, double phase, double conf)
scanMinContrast_ = contrast; scanMinContrast_ = contrast;
scanData_.emplace_back(ScanRecord{ ftarget_, contrast, phase, conf }); scanData_.emplace_back(ScanRecord{ ftarget_, contrast, phase, conf });
if (scanState_ == ScanState::Coarse) { if ((scanStep_ >= 0.0 && ftarget_ >= cfg_.ranges[range_].focusMax) ||
if (ftarget_ >= cfg_.ranges[range_].focusMax || (scanStep_ <= 0.0 && ftarget_ <= cfg_.ranges[range_].focusMin) ||
contrast < cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) { (scanState_ == ScanState::Fine && scanData_.size() >= 3) ||
/* contrast < cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) {
* Finished course scan, or termination based on contrast. double pk = findPeak(scanMaxIndex_);
* Jump to just after max contrast and start fine scan. /*
*/ * Finished a scan, by hitting a limit or due to constrast dropping off.
ftarget_ = std::min(ftarget_, findPeak(scanMaxIndex_) + * If this is a first coarse scan and we didn't bracket the peak, reverse!
2.0 * cfg_.speeds[speed_].stepFine); * If this is a fine scan, or no fine step was defined, we've finished.
scanState_ = ScanState::Fine; * Otherwise, start fine scan in opposite direction.
scanData_.clear(); */
} else if (scanState_ == ScanState::Coarse1 &&
ftarget_ += cfg_.speeds[speed_].stepCoarse; scanData_[0].contrast >= cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) {
} else { /* ScanState::Fine */ scanStep_ = -scanStep_;
if (ftarget_ <= cfg_.ranges[range_].focusMin || scanData_.size() >= 5 || scanState_ = ScanState::Coarse2;
contrast < cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) { } else if (scanState_ == ScanState::Fine || cfg_.speeds[speed_].stepFine <= 0.0) {
/* ftarget_ = pk;
* Finished fine scan, or termination based on contrast.
* Use quadratic peak-finding to find best contrast position.
*/
ftarget_ = findPeak(scanMaxIndex_);
scanState_ = ScanState::Settle; scanState_ = ScanState::Settle;
} else } else if (scanState_ == ScanState::Coarse1 &&
ftarget_ -= cfg_.speeds[speed_].stepFine; scanData_[0].contrast >= cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) {
} scanStep_ = -scanStep_;
scanState_ = ScanState::Coarse2;
} else if (scanStep_ >= 0.0) {
ftarget_ = std::min(pk + cfg_.speeds[speed_].stepFine,
cfg_.ranges[range_].focusMax);
scanStep_ = -cfg_.speeds[speed_].stepFine;
scanState_ = ScanState::Fine;
} else {
ftarget_ = std::max(pk - cfg_.speeds[speed_].stepFine,
cfg_.ranges[range_].focusMin);
scanStep_ = cfg_.speeds[speed_].stepFine;
scanState_ = ScanState::Fine;
}
scanData_.clear();
} else
ftarget_ += scanStep_;
stepCount_ = (ftarget_ == fsmooth_) ? 0 : cfg_.speeds[speed_].stepFrames; stepCount_ = (ftarget_ == fsmooth_) ? 0 : cfg_.speeds[speed_].stepFrames;
} }
@ -622,7 +635,7 @@ void Af::doAF(double contrast, double phase, double conf)
/* else fall through to waiting for a scene change */ /* else fall through to waiting for a scene change */
} }
} }
if (scanState_ < ScanState::Coarse && mode_ == AfModeContinuous) { if (scanState_ < ScanState::Coarse1 && mode_ == AfModeContinuous) {
/* /*
* In CAF mode, not in a scan, and PDAF is unavailable. * In CAF mode, not in a scan, and PDAF is unavailable.
* Wait for a scene change, followed by stability. * Wait for a scene change, followed by stability.
@ -642,7 +655,7 @@ void Af::doAF(double contrast, double phase, double conf)
sceneChangeCount_++; sceneChangeCount_++;
if (sceneChangeCount_ >= cfg_.speeds[speed_].retriggerDelay) if (sceneChangeCount_ >= cfg_.speeds[speed_].retriggerDelay)
startProgrammedScan(); startProgrammedScan();
} else if (scanState_ >= ScanState::Coarse && fsmooth_ == ftarget_) { } else if (scanState_ >= ScanState::Coarse1 && fsmooth_ == ftarget_) {
/* /*
* CDAF-based scanning sequence. * CDAF-based scanning sequence.
* Allow a delay between steps for CDAF FoM statistics to be * Allow a delay between steps for CDAF FoM statistics to be
@ -714,15 +727,27 @@ void Af::startAF()
oldSceneContrast_ = 0.0; oldSceneContrast_ = 0.0;
sceneChangeCount_ = 0; sceneChangeCount_ = 0;
reportState_ = AfState::Scanning; reportState_ = AfState::Scanning;
} else } else {
startProgrammedScan(); startProgrammedScan();
updateLensPosition();
}
} }
void Af::startProgrammedScan() void Af::startProgrammedScan()
{ {
ftarget_ = cfg_.ranges[range_].focusMin; if (!initted_ || mode_ != AfModeContinuous ||
updateLensPosition(); fsmooth_ <= cfg_.ranges[range_].focusMin + 2.0 * cfg_.speeds[speed_].stepCoarse) {
scanState_ = ScanState::Coarse; ftarget_ = cfg_.ranges[range_].focusMin;
scanStep_ = cfg_.speeds[speed_].stepCoarse;
scanState_ = ScanState::Coarse2;
} else if (fsmooth_ >= cfg_.ranges[range_].focusMax - 2.0 * cfg_.speeds[speed_].stepCoarse) {
ftarget_ = cfg_.ranges[range_].focusMax;
scanStep_ = -cfg_.speeds[speed_].stepCoarse;
scanState_ = ScanState::Coarse2;
} else {
scanStep_ = -cfg_.speeds[speed_].stepCoarse;
scanState_ = ScanState::Coarse1;
}
scanMaxContrast_ = 0.0; scanMaxContrast_ = 0.0;
scanMinContrast_ = 1.0e9; scanMinContrast_ = 1.0e9;
scanMaxIndex_ = 0; scanMaxIndex_ = 0;
@ -785,7 +810,9 @@ void Af::prepare(Metadata *imageMetadata)
else else
status.pauseState = AfPauseState::Running; status.pauseState = AfPauseState::Running;
if (mode_ == AfModeAuto && scanState_ != ScanState::Idle) if (scanState_ == ScanState::Idle)
status.state = AfState::Idle;
else if (mode_ == AfModeAuto)
status.state = AfState::Scanning; status.state = AfState::Scanning;
else else
status.state = reportState_; status.state = reportState_;
@ -907,7 +934,7 @@ void Af::setMode(AfAlgorithm::AfMode mode)
pauseFlag_ = false; pauseFlag_ = false;
if (mode == AfModeContinuous) if (mode == AfModeContinuous)
scanState_ = ScanState::Trigger; scanState_ = ScanState::Trigger;
else if (mode != AfModeAuto || scanState_ < ScanState::Coarse) else if (mode != AfModeAuto || scanState_ < ScanState::Coarse1)
goIdle(); goIdle();
} }
} }
@ -923,11 +950,11 @@ void Af::pause(AfAlgorithm::AfPause pause)
if (mode_ == AfModeContinuous) { if (mode_ == AfModeContinuous) {
if (pause == AfPauseResume && pauseFlag_) { if (pause == AfPauseResume && pauseFlag_) {
pauseFlag_ = false; pauseFlag_ = false;
if (scanState_ < ScanState::Coarse) if (scanState_ < ScanState::Coarse1)
scanState_ = ScanState::Trigger; scanState_ = ScanState::Trigger;
} else if (pause != AfPauseResume && !pauseFlag_) { } else if (pause != AfPauseResume && !pauseFlag_) {
pauseFlag_ = true; pauseFlag_ = true;
if (pause == AfPauseImmediate || scanState_ < ScanState::Coarse) if (pause == AfPauseImmediate || scanState_ < ScanState::Coarse1)
goIdle(); goIdle();
} }
} }

View file

@ -75,7 +75,8 @@ private:
Idle = 0, Idle = 0,
Trigger, Trigger,
Pdaf, Pdaf,
Coarse, Coarse1,
Coarse2,
Fine, Fine,
Settle Settle
}; };
@ -90,8 +91,8 @@ private:
}; };
struct SpeedDependentParams { struct SpeedDependentParams {
double stepCoarse; /* used for scans */ double stepCoarse; /* in dioptres; used for scans */
double stepFine; /* used for scans */ double stepFine; /* in dioptres; used for scans */
double contrastRatio; /* used for scan termination and reporting */ double contrastRatio; /* used for scan termination and reporting */
double retriggerRatio; /* contrast and RGB ratio for re-triggering */ double retriggerRatio; /* contrast and RGB ratio for re-triggering */
uint32_t retriggerDelay; /* frames of stability before re-triggering */ uint32_t retriggerDelay; /* frames of stability before re-triggering */
@ -177,7 +178,7 @@ private:
unsigned sameSignCount_; unsigned sameSignCount_;
unsigned sceneChangeCount_; unsigned sceneChangeCount_;
unsigned scanMaxIndex_; unsigned scanMaxIndex_;
double scanMaxContrast_, scanMinContrast_; double scanMaxContrast_, scanMinContrast_, scanStep_;
std::vector<ScanRecord> scanData_; std::vector<ScanRecord> scanData_;
AfState reportState_; AfState reportState_;
}; };