File indexing completed on 2025-09-16 08:13:01
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 LineParametrisation<PointType> linePar,
0021 LineParametrisation<PointType> widthPar, const identifier_t& identifier,
0022 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 if (m_iLayer == m_layers.size()) {
0077 m_layers.resize(m_layers.size() + m_assignBatch);
0078 }
0079
0080 m_hits[m_iHit] = identifier;
0081 m_layers[m_iLayer] = layer;
0082
0083 m_iHit += 1;
0084 m_iLayer += 1;
0085
0086 m_nHits += weight;
0087 m_nLayers += weight;
0088 }
0089
0090 template <class identifier_t>
0091 void Acts::HoughTransformUtils::HoughCell<identifier_t>::reset() {
0092 m_nHits = 0;
0093 m_nLayers = 0;
0094
0095 m_iLayer = 0;
0096 m_iHit = 0;
0097 }
0098
0099 template <class identifier_t>
0100 Acts::HoughTransformUtils::HoughPlane<identifier_t>::HoughPlane(
0101 const HoughPlaneConfig& cfg)
0102 : m_cfg(cfg),
0103 m_houghHist(Axis(0, m_cfg.nBinsX, m_cfg.nBinsX),
0104 Axis(0, m_cfg.nBinsY, m_cfg.nBinsY)) {
0105
0106
0107
0108 }
0109 template <class identifier_t>
0110 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::fillBin(
0111 std::size_t binX, std::size_t binY, const identifier_t& identifier,
0112 unsigned layer, double w) {
0113
0114
0115 m_touchedBins.insert(globalBin({binX, binY}));
0116
0117
0118 m_houghHist.atLocalBins({binX, binY}).fill(identifier, layer, w);
0119
0120 YieldType layers = nLayers(binX, binY);
0121 YieldType hits = nHits(binX, binY);
0122 if (layers > m_maxLayers) {
0123 m_maxLayers = layers;
0124 m_maxLocLayers = {binX, binY};
0125 }
0126 if (hits > m_maxHits) {
0127 m_maxHits = hits;
0128 m_maxLocHits = {binX, binY};
0129 }
0130 }
0131
0132 template <class identifier_t>
0133 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::reset() {
0134
0135
0136 for (auto bin : getNonEmptyBins()) {
0137 m_houghHist.at(bin).reset();
0138 }
0139
0140 m_maxHits = 0.;
0141 m_maxLayers = 0.;
0142
0143 m_iBin = 0;
0144 m_touchedBins.clear();
0145 }
0146
0147 template <class identifier_t>
0148 Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<identifier_t>::
0149 LayerGuidedCombinatoric(const LayerGuidedCombinatoricConfig& cfg)
0150 : m_cfg(cfg) {}
0151
0152 template <class identifier_t>
0153 std::vector<typename Acts::HoughTransformUtils::PeakFinders::
0154 LayerGuidedCombinatoric<identifier_t>::Maximum>
0155 Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<
0156 identifier_t>::findPeaks(const HoughPlane<identifier_t>& plane) const {
0157
0158 std::vector<PeakFinders::LayerGuidedCombinatoric<identifier_t>::Maximum>
0159 maxima;
0160
0161 for (auto nbin : plane.getNonEmptyBins()) {
0162
0163
0164 std::array<std::size_t, 2> xy = plane.axisBins(nbin);
0165
0166 if (passThreshold(plane, xy[0], xy[1])) {
0167
0168 Maximum max;
0169 auto hitIds = plane.hitIds(xy[0], xy[1]);
0170 max.hitIdentifiers.insert(std::make_move_iterator(hitIds.begin()),
0171 std::make_move_iterator(hitIds.end()));
0172 maxima.push_back(max);
0173 }
0174 }
0175 return maxima;
0176 }
0177
0178 template <class identifier_t>
0179 bool Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<
0180 identifier_t>::passThreshold(const HoughPlane<identifier_t>& plane,
0181 std::size_t xBin, std::size_t yBin) const {
0182
0183 if (plane.nLayers(xBin, yBin) < m_cfg.threshold) {
0184 return false;
0185 }
0186
0187 if (m_cfg.localMaxWindowSize == 0) {
0188 return true;
0189 }
0190
0191
0192
0193 for (int dXbin = -m_cfg.localMaxWindowSize; dXbin <= m_cfg.localMaxWindowSize;
0194 dXbin++) {
0195 for (int dYbin = -m_cfg.localMaxWindowSize;
0196 dYbin <= m_cfg.localMaxWindowSize; dYbin++) {
0197
0198 if (dYbin == 0 && dXbin == 0) {
0199 continue;
0200 }
0201
0202 if (static_cast<int>(xBin) + dXbin < 0 ||
0203 static_cast<int>(yBin) + dYbin < 0) {
0204 continue;
0205 }
0206 if (xBin + dXbin >= plane.nBinsX() || yBin + dYbin >= plane.nBinsY()) {
0207 continue;
0208 }
0209
0210 if (plane.nLayers(xBin + dXbin, yBin + dYbin) >
0211 plane.nLayers(xBin, yBin)) {
0212 return false;
0213 }
0214
0215 if (plane.nLayers(xBin + dXbin, yBin + dYbin) <
0216 plane.nLayers(xBin, yBin)) {
0217 continue;
0218 }
0219
0220
0221
0222
0223
0224 if (plane.nHits(xBin + dXbin, yBin + dYbin) > plane.nHits(xBin, yBin)) {
0225 return false;
0226 }
0227
0228
0229 if (plane.nHits(xBin + dXbin, yBin + dYbin) == plane.nHits(xBin, yBin) &&
0230 (dYbin < 0 || (dYbin == 0 && dXbin < 0))) {
0231 return false;
0232 }
0233 }
0234 }
0235 return true;
0236 }
0237 template <class identifier_t>
0238 Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0239 identifier_t>::IslandsAroundMax(const IslandsAroundMaxConfig& cfg)
0240 : m_cfg(cfg) {}
0241
0242 template <class identifier_t>
0243 std::vector<typename Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0244 identifier_t>::Maximum>
0245 Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0246 identifier_t>::findPeaks(const HoughPlane<identifier_t>& plane,
0247 const HoughAxisRanges& ranges) {
0248
0249 YieldType max = plane.maxHits();
0250
0251 YieldType min = std::max(m_cfg.threshold, m_cfg.fractionCutoff * max);
0252
0253 const std::unordered_set<std::size_t>& nonEmptyBins{plane.getNonEmptyBins()};
0254 std::vector<std::size_t> candidates;
0255 candidates.reserve(nonEmptyBins.size());
0256 std::vector<Maximum> maxima;
0257 maxima.reserve(nonEmptyBins.size());
0258
0259 std::unordered_map<std::size_t, YieldType> yieldMap;
0260
0261
0262 for (const std::size_t nbin : nonEmptyBins) {
0263
0264
0265
0266
0267 if (plane.nHits(nbin) > min) {
0268 candidates.push_back(nbin);
0269 yieldMap[nbin] = plane.nHits(nbin);
0270 }
0271 }
0272
0273 std::ranges::sort(candidates, std::greater{}, [&yieldMap](std::size_t c) {
0274 return std::make_tuple(yieldMap[c], c);
0275 });
0276
0277
0278
0279 std::vector<std::size_t> toExplore;
0280 std::vector<std::array<std::size_t, 2>> solution;
0281
0282
0283 for (auto& cand : candidates) {
0284
0285
0286 if (yieldMap[cand] < min) {
0287 continue;
0288 }
0289
0290 std::array<std::size_t, 2> xy = plane.axisBins(cand);
0291
0292 CoordType xCand =
0293 binCenter(ranges.xMin, ranges.xMax, plane.nBinsX(), xy[0]);
0294 CoordType yCand =
0295 binCenter(ranges.yMin, ranges.yMax, plane.nBinsY(), xy[1]);
0296
0297 bool goodSpacing = true;
0298 for (auto& found : maxima) {
0299 if (std::abs(xCand - found.x) < m_cfg.minSpacingBetweenPeaks.first ||
0300 std::abs(yCand - found.y) < m_cfg.minSpacingBetweenPeaks.second) {
0301 goodSpacing = false;
0302 break;
0303 }
0304 }
0305 if (!goodSpacing) {
0306 continue;
0307 }
0308
0309 toExplore.clear();
0310 solution.clear();
0311
0312
0313 toExplore.push_back(cand);
0314
0315 while (!toExplore.empty()) {
0316 extendMaximum(plane, solution, toExplore, min, yieldMap);
0317 }
0318
0319 if (solution.empty()) {
0320 continue;
0321 }
0322
0323 Maximum maximum;
0324 CoordType max_x = 0;
0325 CoordType max_y = 0;
0326 CoordType pos_den = 0;
0327 CoordType ymax = -std::numeric_limits<CoordType>::max();
0328 CoordType ymin = std::numeric_limits<CoordType>::max();
0329 CoordType xmax = -std::numeric_limits<CoordType>::max();
0330 CoordType xmin = std::numeric_limits<CoordType>::max();
0331
0332
0333
0334 std::vector<identifier_t> allHits;
0335 for (auto& [xBin, yBin] : solution) {
0336 auto hidIds = plane.hitIds(xBin, yBin);
0337 allHits.insert(allHits.end(), std::make_move_iterator(hidIds.begin()),
0338 std::make_move_iterator(hidIds.end()));
0339 CoordType xHit =
0340 binCenter(ranges.xMin, ranges.xMax, plane.nBinsX(), xBin);
0341 CoordType yHit =
0342 binCenter(ranges.yMin, ranges.yMax, plane.nBinsY(), yBin);
0343 YieldType nHits = plane.nHits(xBin, yBin);
0344 max_x += xHit * nHits;
0345 max_y += yHit * nHits;
0346 pos_den += nHits;
0347 if (xHit > xmax) {
0348 xmax = xHit;
0349 }
0350 if (yHit > ymax) {
0351 ymax = yHit;
0352 }
0353 if (xHit < xmin) {
0354 xmin = xHit;
0355 }
0356 if (yHit < ymin) {
0357 ymin = yHit;
0358 }
0359 }
0360 maximum.hitIdentifiers.insert(std::make_move_iterator(allHits.begin()),
0361 std::make_move_iterator(allHits.end()));
0362
0363 maximum.x = max_x / pos_den;
0364 maximum.y = max_y / pos_den;
0365
0366 maximum.wx = 0.5 * (xmax - xmin);
0367 maximum.wy = 0.5 * (ymax - ymin);
0368 maxima.push_back(maximum);
0369 }
0370 return maxima;
0371 }
0372
0373 template <class identifier_t>
0374 void Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<identifier_t>::
0375 extendMaximum(
0376 const Acts::HoughTransformUtils::HoughPlane<identifier_t>& houghPlane,
0377 std::vector<std::array<std::size_t, 2>>& inMaximum,
0378 std::vector<std::size_t>& toExplore, YieldType threshold,
0379 std::unordered_map<std::size_t, YieldType>& yieldMap) {
0380
0381
0382
0383 const std::size_t candidate = toExplore.back();
0384 YieldType& yield{yieldMap[candidate]};
0385 toExplore.pop_back();
0386
0387 if (yield < threshold) {
0388 return;
0389 }
0390
0391 auto nextCand = houghPlane.axisBins(candidate);
0392
0393 inMaximum.push_back(nextCand);
0394
0395 yieldMap[candidate] = -1.0f;
0396
0397
0398
0399 for (auto step : m_stepDirections) {
0400 std::array<unsigned long, 2> newCandxy = {nextCand[0] + step.first,
0401 nextCand[1] + step.second};
0402
0403 if (newCandxy[0] >= houghPlane.nBinsX() ||
0404 newCandxy[1] >= houghPlane.nBinsY()) {
0405 continue;
0406 }
0407 std::size_t newCand = houghPlane.globalBin({newCandxy[0], newCandxy[1]});
0408
0409
0410
0411 if (yieldMap[newCand] > threshold) {
0412 toExplore.push_back(newCand);
0413 }
0414 }
0415 }