@@ -46,6 +46,8 @@ Af::SpeedDependentParams::SpeedDependentParams()
4646 : stepCoarse(1.0 ),
4747 stepFine(0.25 ),
4848 contrastRatio(0.75 ),
49+ retriggerRatio(0.75 ),
50+ retriggerDelay(10 ),
4951 pdafGain(-0.02 ),
5052 pdafSquelch(0.125 ),
5153 maxSlew(2.0 ),
@@ -60,6 +62,7 @@ Af::CfgParams::CfgParams()
6062 confThresh(16 ),
6163 confClip(512 ),
6264 skipFrames(5 ),
65+ checkForIR(false ),
6366 map()
6467{
6568}
@@ -87,6 +90,8 @@ void Af::SpeedDependentParams::read(const libcamera::YamlObject ¶ms)
8790 readNumber<double >(stepCoarse, params, " step_coarse" );
8891 readNumber<double >(stepFine, params, " step_fine" );
8992 readNumber<double >(contrastRatio, params, " contrast_ratio" );
93+ readNumber<double >(retriggerRatio, params, " retrigger_ratio" );
94+ readNumber<uint32_t >(retriggerDelay, params, " retrigger_delay" );
9095 readNumber<double >(pdafGain, params, " pdaf_gain" );
9196 readNumber<double >(pdafSquelch, params, " pdaf_squelch" );
9297 readNumber<double >(maxSlew, params, " max_slew" );
@@ -137,6 +142,7 @@ int Af::CfgParams::read(const libcamera::YamlObject ¶ms)
137142 readNumber<uint32_t >(confThresh, params, " conf_thresh" );
138143 readNumber<uint32_t >(confClip, params, " conf_clip" );
139144 readNumber<uint32_t >(skipFrames, params, " skip_frames" );
145+ readNumber<bool >(checkForIR, params, " check_for_ir" );
140146
141147 if (params.contains (" map" ))
142148 map = params[" map" ].get <ipa::Pwl>(ipa::Pwl{});
@@ -176,29 +182,37 @@ Af::Af(Controller *controller)
176182 useWindows_(false ),
177183 phaseWeights_(),
178184 contrastWeights_(),
185+ awbWeights_(),
179186 scanState_(ScanState::Idle),
180187 initted_(false ),
188+ irFlag_(false ),
181189 ftarget_(-1.0 ),
182190 fsmooth_(-1.0 ),
183191 prevContrast_(0.0 ),
192+ oldSceneContrast_(0.0 ),
193+ prevAverage_{ 0.0 , 0.0 , 0.0 },
194+ oldSceneAverage_{ 0.0 , 0.0 , 0.0 },
184195 prevPhase_ (0.0 ),
185196 skipCount_(0 ),
186197 stepCount_(0 ),
187198 dropCount_(0 ),
188199 sameSignCount_(0 ),
200+ sceneChangeCount_(0 ),
189201 scanMaxContrast_(0.0 ),
190202 scanMinContrast_(1.0e9 ),
191203 scanData_(),
192204 reportState_(AfState::Idle)
193205{
194206 /*
195- * Reserve space for data, to reduce memory fragmentation. It's too early
196- * to query the size of the PDAF (from camera) and Contrast (from ISP)
197- * statistics, but these are plausible upper bounds.
207+ * Reserve space for data structures, to reduce memory fragmentation.
208+ * It's too early to query the size of the PDAF sensor data, so guess.
198209 */
210+ windows_.reserve (1 );
199211 phaseWeights_.w .reserve (16 * 12 );
200212 contrastWeights_.w .reserve (getHardwareConfig ().focusRegions .width *
201213 getHardwareConfig ().focusRegions .height );
214+ contrastWeights_.w .reserve (getHardwareConfig ().awbRegions .width *
215+ getHardwareConfig ().awbRegions .height );
202216 scanData_.reserve (32 );
203217}
204218
@@ -309,6 +323,7 @@ void Af::invalidateWeights()
309323{
310324 phaseWeights_.sum = 0 ;
311325 contrastWeights_.sum = 0 ;
326+ awbWeights_.sum = 0 ;
312327}
313328
314329bool Af::getPhase (PdafRegions const ®ions, double &phase, double &conf)
@@ -365,6 +380,54 @@ double Af::getContrast(const FocusRegions &focusStats)
365380 return (contrastWeights_.sum > 0 ) ? ((double )sumWc / (double )contrastWeights_.sum ) : 0.0 ;
366381}
367382
383+ /*
384+ * Get the average R, G, B values in AF window[s] (from AWB statistics).
385+ * Optionally, check if all of {R,G,B} are within 4:5 of each other
386+ * across more than 50% of the counted area and within the AF window:
387+ * for an RGB sensor this strongly suggests that IR lighting is in use.
388+ */
389+
390+ bool Af::getAverageAndTestIr (const RgbyRegions &awbStats, double rgb[3 ])
391+ {
392+ libcamera::Size size = awbStats.size ();
393+ if (size.height != awbWeights_.rows ||
394+ size.width != awbWeights_.cols || awbWeights_.sum == 0 ) {
395+ LOG (RPiAf, Debug) << " Recompute RGB weights " << size.width << ' x' << size.height ;
396+ computeWeights (&awbWeights_, size.height , size.width );
397+ }
398+
399+ uint64_t sr = 0 , sg = 0 , sb = 0 , sw = 1 ;
400+ uint64_t greyCount = 0 , allCount = 0 ;
401+ for (unsigned i = 0 ; i < awbStats.numRegions (); ++i) {
402+ uint64_t r = awbStats.get (i).val .rSum ;
403+ uint64_t g = awbStats.get (i).val .gSum ;
404+ uint64_t b = awbStats.get (i).val .bSum ;
405+ uint64_t w = awbWeights_.w [i];
406+ if (w) {
407+ sw += w;
408+ sr += w * r;
409+ sg += w * g;
410+ sb += w * b;
411+ }
412+ if (cfg_.checkForIR ) {
413+ if (4 * r < 5 * b && 4 * b < 5 * r &&
414+ 4 * r < 5 * g && 4 * g < 5 * r &&
415+ 4 * b < 5 * g && 4 * g < 5 * b)
416+ greyCount += awbStats.get (i).counted ;
417+ allCount += awbStats.get (i).counted ;
418+ }
419+ }
420+
421+ rgb[0 ] = sr / (double )sw;
422+ rgb[1 ] = sg / (double )sw;
423+ rgb[2 ] = sb / (double )sw;
424+
425+ return (cfg_.checkForIR && 2 * greyCount > allCount &&
426+ 4 * sr < 5 * sb && 4 * sb < 5 * sr &&
427+ 4 * sr < 5 * sg && 4 * sg < 5 * sr &&
428+ 4 * sb < 5 * sg && 4 * sg < 5 * sb);
429+ }
430+
368431void Af::doPDAF (double phase, double conf)
369432{
370433 /* Apply loop gain */
@@ -473,6 +536,8 @@ void Af::doScan(double contrast, double phase, double conf)
473536 if (scanData_.empty () || contrast > scanMaxContrast_) {
474537 scanMaxContrast_ = contrast;
475538 scanMaxIndex_ = scanData_.size ();
539+ if (scanState_ != ScanState::Fine)
540+ std::copy (prevAverage_, prevAverage_ + 3 , oldSceneAverage_);
476541 }
477542 if (contrast < scanMinContrast_)
478543 scanMinContrast_ = contrast;
@@ -523,27 +588,63 @@ void Af::doAF(double contrast, double phase, double conf)
523588 sameSignCount_++;
524589 prevPhase_ = phase;
525590
591+ if (mode_ == AfModeManual)
592+ return ; /* nothing to do */
593+
526594 if (scanState_ == ScanState::Pdaf) {
527595 /*
528596 * Use PDAF closed-loop control whenever available, in both CAF
529597 * mode and (for a limited number of iterations) when triggered.
530- * If PDAF fails (due to poor contrast, noise or large defocus),
531- * fall back to a CDAF-based scan. To avoid "nuisance" scans,
532- * scan only after a number of frames with low PDAF confidence .
598+ * If PDAF fails (due to poor contrast, noise or large defocus)
599+ * for at least dropoutFrames, fall back to a CDAF-based scan
600+ * immediately (in triggered-auto) or on scene change (in CAF) .
533601 */
534- if (conf > (dropCount_ ? 1.0 : 0.25 ) * cfg_.confEpsilon ) {
602+ if (conf >= cfg_.confEpsilon ) {
535603 if (mode_ == AfModeAuto || sameSignCount_ >= 3 )
536604 doPDAF (phase, conf);
537605 if (stepCount_ > 0 )
538606 stepCount_--;
539607 else if (mode_ != AfModeContinuous)
540608 scanState_ = ScanState::Idle;
609+ oldSceneContrast_ = contrast;
610+ std::copy (prevAverage_, prevAverage_ + 3 , oldSceneAverage_);
611+ sceneChangeCount_ = 0 ;
541612 dropCount_ = 0 ;
542- } else if (++dropCount_ == cfg_.speeds [speed_].dropoutFrames )
613+ return ;
614+ } else {
615+ dropCount_++;
616+ if (dropCount_ < cfg_.speeds [speed_].dropoutFrames )
617+ return ;
618+ if (mode_ != AfModeContinuous) {
619+ startProgrammedScan ();
620+ return ;
621+ }
622+ /* else fall through to waiting for a scene change */
623+ }
624+ }
625+ if (scanState_ < ScanState::Coarse && mode_ == AfModeContinuous) {
626+ /*
627+ * In CAF mode, not in a scan, and PDAF is unavailable.
628+ * Wait for a scene change, followed by stability.
629+ */
630+ if (contrast + 1.0 < cfg_.speeds [speed_].retriggerRatio * oldSceneContrast_ ||
631+ oldSceneContrast_ + 1.0 < cfg_.speeds [speed_].retriggerRatio * contrast ||
632+ prevAverage_[0 ] + 1.0 < cfg_.speeds [speed_].retriggerRatio * oldSceneAverage_[0 ] ||
633+ oldSceneAverage_[0 ] + 1.0 < cfg_.speeds [speed_].retriggerRatio * prevAverage_[0 ] ||
634+ prevAverage_[1 ] + 1.0 < cfg_.speeds [speed_].retriggerRatio * oldSceneAverage_[1 ] ||
635+ oldSceneAverage_[1 ] + 1.0 < cfg_.speeds [speed_].retriggerRatio * prevAverage_[1 ] ||
636+ prevAverage_[2 ] + 1.0 < cfg_.speeds [speed_].retriggerRatio * oldSceneAverage_[2 ] ||
637+ oldSceneAverage_[2 ] + 1.0 < cfg_.speeds [speed_].retriggerRatio * prevAverage_[2 ]) {
638+ oldSceneContrast_ = contrast;
639+ std::copy (prevAverage_, prevAverage_ + 3 , oldSceneAverage_);
640+ sceneChangeCount_ = 1 ;
641+ } else if (sceneChangeCount_)
642+ sceneChangeCount_++;
643+ if (sceneChangeCount_ >= cfg_.speeds [speed_].retriggerDelay )
543644 startProgrammedScan ();
544645 } else if (scanState_ >= ScanState::Coarse && fsmooth_ == ftarget_) {
545646 /*
546- * Scanning sequence. This means PDAF has become unavailable .
647+ * CDAF-based scanning sequence .
547648 * Allow a delay between steps for CDAF FoM statistics to be
548649 * updated, and a "settling time" at the end of the sequence.
549650 * [A coarse or fine scan can be abandoned if two PDAF samples
@@ -562,11 +663,14 @@ void Af::doAF(double contrast, double phase, double conf)
562663 scanState_ = ScanState::Pdaf;
563664 else
564665 scanState_ = ScanState::Idle;
666+ dropCount_ = 0 ;
667+ sceneChangeCount_ = 0 ;
668+ oldSceneContrast_ = std::max (scanMaxContrast_, prevContrast_);
565669 scanData_.clear ();
566670 } else if (conf >= cfg_.confThresh && earlyTerminationByPhase (phase)) {
671+ std::copy (prevAverage_, prevAverage_ + 3 , oldSceneAverage_);
567672 scanState_ = ScanState::Settle;
568- stepCount_ = (mode_ == AfModeContinuous) ? 0
569- : cfg_.speeds [speed_].stepFrames ;
673+ stepCount_ = (mode_ == AfModeContinuous) ? 0 : cfg_.speeds [speed_].stepFrames ;
570674 } else
571675 doScan (contrast, phase, conf);
572676 }
@@ -596,7 +700,8 @@ void Af::updateLensPosition()
596700void Af::startAF ()
597701{
598702 /* Use PDAF if the tuning file allows it; else CDAF. */
599- if (cfg_.speeds [speed_].dropoutFrames > 0 &&
703+ if (cfg_.speeds [speed_].pdafGain != 0.0 &&
704+ cfg_.speeds [speed_].dropoutFrames > 0 &&
600705 (mode_ == AfModeContinuous || cfg_.speeds [speed_].pdafFrames > 0 )) {
601706 if (!initted_) {
602707 ftarget_ = cfg_.ranges [range_].focusDefault ;
@@ -606,6 +711,8 @@ void Af::startAF()
606711 scanState_ = ScanState::Pdaf;
607712 scanData_.clear ();
608713 dropCount_ = 0 ;
714+ oldSceneContrast_ = 0.0 ;
715+ sceneChangeCount_ = 0 ;
609716 reportState_ = AfState::Scanning;
610717 } else
611718 startProgrammedScan ();
@@ -656,7 +763,7 @@ void Af::prepare(Metadata *imageMetadata)
656763 uint32_t oldSt = stepCount_;
657764 if (imageMetadata->get (" pdaf.regions" , regions) == 0 )
658765 getPhase (regions, phase, conf);
659- doAF (prevContrast_, phase, conf);
766+ doAF (prevContrast_, phase, irFlag_ ? 0 : conf);
660767 updateLensPosition ();
661768 LOG (RPiAf, Debug) << std::fixed << std::setprecision (2 )
662769 << static_cast <unsigned int >(reportState_)
@@ -666,7 +773,8 @@ void Af::prepare(Metadata *imageMetadata)
666773 << " ft" << oldFt << " ->" << ftarget_
667774 << " fs" << oldFs << " ->" << fsmooth_
668775 << " cont=" << (int )prevContrast_
669- << " phase=" << (int )phase << " conf=" << (int )conf;
776+ << " phase=" << (int )phase << " conf=" << (int )conf
777+ << (irFlag_ ? " IR" : " " );
670778 }
671779
672780 /* Report status and produce new lens setting */
@@ -690,6 +798,7 @@ void Af::process(StatisticsPtr &stats, [[maybe_unused]] Metadata *imageMetadata)
690798{
691799 (void )imageMetadata;
692800 prevContrast_ = getContrast (stats->focusRegions );
801+ irFlag_ = getAverageAndTestIr (stats->awbRegions , prevAverage_);
693802}
694803
695804/* Controls */
0 commit comments