File indexing completed on 2025-07-12 08:07:09
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <algorithm>
0010
0011 template <class identifier_t>
0012 template <class PointType>
0013 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::fill(
0014 const PointType& measurement, const HoughAxisRanges& axisRanges,
0015 LineParametrisation<PointType> linePar,
0016 LineParametrisation<PointType> widthPar, const identifier_t& identifier,
0017 unsigned layer, YieldType weight) {
0018
0019 for (std::size_t xBin = 0; xBin < m_cfg.nBinsX; xBin++) {
0020
0021 auto x = binCenter(axisRanges.xMin, axisRanges.xMax, m_cfg.nBinsX, xBin);
0022
0023 CoordType y = linePar(x, measurement);
0024 CoordType dy = widthPar(x, measurement);
0025
0026 int yBinDown =
0027 binIndex(axisRanges.yMin, axisRanges.yMax, m_cfg.nBinsY, y - dy);
0028 int yBinUp =
0029 binIndex(axisRanges.yMin, axisRanges.yMax, m_cfg.nBinsY, y + dy);
0030
0031 for (int yBin = yBinDown; yBin <= yBinUp; ++yBin) {
0032
0033 if (yBin >= static_cast<int>(m_cfg.nBinsY) || yBin < 0) {
0034 continue;
0035 }
0036 fillBin(xBin, yBin, identifier, layer, weight);
0037 }
0038 }
0039 }
0040
0041 template <class identifier_t>
0042 std::span<const identifier_t, std::dynamic_extent>
0043 Acts::HoughTransformUtils::HoughCell<identifier_t>::getHits() const {
0044 std::span<const identifier_t, std::dynamic_extent> hits(m_hits.begin(),
0045 m_iHit);
0046 return hits;
0047 }
0048
0049 template <class identifier_t>
0050 std::span<const unsigned, std::dynamic_extent>
0051 Acts::HoughTransformUtils::HoughCell<identifier_t>::getLayers() const {
0052 std::span<const unsigned, std::dynamic_extent> layers(m_layers.begin(),
0053 m_iLayer);
0054
0055 return layers;
0056 }
0057
0058 template <class identifier_t>
0059 void Acts::HoughTransformUtils::HoughCell<identifier_t>::fill(
0060 const identifier_t& identifier, unsigned layer, YieldType weight) {
0061
0062
0063 if (m_iHit != 0 && m_hits[m_iHit - 1] == identifier) {
0064 return;
0065 }
0066
0067 if (m_iHit == m_hits.size()) {
0068 m_hits.resize(m_hits.size() + m_assignBatch);
0069 }
0070
0071 if (m_iLayer == m_layers.size()) {
0072 m_layers.resize(m_layers.size() + m_assignBatch);
0073 }
0074
0075 m_hits[m_iHit] = identifier;
0076 m_layers[m_iLayer] = layer;
0077
0078 m_iHit += 1;
0079 m_iLayer += 1;
0080
0081 m_nHits += weight;
0082 m_nLayers += weight;
0083 }
0084
0085 template <class identifier_t>
0086 void Acts::HoughTransformUtils::HoughCell<identifier_t>::reset() {
0087 m_nHits = 0;
0088 m_nLayers = 0;
0089
0090 m_iLayer = 0;
0091 m_iHit = 0;
0092 }
0093
0094 template <class identifier_t>
0095 Acts::HoughTransformUtils::HoughPlane<identifier_t>::HoughPlane(
0096 const HoughPlaneConfig& cfg)
0097 : m_cfg(cfg),
0098 m_houghHist(Axis(0, m_cfg.nBinsX, m_cfg.nBinsX),
0099 Axis(0, m_cfg.nBinsY, m_cfg.nBinsY)) {
0100
0101
0102
0103 }
0104 template <class identifier_t>
0105 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::fillBin(
0106 std::size_t binX, std::size_t binY, const identifier_t& identifier,
0107 unsigned layer, double w) {
0108
0109
0110 m_touchedBins.insert(globalBin({binX, binY}));
0111
0112
0113 m_houghHist.atLocalBins({binX, binY}).fill(identifier, layer, w);
0114
0115 YieldType layers = nLayers(binX, binY);
0116 YieldType hits = nHits(binX, binY);
0117 if (layers > m_maxLayers) {
0118 m_maxLayers = layers;
0119 m_maxLocLayers = {binX, binY};
0120 }
0121 if (hits > m_maxHits) {
0122 m_maxHits = hits;
0123 m_maxLocHits = {binX, binY};
0124 }
0125 }
0126
0127 template <class identifier_t>
0128 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::reset() {
0129
0130
0131 for (auto bin : getNonEmptyBins()) {
0132 m_houghHist.at(bin).reset();
0133 }
0134
0135 m_maxHits = 0.;
0136 m_maxLayers = 0.;
0137
0138 m_iBin = 0;
0139 m_touchedBins.clear();
0140 }
0141
0142 template <class identifier_t>
0143 Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<identifier_t>::
0144 LayerGuidedCombinatoric(const LayerGuidedCombinatoricConfig& cfg)
0145 : m_cfg(cfg) {}
0146
0147 template <class identifier_t>
0148 std::vector<typename Acts::HoughTransformUtils::PeakFinders::
0149 LayerGuidedCombinatoric<identifier_t>::Maximum>
0150 Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<
0151 identifier_t>::findPeaks(const HoughPlane<identifier_t>& plane) const {
0152
0153 std::vector<PeakFinders::LayerGuidedCombinatoric<identifier_t>::Maximum>
0154 maxima;
0155
0156 for (auto nbin : plane.getNonEmptyBins()) {
0157
0158
0159 std::array<std::size_t, 2> xy = plane.axisBins(nbin);
0160
0161 if (passThreshold(plane, xy[0], xy[1])) {
0162
0163 Maximum max;
0164 max.hitIdentifiers = plane.hitIds(xy[0], xy[1]);
0165 maxima.push_back(max);
0166 }
0167 }
0168 return maxima;
0169 }
0170
0171 template <class identifier_t>
0172 bool Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<
0173 identifier_t>::passThreshold(const HoughPlane<identifier_t>& plane,
0174 std::size_t xBin, std::size_t yBin) const {
0175
0176 if (plane.nLayers(xBin, yBin) < m_cfg.threshold) {
0177 return false;
0178 }
0179
0180 if (m_cfg.localMaxWindowSize == 0) {
0181 return true;
0182 }
0183
0184
0185
0186 for (int dXbin = -m_cfg.localMaxWindowSize; dXbin <= m_cfg.localMaxWindowSize;
0187 dXbin++) {
0188 for (int dYbin = -m_cfg.localMaxWindowSize;
0189 dYbin <= m_cfg.localMaxWindowSize; dYbin++) {
0190
0191 if (dYbin == 0 && dXbin == 0) {
0192 continue;
0193 }
0194
0195 if (static_cast<int>(xBin) + dXbin < 0 ||
0196 static_cast<int>(yBin) + dYbin < 0) {
0197 continue;
0198 }
0199 if (xBin + dXbin >= plane.nBinsX() || yBin + dYbin >= plane.nBinsY()) {
0200 continue;
0201 }
0202
0203 if (plane.nLayers(xBin + dXbin, yBin + dYbin) >
0204 plane.nLayers(xBin, yBin)) {
0205 return false;
0206 }
0207
0208 if (plane.nLayers(xBin + dXbin, yBin + dYbin) <
0209 plane.nLayers(xBin, yBin)) {
0210 continue;
0211 }
0212
0213
0214
0215
0216
0217 if (plane.nHits(xBin + dXbin, yBin + dYbin) > plane.nHits(xBin, yBin)) {
0218 return false;
0219 }
0220
0221
0222 if (plane.nHits(xBin + dXbin, yBin + dYbin) == plane.nHits(xBin, yBin) &&
0223 (dYbin < 0 || (dYbin == 0 && dXbin < 0))) {
0224 return false;
0225 }
0226 }
0227 }
0228 return true;
0229 }
0230 template <class identifier_t>
0231 Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0232 identifier_t>::IslandsAroundMax(const IslandsAroundMaxConfig& cfg)
0233 : m_cfg(cfg) {}
0234
0235 template <class identifier_t>
0236 std::vector<typename Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0237 identifier_t>::Maximum>
0238 Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0239 identifier_t>::findPeaks(const HoughPlane<identifier_t>& plane,
0240 const HoughAxisRanges& ranges) {
0241
0242 YieldType max = plane.maxHits();
0243
0244 YieldType min = std::max(m_cfg.threshold, m_cfg.fractionCutoff * max);
0245
0246 const std::unordered_set<std::size_t>& nonEmptyBins{plane.getNonEmptyBins()};
0247 std::vector<std::size_t> candidates;
0248 candidates.reserve(nonEmptyBins.size());
0249 std::vector<Maximum> maxima;
0250 maxima.reserve(nonEmptyBins.size());
0251
0252 std::unordered_map<std::size_t, YieldType> yieldMap;
0253
0254
0255 for (const std::size_t nbin : nonEmptyBins) {
0256
0257
0258
0259
0260 if (plane.nHits(nbin) > min) {
0261 candidates.push_back(nbin);
0262 yieldMap[nbin] = plane.nHits(nbin);
0263 }
0264 }
0265
0266 std::sort(candidates.begin(), candidates.end(),
0267 [&yieldMap](const std::size_t bin1, const std::size_t bin2) {
0268 YieldType h1 = yieldMap[bin1];
0269 YieldType h2 = yieldMap[bin2];
0270
0271 if (h1 != h2) {
0272 return h1 > h2;
0273 }
0274 return bin1 > bin2;
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 for (auto& [xBin, yBin] : solution) {
0335 auto hidIds = plane.hitIds(xBin, yBin);
0336 maximum.hitIdentifiers.insert(std::make_move_iterator(hidIds.begin()),
0337 std::make_move_iterator(hidIds.end()));
0338 CoordType xHit =
0339 binCenter(ranges.xMin, ranges.xMax, plane.nBinsX(), xBin);
0340 CoordType yHit =
0341 binCenter(ranges.yMin, ranges.yMax, plane.nBinsY(), yBin);
0342 YieldType nHits = plane.nHits(xBin, yBin);
0343 max_x += xHit * nHits;
0344 max_y += yHit * nHits;
0345 pos_den += nHits;
0346 if (xHit > xmax) {
0347 xmax = xHit;
0348 }
0349 if (yHit > ymax) {
0350 ymax = yHit;
0351 }
0352 if (xHit < xmin) {
0353 xmin = xHit;
0354 }
0355 if (yHit < ymin) {
0356 ymin = yHit;
0357 }
0358 }
0359
0360 maximum.x = max_x / pos_den;
0361 maximum.y = max_y / pos_den;
0362
0363 maximum.wx = 0.5 * (xmax - xmin);
0364 maximum.wy = 0.5 * (ymax - ymin);
0365 maxima.push_back(maximum);
0366 }
0367 return maxima;
0368 }
0369
0370 template <class identifier_t>
0371 void Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<identifier_t>::
0372 extendMaximum(
0373 const Acts::HoughTransformUtils::HoughPlane<identifier_t>& houghPlane,
0374 std::vector<std::array<std::size_t, 2>>& inMaximum,
0375 std::vector<std::size_t>& toExplore, YieldType threshold,
0376 std::unordered_map<std::size_t, YieldType>& yieldMap) {
0377
0378
0379
0380 const std::size_t candidate = toExplore.back();
0381 YieldType& yield{yieldMap[candidate]};
0382 toExplore.pop_back();
0383
0384 if (yield < threshold) {
0385 return;
0386 }
0387
0388 auto nextCand = houghPlane.axisBins(candidate);
0389
0390 inMaximum.push_back(nextCand);
0391
0392 yieldMap[candidate] = -1.0f;
0393
0394
0395
0396 for (auto step : m_stepDirections) {
0397 std::array<unsigned long, 2> newCandxy = {nextCand[0] + step.first,
0398 nextCand[1] + step.second};
0399
0400 if (newCandxy[0] >= houghPlane.nBinsX() ||
0401 newCandxy[1] >= houghPlane.nBinsY()) {
0402 continue;
0403 }
0404 std::size_t newCand = houghPlane.globalBin({newCandxy[0], newCandxy[1]});
0405
0406
0407
0408 if (yieldMap[newCand] > threshold) {
0409 toExplore.push_back(newCand);
0410 }
0411 }
0412 }