LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
HoughBaseAlg.cxx
Go to the documentation of this file.
1 
19 // our header
21 
22 // C/C++ standard library
23 #include <algorithm> // std::lower_bound(), std::min(), std::fill(), ...
24 #include <cmath> // std::sqrt()
25 #include <fstream>
26 #include <limits> // std::numeric_limits<>
27 #include <stdint.h> // uint32_t
28 #include <vector>
29 
30 // ROOT/CLHEP libraries
31 #include "CLHEP/Random/RandFlat.h"
32 #include <TStopwatch.h>
33 
34 // art libraries
40 #include "fhiclcpp/ParameterSet.h"
42 
43 // larsoft libraries
44 #include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom<>()
55 #include "larevt/CalibrationDBI/Interface/ChannelStatusProvider.h"
56 #include "larevt/CalibrationDBI/Interface/ChannelStatusService.h"
59 
60 constexpr double PI = M_PI; // or CLHEP::pi in CLHEP/Units/PhysicalConstants.h
61 
62 #define a0 0 /*-4.172325e-7f*/ /*(-(float)0x7)/((float)0x1000000); */
63 #define a1 1.000025f /*((float)0x1922253)/((float)0x1000000)*2/Pi; */
64 #define a2 -2.652905e-4f /*(-(float)0x2ae6)/((float)0x1000000)*4/(Pi*Pi); */
65 #define a3 -0.165624f /*(-(float)0xa45511)/((float)0x1000000)*8/(Pi*Pi*Pi); */
66 #define a4 -1.964532e-3f /*(-(float)0x30fd3)/((float)0x1000000)*16/(Pi*Pi*Pi*Pi); */
67 #define a5 1.02575e-2f /*((float)0x191cac)/((float)0x1000000)*32/(Pi*Pi*Pi*Pi*Pi); */
68 #define a6 -9.580378e-4f /*(-(float)0x3af27)/((float)0x1000000)*64/(Pi*Pi*Pi*Pi*Pi*Pi); */
69 
70 #define _sin(x) ((((((a6 * (x) + a5) * (x) + a4) * (x) + a3) * (x) + a2) * (x) + a1) * (x) + a0)
71 #define _cos(x) _sin(TMath::Pi() * 0.5 - (x))
72 
73 namespace cluster {
75  public:
76  void Init(unsigned int dx, unsigned int dy, float rhores, unsigned int numACells);
77  std::array<int, 3> AddPointReturnMax(int x, int y);
78  bool SubtractPoint(int x, int y);
79  int GetCell(int row, int col) const;
80  void SetCell(int row, int col, int value) { m_accum[row].set(col, value); }
81  void GetAccumSize(int& numRows, int& numCols)
82  {
83  numRows = m_accum.size();
84  numCols = (int)m_rowLength;
85  }
86  int NumAccumulated() { return m_numAccumulated; }
87  void GetEquation(float row, float col, float& rho, float& theta) const;
88  int GetMax(int& xmax, int& ymax) const;
89 
90  void reconfigure(fhicl::ParameterSet const& pset);
91 
92  private:
96 
98  typedef std::vector<DistancesMap_t> HoughImage_t;
99 
100  unsigned int m_dx;
101  unsigned int m_dy;
102  unsigned int m_rowLength;
103  unsigned int m_numAngleCells;
105  // Note, m_accum is a vector of associative containers,
106  // the vector elements are called by rho, theta is the container key,
107  // the number of hits is the value corresponding to the key
108  HoughImage_t m_accum;
110  std::vector<double> m_cosTable;
111  std::vector<double> m_sinTable;
112 
113  std::array<int, 3> DoAddPointReturnMax(int x, int y, bool bSubtract = false);
114  }; // class HoughTransform
115 }
116 
117 template <typename T>
118 inline T sqr(T v)
119 {
120  return v * v;
121 }
122 
123 //------------------------------------------------------------------------------
124 template <typename K, typename C, size_t S, typename A, unsigned int SC>
126  Key_t key_end)
127 {
128  unchecked_add_range_max(key_begin, key_end, +1, std::numeric_limits<SubCounter_t>::max());
129 } // cluster::HoughTransformCounters<>::increment(begin, end)
130 
131 template <typename K, typename C, size_t S, typename A, unsigned int SC>
133  Key_t key_end)
134 {
135  unchecked_add_range_max(key_begin, key_end, -1, std::numeric_limits<SubCounter_t>::max());
136 } // cluster::HoughTransformCounters<>::decrement(begin, end)
137 
138 template <typename K, typename C, size_t S, typename A, unsigned int SC>
141 {
142  PairValue_t max{Base_t::make_const_iterator(Base_t::counter_map.end(), 0), current_max};
143 
144  typename BaseMap_t::const_iterator iCBlock = Base_t::counter_map.begin(),
145  cend = Base_t::counter_map.end();
146  while (iCBlock != cend) {
147  const CounterBlock_t& block = iCBlock->second;
148  for (size_t index = 0; index < block.size(); ++index) {
149  if (block[index] > max.second)
150  max = {Base_t::make_const_iterator(iCBlock, index), block[index]};
151  ++iCBlock;
152  } // for elements in this block
153  } // while blocks
154  return max;
155 } // cluster::HoughTransformCounters<>::get_max(SubCounter_t)
156 
157 template <typename K, typename C, size_t S, typename A, unsigned int SC>
160 {
161  return get_max(std::numeric_limits<SubCounter_t>::max());
162 }
163 
164 template <typename K, typename C, size_t S, typename A, unsigned int SC>
167  Key_t key_begin,
168  Key_t key_end,
170  typename BaseMap_t::iterator iIP)
171 {
172  if (key_begin > key_end) return value;
173  CounterKey_t key(key_begin);
174  size_t left = key_end - key_begin;
175  while (true) {
176  if ((iIP == Base_t::counter_map.end()) || (iIP->first != key.block)) {
177  // we don't have that block yet
178  iIP = Base_t::counter_map.insert(iIP, {key.block, {}});
179  } // if need to add a block
180  CounterBlock_t& block = iIP->second;
181  size_t n = std::min(left, Base_t::NCounters - key.counter);
182  block.fill(key.counter, n, value);
183  if (left -= n <= 0) break;
184  key.next_block();
185  ++iIP;
186  } // while
187  return value;
188 } // cluster::HoughTransformCounters<>::unchecked_set_range()
189 
190 template <typename K, typename C, size_t S, typename A, unsigned int SC>
193  Key_t key_end,
195 {
196  return unchecked_set_range(
197  key_begin, key_end, value, Base_t::counter_map.lower_bound(CounterKey_t(key_begin).block));
198 } // cluster::HoughTransformCounters<>::unchecked_set_range(no hint)
199 
200 template <typename K, typename C, size_t S, typename A, unsigned int SC>
203  Key_t key_begin,
204  Key_t key_end,
205  SubCounter_t delta,
206  typename BaseMap_t::iterator iIP,
207  SubCounter_t min_max /* = std::numeric_limits<SubCounter_t>::min() */
208 )
209 {
210  PairValue_t max{Base_t::make_const_iterator(Base_t::counter_map.end(), 0), min_max};
211  if (key_begin > key_end) return max;
212  CounterKey_t key(key_begin);
213  size_t left = key_end - key_begin;
214  while (true) {
215  if ((iIP == Base_t::counter_map.end()) || (iIP->first != key.block)) {
216  // we don't have that block yet
217  iIP = Base_t::counter_map.insert(iIP, {key.block, {}});
218  } // if need to add a block
219  CounterBlock_t& block = iIP->second;
220  size_t n = std::min(left, Base_t::NCounters - key.counter);
221  left -= n;
222  while (n--) {
223  SubCounter_t value = (block[key.counter] += delta);
224  if (value > max.second) { max = {Base_t::make_const_iterator(iIP, key.counter), value}; }
225  ++(key.counter);
226  } // for key.counter
227  if (left <= 0) break;
228  key.next_block();
229  ++iIP;
230  } // while
231  return max;
232 } // cluster::HoughTransformCounters<>::unchecked_add_range_max()
233 
234 template <typename K, typename C, size_t S, typename A, unsigned int SC>
237  Key_t key_begin,
238  Key_t key_end,
239  SubCounter_t delta,
240  SubCounter_t min_max /* = std::numeric_limits<SubCounter_t>::min() */
241 )
242 {
243  return unchecked_add_range_max(key_begin,
244  key_end,
245  delta,
246  Base_t::counter_map.lower_bound(CounterKey_t(key_begin).block),
247  min_max);
248 } // cluster::HoughTransformCounters<>::unchecked_add_range_max(no hint)
249 
250 //------------------------------------------------------------------------------
252 {
253  fMaxLines = pset.get<int>("MaxLines");
254  fMinHits = pset.get<int>("MinHits");
255  fSaveAccumulator = pset.get<int>("SaveAccumulator");
256  fNumAngleCells = pset.get<int>("NumAngleCells");
257  fMaxDistance = pset.get<float>("MaxDistance");
258  fMaxSlope = pset.get<float>("MaxSlope");
259  fRhoZeroOutRange = pset.get<int>("RhoZeroOutRange");
260  fThetaZeroOutRange = pset.get<int>("ThetaZeroOutRange");
261  fRhoResolutionFactor = pset.get<float>("RhoResolutionFactor");
262  fPerCluster = pset.get<int>("HitsPerCluster");
263  fMissedHits = pset.get<int>("MissedHits");
264  fMissedHitsDistance = pset.get<float>("MissedHitsDistance");
265  fMissedHitsToLineSize = pset.get<float>("MissedHitsToLineSize");
266 }
267 
268 //------------------------------------------------------------------------------
270  detinfo::DetectorClocksData const& clockData,
271  detinfo::DetectorPropertiesData const& detProp,
273  CLHEP::HepRandomEngine& engine,
274  std::vector<unsigned int>* fpointId_to_clusterId,
275  unsigned int clusterId, // The id of the cluster we are examining
276  unsigned int* nClusters,
277  std::vector<protoTrack>* linesFound)
278 {
279  int nClustersTemp = *nClusters;
280 
281  geo::GeometryCore const* geom = lar::providerFrom<geo::Geometry>();
282  lariov::ChannelStatusProvider const* channelStatus =
283  lar::providerFrom<lariov::ChannelStatusService>();
284 
285  unsigned int wire = 0;
286  unsigned int wireMax = 0;
287  geo::WireID const& wireid = hits[0]->WireID();
288 
289  geo::SigType_t sigt = geom->SignalType(wireid);
290  std::vector<int> skip;
291 
292  auto const num_planes = geom->Nplanes(wireid.asPlaneID().asTPCID());
293  std::vector<double> wire_pitch(num_planes, 0.);
294  for (auto const& plane : geom->Iterate<geo::PlaneGeo>(geo::TPCID{0, 0})) {
295  auto const p = plane.ID().Plane;
296  wire_pitch[p] = plane.WirePitch();
297  }
298 
299  //factor to make x and y scale the same units
300  std::vector<double> xyScale(num_planes, 0.);
301 
303  double driftVelFactor = 0.001 * detProp.DriftVelocity(detProp.Efield(), detProp.Temperature());
304 
305  for (size_t p = 0; p < xyScale.size(); ++p)
306  xyScale[p] = driftVelFactor * sampling_rate(clockData) / wire_pitch[p];
307 
308  float tickToDist = driftVelFactor * sampling_rate(clockData);
309 
310  int x, y;
311  // there must be a better way to find which plane a cluster comes from
312  const int dx = geom->Plane(wireid.asPlaneID()).Nwires(); // number of wires
313  const int dy = detProp.NumberTimeSamples(); // number of time samples.
314  skip.clear();
315  skip.resize(hits.size());
316  std::vector<int> listofxmax;
317  std::vector<int> listofymax;
318  std::vector<int> hitsTemp; //indecies ofcandidate hits
319  std::vector<int> sequenceHolder; //wire numbers of hits in list
320  std::vector<int> channelHolder; //channels of hits in list
321  std::vector<int> currentHits; //working vector of hits
322  std::vector<int> lastHits; //best list of hits
323  std::vector<art::Ptr<recob::Hit>> clusterHits;
324  float indcolscaling = 0.; //a parameter to account for the different
329  if (sigt == geo::kInduction)
330  indcolscaling = 0.;
331  else
332  indcolscaling = 1.;
333  float centerofmassx = 0;
334  float centerofmassy = 0;
335  float denom = 0;
336  float intercept = 0.;
337  float slope = 0.;
338  //this array keeps track of the hits that have already been associated with a line.
339  int xMax = 0;
340  int yMax = 0;
341  int yClearStart;
342  int yClearEnd;
343  int xClearStart;
344  int xClearEnd;
345  int maxCell = 0;
346  float rho;
347  float theta;
348  int accDx(0), accDy(0);
349  float pCornerMin[2];
350  float pCornerMax[2];
351 
352  unsigned int fMaxWire = 0;
353  int iMaxWire = 0;
354  unsigned int fMinWire = 99999999;
355  int iMinWire = -1;
356 
371 
372  mf::LogInfo("HoughBaseAlg") << "dealing with " << hits.size() << " hits";
373 
374  HoughTransform c;
375 
378  c.Init(dx, dy, fRhoResolutionFactor, fNumAngleCells);
380 
381  c.GetAccumSize(accDy, accDx);
382 
383  // Define the prototrack object
384  protoTrack protoTrackToLoad;
385 
386  // The number of lines we've found
387  unsigned int nLinesFound = 0;
388  std::vector<unsigned int> accumPoints(hits.size(), 0);
389  int nAccum = 0;
390 
392  int count = 0;
393  for (auto fpointId_to_clusterIdItr = fpointId_to_clusterId->begin();
394  fpointId_to_clusterIdItr != fpointId_to_clusterId->end();
395  fpointId_to_clusterIdItr++)
396  if (*fpointId_to_clusterIdItr == clusterId) count++;
397 
398  unsigned int randInd;
399 
400  CLHEP::RandFlat flat(engine);
401  TStopwatch w;
402 
403  for (; count > 0;) {
404 
407  randInd = (unsigned int)(flat.fire() * hits.size());
408 
410  if (fpointId_to_clusterId->at(randInd) != clusterId) continue;
411 
412  --count;
413 
415  if (skip[randInd] == 1) { continue; }
416 
418  if (accumPoints[randInd]) continue;
419  accumPoints[randInd] = 1;
420 
422  for (auto listofxmaxItr = listofxmax.begin(); listofxmaxItr != listofxmax.end();
423  ++listofxmaxItr) {
424  yClearStart = listofymax[listofxmaxItr - listofxmax.begin()] - fRhoZeroOutRange;
425  if (yClearStart < 0) yClearStart = 0;
426 
427  yClearEnd = listofymax[listofxmaxItr - listofxmax.begin()] + fRhoZeroOutRange;
428  if (yClearEnd >= accDy) yClearEnd = accDy - 1;
429 
430  xClearStart = *listofxmaxItr - fThetaZeroOutRange;
431  if (xClearStart < 0) xClearStart = 0;
432 
433  xClearEnd = *listofxmaxItr + fThetaZeroOutRange;
434  if (xClearEnd >= accDx) xClearEnd = accDx - 1;
435 
436  for (y = yClearStart; y <= yClearEnd; ++y) {
437  for (x = xClearStart; x <= xClearEnd; ++x) {
438  c.SetCell(y, x, 0);
439  }
440  }
441  }
442 
444  uint32_t channel = hits[randInd]->Channel();
445  wireMax = hits[randInd]->WireID().Wire;
446 
448  std::array<int, 3> max = c.AddPointReturnMax(wireMax, (int)(hits[randInd]->PeakTime()));
449  maxCell = max[0];
450  xMax = max[1];
451  yMax = max[2];
452  ++nAccum;
453 
454  // The threshold calculation using a Binomial distribution
455  double binomProbSum = TMath::BinomialI(1 / (double)accDx, nAccum, maxCell);
456  if (binomProbSum > 1e-21) continue;
457 
460  denom = centerofmassx = centerofmassy = 0;
461 
462  if (xMax > 0 && yMax > 0 && xMax + 1 < accDx && yMax + 1 < accDy) {
463  for (int i = -1; i < 2; ++i) {
464  for (int j = -1; j < 2; ++j) {
465  denom += c.GetCell(yMax + i, xMax + j);
466  centerofmassx += j * c.GetCell(yMax + i, xMax + j);
467  centerofmassy += i * c.GetCell(yMax + i, xMax + j);
468  }
469  }
470  centerofmassx /= denom;
471  centerofmassy /= denom;
472  }
473  else
474  centerofmassx = centerofmassy = 0;
475 
477  listofxmax.push_back(xMax);
478  listofymax.push_back(yMax);
479 
480  // Find the lines equation
481  c.GetEquation(yMax + centerofmassy, xMax + centerofmassx, rho, theta);
482  MF_LOG_DEBUG("HoughBaseAlg") << "Transform(II) found maximum at (d=" << rho << " a=" << theta
483  << ")"
484  " from absolute maximum "
485  << c.GetCell(yMax, xMax) << " at (d=" << yMax << ", a=" << xMax
486  << ")";
487  slope = -1. / tan(theta);
488  intercept = (rho / sin(theta));
489  float distance;
490 
491  if (!std::isinf(slope) && !std::isnan(slope)) {
492  channelHolder.clear();
493  sequenceHolder.clear();
494  fMaxWire = 0;
495  iMaxWire = 0;
496  fMinWire = 99999999;
497  iMinWire = -1;
498  hitsTemp.clear();
499  for (auto hitsItr = hits.cbegin(); hitsItr != hits.cend(); ++hitsItr) {
500  wire = (*hitsItr)->WireID().Wire;
501  if (fpointId_to_clusterId->at(hitsItr - hits.begin()) != clusterId) continue;
502  channel = (*hitsItr)->Channel();
503  distance = (std::abs((*hitsItr)->PeakTime() - slope * (float)((*hitsItr)->WireID().Wire) -
504  intercept) /
505  (std::sqrt(sqr(xyScale[(*hitsItr)->WireID().Plane] * slope) + 1.)));
506  if (distance < fMaxDistance + (*hitsItr)->RMS() + indcolscaling &&
507  skip[hitsItr - hits.begin()] != 1) {
508  hitsTemp.push_back(hitsItr - hits.begin());
509  sequenceHolder.push_back(wire);
510  channelHolder.push_back(channel);
511  }
512  }
513 
514  if (hitsTemp.size() < 5) continue;
515  currentHits.clear();
516  lastHits.clear();
517  int j;
518  currentHits.push_back(0);
519  for (auto sequenceHolderItr = sequenceHolder.begin();
520  sequenceHolderItr + 1 != sequenceHolder.end();
521  ++sequenceHolderItr) {
522  j = 1;
523  while (channelStatus->IsBad(sequenceHolderItr - sequenceHolder.begin() + j))
524  j++;
525  if (sequenceHolder[sequenceHolderItr - sequenceHolder.begin() + 1] -
526  sequenceHolder[sequenceHolderItr - sequenceHolder.begin()] <=
527  j + fMissedHits)
528  currentHits.push_back(sequenceHolderItr - sequenceHolder.begin() + 1);
529  else if (currentHits.size() > lastHits.size()) {
530  lastHits = currentHits;
531  currentHits.clear();
532  }
533  else
534  currentHits.clear();
535  }
536  if (currentHits.size() > lastHits.size()) lastHits = currentHits;
537 
538  clusterHits.clear();
539 
540  if (lastHits.size() < (unsigned int)fMinHits) continue;
541 
542  if (std::abs(slope) > fMaxSlope) { continue; }
543 
544  // Add new line to list of lines
545  float totalQ = 0;
546  std::vector<art::Ptr<recob::Hit>> lineHits;
547  nClustersTemp++;
548  for (auto lastHitsItr = lastHits.begin(); lastHitsItr != lastHits.end(); ++lastHitsItr) {
549  fpointId_to_clusterId->at(hitsTemp[(*lastHitsItr)]) = nClustersTemp - 1;
550  totalQ += hits[hitsTemp[(*lastHitsItr)]]->Integral();
551  wire = hits[hitsTemp[(*lastHitsItr)]]->WireID().Wire;
552 
553  if (!accumPoints[hitsTemp[(*lastHitsItr)]]) count--;
554 
555  skip[hitsTemp[(*lastHitsItr)]] = 1;
556 
557  lineHits.push_back(hits[hitsTemp[(*lastHitsItr)]]);
558 
560  if (accumPoints[hitsTemp[(*lastHitsItr)]])
561  c.SubtractPoint(wire, (int)(hits[hitsTemp[(*lastHitsItr)]]->PeakTime()));
562 
563  if (wire < fMinWire) {
564  fMinWire = wire;
565  iMinWire = hitsTemp[(*lastHitsItr)];
566  }
567  if (wire > fMaxWire) {
568  fMaxWire = wire;
569  iMaxWire = hitsTemp[(*lastHitsItr)];
570  }
571  }
572  int pnum = hits[iMinWire]->WireID().Plane;
573  pCornerMin[0] = (hits[iMinWire]->WireID().Wire) * wire_pitch[pnum];
574  pCornerMin[1] = hits[iMinWire]->PeakTime() * tickToDist;
575  pCornerMax[0] = (hits[iMaxWire]->WireID().Wire) * wire_pitch[pnum];
576  pCornerMax[1] = hits[iMaxWire]->PeakTime() * tickToDist;
577 
578  protoTrackToLoad.Init(nClustersTemp - 1,
579  pnum,
580  slope,
581  intercept,
582  totalQ,
583  pCornerMin[0],
584  pCornerMin[1],
585  pCornerMax[0],
586  pCornerMax[1],
587  iMinWire,
588  iMaxWire,
589  fMinWire,
590  fMaxWire,
591  lineHits);
592  linesFound->push_back(protoTrackToLoad);
593 
594  }
595 
596  nLinesFound++;
597 
598  if (nLinesFound > (unsigned int)fMaxLines) break;
599 
600  }
601 
602  lastHits.clear();
603 
604  listofxmax.clear();
605  listofymax.clear();
606 
607  // saves a bitmap image of the accumulator (useful for debugging),
608  // with scaling based on the maximum cell value
609  if (fSaveAccumulator) {
610  unsigned char* outPix = new unsigned char[accDx * accDy];
611  //finds the maximum cell in the accumulator for image scaling
612  int cell, pix = 0, maxCell = 0;
613  for (int y = 0; y < accDy; ++y) {
614  for (int x = 0; x < accDx; ++x) {
615  cell = c.GetCell(y, x);
616  if (cell > maxCell) maxCell = cell;
617  }
618  }
619  for (int y = 0; y < accDy; ++y) {
620  for (int x = 0; x < accDx; ++x) {
621  //scales the pixel weights based on the maximum cell value
622  if (maxCell > 0) pix = (int)((1500 * c.GetCell(y, x)) / maxCell);
623  outPix[y * accDx + x] = pix;
624  }
625  }
626 
627  HLSSaveBMPFile("houghaccum.bmp", outPix, accDx, accDy);
628  delete[] outPix;
629  } // end if saving accumulator
630 
631  *nClusters = nClustersTemp;
632 
633  return 1;
634 }
635 
636 //------------------------------------------------------------------------------
637 inline int cluster::HoughTransform::GetCell(int row, int col) const
638 {
639  return m_accum[row][col];
640 } // cluster::HoughTransform::GetCell()
641 
642 //------------------------------------------------------------------------------
643 // returns a vector<int> where the first is the overall maximum,
644 // the second is the max x value, and the third is the max y value.
645 inline std::array<int, 3> cluster::HoughTransform::AddPointReturnMax(int x, int y)
646 {
647  if ((x > (int)m_dx) || (y > (int)m_dy) || x < 0.0 || y < 0.0) {
648  std::array<int, 3> max;
649  max.fill(0);
650  return max;
651  }
652  return DoAddPointReturnMax(x, y, false); // false = add
653 }
654 
655 //------------------------------------------------------------------------------
657 {
658  if ((x > (int)m_dx) || (y > (int)m_dy) || x < 0.0 || y < 0.0) return false;
659  DoAddPointReturnMax(x, y, true); // true = subtract
660  return true;
661 }
662 
663 //------------------------------------------------------------------------------
664 void cluster::HoughTransform::Init(unsigned int dx,
665  unsigned int dy,
666  float rhores,
667  unsigned int numACells)
668 {
669  m_numAngleCells = numACells;
670  m_rhoResolutionFactor = rhores;
671 
672  m_accum.clear();
673  //--- BEGIN issue #19494 -----------------------------------------------------
674  // BulkAllocator.h is currently broken; see issue #19494 and comment in header.
675 #if 0
676  // set the custom allocator for nodes to allocate large chunks of nodes;
677  // one node is 40 bytes plus the size of the counters block.
678  // The math over there sets a bit less than 10 MiB per chunk.
679  // to find out the right type name to put here, comment out this line
680  // (it will suppress some noise), set bDebug to true in
681  // lardata/Utilities/BulkAllocator.h and run this module;
682  // all BulkAllocator instances will advertise that they are being created,
683  // mentioning their referring type. You can also simplyfy it by using the
684  // available typedefs, like here:
686  std::_Rb_tree_node
687  <std::pair<const DistancesMap_t::Key_t, DistancesMap_t::CounterBlock_t>>
688  >::SetChunkSize(
689  10 * ((1048576 / (40 + sizeof(DistancesMap_t::CounterBlock_t))) & ~0x1FFU)
690  );
691 #endif // 0
692  //--- END issue #19494 -------------------------------------------------------
693 
694  m_numAccumulated = 0;
695  m_dx = dx;
696  m_dy = dy;
697  m_rowLength = (unsigned int)(m_rhoResolutionFactor * 2 * std::sqrt(dx * dx + dy * dy));
698  m_accum.resize(m_numAngleCells);
699 
700  // this math must be coherent with the one in GetEquation()
701  double angleStep = PI / m_numAngleCells;
702  m_cosTable.resize(m_numAngleCells);
703  m_sinTable.resize(m_numAngleCells);
704  for (size_t iAngleStep = 0; iAngleStep < m_numAngleCells; ++iAngleStep) {
705  double a = iAngleStep * angleStep;
706  m_cosTable[iAngleStep] = cos(a);
707  m_sinTable[iAngleStep] = sin(a);
708  }
709 }
710 
711 //------------------------------------------------------------------------------
712 void cluster::HoughTransform::GetEquation(float row, float col, float& rho, float& theta) const
713 {
714  theta = (TMath::Pi() * row) / m_numAngleCells;
715  rho = (col - (m_rowLength / 2.)) / m_rhoResolutionFactor;
716 } // cluster::HoughTransform::GetEquation()
717 
718 //------------------------------------------------------------------------------
719 int cluster::HoughTransform::GetMax(int& xmax, int& ymax) const
720 {
721  int maxVal = -1;
722  for (unsigned int i = 0; i < m_accum.size(); i++) {
723 
724  DistancesMap_t::PairValue_t max_counter = m_accum[i].get_max(maxVal);
725  if (max_counter.second > maxVal) {
726  maxVal = max_counter.second;
727  xmax = i;
728  ymax = max_counter.first.key();
729  }
730  } // for angle
731 
732  return maxVal;
733 }
734 
735 //------------------------------------------------------------------------------
736 // returns a vector<int> where the first is the overall maximum,
737 // the second is the max x value, and the third is the max y value.
739  int y,
740  bool bSubtract /* = false */)
741 {
742  std::array<int, 3> max;
743  max.fill(-1);
744 
745  // max_val is the current maximum number of hits aligned on a line so far;
746  // currently the code ignores all the lines with just two aligned hits
747  int max_val = 2;
748 
749  const int distCenter = (int)(m_rowLength / 2.);
750 
751  // prime the lastDist variable so our linear fill works below
752  // lastDist represents next distance to be incremented (but see below)
753  int lastDist = (int)(distCenter + (m_rhoResolutionFactor * x));
754 
755  // loop through all angles a from 0 to 180 degrees
756  // (the value of the angle is established in definition of m_cosTable and
757  // m_sinTable in HoughTransform::Init()
758  for (size_t iAngleStep = 1; iAngleStep < m_numAngleCells; ++iAngleStep) {
759 
760  // Calculate the basic line equation dist = cos(a)*x + sin(a)*y.
761  // Shift to center of row to cover negative values;
762  // this math must also be coherent with the one in GetEquation()
763  const int dist = (int)(distCenter + m_rhoResolutionFactor * (m_cosTable[iAngleStep] * x +
764  m_sinTable[iAngleStep] * y));
765 
766  /*
767  * For this angle, we are going to increment all the cells starting from the
768  * last distance in the previous loop, up to the current one (dist),
769  * with the exception that if we are incrementing more than one cell,
770  * we do not increment dist cell itself (it will be incremented in the
771  * next angle).
772  * The cell of the last distance is always incremented,
773  * whether it was also for the previous angle (in case there was only one
774  * distance to be incremented) or not (if there was a sequence of distances
775  * to increment, and then the last distance was not).
776  * First we increment the last cell of our range; this provides us with a
777  * hint of where the immediate previous cell should be, which saves us a
778  * look up.
779  * We collect and return information about the local maximum among the cells
780  * we are increasing.
781  */
782 
783  // establish the range of cells to increase: [ first_dist, end_dist [ ;
784  // also set lastDist so that it points to the next cell to be incremented,
785  // according to the rules described above
786  int first_dist;
787  int end_dist;
788  if (lastDist == dist) {
789  // the range is [ dist, dist + 1 [ (that is, [ dist ]
790  first_dist = dist;
791  end_dist = dist + 1;
792  }
793  else {
794  // the range is [ lastDist, dist [ or ] dist, lastDist]
795  first_dist = dist > lastDist ? lastDist : dist + 1;
796  end_dist = dist > lastDist ? dist : lastDist + 1;
797  }
798 
799  DistancesMap_t& distMap = m_accum[iAngleStep];
800  if (bSubtract) { distMap.decrement(first_dist, end_dist); }
801  else {
802  DistancesMap_t::PairValue_t max_counter =
803  distMap.increment_and_get_max(first_dist, end_dist, max_val);
804 
805  if (max_counter.second > max_val) {
806  // DEBUG
807  // std::cout << " <NEW MAX " << max_val << " => " << max_counter.second << " >" << std::endl;
808  // BUG the double brace syntax is required to work around clang bug 21629
809  // (https://bugs.llvm.org/show_bug.cgi?id=21629)
810  max = {{max_counter.second, max_counter.first.key(), (int)iAngleStep}};
811  max_val = max_counter.second;
812  }
813  }
814  lastDist = dist;
815 
816  // DEBUG
817  // std::cout << "\n (max " << max[1] << " => " << max[0] << ")" << std::endl;
818  // }
819  } // for angles
820  if (bSubtract)
822  else
824 
825  //mf::LogVerbatim("HoughBaseAlg") << "Add point says xmax: " << *xmax << " ymax: " << *ymax << std::endl;
826 
827  return max;
828 } // cluster::HoughTransform::DoAddPointReturnMax()
829 
830 //------------------------------------------------------------------------------
831 //this method saves a BMP image of the Hough Accumulator, which can be viewed with gimp
832 void cluster::HoughBaseAlg::HLSSaveBMPFile(const char* fileName, unsigned char* pix, int dx, int dy)
833 {
834  std::ofstream bmpFile(fileName, std::ios::binary);
835  bmpFile.write("B", 1);
836  bmpFile.write("M", 1);
837  int bitsOffset = 54 + 256 * 4;
838  int size = bitsOffset + dx * dy; //header plus 256 entry LUT plus pixels
839  bmpFile.write((const char*)&size, 4);
840  int reserved = 0;
841  bmpFile.write((const char*)&reserved, 4);
842  bmpFile.write((const char*)&bitsOffset, 4);
843  int bmiSize = 40;
844  bmpFile.write((const char*)&bmiSize, 4);
845  bmpFile.write((const char*)&dx, 4);
846  bmpFile.write((const char*)&dy, 4);
847  short planes = 1;
848  bmpFile.write((const char*)&planes, 2);
849  short bitCount = 8;
850  bmpFile.write((const char*)&bitCount, 2);
851  int i, temp = 0;
852  for (i = 0; i < 6; i++)
853  bmpFile.write((const char*)&temp, 4); // zero out optional color info
854  // write a linear LUT
855  char lutEntry[4]; // blue,green,red
856  lutEntry[3] = 0; // reserved part
857  for (i = 0; i < 256; i++) {
858  lutEntry[0] = lutEntry[1] = lutEntry[2] = i;
859  bmpFile.write(lutEntry, sizeof lutEntry);
860  }
861  // write the actual pixels
862  bmpFile.write((const char*)pix, dx * dy);
863 }
864 
865 //------------------------------------------------------------------------------
867  std::vector<recob::Cluster>& ccol,
869  CLHEP::HepRandomEngine& engine,
870  art::Event const& evt,
871  std::string const& label)
872 {
873  std::vector<int> skip;
874 
875  art::FindManyP<recob::Hit> fmh(clusIn, evt, label);
876 
877  geo::GeometryCore const* geom = lar::providerFrom<geo::Geometry>();
878  HoughTransform c;
879 
880  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(evt);
881  auto const detProp =
883  util::GeometryUtilities const gser{*geom, clockData, detProp};
884 
885  // prepare the algorithm to compute the cluster characteristics;
886  // we use the "standard" one here; configuration would happen here,
887  // but we are using the default configuration for that algorithm
889 
890  std::vector<art::Ptr<recob::Hit>> hit;
891 
892  for (auto view : geom->Views()) {
893 
894  MF_LOG_DEBUG("HoughBaseAlg") << "Analyzing view " << view;
895 
896  art::PtrVector<recob::Cluster>::const_iterator clusterIter = clusIn.begin();
897  int clusterID = 0; //the unique ID of the cluster
898 
899  size_t cinctr = 0;
900  while (clusterIter != clusIn.end()) {
901 
902  MF_LOG_DEBUG("HoughBaseAlg")
903  << "Analyzing cinctr=" << cinctr << " which is in view " << (*clusterIter)->View();
904 
905  hit.clear();
906  if (fPerCluster) {
907  if ((*clusterIter)->View() == view) hit = fmh.at(cinctr);
908  }
909  else {
910  while (clusterIter != clusIn.end()) {
911  if ((*clusterIter)->View() == view) {
912 
913  hit = fmh.at(cinctr);
914  } // end if cluster is in correct view
915  clusterIter++;
916  ++cinctr;
917  } //end loop over clusters
918  } //end if not fPerCluster
919  if (hit.size() == 0) {
920  if (fPerCluster) {
921  clusterIter++;
922  ++cinctr;
923  }
924  continue;
925  }
926 
927  MF_LOG_DEBUG("HoughBaseAlg") << "We have all the hits..." << hit.size();
928 
929  std::vector<double> slopevec;
930  std::vector<ChargeInfo_t> totalQvec;
931  std::vector<art::PtrVector<recob::Hit>> planeClusHitsOut;
932  this->FastTransform(clockData, detProp, hit, planeClusHitsOut, engine, slopevec, totalQvec);
933 
934  MF_LOG_DEBUG("HoughBaseAlg") << "Made it through FastTransform" << planeClusHitsOut.size();
935 
936  for (size_t xx = 0; xx < planeClusHitsOut.size(); ++xx) {
937  auto const& hits = planeClusHitsOut.at(xx);
938  recob::Hit const& FirstHit = *hits.front();
939  recob::Hit const& LastHit = *hits.back();
940  const unsigned int sw = FirstHit.WireID().Wire;
941  const unsigned int ew = LastHit.WireID().Wire;
942 
943  // feed the algorithm with all the cluster hits
944  ClusterParamAlgo.ImportHits(gser, hits);
945 
946  // create the recob::Cluster directly in the vector;
947  // NOTE usually we would use cluster::ClusterCreator to save some typing
948  // and some mistakes. In this case, we don't want to pull in the
949  // dependency on ClusterFinder, where ClusterCreator currently lives
950  ccol.emplace_back(float(sw), // start_wire
951  0., // sigma_start_wire
952  FirstHit.PeakTime(), // start_tick
953  FirstHit.SigmaPeakTime(), // sigma_start_tick
954  ClusterParamAlgo.StartCharge(gser).value(),
955  ClusterParamAlgo.StartAngle().value(),
956  ClusterParamAlgo.StartOpeningAngle().value(),
957  float(ew), // end_wire
958  0., // sigma_end_wire,
959  LastHit.PeakTime(), // end_tick
960  LastHit.SigmaPeakTime(), // sigma_end_tick
961  ClusterParamAlgo.EndCharge(gser).value(),
962  ClusterParamAlgo.EndAngle().value(),
963  ClusterParamAlgo.EndOpeningAngle().value(),
964  ClusterParamAlgo.Integral().value(),
965  ClusterParamAlgo.IntegralStdDev().value(),
966  ClusterParamAlgo.SummedADC().value(),
967  ClusterParamAlgo.SummedADCStdDev().value(),
968  ClusterParamAlgo.NHits(),
969  ClusterParamAlgo.MultipleHitDensity(),
970  ClusterParamAlgo.Width(gser),
971  clusterID,
972  FirstHit.View(),
973  FirstHit.WireID().planeID(),
975 
976  ++clusterID;
977  clusHitsOut.push_back(planeClusHitsOut.at(xx));
978  }
979 
980  hit.clear();
981  if (clusterIter != clusIn.end()) {
982  clusterIter++;
983  ++cinctr;
984  }
985  // listofxmax.clear();
986  // listofymax.clear();
987  } // end loop over clusters
988 
989  } // end loop over views
990 
991  return ccol.size();
992 }
993 
994 //------------------------------------------------------------------------------
996  detinfo::DetectorPropertiesData const& detProp,
997  std::vector<art::Ptr<recob::Hit>> const& clusIn,
999  CLHEP::HepRandomEngine& engine)
1000 {
1001  std::vector<double> slopevec;
1002  std::vector<ChargeInfo_t> totalQvec;
1003  return FastTransform(clockData, detProp, clusIn, clusHitsOut, engine, slopevec, totalQvec);
1004 }
1005 
1006 //------------------------------------------------------------------------------
1008  detinfo::DetectorPropertiesData const& detProp,
1009  std::vector<art::Ptr<recob::Hit>> const& clusIn,
1011  CLHEP::HepRandomEngine& engine,
1012  std::vector<double>& slopevec,
1013  std::vector<ChargeInfo_t>& totalQvec)
1014 {
1015  std::vector<int> skip;
1016 
1017  geo::GeometryCore const* geom = lar::providerFrom<geo::Geometry>();
1018  lariov::ChannelStatusProvider const* channelStatus =
1019  lar::providerFrom<lariov::ChannelStatusService>();
1020 
1021  CLHEP::RandFlat flat(engine);
1022 
1023  std::vector<art::Ptr<recob::Hit>> hit;
1024 
1025  size_t cinctr = 0;
1026  hit.clear();
1027  for (std::vector<art::Ptr<recob::Hit>>::const_iterator ii = clusIn.begin(); ii != clusIn.end();
1028  ii++)
1029  hit.push_back((*ii)); // this is new
1030 
1031  if (hit.size() == 0) {
1032  if (fPerCluster) { ++cinctr; }
1033  return -1;
1034  }
1035 
1036  geo::WireID const& wireid = hit.at(0)->WireID();
1037  auto const& tpcid = wireid.asPlaneID().asTPCID();
1038 
1039  geo::SigType_t sigt = geom->SignalType(wireid);
1040 
1041  if (hit.size() == 0) {
1042  if (fPerCluster) { ++cinctr; }
1043  return -1; // continue;
1044  }
1045 
1046  auto const num_planes = geom->Nplanes(tpcid);
1047  std::vector<double> wire_pitch(num_planes, 0.);
1048  for (auto const& plane : geom->Iterate<geo::PlaneGeo>(tpcid)) {
1049  auto const p = plane.ID().Plane;
1050  wire_pitch[p] = plane.WirePitch();
1051  }
1052 
1053  //factor to make x and y scale the same units
1054  std::vector<double> xyScale(num_planes, 0.);
1055 
1057  double driftVelFactor = 0.001 * detProp.DriftVelocity(detProp.Efield(), detProp.Temperature());
1058 
1059  for (size_t p = 0; p < xyScale.size(); ++p)
1060  xyScale[p] = driftVelFactor * sampling_rate(clockData) / wire_pitch[p];
1061 
1062  int x = 0, y = 0;
1063  int dx = geom->Plane(wireid.asPlaneID()).Nwires(); // number of wires
1064  const int dy = detProp.ReadOutWindowSize(); // number of time samples.
1065  skip.clear();
1066  skip.resize(hit.size());
1067  std::vector<int> listofxmax;
1068  std::vector<int> listofymax;
1069  std::vector<int> hitTemp; //indecies ofcandidate hits
1070  std::vector<int> sequenceHolder; //channels of hits in list
1071  std::vector<int> currentHits; //working vector of hits
1072  std::vector<int> lastHits; //best list of hits
1073  art::PtrVector<recob::Hit> clusterHits;
1074  float indcolscaling = 0.; //a parameter to account for the different
1075  //characteristic hit width of induction and collection plane
1076  float centerofmassx = 0;
1077  float centerofmassy = 0;
1078  float denom = 0;
1079  float intercept = 0.;
1080  float slope = 0.;
1081  //this array keeps track of the hits that have already been associated with a line.
1082  int xMax = 0;
1083  int yMax = 0;
1084  float rho;
1085  float theta;
1086  int accDx(0), accDy(0);
1087 
1088  HoughTransform c;
1089  //Init specifies the size of the two-dimensional accumulator
1090  //(based on the arguments, number of wires and number of time samples).
1091  //adds all of the hits (that have not yet been associated with a line) to the accumulator
1092  c.Init(dx, dy, fRhoResolutionFactor, fNumAngleCells);
1093 
1094  // count is how many points are left to randomly insert
1095  unsigned int count = hit.size();
1096  std::vector<unsigned int> accumPoints;
1097  accumPoints.resize(hit.size());
1098  int nAccum = 0;
1099  unsigned int nLinesFound = 0;
1100 
1101  for (; count > 0; count--) {
1102 
1103  // The random hit we are examining
1104  unsigned int randInd = (unsigned int)(flat.fire() * hit.size());
1105 
1106  MF_LOG_DEBUG("HoughBaseAlg") << "randInd=" << randInd << " and size is " << hit.size();
1107 
1108  // Skip if it's already in a line
1109  if (skip.at(randInd) == 1) continue;
1110 
1111  // If we have already accumulated the point, skip it
1112  if (accumPoints.at(randInd)) continue;
1113  accumPoints.at(randInd) = 1;
1114 
1115  // zeroes out the neighborhood of all previous lines
1116  for (unsigned int i = 0; i < listofxmax.size(); ++i) {
1117  int yClearStart = listofymax.at(i) - fRhoZeroOutRange;
1118  if (yClearStart < 0) yClearStart = 0;
1119 
1120  int yClearEnd = listofymax.at(i) + fRhoZeroOutRange;
1121  if (yClearEnd >= accDy) yClearEnd = accDy - 1;
1122 
1123  int xClearStart = listofxmax.at(i) - fThetaZeroOutRange;
1124  if (xClearStart < 0) xClearStart = 0;
1125 
1126  int xClearEnd = listofxmax.at(i) + fThetaZeroOutRange;
1127  if (xClearEnd >= accDx) xClearEnd = accDx - 1;
1128 
1129  for (y = yClearStart; y <= yClearEnd; ++y) {
1130  for (x = xClearStart; x <= xClearEnd; ++x) {
1131  c.SetCell(y, x, 0);
1132  }
1133  }
1134  } // end loop over size of listxmax
1135 
1136  //find the weightiest cell in the accumulator.
1137  int maxCell = 0;
1138  unsigned int wireMax = hit.at(randInd)->WireID().Wire;
1139 
1140  // Add the randomly selected point to the accumulator
1141  std::array<int, 3> max = c.AddPointReturnMax(wireMax, (int)(hit.at(randInd)->PeakTime()));
1142  maxCell = max.at(0);
1143  xMax = max.at(1);
1144  yMax = max.at(2);
1145  nAccum++;
1146 
1147  // Continue if the biggest maximum for the randomly selected point is smaller than fMinHits
1148  if (maxCell < fMinHits) continue;
1149 
1150  // Find the center of mass of the 3x3 cell system (with maxCell at the center).
1151  denom = centerofmassx = centerofmassy = 0;
1152 
1153  if (xMax > 0 && yMax > 0 && xMax + 1 < accDx && yMax + 1 < accDy) {
1154  for (int i = -1; i < 2; ++i) {
1155  for (int j = -1; j < 2; ++j) {
1156  int cell = c.GetCell(yMax + i, xMax + j);
1157  denom += cell;
1158  centerofmassx += j * cell;
1159  centerofmassy += i * cell;
1160  }
1161  }
1162  centerofmassx /= denom;
1163  centerofmassy /= denom;
1164  }
1165  else
1166  centerofmassx = centerofmassy = 0;
1167 
1168  //fill the list of cells that have already been found
1169  listofxmax.push_back(xMax);
1170  listofymax.push_back(yMax);
1171 
1172  // Find the lines equation
1173  c.GetEquation(yMax + centerofmassy, xMax + centerofmassx, rho, theta);
1174  MF_LOG_DEBUG("HoughBaseAlg") << "Transform(I) found maximum at (d=" << rho << " a=" << theta
1175  << ")"
1176  " from absolute maximum "
1177  << c.GetCell(yMax, xMax) << " at (d=" << yMax << ", a=" << xMax
1178  << ")";
1179  slope = -1. / tan(theta);
1180  intercept = (rho / sin(theta));
1181  double distance;
1185  if (sigt == geo::kInduction)
1186  indcolscaling = 5.;
1187  else
1188  indcolscaling = 0.;
1189 
1190  // note this doesn't work for wrapped wire planes!
1191  if (!std::isinf(slope) && !std::isnan(slope)) {
1192  sequenceHolder.clear();
1193  hitTemp.clear();
1194  for (size_t i = 0; i < hit.size(); ++i) {
1195  distance = (TMath::Abs(hit.at(i)->PeakTime() - slope * (double)(hit.at(i)->WireID().Wire) -
1196  intercept) /
1197  (std::sqrt(pow(xyScale[hit.at(i)->WireID().Plane] * slope, 2) + 1)));
1198 
1199  if (distance < fMaxDistance + hit.at(i)->RMS() + indcolscaling && skip.at(i) != 1) {
1200  hitTemp.push_back(i);
1201  sequenceHolder.push_back(hit.at(i)->Channel());
1202  }
1203 
1204  } // end loop over hits
1205 
1206  if (hitTemp.size() < 2) continue;
1207  currentHits.clear();
1208  lastHits.clear();
1209  int j;
1210  currentHits.push_back(0);
1211  for (size_t i = 0; i + 1 < sequenceHolder.size(); ++i) {
1212  j = 1;
1213  while ((channelStatus->IsBad(sequenceHolder.at(i) + j)) == true)
1214  j++;
1215  if (sequenceHolder.at(i + 1) - sequenceHolder.at(i) <= j + fMissedHits)
1216  currentHits.push_back(i + 1);
1217  else if (currentHits.size() > lastHits.size()) {
1218  lastHits = currentHits;
1219  currentHits.clear();
1220  }
1221  else
1222  currentHits.clear();
1223  }
1224 
1225  if (currentHits.size() > lastHits.size()) lastHits = currentHits;
1226 
1227  // Check if lastHits has hits with big gaps in it
1228  // lastHits[i] is ordered in increasing channel and then increasing peak time,
1229  // as a consequence, if the line has a negative slope and there are multiple hits in the line for a channel,
1230  // we have to go back to the first hit (in terms of lastHits[i]) of that channel to find the distance
1231  // between hits
1232  //std::cout << "New line" << std::endl;
1233  double tickToDist = detProp.DriftVelocity(detProp.Efield(), detProp.Temperature());
1234  tickToDist *= 1.e-3 * sampling_rate(clockData); // 1e-3 is conversion of 1/us to 1/ns
1235  int missedHits = 0;
1236  int lastHitsChannel = 0; //lastHits.at(0);
1237  int nHitsPerChannel = 1;
1238 
1239  MF_LOG_DEBUG("HoughBaseAlg") << "filling the pCorner arrays around here..."
1240  << "\n but first, lastHits size is " << lastHits.size()
1241  << " and lastHitsChannel=" << lastHitsChannel;
1242 
1243  double pCorner0[2];
1244  double pCorner1[2];
1245  unsigned int lastChannel = hit.at(hitTemp.at(lastHits.at(0)))->Channel();
1246 
1247  for (size_t i = 0; i < lastHits.size() - 1; ++i) {
1248  bool newChannel = false;
1249  if (slope < 0) {
1250  if (hit.at(hitTemp.at(lastHits.at(i + 1)))->Channel() != lastChannel) {
1251  newChannel = true;
1252  }
1253  if (hit.at(hitTemp.at(lastHits.at(i + 1)))->Channel() == lastChannel) nHitsPerChannel++;
1254  }
1255 
1258 
1259  if (slope > 0 || (!newChannel && nHitsPerChannel <= 1)) {
1260 
1261  pCorner0[0] = (hit.at(hitTemp.at(lastHits.at(i)))->Channel()) * wire_pitch[0];
1262  pCorner0[1] = hit.at(hitTemp.at(lastHits.at(i)))->PeakTime() * tickToDist;
1263  pCorner1[0] = (hit.at(hitTemp.at(lastHits.at(i + 1)))->Channel()) * wire_pitch[0];
1264  pCorner1[1] = hit.at(hitTemp.at(lastHits.at(i + 1)))->PeakTime() * tickToDist;
1265  if (std::sqrt(pow(pCorner0[0] - pCorner1[0], 2) + pow(pCorner0[1] - pCorner1[1], 2)) >
1266  fMissedHitsDistance)
1267  missedHits++;
1268  }
1269 
1270  else if (slope < 0 && newChannel && nHitsPerChannel > 1) {
1271 
1272  pCorner0[0] =
1273  (hit.at(hitTemp.at(lastHits.at(lastHitsChannel)))->Channel()) * wire_pitch[0];
1274  pCorner0[1] = hit.at(hitTemp.at(lastHits.at(lastHitsChannel)))->PeakTime() * tickToDist;
1275  pCorner1[0] = (hit.at(hitTemp.at(lastHits.at(i + 1)))->Channel()) * wire_pitch[0];
1276  pCorner1[1] = hit.at(hitTemp.at(lastHits.at(i + 1)))->PeakTime() * tickToDist;
1277  if (std::sqrt(pow(pCorner0[0] - pCorner1[0], 2) + pow(pCorner0[1] - pCorner1[1], 2)) >
1278  fMissedHitsDistance)
1279  missedHits++;
1280  lastChannel = hit.at(hitTemp.at(lastHits.at(i)))->Channel();
1281  lastHitsChannel = i + 1;
1282  nHitsPerChannel = 0;
1283  }
1284  }
1285 
1286  if ((double)missedHits / ((double)lastHits.size() - 1) > fMissedHitsToLineSize) continue;
1287 
1288  clusterHits.clear();
1289  if (lastHits.size() < 5) continue;
1290 
1291  // reduce rounding errors by using double (RMS is very sensitive to them)
1292  lar::util::StatCollector<double> integralQ, summedQ;
1293 
1294  for (size_t i = 0; i < lastHits.size(); ++i) {
1295  clusterHits.push_back(hit.at(hitTemp.at(lastHits.at(i))));
1296  integralQ.add(clusterHits.back()->Integral());
1297  summedQ.add(clusterHits.back()->SummedADC());
1298  skip.at(hitTemp.at(lastHits.at(i))) = 1;
1299  }
1300  //protection against very steep uncorrelated hits
1301  if (std::abs(slope) > fMaxSlope) continue;
1302 
1303  clusHitsOut.push_back(clusterHits);
1304  slopevec.push_back(slope);
1305  totalQvec.emplace_back(integralQ.Sum(),
1306  integralQ.RMS(), // TODO biased value; should unbias?
1307  summedQ.Sum(),
1308  summedQ.RMS() // TODO biased value; should unbias?
1309  );
1310 
1311  } // end if !std::isnan
1312 
1313  nLinesFound++;
1314 
1315  if (nLinesFound > (unsigned int)fMaxLines) break;
1316 
1317  } // end loop over hits
1318 
1319  // saves a bitmap image of the accumulator (useful for debugging),
1320  // with scaling based on the maximum cell value
1321  if (fSaveAccumulator) {
1322  //finds the maximum cell in the accumulator for image scaling
1323  int cell, pix = 0, maxCell = 0;
1324  for (y = 0; y < accDy; ++y) {
1325  for (x = 0; x < accDx; ++x) {
1326  cell = c.GetCell(y, x);
1327  if (cell > maxCell) maxCell = cell;
1328  }
1329  }
1330 
1331  std::unique_ptr<unsigned char[]> outPix(new unsigned char[accDx * accDy]);
1332  unsigned int PicIndex = 0;
1333  for (y = 0; y < accDy; ++y) {
1334  for (x = 0; x < accDx; ++x) {
1335  //scales the pixel weights based on the maximum cell value
1336  if (maxCell > 0) pix = (int)((1500 * c.GetCell(y, x)) / maxCell);
1337  outPix[PicIndex++] = pix;
1338  }
1339  }
1340 
1341  HLSSaveBMPFile("houghaccum.bmp", outPix.get(), accDx, accDy);
1342  } // end if saving accumulator
1343 
1344  hit.clear();
1345  lastHits.clear();
1346  listofxmax.clear();
1347  listofymax.clear();
1348  return clusHitsOut.size();
1349 }
1350 
1351 //------------------------------------------------------------------------------
1354  double& slope,
1355  double& intercept)
1356 {
1357  HoughTransform c;
1358 
1360 
1361  int dx = geom->Nwires(geo::PlaneID{0, 0, 0}); // number of wires
1362  const int dy = detProp.ReadOutWindowSize(); // number of time samples.
1363 
1364  c.Init(dx, dy, fRhoResolutionFactor, fNumAngleCells);
1365 
1366  for (unsigned int i = 0; i < hits.size(); ++i) {
1367  c.AddPointReturnMax(hits[i]->WireID().Wire, (int)(hits[i]->PeakTime()));
1368  } // end loop over hits
1369 
1370  //gets the actual two-dimensional size of the accumulator
1371  int accDx = 0;
1372  int accDy = 0;
1373  c.GetAccumSize(accDy, accDx);
1374 
1375  //find the weightiest cell in the accumulator.
1376  int xMax = 0;
1377  int yMax = 0;
1378  c.GetMax(yMax, xMax);
1379 
1380  //find the center of mass of the 3x3 cell system (with maxCell at the center).
1381  float centerofmassx = 0.;
1382  float centerofmassy = 0.;
1383  float denom = 0.;
1384 
1385  if (xMax > 0 && yMax > 0 && xMax + 1 < accDx && yMax + 1 < accDy) {
1386  for (int i = -1; i < 2; ++i) {
1387  for (int j = -1; j < 2; ++j) {
1388  denom += c.GetCell(yMax + i, xMax + j);
1389  centerofmassx += j * c.GetCell(yMax + i, xMax + j);
1390  centerofmassy += i * c.GetCell(yMax + i, xMax + j);
1391  }
1392  }
1393  centerofmassx /= denom;
1394  centerofmassy /= denom;
1395  }
1396  else
1397  centerofmassx = centerofmassy = 0;
1398 
1399  float rho = 0.;
1400  float theta = 0.;
1401  c.GetEquation(yMax + centerofmassy, xMax + centerofmassx, rho, theta);
1402  MF_LOG_DEBUG("HoughBaseAlg") << "Transform(III) found maximum at (d=" << rho << " a=" << theta
1403  << ")"
1404  " from absolute maximum "
1405  << c.GetCell(yMax, xMax) << " at (d=" << yMax << ", a=" << xMax
1406  << ")";
1407  slope = -1. / tan(theta);
1408  intercept = rho / sin(theta);
1409 
1412 
1413  return hits.size();
1414 }
typename Base_t::SubCounter_t SubCounter_t
Definition: HoughBaseAlg.h:290
Float_t x
Definition: compare.C:6
intermediate_table::iterator iterator
details::range_type< T > Iterate() const
Initializes the specified ID with the ID of the first cryostat.
Definition: GeometryCore.h:541
void Init(unsigned int dx, unsigned int dy, float rhores, unsigned int numACells)
Double_t xx
Definition: macro.C:12
Utilities related to art service access.
typename Traits_t::CounterBlock_t CounterBlock_t
Definition: CountersMap.h:154
decltype(auto) constexpr cend(T &&obj)
ADL-aware version of std::cend.
Definition: StdUtils.h:93
Encapsulate the construction of a single cyostat.
int GetMax(int &xmax, int &ymax) const
KEY Key_t
type of counter key in the map
Definition: CountersMap.h:138
std::vector< double > m_cosTable
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
Declaration of signal hit object.
Float_t y
Definition: compare.C:6
typename Base_t::CounterKey_t CounterKey_t
Definition: HoughBaseAlg.h:433
The data type to uniquely identify a Plane.
Definition: geo_types.h:463
double Temperature() const
In kelvin.
unsigned int m_numAngleCells
constexpr auto abs(T v)
Returns the absolute value of the argument.
size_t FastTransform(const std::vector< art::Ptr< recob::Cluster >> &clusIn, std::vector< recob::Cluster > &ccol, std::vector< art::PtrVector< recob::Hit >> &clusHitsOut, CLHEP::HepRandomEngine &engine, art::Event const &evt, std::string const &label)
std::vector< DistancesMap_t > HoughImage_t
Type of the Hough transform (angle, distance) map with custom allocator.
geo::View_t View() const
View for the plane of the hit.
Definition: Hit.h:276
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:563
geo::WireID const & WireID() const
Initial tdc tick for hit.
Definition: Hit.h:280
Cluster finding and building.
std::pair< const_iterator, SubCounter_t > PairValue_t
Pair identifying a counter and its current value.
Definition: HoughBaseAlg.h:295
Classes gathering simple statistics.
bool SubtractPoint(int x, int y)
Weight_t RMS() const
Returns the root mean square.
constexpr double PI
void SetCell(int row, int col, int value)
static const SentryArgument_t Sentry
An instance of the sentry object.
Definition: Cluster.h:174
void ImportHits(util::GeometryUtilities const &gser, Iter begin, Iter end)
Calls SetHits() with the hits in the sequence.
double Efield(unsigned int planegap=0) const
kV/cm
PairValue_t unchecked_add_range_max(Key_t key_begin, Key_t key_end, SubCounter_t delta, typename BaseMap_t::iterator start, SubCounter_t min_max=std::numeric_limits< SubCounter_t >::min())
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:101
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:289
reference back()
Definition: PtrVector.h:387
PlaneGeo const & Plane(PlaneID const &planeid) const
Returns the specified wire.
void hits()
Definition: readHits.C:15
typename data_t::const_iterator const_iterator
Definition: PtrVector.h:55
std::array< int, 3 > DoAddPointReturnMax(int x, int y, bool bSubtract=false)
IDparameter< geo::WireID > WireID
Member type of validated geo::WireID parameter.
void push_back(Ptr< U > const &p)
Definition: PtrVector.h:435
Signal from induction planes.
Definition: geo_types.h:151
Weight_t Sum() const
Returns the weighted sum of the values.
void GetAccumSize(int &numRows, int &numCols)
enum geo::_plane_sigtype SigType_t
Enumerate the possible plane projections.
Int_t col[ntarg]
Definition: Style.C:29
Wrapper for ClusterParamsAlgBase objects to accept diverse input.
T get(std::string const &key) const
Definition: ParameterSet.h:314
Geometry information for a single wire plane.The plane is represented in the geometry by a solid whic...
Definition: PlaneGeo.h:78
iterator end()
Definition: PtrVector.h:231
Aggressive allocator reserving a lot of memory in advance.
Definition: BulkAllocator.h:89
Wrapper for ClusterParamsAlgBase objects to accept arbitrary input.
constexpr PlaneID const & asPlaneID() const
Conversion to PlaneID (for convenience of notation).
Definition: geo_types.h:520
void reconfigure(fhicl::ParameterSet const &pset)
double DriftVelocity(double efield=0., double temperature=0.) const
cm/us
The data type to uniquely identify a TPC.
Definition: geo_types.h:381
Description of geometry of one entire detector.
Definition: GeometryCore.h:119
Declaration of cluster object.
int GetCell(int row, int col) const
SubCounter_t decrement(Key_t key)
Decrements by 1 the specified counter.
Definition: HoughBaseAlg.h:323
std::array< int, 3 > AddPointReturnMax(int x, int y)
double value
Definition: spectrum.C:18
Detector simulation of raw signals on wires.
constexpr TPCID const & asTPCID() const
Conversion to TPCID (for convenience of notation).
Definition: geo_types.h:438
void GetEquation(float row, float col, float &rho, float &theta) const
PairValue_t get_max() const
Increments by 1 the specified counters and returns the maximum.
CountersMap with access optimized for Hough Transform algorithm.
Definition: HoughBaseAlg.h:279
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:94
Float_t sw
Definition: plot.C:20
float PeakTime() const
Time of the signal peak, in tick units.
Definition: Hit.h:220
typename Base_t::CounterBlock_t CounterBlock_t
Definition: HoughBaseAlg.h:291
SigType_t SignalType(PlaneID const &pid) const
Returns the type of signal on the channels of specified TPC plane.
HoughTransformCounters< int, signed char, 64 > BaseMap_t
rho -> # hits (for convenience)
Encapsulate the construction of a single detector plane.
Contains all timing reference information for the detector.
constexpr double dist(const TReal *x, const TReal *y, const unsigned int dimension)
#define MF_LOG_DEBUG(id)
HoughImage_t m_accum
column (map key)=rho, row (vector index)=theta
unsigned int Nwires(PlaneID const &planeid) const
Returns the total number of wires in the specified plane.
SubCounter_t increment(Key_t key)
Increments by 1 the specified counter.
Definition: HoughBaseAlg.h:316
void Init(unsigned int num=999999, unsigned int pnum=999999, float slope=999999, float intercept=999999, float totalQTemp=-999999, float Min0=999999, float Min1=999999, float Max0=-999999, float Max1=-999999, int iMinWireTemp=999999, int iMaxWireTemp=-999999, int minWireTemp=999999, int maxWireTemp=-999999, std::vector< art::Ptr< recob::Hit >> hitsTemp=std::vector< art::Ptr< recob::Hit >>())
Definition: HoughBaseAlg.h:224
std::set< View_t > const & Views() const
Returns a list of possible views in the detector.
unsigned int Nplanes(TPCID const &tpcid=tpc_zero) const
Returns the total number of planes in the specified TPC.
Definition: GeometryCore.h:977
Char_t n[5]
HoughBaseAlg(fhicl::ParameterSet const &pset)
float SigmaPeakTime() const
Uncertainty for the signal peak, in tick units.
Definition: Hit.h:224
std::vector< double > m_sinTable
size_t Transform(const detinfo::DetectorClocksData &clockData, detinfo::DetectorPropertiesData const &detProp, std::vector< art::Ptr< recob::Hit >> const &hits, CLHEP::HepRandomEngine &engine, std::vector< unsigned int > *fpointId_to_clusterId, unsigned int clusterId, unsigned int *nClusters, std::vector< protoTrack > *protoTracks)
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:46
constexpr PlaneID const & planeID() const
Definition: geo_types.h:620
Counter_t SubCounter_t
Type of the subcounter (that is, the actual counter)
Definition: CountersMap.h:152
Interface to class computing cluster parameters.
TCEvent evt
Definition: DataStructs.cxx:8
typename Base_t::const_iterator const_iterator
Definition: HoughBaseAlg.h:292
Float_t e
Definition: plot.C:35
double sampling_rate(DetectorClocksData const &data)
Returns the period of the TPC readout electronics clock.
PairValue_t increment_and_get_max(Key_t key_begin, Key_t key_end)
Increments by 1 the specified counters and returns the maximum.
Definition: HoughBaseAlg.h:366
void clear()
Definition: PtrVector.h:533
Float_t w
Definition: plot.C:20
Collects statistics on a single quantity (weighted)
HoughTransformCounters< int, signed char, 64 > DistancesMap_t
art framework interface to geometry description
Encapsulate the construction of a single detector plane.
void add(Data_t value, Weight_t weight=Weight_t(1.0))
Adds one entry with specified value and weight.
T sqr(T v)
void HLSSaveBMPFile(char const *, unsigned char *, int, int)
SubCounter_t unchecked_set_range(Key_t key_begin, Key_t key_end, SubCounter_t value, typename BaseMap_t::iterator start)