[{"id":26797,"web_url":"https://patchwork.libcamera.org/comment/26797/","msgid":"<20230330181609.cpp7h4iaaevtxyui@uno.localdomain>","date":"2023-03-30T18:16:09","subject":"Re: [libcamera-devel] [PATCH v2 09/10] ipa: raspberrypi: Generalise\n\tthe autofocus algorithm","submitter":{"id":143,"url":"https://patchwork.libcamera.org/api/people/143/","name":"Jacopo Mondi","email":"jacopo.mondi@ideasonboard.com"},"content":"Hi Nick\n\nOn Mon, Mar 27, 2023 at 01:20:29PM +0100, Naushir Patuck via libcamera-devel wrote:\n> From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>\n>\n> Remove any hard-coded assumptions about the target hardware platform\n> from the autofocus algorithm. Instead, use the \"target\" string provided\n> by the camera tuning config and generalised statistics structures to\n> determing parameters such as grid and region sizes.\n>\n> Additionally, PDAF statistics are represented by a generalised region\n> statistics structure to be device agnostic.\n>\n> These changes also require the autofocus algorithm to initialise\n> region weights on the first frame's prepare()/process() call rather\n> than during initialisation.\n>\n> Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>\n> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> Tested-by: Naushir Patuck <naush@raspberrypi.com>\n\nI won't pretend I've actually gone through the calculations, but the\nfew comments I had on v1 have been clarified\n\nReviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n\nThanks\n  j\n\n> ---\n>  src/ipa/raspberrypi/cam_helper_imx708.cpp  |  23 ++-\n>  src/ipa/raspberrypi/controller/pdaf_data.h |  21 +--\n>  src/ipa/raspberrypi/controller/rpi/af.cpp  | 176 +++++++++++----------\n>  src/ipa/raspberrypi/controller/rpi/af.h    |  28 ++--\n>  4 files changed, 133 insertions(+), 115 deletions(-)\n>\n> diff --git a/src/ipa/raspberrypi/cam_helper_imx708.cpp b/src/ipa/raspberrypi/cam_helper_imx708.cpp\n> index 1f213d3c0833..641ba18f4b9d 100644\n> --- a/src/ipa/raspberrypi/cam_helper_imx708.cpp\n> +++ b/src/ipa/raspberrypi/cam_helper_imx708.cpp\n> @@ -69,11 +69,14 @@ private:\n>  \t/* Largest long exposure scale factor given as a left shift on the frame length. */\n>  \tstatic constexpr int longExposureShiftMax = 7;\n>\n> +\tstatic constexpr int pdafStatsRows = 12;\n> +\tstatic constexpr int pdafStatsCols = 16;\n> +\n>  \tvoid populateMetadata(const MdParser::RegisterMap &registers,\n>  \t\t\t      Metadata &metadata) const override;\n>\n>  \tstatic bool parsePdafData(const uint8_t *ptr, size_t len, unsigned bpp,\n> -\t\t\t\t  PdafData &pdaf);\n> +\t\t\t\t  PdafRegions &pdaf);\n>\n>  \tbool parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp);\n>  \tvoid putAGCStatistics(StatisticsPtr stats);\n> @@ -120,11 +123,11 @@ void CamHelperImx708::prepare(libcamera::Span<const uint8_t> buffer, Metadata &m\n>  \tsize_t bytesPerLine = (mode_.width * mode_.bitdepth) >> 3;\n>\n>  \tif (buffer.size() > 2 * bytesPerLine) {\n> -\t\tPdafData pdaf;\n> +\t\tPdafRegions pdaf;\n>  \t\tif (parsePdafData(&buffer[2 * bytesPerLine],\n>  \t\t\t\t  buffer.size() - 2 * bytesPerLine,\n>  \t\t\t\t  mode_.bitdepth, pdaf))\n> -\t\t\tmetadata.set(\"pdaf.data\", pdaf);\n> +\t\t\tmetadata.set(\"pdaf.regions\", pdaf);\n>  \t}\n>\n>  \t/* Parse AE-HIST data where present */\n> @@ -239,7 +242,7 @@ void CamHelperImx708::populateMetadata(const MdParser::RegisterMap &registers,\n>  }\n>\n>  bool CamHelperImx708::parsePdafData(const uint8_t *ptr, size_t len,\n> -\t\t\t\t    unsigned bpp, PdafData &pdaf)\n> +\t\t\t\t    unsigned bpp, PdafRegions &pdaf)\n>  {\n>  \tsize_t step = bpp >> 1; /* bytes per PDAF grid entry */\n>\n> @@ -248,13 +251,17 @@ bool CamHelperImx708::parsePdafData(const uint8_t *ptr, size_t len,\n>  \t\treturn false;\n>  \t}\n>\n> +\tpdaf.init({ pdafStatsCols, pdafStatsRows });\n> +\n>  \tptr += 2 * step;\n> -\tfor (unsigned i = 0; i < PDAF_DATA_ROWS; ++i) {\n> -\t\tfor (unsigned j = 0; j < PDAF_DATA_COLS; ++j) {\n> +\tfor (unsigned i = 0; i < pdafStatsRows; ++i) {\n> +\t\tfor (unsigned j = 0; j < pdafStatsCols; ++j) {\n>  \t\t\tunsigned c = (ptr[0] << 3) | (ptr[1] >> 5);\n>  \t\t\tint p = (((ptr[1] & 0x0F) - (ptr[1] & 0x10)) << 6) | (ptr[2] >> 2);\n> -\t\t\tpdaf.conf[i][j] = c;\n> -\t\t\tpdaf.phase[i][j] = c ? p : 0;\n> +\t\t\tPdafData pdafData;\n> +\t\t\tpdafData.conf = c;\n> +\t\t\tpdafData.phase = c ? p : 0;\n> +\t\t\tpdaf.set(libcamera::Point(j, i), { pdafData, 1, 0 });\n>  \t\t\tptr += step;\n>  \t\t}\n>  \t}\n> diff --git a/src/ipa/raspberrypi/controller/pdaf_data.h b/src/ipa/raspberrypi/controller/pdaf_data.h\n> index 03c00d72c0e8..ae6ab996ded0 100644\n> --- a/src/ipa/raspberrypi/controller/pdaf_data.h\n> +++ b/src/ipa/raspberrypi/controller/pdaf_data.h\n> @@ -2,20 +2,23 @@\n>  /*\n>   * Copyright (C) 2022, Raspberry Pi Ltd\n>   *\n> - * pdaf_data.h - PDAF Metadata; for now this is\n> - * largely based on IMX708's PDAF \"Type 1\" output.\n> + * pdaf_data.h - PDAF Metadata\n>   */\n>  #pragma once\n>\n>  #include <stdint.h>\n>\n> -#define PDAF_DATA_ROWS 12\n> -#define PDAF_DATA_COLS 16\n> +#include \"region_stats.h\"\n>\n> -struct PdafData {\n> -\t/* Confidence values, in raster order, in arbitrary units */\n> -\tuint16_t conf[PDAF_DATA_ROWS][PDAF_DATA_COLS];\n> +namespace RPiController {\n>\n> -\t/* Phase error, in raster order, in s11 Q4 format (S.6.4) */\n> -\tint16_t phase[PDAF_DATA_ROWS][PDAF_DATA_COLS];\n> +struct PdafData {\n> +\t/* Confidence, in arbitrary units */\n> +\tuint16_t conf;\n> +\t/* Phase error, in s16 Q4 format (S.11.4) */\n> +\tint16_t phase;\n>  };\n> +\n> +using PdafRegions = RegionStats<PdafData>;\n> +\n> +}; // namespace RPiController\n> diff --git a/src/ipa/raspberrypi/controller/rpi/af.cpp b/src/ipa/raspberrypi/controller/rpi/af.cpp\n> index a623651875f2..ed0c8a94d064 100644\n> --- a/src/ipa/raspberrypi/controller/rpi/af.cpp\n> +++ b/src/ipa/raspberrypi/controller/rpi/af.cpp\n> @@ -174,9 +174,8 @@ Af::Af(Controller *controller)\n>  \t  statsRegion_(0, 0, 0, 0),\n>  \t  windows_(),\n>  \t  useWindows_(false),\n> -\t  phaseWeights_{},\n> -\t  contrastWeights_{},\n> -\t  sumWeights_(0),\n> +\t  phaseWeights_(),\n> +\t  contrastWeights_(),\n>  \t  scanState_(ScanState::Idle),\n>  \t  initted_(false),\n>  \t  ftarget_(-1.0),\n> @@ -190,7 +189,15 @@ Af::Af(Controller *controller)\n>  \t  scanData_(),\n>  \t  reportState_(AfState::Idle)\n>  {\n> -\tscanData_.reserve(24);\n> +\t/*\n> +\t * Reserve space for data, to reduce memory fragmentation. It's too early\n> +\t * to query the size of the PDAF (from camera) and Contrast (from ISP)\n> +\t * statistics, but these are plausible upper bounds.\n> +\t */\n> +\tphaseWeights_.w.reserve(16 * 12);\n> +\tcontrastWeights_.w.reserve(getHardwareConfig().focusRegions.width *\n> +\t\t\t\t   getHardwareConfig().focusRegions.height);\n> +\tscanData_.reserve(32);\n>  }\n>\n>  Af::~Af()\n> @@ -226,7 +233,7 @@ void Af::switchMode(CameraMode const &cameraMode, [[maybe_unused]] Metadata *met\n>  \t\t\t  << statsRegion_.y << ','\n>  \t\t\t  << statsRegion_.width << ','\n>  \t\t\t  << statsRegion_.height;\n> -\tcomputeWeights();\n> +\tinvalidateWeights();\n>\n>  \tif (scanState_ >= ScanState::Coarse && scanState_ < ScanState::Settle) {\n>  \t\t/*\n> @@ -239,111 +246,99 @@ void Af::switchMode(CameraMode const &cameraMode, [[maybe_unused]] Metadata *met\n>  \tskipCount_ = cfg_.skipFrames;\n>  }\n>\n> -void Af::computeWeights()\n> +void Af::computeWeights(RegionWeights *wgts, unsigned rows, unsigned cols)\n>  {\n> -\tconstexpr int MaxCellWeight = 240 / (int)MaxWindows;\n> +\twgts->rows = rows;\n> +\twgts->cols = cols;\n> +\twgts->sum = 0;\n> +\twgts->w.resize(rows * cols);\n> +\tstd::fill(wgts->w.begin(), wgts->w.end(), 0);\n>\n> -\tsumWeights_ = 0;\n> -\tfor (int i = 0; i < PDAF_DATA_ROWS; ++i)\n> -\t\tstd::fill(phaseWeights_[i], phaseWeights_[i] + PDAF_DATA_COLS, 0);\n> -\n> -\tif (useWindows_ &&\n> -\t    statsRegion_.width >= PDAF_DATA_COLS && statsRegion_.height >= PDAF_DATA_ROWS) {\n> +\tif (rows > 0 && cols > 0 && useWindows_ &&\n> +\t    statsRegion_.height >= rows && statsRegion_.width >= cols) {\n>  \t\t/*\n>  \t\t * Here we just merge all of the given windows, weighted by area.\n>  \t\t * \\todo Perhaps a better approach might be to find the phase in each\n>  \t\t * window and choose either the closest or the highest-confidence one?\n> -\t\t *\n> -\t\t * Using mostly \"int\" arithmetic, because Rectangle has signed x, y\n> +\t\t * Ensure weights sum to less than (1<<16). 46080 is a \"round number\"\n> +\t\t * below 65536, for better rounding when window size is a simple\n> +\t\t * fraction of image dimensions.\n>  \t\t */\n> -\t\tint cellH = (int)(statsRegion_.height / PDAF_DATA_ROWS);\n> -\t\tint cellW = (int)(statsRegion_.width / PDAF_DATA_COLS);\n> -\t\tint cellA = cellH * cellW;\n> +\t\tconst unsigned maxCellWeight = 46080u / (MaxWindows * rows * cols);\n> +\t\tconst unsigned cellH = statsRegion_.height / rows;\n> +\t\tconst unsigned cellW = statsRegion_.width / cols;\n> +\t\tconst unsigned cellA = cellH * cellW;\n>\n>  \t\tfor (auto &w : windows_) {\n> -\t\t\tfor (int i = 0; i < PDAF_DATA_ROWS; ++i) {\n> -\t\t\t\tint y0 = std::max(statsRegion_.y + cellH * i, w.y);\n> -\t\t\t\tint y1 = std::min(statsRegion_.y + cellH * (i + 1), w.y + (int)(w.height));\n> +\t\t\tfor (unsigned r = 0; r < rows; ++r) {\n> +\t\t\t\tint y0 = std::max(statsRegion_.y + (int)(cellH * r), w.y);\n> +\t\t\t\tint y1 = std::min(statsRegion_.y + (int)(cellH * (r + 1)),\n> +\t\t\t\t\t\t  w.y + (int)(w.height));\n>  \t\t\t\tif (y0 >= y1)\n>  \t\t\t\t\tcontinue;\n>  \t\t\t\ty1 -= y0;\n> -\t\t\t\tfor (int j = 0; j < PDAF_DATA_COLS; ++j) {\n> -\t\t\t\t\tint x0 = std::max(statsRegion_.x + cellW * j, w.x);\n> -\t\t\t\t\tint x1 = std::min(statsRegion_.x + cellW * (j + 1), w.x + (int)(w.width));\n> +\t\t\t\tfor (unsigned c = 0; c < cols; ++c) {\n> +\t\t\t\t\tint x0 = std::max(statsRegion_.x + (int)(cellW * c), w.x);\n> +\t\t\t\t\tint x1 = std::min(statsRegion_.x + (int)(cellW * (c + 1)),\n> +\t\t\t\t\t\t\t  w.x + (int)(w.width));\n>  \t\t\t\t\tif (x0 >= x1)\n>  \t\t\t\t\t\tcontinue;\n> -\t\t\t\t\tint a = y1 * (x1 - x0);\n> -\t\t\t\t\ta = (MaxCellWeight * a + cellA - 1) / cellA;\n> -\t\t\t\t\tphaseWeights_[i][j] += a;\n> -\t\t\t\t\tsumWeights_ += a;\n> +\t\t\t\t\tunsigned a = y1 * (x1 - x0);\n> +\t\t\t\t\ta = (maxCellWeight * a + cellA - 1) / cellA;\n> +\t\t\t\t\twgts->w[r * cols + c] += a;\n> +\t\t\t\t\twgts->sum += a;\n>  \t\t\t\t}\n>  \t\t\t}\n>  \t\t}\n>  \t}\n>\n> -\tif (sumWeights_ == 0) {\n> -\t\t/*\n> -\t\t * Default AF window is the middle 1/2 width of the middle 1/3 height\n> -\t\t * since this maps nicely to both PDAF (16x12) and Focus (4x3) grids.\n> -\t\t */\n> -\t\tfor (int i = PDAF_DATA_ROWS / 3; i < 2 * PDAF_DATA_ROWS / 3; ++i) {\n> -\t\t\tfor (int j = PDAF_DATA_COLS / 4; j < 3 * PDAF_DATA_COLS / 4; ++j) {\n> -\t\t\t\tphaseWeights_[i][j] = MaxCellWeight;\n> -\t\t\t\tsumWeights_ += MaxCellWeight;\n> +\tif (wgts->sum == 0) {\n> +\t\t/* Default AF window is the middle 1/2 width of the middle 1/3 height */\n> +\t\tfor (unsigned r = rows / 3; r < rows - rows / 3; ++r) {\n> +\t\t\tfor (unsigned c = cols / 4; c < cols - cols / 4; ++c) {\n> +\t\t\t\twgts->w[r * cols + c] = 1;\n> +\t\t\t\twgts->sum += 1;\n>  \t\t\t}\n>  \t\t}\n>  \t}\n> +}\n>\n> -\t/* Scale from PDAF to Focus Statistics grid (which has fixed size 4x3) */\n> -\tconstexpr int FocusStatsRows = 3;\n> -\tconstexpr int FocusStatsCols = 4;\n> -\tstatic_assert(FOCUS_REGIONS == FocusStatsRows * FocusStatsCols);\n> -\tstatic_assert(PDAF_DATA_ROWS % FocusStatsRows == 0);\n> -\tstatic_assert(PDAF_DATA_COLS % FocusStatsCols == 0);\n> -\tconstexpr int YFactor = PDAF_DATA_ROWS / FocusStatsRows;\n> -\tconstexpr int XFactor = PDAF_DATA_COLS / FocusStatsCols;\n> -\n> -\tLOG(RPiAf, Debug) << \"Recomputed weights:\";\n> -\tfor (int i = 0; i < FocusStatsRows; ++i) {\n> -\t\tfor (int j = 0; j < FocusStatsCols; ++j) {\n> -\t\t\tunsigned w = 0;\n> -\t\t\tfor (int y = 0; y < YFactor; ++y)\n> -\t\t\t\tfor (int x = 0; x < XFactor; ++x)\n> -\t\t\t\t\tw += phaseWeights_[YFactor * i + y][XFactor * j + x];\n> -\t\t\tcontrastWeights_[FocusStatsCols * i + j] = w;\n> -\t\t}\n> -\t\tLOG(RPiAf, Debug) << \"   \"\n> -\t\t\t\t  << contrastWeights_[FocusStatsCols * i + 0] << \" \"\n> -\t\t\t\t  << contrastWeights_[FocusStatsCols * i + 1] << \" \"\n> -\t\t\t\t  << contrastWeights_[FocusStatsCols * i + 2] << \" \"\n> -\t\t\t\t  << contrastWeights_[FocusStatsCols * i + 3];\n> -\t}\n> +void Af::invalidateWeights()\n> +{\n> +\tphaseWeights_.sum = 0;\n> +\tcontrastWeights_.sum = 0;\n>  }\n>\n> -bool Af::getPhase(PdafData const &data, double &phase, double &conf) const\n> +bool Af::getPhase(PdafRegions const &regions, double &phase, double &conf)\n>  {\n> +\tlibcamera::Size size = regions.size();\n> +\tif (size.height != phaseWeights_.rows || size.width != phaseWeights_.cols ||\n> +\t    phaseWeights_.sum == 0) {\n> +\t\tLOG(RPiAf, Debug) << \"Recompute Phase weights \" << size.width << 'x' << size.height;\n> +\t\tcomputeWeights(&phaseWeights_, size.height, size.width);\n> +\t}\n> +\n>  \tuint32_t sumWc = 0;\n>  \tint64_t sumWcp = 0;\n> -\n> -\tfor (unsigned i = 0; i < PDAF_DATA_ROWS; ++i) {\n> -\t\tfor (unsigned j = 0; j < PDAF_DATA_COLS; ++j) {\n> -\t\t\tif (phaseWeights_[i][j]) {\n> -\t\t\t\tuint32_t c = data.conf[i][j];\n> -\t\t\t\tif (c >= cfg_.confThresh) {\n> -\t\t\t\t\tif (c > cfg_.confClip)\n> -\t\t\t\t\t\tc = cfg_.confClip;\n> -\t\t\t\t\tc -= (cfg_.confThresh >> 2);\n> -\t\t\t\t\tsumWc += phaseWeights_[i][j] * c;\n> -\t\t\t\t\tc -= (cfg_.confThresh >> 2);\n> -\t\t\t\t\tsumWcp += phaseWeights_[i][j] * data.phase[i][j] * (int64_t)c;\n> -\t\t\t\t}\n> +\tfor (unsigned i = 0; i < regions.numRegions(); ++i) {\n> +\t\tunsigned w = phaseWeights_.w[i];\n> +\t\tif (w) {\n> +\t\t\tconst PdafData &data = regions.get(i).val;\n> +\t\t\tunsigned c = data.conf;\n> +\t\t\tif (c >= cfg_.confThresh) {\n> +\t\t\t\tif (c > cfg_.confClip)\n> +\t\t\t\t\tc = cfg_.confClip;\n> +\t\t\t\tc -= (cfg_.confThresh >> 2);\n> +\t\t\t\tsumWc += w * c;\n> +\t\t\t\tc -= (cfg_.confThresh >> 2);\n> +\t\t\t\tsumWcp += (int64_t)(w * c) * (int64_t)data.phase;\n>  \t\t\t}\n>  \t\t}\n>  \t}\n>\n> -\tif (0 < sumWeights_ && sumWeights_ <= sumWc) {\n> +\tif (0 < phaseWeights_.sum && phaseWeights_.sum <= sumWc) {\n>  \t\tphase = (double)sumWcp / (double)sumWc;\n> -\t\tconf = (double)sumWc / (double)sumWeights_;\n> +\t\tconf = (double)sumWc / (double)phaseWeights_.sum;\n>  \t\treturn true;\n>  \t} else {\n>  \t\tphase = 0.0;\n> @@ -352,14 +347,21 @@ bool Af::getPhase(PdafData const &data, double &phase, double &conf) const\n>  \t}\n>  }\n>\n> -double Af::getContrast(const FocusRegions &focusStats) const\n> +double Af::getContrast(const FocusRegions &focusStats)\n>  {\n> -\tuint32_t sumWc = 0;\n> +\tlibcamera::Size size = focusStats.size();\n> +\tif (size.height != contrastWeights_.rows ||\n> +\t    size.width != contrastWeights_.cols || contrastWeights_.sum == 0) {\n> +\t\tLOG(RPiAf, Debug) << \"Recompute Contrast weights \"\n> +\t\t\t\t  << size.width << 'x' << size.height;\n> +\t\tcomputeWeights(&contrastWeights_, size.height, size.width);\n> +\t}\n>\n> +\tuint64_t sumWc = 0;\n>  \tfor (unsigned i = 0; i < focusStats.numRegions(); ++i)\n> -\t\tsumWc += contrastWeights_[i] * focusStats.get(i).val;\n> +\t\tsumWc += contrastWeights_.w[i] * focusStats.get(i).val;\n>\n> -\treturn (sumWeights_ == 0) ? 0.0 : (double)sumWc / (double)sumWeights_;\n> +\treturn (contrastWeights_.sum > 0) ? ((double)sumWc / (double)contrastWeights_.sum) : 0.0;\n>  }\n>\n>  void Af::doPDAF(double phase, double conf)\n> @@ -623,14 +625,14 @@ void Af::prepare(Metadata *imageMetadata)\n>\n>  \tif (initted_) {\n>  \t\t/* Get PDAF from the embedded metadata, and run AF algorithm core */\n> -\t\tPdafData data;\n> +\t\tPdafRegions regions;\n>  \t\tdouble phase = 0.0, conf = 0.0;\n>  \t\tdouble oldFt = ftarget_;\n>  \t\tdouble oldFs = fsmooth_;\n>  \t\tScanState oldSs = scanState_;\n>  \t\tuint32_t oldSt = stepCount_;\n> -\t\tif (imageMetadata->get(\"pdaf.data\", data) == 0)\n> -\t\t\tgetPhase(data, phase, conf);\n> +\t\tif (imageMetadata->get(\"pdaf.regions\", regions) == 0)\n> +\t\t\tgetPhase(regions, phase, conf);\n>  \t\tdoAF(prevContrast_, phase, conf);\n>  \t\tupdateLensPosition();\n>  \t\tLOG(RPiAf, Debug) << std::fixed << std::setprecision(2)\n> @@ -691,7 +693,7 @@ void Af::setMetering(bool mode)\n>  {\n>  \tif (useWindows_ != mode) {\n>  \t\tuseWindows_ = mode;\n> -\t\tcomputeWeights();\n> +\t\tinvalidateWeights();\n>  \t}\n>  }\n>\n> @@ -708,7 +710,9 @@ void Af::setWindows(libcamera::Span<libcamera::Rectangle const> const &wins)\n>  \t\tif (windows_.size() >= MaxWindows)\n>  \t\t\tbreak;\n>  \t}\n> -\tcomputeWeights();\n> +\n> +\tif (useWindows_)\n> +\t\tinvalidateWeights();\n>  }\n>\n>  bool Af::setLensPosition(double dioptres, int *hwpos)\n> diff --git a/src/ipa/raspberrypi/controller/rpi/af.h b/src/ipa/raspberrypi/controller/rpi/af.h\n> index 7959371baf64..b479feb88c39 100644\n> --- a/src/ipa/raspberrypi/controller/rpi/af.h\n> +++ b/src/ipa/raspberrypi/controller/rpi/af.h\n> @@ -11,12 +11,6 @@\n>  #include \"../pdaf_data.h\"\n>  #include \"../pwl.h\"\n>\n> -/*\n> - * \\todo FOCUS_REGIONS is taken from bcm2835-isp.h, but should be made as a\n> - * generic RegionStats structure.\n> - */\n> -#define FOCUS_REGIONS 12\n> -\n>  /*\n>   * This algorithm implements a hybrid of CDAF and PDAF, favouring PDAF.\n>   *\n> @@ -121,9 +115,20 @@ private:\n>  \t\tdouble conf;\n>  \t};\n>\n> -\tvoid computeWeights();\n> -\tbool getPhase(PdafData const &data, double &phase, double &conf) const;\n> -\tdouble getContrast(const FocusRegions &focusStats) const;\n> +\tstruct RegionWeights {\n> +\t\tunsigned rows;\n> +\t\tunsigned cols;\n> +\t\tuint32_t sum;\n> +\t\tstd::vector<uint16_t> w;\n> +\n> +\t\tRegionWeights()\n> +\t\t\t: rows(0), cols(0), sum(0), w() {}\n> +\t};\n> +\n> +\tvoid computeWeights(RegionWeights *wgts, unsigned rows, unsigned cols);\n> +\tvoid invalidateWeights();\n> +\tbool getPhase(PdafRegions const &regions, double &phase, double &conf);\n> +\tdouble getContrast(const FocusRegions &focusStats);\n>  \tvoid doPDAF(double phase, double conf);\n>  \tbool earlyTerminationByPhase(double phase);\n>  \tdouble findPeak(unsigned index) const;\n> @@ -143,9 +148,8 @@ private:\n>  \tlibcamera::Rectangle statsRegion_;\n>  \tstd::vector<libcamera::Rectangle> windows_;\n>  \tbool useWindows_;\n> -\tuint8_t phaseWeights_[PDAF_DATA_ROWS][PDAF_DATA_COLS];\n> -\tuint16_t contrastWeights_[FOCUS_REGIONS];\n> -\tuint32_t sumWeights_;\n> +\tRegionWeights phaseWeights_;\n> +\tRegionWeights contrastWeights_;\n>\n>  \t/* Working state. */\n>  \tScanState scanState_;\n> --\n> 2.34.1\n>","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 B06F1C326B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 30 Mar 2023 18:16:14 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1A6FF626E2;\n\tThu, 30 Mar 2023 20:16:14 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A662E61EC7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 30 Mar 2023 20:16:12 +0200 (CEST)","from ideasonboard.com (93-61-96-190.ip145.fastwebnet.it\n\t[93.61.96.190])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 38DD06E0;\n\tThu, 30 Mar 2023 20:16:12 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1680200174;\n\tbh=xp3UrVjlqpnqZkxTDJfIBZHCiG5PdNdkxQWn0NX3ENs=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=bElipPgtJFsvYDpcmx0qSEhJB5k1rahSuAOSKHOYZ8i6R9KmxwSa7F+LDvUECBV2z\n\t+MG9x6v/bnR1RLdknmJvoaBu7jdZVOBaRmYc04pmEifS/3jKYfVMJEQ9DpiTlgmAQ3\n\tT72TgojaBDs84SVRtx498ETBqvFleEQ6Q4MvCySXwGJOKlvJkE9gJtt0ExZrMDj6zz\n\tBzmqIT3hRnN2cykYdep/EMIGqwTdLV7GMcvNewvzu0byvo49rbRJfau8c27KGMwSjG\n\tsrNOKko9RHo5MPEgFgrTKBy4lIJJWMdMrLU8yXVquNy5wFBx3MznxA9hZB4QXwa6WA\n\t9pZiYEvAuJbXg==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1680200172;\n\tbh=xp3UrVjlqpnqZkxTDJfIBZHCiG5PdNdkxQWn0NX3ENs=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=PIWP4q6HYwHhbIiwAYJWlxHWzXbQVvzhmS95tZld+LVtVrYfh2wEalQD18S+Om8Tq\n\t0rUxkNhMNwf8HcLaFqEysNHPpKK/zlvn5gWOue/JwXn/MHMBgXeuojBnrf8ASes7Qc\n\tqFxI4nXuFd9xSmRRX/WUdfy1HKqC6FgltZl1FhDM="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"PIWP4q6H\"; dkim-atps=neutral","Date":"Thu, 30 Mar 2023 20:16:09 +0200","To":"Naushir Patuck <naush@raspberrypi.com>","Message-ID":"<20230330181609.cpp7h4iaaevtxyui@uno.localdomain>","References":"<20230327122030.11756-1-naush@raspberrypi.com>\n\t<20230327122030.11756-10-naush@raspberrypi.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20230327122030.11756-10-naush@raspberrypi.com>","Subject":"Re: [libcamera-devel] [PATCH v2 09/10] ipa: raspberrypi: Generalise\n\tthe autofocus algorithm","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>","From":"Jacopo Mondi via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Cc":"Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>,\n\tlibcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]