From patchwork Mon Mar 27 12:20:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 18478 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 8FCF3C329F for ; Mon, 27 Mar 2023 12:20:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4783D62752; Mon, 27 Mar 2023 14:20:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1679919655; bh=yr0ktkwDyN3/Tu/8D/ANe+ofHGDWo4sO1gP6XkDzX0Q=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=r4Irz/ox6EDLKqNFZC3M9napDfYeuCBgqR7Zp006bTk8s+T4B6UgbAMTl5Au9eI3X 23SKBujuqwwU24jDX2MoC/VVAVxwJK0SkNGd8osEkrbCIN6RE0KSM7mjmFCzlxIPgT Aft3NeaEe8ZCxGbfPDA5yGYctrhx0sjymfMF6ImS3HwMcTPStvisN+0L5M5yO7UBsG VnIC7GBKO5/JcJethMzekMeAAVD9loeMF2gmI+GQhTS8CnYeCmWLc6HA6fKtBW8X9h cBlIqfNeI5Z9BwJWlkr3Kp9D98b0PnwbcswBmvUaOEVx5Xee9rCClFglO+a/XQXs+Y qsUOu7aUlyopg== Received: from mail-io1-xd30.google.com (mail-io1-xd30.google.com [IPv6:2607:f8b0:4864:20::d30]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5F92762763 for ; Mon, 27 Mar 2023 14:20:51 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="qJAYLiUt"; dkim-atps=neutral Received: by mail-io1-xd30.google.com with SMTP id d14so3784981ion.9 for ; Mon, 27 Mar 2023 05:20:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1679919651; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=F00/QMQd7UwuEW037ja5uCEEW7iXoeIOE6r3lrYMnjg=; b=qJAYLiUtzuKwbluPYRZ2Q51iLVRwnh6ejp9r6En1jTwdRxUDXz7vj4isz/qsjnJNBM 3Zt8G9t5vm0qvqsAS9IdbTVN0LxkTVFBxJzOoqkBGLqOSQVHekWVcxZTZp/jw+iqI+cw 8stpDZvUdEcERWHIRc6/qoYGyqJZNiNrjpJPTHZblVnWwgEXiN+i/++2Ev6eOuL+YMMV OtjYDYWZyj6yTt24Jj2uPQkhMAUrtk1DqqcB1tH7EtMjemNdRIfaBLaCRIQpf6mnpf8d FG/u4u63IvvFFzz6Z83j/mtN/++2Nty5GyjZxpfeFEv3gQ6qTsQJ9pcfkO+Xom4l74jV No4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679919651; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=F00/QMQd7UwuEW037ja5uCEEW7iXoeIOE6r3lrYMnjg=; b=KMFyVTX+Mh9eGER0g7JRwlFqNrcD8traPnxQWIj6w+nWYkE3HTmSA7v1oZR6H42xRh 9/QTxmhEfSU9cIysZOHVpLGVk+N6is3zToRmw2cmeVINdMg1lTyDkL36ztMGIEdtW03X 9XzT37dPCKePIc6UAC9sh8GZd84UWC/ELyy8yyeKNO1/YT/mPvWElFwPqjv1s0n0e5S/ eYtrMU8pTx8Ej5Qw7gBEoKzudOu0GOPXCght+u9XoVNsHugI3J7DMcX/tKpKRi7iU058 r12XvyIdim9FKzVEXA34UiSd8DxQpXaPoJltS2qYWOSWnvTyny4UtSASmP/9oS1szqEh V0hQ== X-Gm-Message-State: AO0yUKWDNsVOlqjB+wKZTWcA2AMgXtJCkk53esWAFV2glp6OfSvbPan6 /kq8uNBmtn36vnOKaw5XypNIcF8TyWGw8VoRKGiUrg== X-Google-Smtp-Source: AK7set8m2QwvI0YscrB6EHp+2DHTC+xNtDUfgLp69wMcVEcFCO/xsKsdclTNfNEB6skv4DM8k199eg== X-Received: by 2002:a6b:f117:0:b0:758:dba1:471c with SMTP id e23-20020a6bf117000000b00758dba1471cmr8876800iog.16.1679919650581; Mon, 27 Mar 2023 05:20:50 -0700 (PDT) Received: from localhost.localdomain ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id r7-20020a5edb47000000b00758ff97aaedsm3239026iop.16.2023.03.27.05.20.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Mar 2023 05:20:50 -0700 (PDT) To: libcamera-devel@lists.libcamera.org Date: Mon, 27 Mar 2023 13:20:29 +0100 Message-Id: <20230327122030.11756-10-naush@raspberrypi.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230327122030.11756-1-naush@raspberrypi.com> References: <20230327122030.11756-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 09/10] ipa: raspberrypi: Generalise the autofocus algorithm X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Naushir Patuck via libcamera-devel From: Naushir Patuck Reply-To: Naushir Patuck Cc: Nick Hollinghurst Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Nick Hollinghurst Remove any hard-coded assumptions about the target hardware platform from the autofocus algorithm. Instead, use the "target" string provided by the camera tuning config and generalised statistics structures to determing parameters such as grid and region sizes. Additionally, PDAF statistics are represented by a generalised region statistics structure to be device agnostic. These changes also require the autofocus algorithm to initialise region weights on the first frame's prepare()/process() call rather than during initialisation. Signed-off-by: Nick Hollinghurst Signed-off-by: Naushir Patuck Tested-by: Naushir Patuck Reviewed-by: Jacopo Mondi --- src/ipa/raspberrypi/cam_helper_imx708.cpp | 23 ++- src/ipa/raspberrypi/controller/pdaf_data.h | 21 +-- src/ipa/raspberrypi/controller/rpi/af.cpp | 176 +++++++++++---------- src/ipa/raspberrypi/controller/rpi/af.h | 28 ++-- 4 files changed, 133 insertions(+), 115 deletions(-) diff --git a/src/ipa/raspberrypi/cam_helper_imx708.cpp b/src/ipa/raspberrypi/cam_helper_imx708.cpp index 1f213d3c0833..641ba18f4b9d 100644 --- a/src/ipa/raspberrypi/cam_helper_imx708.cpp +++ b/src/ipa/raspberrypi/cam_helper_imx708.cpp @@ -69,11 +69,14 @@ private: /* Largest long exposure scale factor given as a left shift on the frame length. */ static constexpr int longExposureShiftMax = 7; + static constexpr int pdafStatsRows = 12; + static constexpr int pdafStatsCols = 16; + void populateMetadata(const MdParser::RegisterMap ®isters, Metadata &metadata) const override; static bool parsePdafData(const uint8_t *ptr, size_t len, unsigned bpp, - PdafData &pdaf); + PdafRegions &pdaf); bool parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp); void putAGCStatistics(StatisticsPtr stats); @@ -120,11 +123,11 @@ void CamHelperImx708::prepare(libcamera::Span buffer, Metadata &m size_t bytesPerLine = (mode_.width * mode_.bitdepth) >> 3; if (buffer.size() > 2 * bytesPerLine) { - PdafData pdaf; + PdafRegions pdaf; if (parsePdafData(&buffer[2 * bytesPerLine], buffer.size() - 2 * bytesPerLine, mode_.bitdepth, pdaf)) - metadata.set("pdaf.data", pdaf); + metadata.set("pdaf.regions", pdaf); } /* Parse AE-HIST data where present */ @@ -239,7 +242,7 @@ void CamHelperImx708::populateMetadata(const MdParser::RegisterMap ®isters, } bool CamHelperImx708::parsePdafData(const uint8_t *ptr, size_t len, - unsigned bpp, PdafData &pdaf) + unsigned bpp, PdafRegions &pdaf) { size_t step = bpp >> 1; /* bytes per PDAF grid entry */ @@ -248,13 +251,17 @@ bool CamHelperImx708::parsePdafData(const uint8_t *ptr, size_t len, return false; } + pdaf.init({ pdafStatsCols, pdafStatsRows }); + ptr += 2 * step; - for (unsigned i = 0; i < PDAF_DATA_ROWS; ++i) { - for (unsigned j = 0; j < PDAF_DATA_COLS; ++j) { + for (unsigned i = 0; i < pdafStatsRows; ++i) { + for (unsigned j = 0; j < pdafStatsCols; ++j) { unsigned c = (ptr[0] << 3) | (ptr[1] >> 5); int p = (((ptr[1] & 0x0F) - (ptr[1] & 0x10)) << 6) | (ptr[2] >> 2); - pdaf.conf[i][j] = c; - pdaf.phase[i][j] = c ? p : 0; + PdafData pdafData; + pdafData.conf = c; + pdafData.phase = c ? p : 0; + pdaf.set(libcamera::Point(j, i), { pdafData, 1, 0 }); ptr += step; } } diff --git a/src/ipa/raspberrypi/controller/pdaf_data.h b/src/ipa/raspberrypi/controller/pdaf_data.h index 03c00d72c0e8..ae6ab996ded0 100644 --- a/src/ipa/raspberrypi/controller/pdaf_data.h +++ b/src/ipa/raspberrypi/controller/pdaf_data.h @@ -2,20 +2,23 @@ /* * Copyright (C) 2022, Raspberry Pi Ltd * - * pdaf_data.h - PDAF Metadata; for now this is - * largely based on IMX708's PDAF "Type 1" output. + * pdaf_data.h - PDAF Metadata */ #pragma once #include -#define PDAF_DATA_ROWS 12 -#define PDAF_DATA_COLS 16 +#include "region_stats.h" -struct PdafData { - /* Confidence values, in raster order, in arbitrary units */ - uint16_t conf[PDAF_DATA_ROWS][PDAF_DATA_COLS]; +namespace RPiController { - /* Phase error, in raster order, in s11 Q4 format (S.6.4) */ - int16_t phase[PDAF_DATA_ROWS][PDAF_DATA_COLS]; +struct PdafData { + /* Confidence, in arbitrary units */ + uint16_t conf; + /* Phase error, in s16 Q4 format (S.11.4) */ + int16_t phase; }; + +using PdafRegions = RegionStats; + +}; // namespace RPiController diff --git a/src/ipa/raspberrypi/controller/rpi/af.cpp b/src/ipa/raspberrypi/controller/rpi/af.cpp index a623651875f2..ed0c8a94d064 100644 --- a/src/ipa/raspberrypi/controller/rpi/af.cpp +++ b/src/ipa/raspberrypi/controller/rpi/af.cpp @@ -174,9 +174,8 @@ Af::Af(Controller *controller) statsRegion_(0, 0, 0, 0), windows_(), useWindows_(false), - phaseWeights_{}, - contrastWeights_{}, - sumWeights_(0), + phaseWeights_(), + contrastWeights_(), scanState_(ScanState::Idle), initted_(false), ftarget_(-1.0), @@ -190,7 +189,15 @@ Af::Af(Controller *controller) scanData_(), reportState_(AfState::Idle) { - scanData_.reserve(24); + /* + * Reserve space for data, to reduce memory fragmentation. It's too early + * to query the size of the PDAF (from camera) and Contrast (from ISP) + * statistics, but these are plausible upper bounds. + */ + phaseWeights_.w.reserve(16 * 12); + contrastWeights_.w.reserve(getHardwareConfig().focusRegions.width * + getHardwareConfig().focusRegions.height); + scanData_.reserve(32); } Af::~Af() @@ -226,7 +233,7 @@ void Af::switchMode(CameraMode const &cameraMode, [[maybe_unused]] Metadata *met << statsRegion_.y << ',' << statsRegion_.width << ',' << statsRegion_.height; - computeWeights(); + invalidateWeights(); if (scanState_ >= ScanState::Coarse && scanState_ < ScanState::Settle) { /* @@ -239,111 +246,99 @@ void Af::switchMode(CameraMode const &cameraMode, [[maybe_unused]] Metadata *met skipCount_ = cfg_.skipFrames; } -void Af::computeWeights() +void Af::computeWeights(RegionWeights *wgts, unsigned rows, unsigned cols) { - constexpr int MaxCellWeight = 240 / (int)MaxWindows; + wgts->rows = rows; + wgts->cols = cols; + wgts->sum = 0; + wgts->w.resize(rows * cols); + std::fill(wgts->w.begin(), wgts->w.end(), 0); - sumWeights_ = 0; - for (int i = 0; i < PDAF_DATA_ROWS; ++i) - std::fill(phaseWeights_[i], phaseWeights_[i] + PDAF_DATA_COLS, 0); - - if (useWindows_ && - statsRegion_.width >= PDAF_DATA_COLS && statsRegion_.height >= PDAF_DATA_ROWS) { + if (rows > 0 && cols > 0 && useWindows_ && + statsRegion_.height >= rows && statsRegion_.width >= cols) { /* * Here we just merge all of the given windows, weighted by area. * \todo Perhaps a better approach might be to find the phase in each * window and choose either the closest or the highest-confidence one? - * - * Using mostly "int" arithmetic, because Rectangle has signed x, y + * Ensure weights sum to less than (1<<16). 46080 is a "round number" + * below 65536, for better rounding when window size is a simple + * fraction of image dimensions. */ - int cellH = (int)(statsRegion_.height / PDAF_DATA_ROWS); - int cellW = (int)(statsRegion_.width / PDAF_DATA_COLS); - int cellA = cellH * cellW; + const unsigned maxCellWeight = 46080u / (MaxWindows * rows * cols); + const unsigned cellH = statsRegion_.height / rows; + const unsigned cellW = statsRegion_.width / cols; + const unsigned cellA = cellH * cellW; for (auto &w : windows_) { - for (int i = 0; i < PDAF_DATA_ROWS; ++i) { - int y0 = std::max(statsRegion_.y + cellH * i, w.y); - int y1 = std::min(statsRegion_.y + cellH * (i + 1), w.y + (int)(w.height)); + for (unsigned r = 0; r < rows; ++r) { + int y0 = std::max(statsRegion_.y + (int)(cellH * r), w.y); + int y1 = std::min(statsRegion_.y + (int)(cellH * (r + 1)), + w.y + (int)(w.height)); if (y0 >= y1) continue; y1 -= y0; - for (int j = 0; j < PDAF_DATA_COLS; ++j) { - int x0 = std::max(statsRegion_.x + cellW * j, w.x); - int x1 = std::min(statsRegion_.x + cellW * (j + 1), w.x + (int)(w.width)); + for (unsigned c = 0; c < cols; ++c) { + int x0 = std::max(statsRegion_.x + (int)(cellW * c), w.x); + int x1 = std::min(statsRegion_.x + (int)(cellW * (c + 1)), + w.x + (int)(w.width)); if (x0 >= x1) continue; - int a = y1 * (x1 - x0); - a = (MaxCellWeight * a + cellA - 1) / cellA; - phaseWeights_[i][j] += a; - sumWeights_ += a; + unsigned a = y1 * (x1 - x0); + a = (maxCellWeight * a + cellA - 1) / cellA; + wgts->w[r * cols + c] += a; + wgts->sum += a; } } } } - if (sumWeights_ == 0) { - /* - * Default AF window is the middle 1/2 width of the middle 1/3 height - * since this maps nicely to both PDAF (16x12) and Focus (4x3) grids. - */ - for (int i = PDAF_DATA_ROWS / 3; i < 2 * PDAF_DATA_ROWS / 3; ++i) { - for (int j = PDAF_DATA_COLS / 4; j < 3 * PDAF_DATA_COLS / 4; ++j) { - phaseWeights_[i][j] = MaxCellWeight; - sumWeights_ += MaxCellWeight; + if (wgts->sum == 0) { + /* Default AF window is the middle 1/2 width of the middle 1/3 height */ + for (unsigned r = rows / 3; r < rows - rows / 3; ++r) { + for (unsigned c = cols / 4; c < cols - cols / 4; ++c) { + wgts->w[r * cols + c] = 1; + wgts->sum += 1; } } } +} - /* Scale from PDAF to Focus Statistics grid (which has fixed size 4x3) */ - constexpr int FocusStatsRows = 3; - constexpr int FocusStatsCols = 4; - static_assert(FOCUS_REGIONS == FocusStatsRows * FocusStatsCols); - static_assert(PDAF_DATA_ROWS % FocusStatsRows == 0); - static_assert(PDAF_DATA_COLS % FocusStatsCols == 0); - constexpr int YFactor = PDAF_DATA_ROWS / FocusStatsRows; - constexpr int XFactor = PDAF_DATA_COLS / FocusStatsCols; - - LOG(RPiAf, Debug) << "Recomputed weights:"; - for (int i = 0; i < FocusStatsRows; ++i) { - for (int j = 0; j < FocusStatsCols; ++j) { - unsigned w = 0; - for (int y = 0; y < YFactor; ++y) - for (int x = 0; x < XFactor; ++x) - w += phaseWeights_[YFactor * i + y][XFactor * j + x]; - contrastWeights_[FocusStatsCols * i + j] = w; - } - LOG(RPiAf, Debug) << " " - << contrastWeights_[FocusStatsCols * i + 0] << " " - << contrastWeights_[FocusStatsCols * i + 1] << " " - << contrastWeights_[FocusStatsCols * i + 2] << " " - << contrastWeights_[FocusStatsCols * i + 3]; - } +void Af::invalidateWeights() +{ + phaseWeights_.sum = 0; + contrastWeights_.sum = 0; } -bool Af::getPhase(PdafData const &data, double &phase, double &conf) const +bool Af::getPhase(PdafRegions const ®ions, double &phase, double &conf) { + libcamera::Size size = regions.size(); + if (size.height != phaseWeights_.rows || size.width != phaseWeights_.cols || + phaseWeights_.sum == 0) { + LOG(RPiAf, Debug) << "Recompute Phase weights " << size.width << 'x' << size.height; + computeWeights(&phaseWeights_, size.height, size.width); + } + uint32_t sumWc = 0; int64_t sumWcp = 0; - - for (unsigned i = 0; i < PDAF_DATA_ROWS; ++i) { - for (unsigned j = 0; j < PDAF_DATA_COLS; ++j) { - if (phaseWeights_[i][j]) { - uint32_t c = data.conf[i][j]; - if (c >= cfg_.confThresh) { - if (c > cfg_.confClip) - c = cfg_.confClip; - c -= (cfg_.confThresh >> 2); - sumWc += phaseWeights_[i][j] * c; - c -= (cfg_.confThresh >> 2); - sumWcp += phaseWeights_[i][j] * data.phase[i][j] * (int64_t)c; - } + for (unsigned i = 0; i < regions.numRegions(); ++i) { + unsigned w = phaseWeights_.w[i]; + if (w) { + const PdafData &data = regions.get(i).val; + unsigned c = data.conf; + if (c >= cfg_.confThresh) { + if (c > cfg_.confClip) + c = cfg_.confClip; + c -= (cfg_.confThresh >> 2); + sumWc += w * c; + c -= (cfg_.confThresh >> 2); + sumWcp += (int64_t)(w * c) * (int64_t)data.phase; } } } - if (0 < sumWeights_ && sumWeights_ <= sumWc) { + if (0 < phaseWeights_.sum && phaseWeights_.sum <= sumWc) { phase = (double)sumWcp / (double)sumWc; - conf = (double)sumWc / (double)sumWeights_; + conf = (double)sumWc / (double)phaseWeights_.sum; return true; } else { phase = 0.0; @@ -352,14 +347,21 @@ bool Af::getPhase(PdafData const &data, double &phase, double &conf) const } } -double Af::getContrast(const FocusRegions &focusStats) const +double Af::getContrast(const FocusRegions &focusStats) { - uint32_t sumWc = 0; + libcamera::Size size = focusStats.size(); + if (size.height != contrastWeights_.rows || + size.width != contrastWeights_.cols || contrastWeights_.sum == 0) { + LOG(RPiAf, Debug) << "Recompute Contrast weights " + << size.width << 'x' << size.height; + computeWeights(&contrastWeights_, size.height, size.width); + } + uint64_t sumWc = 0; for (unsigned i = 0; i < focusStats.numRegions(); ++i) - sumWc += contrastWeights_[i] * focusStats.get(i).val; + sumWc += contrastWeights_.w[i] * focusStats.get(i).val; - return (sumWeights_ == 0) ? 0.0 : (double)sumWc / (double)sumWeights_; + return (contrastWeights_.sum > 0) ? ((double)sumWc / (double)contrastWeights_.sum) : 0.0; } void Af::doPDAF(double phase, double conf) @@ -623,14 +625,14 @@ void Af::prepare(Metadata *imageMetadata) if (initted_) { /* Get PDAF from the embedded metadata, and run AF algorithm core */ - PdafData data; + PdafRegions regions; double phase = 0.0, conf = 0.0; double oldFt = ftarget_; double oldFs = fsmooth_; ScanState oldSs = scanState_; uint32_t oldSt = stepCount_; - if (imageMetadata->get("pdaf.data", data) == 0) - getPhase(data, phase, conf); + if (imageMetadata->get("pdaf.regions", regions) == 0) + getPhase(regions, phase, conf); doAF(prevContrast_, phase, conf); updateLensPosition(); LOG(RPiAf, Debug) << std::fixed << std::setprecision(2) @@ -691,7 +693,7 @@ void Af::setMetering(bool mode) { if (useWindows_ != mode) { useWindows_ = mode; - computeWeights(); + invalidateWeights(); } } @@ -708,7 +710,9 @@ void Af::setWindows(libcamera::Span const &wins) if (windows_.size() >= MaxWindows) break; } - computeWeights(); + + if (useWindows_) + invalidateWeights(); } bool Af::setLensPosition(double dioptres, int *hwpos) diff --git a/src/ipa/raspberrypi/controller/rpi/af.h b/src/ipa/raspberrypi/controller/rpi/af.h index 7959371baf64..b479feb88c39 100644 --- a/src/ipa/raspberrypi/controller/rpi/af.h +++ b/src/ipa/raspberrypi/controller/rpi/af.h @@ -11,12 +11,6 @@ #include "../pdaf_data.h" #include "../pwl.h" -/* - * \todo FOCUS_REGIONS is taken from bcm2835-isp.h, but should be made as a - * generic RegionStats structure. - */ -#define FOCUS_REGIONS 12 - /* * This algorithm implements a hybrid of CDAF and PDAF, favouring PDAF. * @@ -121,9 +115,20 @@ private: double conf; }; - void computeWeights(); - bool getPhase(PdafData const &data, double &phase, double &conf) const; - double getContrast(const FocusRegions &focusStats) const; + struct RegionWeights { + unsigned rows; + unsigned cols; + uint32_t sum; + std::vector w; + + RegionWeights() + : rows(0), cols(0), sum(0), w() {} + }; + + void computeWeights(RegionWeights *wgts, unsigned rows, unsigned cols); + void invalidateWeights(); + bool getPhase(PdafRegions const ®ions, double &phase, double &conf); + double getContrast(const FocusRegions &focusStats); void doPDAF(double phase, double conf); bool earlyTerminationByPhase(double phase); double findPeak(unsigned index) const; @@ -143,9 +148,8 @@ private: libcamera::Rectangle statsRegion_; std::vector windows_; bool useWindows_; - uint8_t phaseWeights_[PDAF_DATA_ROWS][PDAF_DATA_COLS]; - uint16_t contrastWeights_[FOCUS_REGIONS]; - uint32_t sumWeights_; + RegionWeights phaseWeights_; + RegionWeights contrastWeights_; /* Working state. */ ScanState scanState_;