File indexing completed on 2026-05-03 07:38:20
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Seeding/HoughTransformUtils.hpp"
0012
0013 #include <algorithm>
0014 #include <tuple>
0015
0016 template <class identifier_t>
0017 template <class PointType>
0018 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::fill(
0019 const PointType& measurement, const HoughAxisRanges& axisRanges,
0020 const LineParametrisation<PointType>& linePar,
0021 const LineParametrisation<PointType>& widthPar,
0022 const identifier_t& identifier, unsigned layer, YieldType weight) {
0023
0024 for (std::size_t xBin = 0; xBin < m_cfg.nBinsX; xBin++) {
0025
0026 auto x = binCenter(axisRanges.xMin, axisRanges.xMax, m_cfg.nBinsX, xBin);
0027
0028 CoordType y = linePar(x, measurement);
0029 CoordType dy = widthPar(x, measurement);
0030
0031 int yBinDown =
0032 binIndex(axisRanges.yMin, axisRanges.yMax, m_cfg.nBinsY, y - dy);
0033 int yBinUp =
0034 binIndex(axisRanges.yMin, axisRanges.yMax, m_cfg.nBinsY, y + dy);
0035
0036 for (int yBin = yBinDown; yBin <= yBinUp; ++yBin) {
0037
0038 if (yBin >= static_cast<int>(m_cfg.nBinsY) || yBin < 0) {
0039 continue;
0040 }
0041 fillBin(xBin, yBin, identifier, layer, weight);
0042 }
0043 }
0044 }
0045
0046 template <class identifier_t>
0047 std::span<const identifier_t, std::dynamic_extent>
0048 Acts::HoughTransformUtils::HoughCell<identifier_t>::getHits() const {
0049 std::span<const identifier_t, std::dynamic_extent> hits(m_hits.begin(),
0050 m_iHit);
0051 return hits;
0052 }
0053
0054 template <class identifier_t>
0055 std::span<const unsigned, std::dynamic_extent>
0056 Acts::HoughTransformUtils::HoughCell<identifier_t>::getLayers() const {
0057 std::span<const unsigned, std::dynamic_extent> layers(m_layers.begin(),
0058 m_iLayer);
0059
0060 return layers;
0061 }
0062
0063 template <class identifier_t>
0064 void Acts::HoughTransformUtils::HoughCell<identifier_t>::fill(
0065 const identifier_t& identifier, unsigned layer, YieldType weight) {
0066
0067
0068 if (m_iHit != 0 && m_hits[m_iHit - 1] == identifier) {
0069 return;
0070 }
0071
0072 if (m_iHit == m_hits.size()) {
0073 m_hits.resize(m_hits.size() + m_assignBatch);
0074 }
0075
0076 m_hits[m_iHit] = identifier;
0077 m_iHit += 1;
0078 m_nHits += weight;
0079
0080
0081 if (m_iLayer != 0 && m_layers[m_iLayer - 1] == layer) {
0082 return;
0083 }
0084
0085 if (m_iLayer == m_layers.size()) {
0086 m_layers.resize(m_layers.size() + m_assignBatch);
0087 }
0088
0089 m_layers[m_iLayer] = layer;
0090 m_iLayer += 1;
0091 m_nLayers += weight;
0092 }
0093
0094 template <class identifier_t>
0095 void Acts::HoughTransformUtils::HoughCell<identifier_t>::reset() {
0096 m_nHits = 0;
0097 m_nLayers = 0;
0098
0099 m_iLayer = 0;
0100 m_iHit = 0;
0101 }
0102
0103 template <class identifier_t>
0104 Acts::HoughTransformUtils::HoughPlane<identifier_t>::HoughPlane(
0105 const HoughPlaneConfig& cfg)
0106 : m_cfg(cfg),
0107 m_houghHist(Axis(0, m_cfg.nBinsX, m_cfg.nBinsX),
0108 Axis(0, m_cfg.nBinsY, m_cfg.nBinsY)) {
0109
0110
0111
0112 }
0113 template <class identifier_t>
0114 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::fillBin(
0115 std::size_t binX, std::size_t binY, const identifier_t& identifier,
0116 unsigned layer, double w) {
0117
0118
0119 m_touchedBins.insert(globalBin({binX, binY}));
0120
0121
0122 m_houghHist.atLocalBins({binX, binY}).fill(identifier, layer, w);
0123
0124 YieldType layers = nLayers(binX, binY);
0125 YieldType hits = nHits(binX, binY);
0126 if (layers > m_maxLayers) {
0127 m_maxLayers = layers;
0128 m_maxLocLayers = {binX, binY};
0129 }
0130 if (hits > m_maxHits) {
0131 m_maxHits = hits;
0132 m_maxLocHits = {binX, binY};
0133 }
0134 }
0135
0136 template <class identifier_t>
0137 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::reset() {
0138
0139
0140 for (auto bin : getNonEmptyBins()) {
0141 m_houghHist.at(bin).reset();
0142 }
0143
0144 m_maxHits = 0.;
0145 m_maxLayers = 0.;
0146
0147 m_iBin = 0;
0148 m_touchedBins.clear();
0149 }
0150 template <class identifier_t>
0151 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::checkIndices(
0152 std::size_t xBin, std::size_t yBin) const {
0153 if (xBin >= nBinsX()) {
0154 throw std::out_of_range("When accessing HoughPlane, X index " +
0155 std::to_string(xBin) +
0156 " is >= " + std::to_string(nBinsX()));
0157 }
0158 if (yBin >= nBinsY()) {
0159 throw std::out_of_range("When accessing HoughPlane, Y index " +
0160 std::to_string(yBin) +
0161 " is >= " + std::to_string(nBinsY()));
0162 }
0163 }
0164
0165 template <class identifier_t>
0166 Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<identifier_t>::
0167 LayerGuidedCombinatoric(const LayerGuidedCombinatoricConfig& cfg)
0168 : m_cfg(cfg) {}
0169
0170 template <class identifier_t>
0171 std::vector<typename Acts::HoughTransformUtils::PeakFinders::
0172 LayerGuidedCombinatoric<identifier_t>::Maximum>
0173 Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<
0174 identifier_t>::findPeaks(const HoughPlane<identifier_t>& plane) const {
0175
0176 std::vector<PeakFinders::LayerGuidedCombinatoric<identifier_t>::Maximum>
0177 maxima;
0178
0179 for (auto nbin : plane.getNonEmptyBins()) {
0180
0181
0182 std::array<std::size_t, 2> xy = plane.axisBins(nbin);
0183
0184 if (passThreshold(plane, xy[0], xy[1])) {
0185
0186 Maximum max;
0187 auto hitIds = plane.hitIds(xy[0], xy[1]);
0188 max.hitIdentifiers.insert(std::make_move_iterator(hitIds.begin()),
0189 std::make_move_iterator(hitIds.end()));
0190 maxima.push_back(max);
0191 }
0192 }
0193 return maxima;
0194 }
0195
0196 template <class identifier_t>
0197 bool Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<
0198 identifier_t>::passThreshold(const HoughPlane<identifier_t>& plane,
0199 std::size_t xBin, std::size_t yBin) const {
0200
0201 if (plane.nLayers(xBin, yBin) < m_cfg.threshold) {
0202 return false;
0203 }
0204
0205 if (m_cfg.localMaxWindowSize == 0) {
0206 return true;
0207 }
0208
0209
0210
0211 for (int dXbin = -m_cfg.localMaxWindowSize; dXbin <= m_cfg.localMaxWindowSize;
0212 dXbin++) {
0213 for (int dYbin = -m_cfg.localMaxWindowSize;
0214 dYbin <= m_cfg.localMaxWindowSize; dYbin++) {
0215
0216 if (dYbin == 0 && dXbin == 0) {
0217 continue;
0218 }
0219
0220 if (static_cast<int>(xBin) + dXbin < 0 ||
0221 static_cast<int>(yBin) + dYbin < 0) {
0222 continue;
0223 }
0224 if (xBin + dXbin >= plane.nBinsX() || yBin + dYbin >= plane.nBinsY()) {
0225 continue;
0226 }
0227
0228 if (plane.nLayers(xBin + dXbin, yBin + dYbin) >
0229 plane.nLayers(xBin, yBin)) {
0230 return false;
0231 }
0232
0233 if (plane.nLayers(xBin + dXbin, yBin + dYbin) <
0234 plane.nLayers(xBin, yBin)) {
0235 continue;
0236 }
0237
0238
0239
0240
0241
0242 if (plane.nHits(xBin + dXbin, yBin + dYbin) > plane.nHits(xBin, yBin)) {
0243 return false;
0244 }
0245
0246
0247 if (plane.nHits(xBin + dXbin, yBin + dYbin) == plane.nHits(xBin, yBin) &&
0248 (dYbin < 0 || (dYbin == 0 && dXbin < 0))) {
0249 return false;
0250 }
0251 }
0252 }
0253 return true;
0254 }
0255 template <class identifier_t>
0256 Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0257 identifier_t>::IslandsAroundMax(const IslandsAroundMaxConfig& cfg)
0258 : m_cfg(cfg) {}
0259
0260 template <class identifier_t>
0261 std::vector<typename Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0262 identifier_t>::Maximum>
0263 Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0264 identifier_t>::findPeaks(const HoughPlane<identifier_t>& plane,
0265 const HoughAxisRanges& ranges) {
0266
0267 YieldType max = plane.maxHits();
0268
0269 YieldType min = std::max(m_cfg.threshold, m_cfg.fractionCutoff * max);
0270
0271 const std::unordered_set<std::size_t>& nonEmptyBins{plane.getNonEmptyBins()};
0272 std::vector<std::size_t> candidates;
0273 candidates.reserve(nonEmptyBins.size());
0274 std::vector<Maximum> maxima;
0275 maxima.reserve(nonEmptyBins.size());
0276
0277 std::unordered_map<std::size_t, YieldType> yieldMap;
0278
0279
0280 for (const std::size_t nbin : nonEmptyBins) {
0281
0282
0283
0284
0285 if (plane.nHits(nbin) > min) {
0286 candidates.push_back(nbin);
0287 yieldMap[nbin] = plane.nHits(nbin);
0288 }
0289 }
0290
0291 std::ranges::sort(candidates, std::greater{}, [&yieldMap](std::size_t c) {
0292 return std::make_tuple(yieldMap[c], c);
0293 });
0294
0295
0296
0297 std::vector<std::size_t> toExplore;
0298 std::vector<std::array<std::size_t, 2>> solution;
0299
0300
0301 for (auto& cand : candidates) {
0302
0303
0304 if (yieldMap[cand] < min) {
0305 continue;
0306 }
0307
0308 std::array<std::size_t, 2> xy = plane.axisBins(cand);
0309
0310 CoordType xCand =
0311 binCenter(ranges.xMin, ranges.xMax, plane.nBinsX(), xy[0]);
0312 CoordType yCand =
0313 binCenter(ranges.yMin, ranges.yMax, plane.nBinsY(), xy[1]);
0314
0315 bool goodSpacing = true;
0316 for (auto& found : maxima) {
0317 if (std::abs(xCand - found.x) < m_cfg.minSpacingBetweenPeaks.first ||
0318 std::abs(yCand - found.y) < m_cfg.minSpacingBetweenPeaks.second) {
0319 goodSpacing = false;
0320 break;
0321 }
0322 }
0323 if (!goodSpacing) {
0324 continue;
0325 }
0326
0327 toExplore.clear();
0328 solution.clear();
0329
0330
0331 toExplore.push_back(cand);
0332
0333 while (!toExplore.empty()) {
0334 extendMaximum(plane, solution, toExplore, min, yieldMap);
0335 }
0336
0337 if (solution.empty()) {
0338 continue;
0339 }
0340
0341 Maximum maximum;
0342 CoordType max_x = 0;
0343 CoordType max_y = 0;
0344 CoordType pos_den = 0;
0345 CoordType ymax = -std::numeric_limits<CoordType>::max();
0346 CoordType ymin = std::numeric_limits<CoordType>::max();
0347 CoordType xmax = -std::numeric_limits<CoordType>::max();
0348 CoordType xmin = std::numeric_limits<CoordType>::max();
0349
0350
0351
0352 std::vector<identifier_t> allHits;
0353 for (auto& [xBin, yBin] : solution) {
0354 auto hidIds = plane.hitIds(xBin, yBin);
0355 allHits.insert(allHits.end(), std::make_move_iterator(hidIds.begin()),
0356 std::make_move_iterator(hidIds.end()));
0357 CoordType xHit =
0358 binCenter(ranges.xMin, ranges.xMax, plane.nBinsX(), xBin);
0359 CoordType yHit =
0360 binCenter(ranges.yMin, ranges.yMax, plane.nBinsY(), yBin);
0361 YieldType nHits = plane.nHits(xBin, yBin);
0362 max_x += xHit * nHits;
0363 max_y += yHit * nHits;
0364 pos_den += nHits;
0365 if (xHit > xmax) {
0366 xmax = xHit;
0367 }
0368 if (yHit > ymax) {
0369 ymax = yHit;
0370 }
0371 if (xHit < xmin) {
0372 xmin = xHit;
0373 }
0374 if (yHit < ymin) {
0375 ymin = yHit;
0376 }
0377 }
0378 maximum.hitIdentifiers.insert(std::make_move_iterator(allHits.begin()),
0379 std::make_move_iterator(allHits.end()));
0380
0381 maximum.x = max_x / pos_den;
0382 maximum.y = max_y / pos_den;
0383
0384 maximum.wx = 0.5 * (xmax - xmin);
0385 maximum.wy = 0.5 * (ymax - ymin);
0386 maxima.push_back(maximum);
0387 }
0388 return maxima;
0389 }
0390
0391 template <class identifier_t>
0392 void Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<identifier_t>::
0393 extendMaximum(
0394 const Acts::HoughTransformUtils::HoughPlane<identifier_t>& houghPlane,
0395 std::vector<std::array<std::size_t, 2>>& inMaximum,
0396 std::vector<std::size_t>& toExplore, YieldType threshold,
0397 std::unordered_map<std::size_t, YieldType>& yieldMap) {
0398
0399
0400
0401 const std::size_t candidate = toExplore.back();
0402 YieldType& yield{yieldMap[candidate]};
0403 toExplore.pop_back();
0404
0405 if (yield < threshold) {
0406 return;
0407 }
0408
0409 auto nextCand = houghPlane.axisBins(candidate);
0410
0411 inMaximum.push_back(nextCand);
0412
0413 yieldMap[candidate] = -1.0f;
0414
0415
0416
0417 for (auto step : m_stepDirections) {
0418 std::array<unsigned long, 2> newCandxy = {nextCand[0] + step.first,
0419 nextCand[1] + step.second};
0420
0421 if (newCandxy[0] >= houghPlane.nBinsX() ||
0422 newCandxy[1] >= houghPlane.nBinsY()) {
0423 continue;
0424 }
0425 std::size_t newCand = houghPlane.globalBin({newCandxy[0], newCandxy[1]});
0426
0427
0428
0429 if (yieldMap[newCand] > threshold) {
0430 toExplore.push_back(newCand);
0431 }
0432 }
0433 }
0434
0435 namespace {
0436
0437 template <typename identifier_t>
0438 bool passWindow(
0439 const typename Acts::HoughTransformUtils::HoughPlane<identifier_t>::Index
0440 index,
0441 const Acts::HoughTransformUtils::HoughPlane<identifier_t>& plane,
0442 const Acts::HoughTransformUtils::PeakFinders::SlidingWindowConfig& config) {
0443 using YieldType = Acts::HoughTransformUtils::YieldType;
0444 auto [xmax, ymax] = index;
0445 const YieldType max = plane.nHits(xmax, ymax);
0446
0447
0448 for (std::size_t x = xmax - config.xWindowSize;
0449 x <= xmax + config.xWindowSize; ++x) {
0450 for (std::size_t y = ymax - config.yWindowSize;
0451 y <= ymax + config.yWindowSize; ++y) {
0452 const float xdist = static_cast<float>(x) - static_cast<float>(xmax);
0453 const float ydist = static_cast<float>(y) - static_cast<float>(ymax);
0454 const bool above = ydist + 0.1 > xdist;
0455 const YieldType numOfHits = plane.nHits(x, y);
0456
0457 if (above && numOfHits > max) {
0458 return false;
0459 }
0460
0461 if (!above && numOfHits >= max) {
0462 return false;
0463 }
0464 }
0465 }
0466 return true;
0467 }
0468
0469 template <typename identifier_t>
0470 typename Acts::HoughTransformUtils::HoughPlane<identifier_t>::Index
0471 slidingWindowRecenter(
0472 const typename Acts::HoughTransformUtils::HoughPlane<identifier_t>::Index
0473 index,
0474 const Acts::HoughTransformUtils::HoughPlane<identifier_t>& plane,
0475 const Acts::HoughTransformUtils::PeakFinders::SlidingWindowConfig& config) {
0476 using YieldType = Acts::HoughTransformUtils::YieldType;
0477 YieldType xw = 0;
0478 YieldType yw = 0;
0479 YieldType tot = 0;
0480 auto [xcenter, ycenter] = index;
0481 YieldType maxValue = plane.nHits(xcenter, ycenter);
0482 for (std::size_t x = xcenter - config.xRecenterSize;
0483 x <= xcenter + config.xRecenterSize; ++x) {
0484 for (std::size_t y = ycenter - config.yRecenterSize;
0485 y <= ycenter + config.yRecenterSize; ++y) {
0486 const YieldType numOfHits = plane.nHits(x, y);
0487 if (numOfHits >= maxValue) {
0488 xw += x * numOfHits;
0489 yw += y * numOfHits;
0490 tot += numOfHits;
0491 }
0492 }
0493 }
0494 return {static_cast<std::size_t>(xw / tot),
0495 static_cast<std::size_t>(yw / tot)};
0496 }
0497
0498 }
0499
0500 template <typename identifier_t>
0501 std::vector<typename Acts::HoughTransformUtils::HoughPlane<identifier_t>::Index>
0502 Acts::HoughTransformUtils::PeakFinders::slidingWindowPeaks(
0503 const Acts::HoughTransformUtils::HoughPlane<identifier_t>& plane,
0504 const Acts::HoughTransformUtils::PeakFinders::SlidingWindowConfig& config) {
0505 using IndexType = Acts::HoughTransformUtils::HoughPlane<identifier_t>::Index;
0506 std::vector<IndexType> output;
0507
0508 for (std::size_t x = config.xWindowSize;
0509 x < plane.nBinsX() - config.xWindowSize; ++x) {
0510 for (std::size_t y = config.yWindowSize;
0511 y < plane.nBinsY() - config.yWindowSize; ++y) {
0512 if (plane.nHits(x, y) >= config.threshold) {
0513 const IndexType index({x, y});
0514 if (passWindow(index, plane, config)) {
0515 output.push_back(config.recenter
0516 ? slidingWindowRecenter(index, plane, config)
0517 : index);
0518 }
0519 }
0520 }
0521 }
0522 return output;
0523 }
0524
0525 template <typename identifier_t, typename pixel_value_t>
0526 std::vector<pixel_value_t>
0527 Acts::HoughTransformUtils::PeakFinders::hitsCountImage(
0528 const Acts::HoughTransformUtils::HoughPlane<identifier_t>& plane,
0529 typename Acts::HoughTransformUtils::HoughPlane<identifier_t>::Index index,
0530 std::size_t xSize, std::size_t ySize,
0531 const std::function<pixel_value_t(const HoughPlane<identifier_t>&, int,
0532 int)>& summaryFunction) {
0533 std::vector<pixel_value_t> output;
0534 output.reserve((xSize) * (ySize));
0535
0536 auto [xcenter, ycenter] = index;
0537
0538 auto isInside = [&plane](int x, int y) -> bool {
0539 return 0 <= x && x < static_cast<int>(plane.nBinsX()) && 0 <= y &&
0540 y < static_cast<int>(plane.nBinsY());
0541 };
0542
0543 for (std::size_t x = 0; x < xSize; ++x) {
0544 for (std::size_t y = 0; y < ySize; ++y) {
0545 int xPlaneCoord = x + xcenter - xSize / 2;
0546 int yPlaneCoord = y + ycenter - ySize / 2;
0547 output.push_back(isInside(xPlaneCoord, yPlaneCoord)
0548 ? summaryFunction(plane, xPlaneCoord, yPlaneCoord)
0549 : pixel_value_t{});
0550 }
0551 }
0552 return output;
0553 }