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