File indexing completed on 2026-08-30 08:14:27
0001 #include "StripeDetector.h"
0002
0003 #include "helpers.h"
0004 #include "parameters.h"
0005
0006 #include <TH2.h>
0007
0008 #include <algorithm>
0009 #include <cmath>
0010 #include <iostream>
0011 #include <queue>
0012 #include <string>
0013 #include <array>
0014 #include <vector>
0015
0016 bool compare_seed_bins(const std::array<double, 5> &lhs, const std::array<double, 5> &rhs)
0017 {
0018
0019
0020 if (lhs[component_content] != rhs[component_content])
0021 {
0022 return lhs[component_content] > rhs[component_content];
0023 }
0024 if (static_cast<int>(lhs[component_bin_y]) != static_cast<int>(rhs[component_bin_y]))
0025 {
0026 return static_cast<int>(lhs[component_bin_y]) < static_cast<int>(rhs[component_bin_y]);
0027 }
0028 return static_cast<int>(lhs[component_bin_x]) < static_cast<int>(rhs[component_bin_x]);
0029 }
0030
0031 bool compare_stripe_content(const std::array<double, 3> &lhs, const std::array<double, 3> &rhs)
0032 {
0033
0034 return lhs[stripe_content] > rhs[stripe_content];
0035 }
0036
0037 int StripeDetector::wrapped_phi_bin(int bin, int nBinsX)
0038 {
0039
0040 while (bin < 1)
0041 {
0042 bin += nBinsX;
0043 }
0044 while (bin > nBinsX)
0045 {
0046 bin -= nBinsX;
0047 }
0048 return bin;
0049 }
0050
0051 void StripeDetector::normalize_phi_to_axis(TH2 *histogram, double &phi)
0052 {
0053
0054
0055 const double phiMin = histogram->GetXaxis()->GetXmin();
0056 const double phiMax = histogram->GetXaxis()->GetXmax();
0057 const double phiWidth = phiMax - phiMin;
0058 while (phi < phiMin)
0059 {
0060 phi += phiWidth;
0061 }
0062 while (phi >= phiMax)
0063 {
0064 phi -= phiWidth;
0065 }
0066 }
0067
0068 bool StripeDetector::detect(TH2 *histogram, std::vector<std::array<double, 3>> &stripes)
0069 {
0070
0071
0072 if (!initialize(histogram, stripes) || !create_working_histogram(histogram))
0073 {
0074 clear();
0075 return false;
0076 }
0077
0078 collect_seed_bins(histogram);
0079 build_connected_components();
0080 convert_components_to_raw_stripes(histogram);
0081 stripes = m_rawStripes;
0082 clear();
0083 return true;
0084 }
0085
0086 bool StripeDetector::initialize(TH2 *histogram, std::vector<std::array<double, 3>> &stripes)
0087 {
0088
0089
0090 m_nBinsX = histogram ? histogram->GetNbinsX() : 0;
0091 m_nBinsY = histogram ? histogram->GetNbinsY() : 0;
0092
0093 stripes.clear();
0094 m_seedBinsByPhi.assign(m_nBinsX + 2, {});
0095 m_components.clear();
0096 m_rawStripes.clear();
0097 m_nSeedBins = 0;
0098
0099 if (!histogram)
0100 {
0101 std::cout << "ERROR: Null histogram passed to StripeDetector" << std::endl;
0102 return false;
0103 }
0104
0105 return true;
0106 }
0107
0108 bool StripeDetector::create_working_histogram(TH2 *histogram)
0109 {
0110
0111 clear();
0112 if (!histogram)
0113 {
0114 return false;
0115 }
0116
0117 m_workingHist = dynamic_cast<TH2 *>(histogram->Clone((std::string(histogram->GetName()) + "_floodFillInput").c_str()));
0118 if (!m_workingHist)
0119 {
0120 std::cout << "ERROR: Could not clone input histogram for stripe detection" << std::endl;
0121 return false;
0122 }
0123
0124 m_workingHist->SetDirectory(nullptr);
0125 return true;
0126 }
0127
0128 void StripeDetector::clear()
0129 {
0130
0131 delete m_workingHist;
0132 m_workingHist = nullptr;
0133 }
0134
0135 void StripeDetector::collect_seed_bins(TH2 *histogram)
0136 {
0137
0138
0139 if (!histogram || !m_workingHist)
0140 {
0141 return;
0142 }
0143
0144 m_seedBinsByPhi.assign(m_nBinsX + 2, {});
0145 m_nSeedBins = 0;
0146 for (int i = 1; i <= m_nBinsX; i++)
0147 {
0148 for (int j = 1; j <= m_nBinsY; j++)
0149 {
0150 const double content = m_workingHist->GetBinContent(i, j);
0151 if (content <= 0.0 || content < stripeFloodSeedMinContent)
0152 {
0153 continue;
0154 }
0155
0156 const double r = histogram->GetYaxis()->GetBinCenter(j);
0157 const double phi = histogram->GetXaxis()->GetBinCenter(i);
0158 std::array<double, 5> seed{};
0159 seed[component_bin_x] = i;
0160 seed[component_bin_y] = j;
0161 seed[component_phi] = phi;
0162 seed[component_r] = r;
0163 seed[component_content] = content;
0164 m_seedBinsByPhi[i].push_back(seed);
0165 }
0166 }
0167
0168 for (int i = 1; i <= m_nBinsX; i++)
0169 {
0170 m_nSeedBins += m_seedBinsByPhi[i].size();
0171 }
0172 }
0173
0174 void StripeDetector::build_connected_components()
0175 {
0176
0177
0178
0179 m_components.clear();
0180 if (!m_workingHist)
0181 {
0182 return;
0183 }
0184
0185 std::vector<std::array<double, 5>> seeds;
0186 seeds.reserve(m_nSeedBins);
0187 for (int i = 1; i <= m_nBinsX; i++)
0188 {
0189 for (const auto &point : m_seedBinsByPhi[i])
0190 {
0191 seeds.push_back(point);
0192 }
0193 }
0194
0195 std::sort(seeds.begin(), seeds.end(), compare_seed_bins);
0196
0197 std::vector<std::vector<bool>> used(m_nBinsX + 1, std::vector<bool>(m_nBinsY + 1, false));
0198
0199
0200
0201 for (const auto &seed : seeds)
0202 {
0203 if (used[static_cast<int>(seed[component_bin_x])][static_cast<int>(seed[component_bin_y])])
0204 {
0205 continue;
0206 }
0207
0208 std::vector<std::array<double, 5>> component;
0209 std::queue<std::array<double, 5>> queue;
0210 used[static_cast<int>(seed[component_bin_x])][static_cast<int>(seed[component_bin_y])] = true;
0211 queue.push(seed);
0212
0213 int minBinPhi = static_cast<int>(seed[component_bin_x]);
0214 int maxBinPhi = static_cast<int>(seed[component_bin_x]);
0215 int minBinR = static_cast<int>(seed[component_bin_y]);
0216 int maxBinR = static_cast<int>(seed[component_bin_y]);
0217
0218 while (!queue.empty())
0219 {
0220 const std::array<double, 5> current = queue.front();
0221 queue.pop();
0222 component.push_back(current);
0223
0224 for (int dx = -stripeFloodMaxPhiGapBins - 1; dx <= stripeFloodMaxPhiGapBins + 1; dx++)
0225 {
0226 for (int dy = -stripeFloodMaxRGapBins - 1; dy <= stripeFloodMaxRGapBins + 1; dy++)
0227 {
0228 if (dx == 0 && dy == 0)
0229 {
0230 continue;
0231 }
0232
0233 const int binX = wrapped_phi_bin(static_cast<int>(current[component_bin_x]) + dx, m_nBinsX);
0234 const int binY = static_cast<int>(current[component_bin_y]) + dy;
0235 if (binY < 1 || binY > m_nBinsY || used[binX][binY])
0236 {
0237 continue;
0238 }
0239
0240 const double content = m_workingHist->GetBinContent(binX, binY);
0241 if (content < stripeFloodGrowMinContent)
0242 {
0243 continue;
0244 }
0245
0246
0247
0248 const int candidateMinPhi = std::min(minBinPhi, binX);
0249 const int candidateMaxPhi = std::max(maxBinPhi, binX);
0250 const int candidateMinR = std::min(minBinR, binY);
0251 const int candidateMaxR = std::max(maxBinR, binY);
0252 if (candidateMaxPhi - candidateMinPhi + 1 > stripeFloodMaxPhiSpanBins || candidateMaxR - candidateMinR + 1 > stripeFloodMaxRSpanBins)
0253 {
0254 continue;
0255 }
0256
0257 const double r = m_workingHist->GetYaxis()->GetBinCenter(binY);
0258 const double phi = m_workingHist->GetXaxis()->GetBinCenter(binX);
0259 used[binX][binY] = true;
0260 minBinPhi = candidateMinPhi;
0261 maxBinPhi = candidateMaxPhi;
0262 minBinR = candidateMinR;
0263 maxBinR = candidateMaxR;
0264 std::array<double, 5> neighbor{};
0265 neighbor[component_bin_x] = binX;
0266 neighbor[component_bin_y] = binY;
0267 neighbor[component_phi] = phi;
0268 neighbor[component_r] = r;
0269 neighbor[component_content] = content;
0270 queue.push(neighbor);
0271 }
0272 }
0273 }
0274
0275 if (!component.empty())
0276 {
0277 m_components.push_back(component);
0278 }
0279 }
0280 }
0281
0282 void StripeDetector::convert_components_to_raw_stripes(TH2 *histogram)
0283 {
0284
0285
0286
0287 if (!histogram)
0288 {
0289 return;
0290 }
0291
0292 m_rawStripes.clear();
0293 for (const auto &component : m_components)
0294 {
0295 if (static_cast<int>(component.size()) < stripeFloodMinBins)
0296 {
0297 continue;
0298 }
0299
0300 double sumW = 0.0;
0301 double sumPhi = 0.0;
0302 double sumR = 0.0;
0303 double totalContent = 0.0;
0304 const double seedPhi = component.front()[component_phi];
0305 for (const auto &point : component)
0306 {
0307
0308
0309 const double dphi = wrap_delta_phi(point[component_phi] - seedPhi);
0310 sumW += point[component_content];
0311 sumPhi += point[component_content] * dphi;
0312 sumR += point[component_content] * point[component_r];
0313 totalContent += point[component_content];
0314 }
0315
0316 if (sumW <= 0.0 || totalContent < stripeFloodMinTotalContent)
0317 {
0318 continue;
0319 }
0320
0321 std::array<double, 3> cluster{};
0322 cluster[stripe_phi] = seedPhi + sumPhi / sumW;
0323 normalize_phi_to_axis(histogram, cluster[stripe_phi]);
0324 cluster[stripe_r] = sumR / sumW;
0325 cluster[stripe_content] = totalContent;
0326 m_rawStripes.push_back(cluster);
0327 }
0328
0329 std::sort(m_rawStripes.begin(), m_rawStripes.end(), compare_stripe_content);
0330
0331 std::vector<std::array<double, 3>> uniqueStripes;
0332 uniqueStripes.reserve(m_rawStripes.size());
0333
0334 for (const auto &candidate : m_rawStripes)
0335 {
0336 bool duplicate = false;
0337 for (const auto &kept : uniqueStripes)
0338 {
0339 const double deltaR = std::abs(candidate[stripe_r] - kept[stripe_r]);
0340 const double deltaPhi = std::abs(wrap_delta_phi(candidate[stripe_phi] - kept[stripe_phi]));
0341 if (deltaR <= duplicateStripeMaxDeltaRCm && deltaPhi <= duplicateStripeMaxDeltaPhiRad)
0342 {
0343 duplicate = true;
0344 break;
0345 }
0346 }
0347 if (!duplicate)
0348 {
0349 uniqueStripes.push_back(candidate);
0350 }
0351 }
0352 m_rawStripes.swap(uniqueStripes);
0353 }