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