LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
HoughBaseAlg.h
Go to the documentation of this file.
1 // HoughBaseAlg.h
3 //
4 // HoughBaseAlg class
5 //
6 // Ben Carls (bcarls@fnal.gov)
7 // Some optimization by Gianluca Petrillo (petrillo@fnal.gov)
8 //
9 // Hough transform algorithm
10 // ============================================================================
11 //
12 // This implementation is a pattern recognition algorithm trying to detect
13 // straight lines in a image.
14 // In our application, the image is a hitmap on a wire plane, represented by
15 // wire number as one coordinate and drift time as the other.
16 // For each hit coordinate, all straight lines through that coordinate are
17 // recorded on counters, one for each line. If a counter rises to two, it
18 // mesans that there are two hits who can cast that line, i.e. there are two
19 // hits on that line.
20 // A line can be represented by two independent parameters, therefore we need a
21 // two-dimension parameter space to represent all of them (two degrees of
22 // freedom).
23 // Since there are infinite straight lines, each counter represents not just
24 // one line but a small region of parameter space.
25 // Passing by one point constraints one of the two parameters, therefore the
26 // parameter set of a generic line passing by a given point has one degree of
27 // freedom.
28 // We follow the custom of choosing as parameters of an arbitrary line passing
29 // through a point (x, y) the angle (from "x" axis) of the line, and the
30 // distance of the line from the chosen point. Fixing the point, we can assign
31 // freedom to the angle (that has a limited range by definition) and then
32 // the distance will be defined by some functional form d(angle).
33 // The shape (d vs. a) of the set of parameters of all lines passing by a point
34 // is a sinusoidal curve. We can define the angle to be between 0 and pi
35 // (half a period) and the distance potentially covers the whole real axis
36 // (but actually it will be never larger than the distance of the selected
37 // point from the origin).
38 //
39 // The implementation of the algorithm is based on a "accumulator", the set
40 // of counters of how many hits are passed by a given line (represented by
41 // its tow parameters). The accumulator is a two-dimensional container of
42 // counters, with the first dimension given by the angle and the second by the
43 // distance.
44 // In this scenario, all angles are sampled, therefore each angle will have
45 // at least one count per hit (somewhere at some distance d).
46 // Each angle will see one entry per hit.
47 // We choose to have a large number of sampled angles (the current standard
48 // configuration says 10800), therefore most of the counters will be empty.
49 // The sinusoidal shape can be steep enough that got example the angle a(1)
50 // has d(a1)=30 and the next angle (a2) has d(a2)=50. In this case we cover
51 // also the distances between 31 and 50, (assigning them, as an approximation,
52 // to a2). In this way we don't leave gaps and make sure that each two
53 // sinusoidal curves cross in at least one point, or, equivalently, that we
54 // can always find at least one straight line throw any two points.
55 // This translates in having very often the counts at a given angle clustered
56 // around some distances.
57 //
58 // We need to discretize the angles and the distances. Tests show that for a
59 // "natural" plane size of 9600 (TDC counts) x ~3000 (wires), we need to use
60 // O(10000) angles and to oversample the distance, that typically goes in the
61 // range [0-10000], by some factor (5 in the default parameters).
62 // The oversampling factor is just an artifact of the fact that we use integral
63 // distances but we want to have a resolution better than "1" -- we just
64 // multiply the distance by e.g. 5 and we get that the distace "1" actually
65 // means 1/5 = 0.2.
66 // Note that the algorithm is usually applied to subsets of that plane rather
67 // than on the full plane, making the typical distance range somehow smaller.
68 //
69 // The fastest data structure for the accumulator is a two-dimensional array.
70 // The proper size of this array would be some gigabyte, that makes this
71 // approach unfeasible. Since the dimension of angles has a very clear number
72 // of "bins" (covering always a fixed 0-pi range), but for each given angle
73 // the counters actually used are a few, a sparse structure (associative
74 // container, or "map") is used to describe all the counters at a given angle,
75 // with key the parameter r (discretized).
76 // Since all the angles always have data, the outer container is a vector
77 // whose index is the parameter a (also discretized).
78 // Therefore, for a given line (a, r), we can find how many hits pass though
79 // it by finding the associative container of the angle a from a vector,
80 // and in there looking for an entry with d as key: accum[a][d].
81 //
82 // Optimization
83 // ----------------------------------------------------------------------------
84 //
85 // Given the constraints and data structure described above, it turns out that
86 // a lot of time is spent creating new counters. On each hit, and each angle,
87 // one or more elements of the associative containers must be created.
88 // In total, millions of counter instances ar used at once.
89 // The standard C++ implementation of it, std::map, dynamically a new node
90 // each time a new counter is required, and in the end it frees them one by
91 // one. Both operations are very time-demanding.
92 // We used a custom memory allocator (BulkAllocator) that prepares memory
93 // for chunks of nodes and then returns a preallocated space at each request
94 // for a new node. The allocator is designed to be fast, giving up features:
95 // nodes are never really freed, that saves a lot of book-keeping.
96 // A lot of tricky aspects of it are documented in its own header file.
97 // Also freeing the memory is fast, since it implies the deletion of a few
98 // (very large) chunks rather than millions.
99 // The other aspect taking a lot of time is the lookup of a counter: where is
100 // counter (a,d)? the location of a is fast (constant time, from a vector).
101 // The location of d is the next best thing, a binary tree with log(N) access
102 // time. Unfortunately a balanced tree needs to be rebalanced often, and that
103 // takes a lot of time. Also, N may be "small" (O(1000)), but there are still
104 // million insertions and look ups.
105 // We use here a replacement of the plain map. Since it often happens that,
106 // to "fill the gaps", sequential counters are allocated and increased,
107 // we have blocks of couners allocated together (in the current version, 64
108 // of them). This can save memory in case of crowded spaces: the overhead
109 // for each node is 40 bytes, whether it is a single counter or a block of
110 // 64). Look up within the same block becomes constant-time.
111 // Also, to access sequences of counters, special code is used so that we
112 // take advantage of the result from the previous look up to perform the next
113 // one, including also the insertion of a new counter block after an existing
114 // one.
115 // Finally, the algorithm acts when it finds a relatively small number of
116 // aligned hits, afterward removing the hits already clustered. The hit count
117 // keeps small all the time: we use a signed char as basic data type for
118 // the counters, allowing a maximum of 127 aligned hits. This saves a lot of
119 // memory, at the cost of a small slow-down for large (e.g. 64-bit) bus
120 // architectures. No check is performed for overflow; that can also be
121 // implemented at a small cost.
122 //
123 //
125 #ifndef HOUGHBASEALG_H
126 #define HOUGHBASEALG_H
127 
128 #include <array>
129 #include <map>
130 #include <memory> // std::allocator<>
131 #include <string>
132 #include <utility> // std::pair<>
133 #include <vector>
134 
135 namespace art {
136  class Event;
137 }
140 namespace fhicl {
141  class ParameterSet;
142 }
143 
145 
146 namespace CLHEP {
147  class HepRandomEngine;
148 }
149 namespace detinfo {
150  class DetectorClocksData;
151  class DetectorPropertiesData;
152 }
154 #include "lardataobj/RecoBase/Hit.h"
155 
156 struct houghCorner {
157  double strength = 0;
158  double p0 = 0;
159  double p1 = 0;
160  houghCorner(double strengthTemp = 0, double p0Temp = 0, double p1Temp = 0)
161  {
162  strength = strengthTemp;
163  p0 = p0Temp;
164  p1 = p1Temp;
165  }
166 
167  bool operator<(const houghCorner& houghCornerComp) const
168  {
169  return (strength < houghCornerComp.strength);
170  }
171 };
172 
173 // This stores information about merged lines
174 struct mergedLines {
175  double totalQ = 0;
176  double pMin0 = 0;
177  double pMin1 = 0;
178  double pMax0 = 0;
179  double pMax1 = 0;
180  int clusterNumber = -999999;
181  double showerLikeness = 0;
182  mergedLines(double totalQTemp = 0,
183  double pMin0Temp = 0,
184  double pMin1Temp = 0,
185  double pMax0Temp = 0,
186  double pMax1Temp = 0,
187  double clusterNumberTemp = -999999,
188  double showerLikenessTemp = 0)
189  {
190  totalQ = totalQTemp;
191  pMin0 = pMin0Temp;
192  pMin1 = pMin1Temp;
193  pMax0 = pMax0Temp;
194  pMax1 = pMax1Temp;
195  clusterNumber = clusterNumberTemp;
196  showerLikeness = showerLikenessTemp;
197  }
198 };
199 
200 struct protoTrack {
201  int clusterNumber = 999999;
202  int planeNumber = 999999;
203  int oldClusterNumber = 999999;
204  float clusterSlope = 999999;
205  float clusterIntercept = 999999;
206  float totalQ = -999999;
207  float pMin0 = 999999;
208  float pMin1 = 999999;
209  float pMax0 = -999999;
210  float pMax1 = -999999;
211  float iMinWire = 999999;
212  float iMaxWire = -999999;
213  float minWire = 999999;
214  float maxWire = -999999;
215  float isolation = -999999;
216  float showerLikeness = -999999;
217  bool merged = false;
218  bool showerMerged = false;
219  bool mergedLeft = false;
220  bool mergedRight = false;
221  std::vector<art::Ptr<recob::Hit>> hits;
223 
224  void Init(unsigned int num = 999999,
225  unsigned int pnum = 999999,
226  float slope = 999999,
227  float intercept = 999999,
228  float totalQTemp = -999999,
229  float Min0 = 999999,
230  float Min1 = 999999,
231  float Max0 = -999999,
232  float Max1 = -999999,
233  int iMinWireTemp = 999999,
234  int iMaxWireTemp = -999999,
235  int minWireTemp = 999999,
236  int maxWireTemp = -999999,
238  {
239  clusterNumber = num;
240  planeNumber = pnum;
241  oldClusterNumber = num;
242  clusterSlope = slope;
243  clusterIntercept = intercept;
244  totalQ = totalQTemp;
245  pMin0 = Min0;
246  pMin1 = Min1;
247  pMax0 = Max0;
248  pMax1 = Max1;
249  iMinWire = iMinWireTemp;
250  iMaxWire = iMaxWireTemp;
251  minWire = minWireTemp;
252  maxWire = maxWireTemp;
253  merged = false;
254  showerMerged = false;
255  showerLikeness = 0;
256  hits.swap(hitsTemp);
257  }
258 };
259 
260 namespace cluster {
261 
274  template <typename KEY,
275  typename COUNTER,
276  size_t SIZE,
277  typename ALLOC = std::allocator<std::pair<KEY, std::array<COUNTER, SIZE>>>,
278  unsigned int SUBCOUNTERS = 1>
279  class HoughTransformCounters : public lar::CountersMap<KEY, COUNTER, SIZE, ALLOC, SUBCOUNTERS> {
280  public:
285 
286  // import useful types
287  using BaseMap_t = typename Base_t::BaseMap_t;
289  using Key_t = typename Base_t::Key_t;
293 
295  using PairValue_t = std::pair<const_iterator, SubCounter_t>;
296 
299 
302 
309  SubCounter_t set(Key_t key, SubCounter_t value) { return Base_t::set(key, value); }
310 
316  SubCounter_t increment(Key_t key) { return Base_t::increment(key); }
317 
323  SubCounter_t decrement(Key_t key) { return Base_t::decrement(key); }
324 
333  SubCounter_t set(Key_t key_begin, Key_t key_end, SubCounter_t value)
334  {
335  return unchecked_set_range(key_begin, key_end, value);
336  }
337 
344  void increment(Key_t key_begin, Key_t key_end);
345 
367  {
368  return unchecked_add_range_max(key_begin, key_end, +1);
369  }
370 
385  PairValue_t increment_and_get_max(Key_t key_begin, Key_t key_end, SubCounter_t current_max)
386  {
387  return unchecked_add_range_max(key_begin, key_end, +1, current_max);
388  }
389 
396  void decrement(Key_t key_begin, Key_t key_end);
397 
416  PairValue_t get_max(SubCounter_t current_max) const;
417 
430  PairValue_t get_max() const;
431 
432  protected:
434 
435  private:
436  SubCounter_t unchecked_set_range(Key_t key_begin,
437  Key_t key_end,
439  typename BaseMap_t::iterator start);
440  SubCounter_t unchecked_set_range(Key_t key_begin, Key_t key_end, SubCounter_t value);
441  PairValue_t unchecked_add_range_max(
442  Key_t key_begin,
443  Key_t key_end,
444  SubCounter_t delta,
445  typename BaseMap_t::iterator start,
446  SubCounter_t min_max = std::numeric_limits<SubCounter_t>::min());
447  PairValue_t unchecked_add_range_max(
448  Key_t key_begin,
449  Key_t key_end,
450  SubCounter_t delta,
451  SubCounter_t min_max = std::numeric_limits<SubCounter_t>::min());
452 
453  }; // class HoughTransformCounters
454 
455  class HoughBaseAlg {
456  public:
458  struct ChargeInfo_t {
459  float integral = 0.0F;
460  float integral_stddev = 0.0F;
461  float summedADC = 0.0F;
462  float summedADC_stddev = 0.0F;
463 
464  ChargeInfo_t(float in, float in_stdev, float sum, float sum_stdev)
465  : integral(in), integral_stddev(in_stdev), summedADC(sum), summedADC_stddev(sum_stdev)
466  {}
467  }; // ChargeInfo_t
468 
469  explicit HoughBaseAlg(fhicl::ParameterSet const& pset);
470  virtual ~HoughBaseAlg() = default;
471 
472  size_t FastTransform(const std::vector<art::Ptr<recob::Cluster>>& clusIn,
473  std::vector<recob::Cluster>& ccol,
475  CLHEP::HepRandomEngine& engine,
476  art::Event const& evt,
477  std::string const& label);
478 
479  size_t Transform(const detinfo::DetectorClocksData& clockData,
480  detinfo::DetectorPropertiesData const& detProp,
481  std::vector<art::Ptr<recob::Hit>> const& hits,
482  CLHEP::HepRandomEngine& engine,
483  std::vector<unsigned int>* fpointId_to_clusterId,
484  unsigned int clusterId, // The id of the cluster we are examining
485  unsigned int* nClusters,
486  std::vector<protoTrack>* protoTracks);
487 
488  // interface to look for lines only on a set of hits,without slope and
489  // totalQ arrays
490  size_t FastTransform(detinfo::DetectorClocksData const& clockData,
491  detinfo::DetectorPropertiesData const& detProp,
492  std::vector<art::Ptr<recob::Hit>> const& clusIn,
494  CLHEP::HepRandomEngine& engine);
495 
496  // interface to look for lines only on a set of hits
497  size_t FastTransform(detinfo::DetectorClocksData const& clockData,
498  detinfo::DetectorPropertiesData const& detProp,
499  std::vector<art::Ptr<recob::Hit>> const& clusIn,
501  CLHEP::HepRandomEngine& engine,
502  std::vector<double>& slope,
503  std::vector<ChargeInfo_t>& totalQ);
504 
505  size_t Transform(std::vector<art::Ptr<recob::Hit>> const& hits);
506 
507  size_t Transform(detinfo::DetectorPropertiesData const& detProp,
508  std::vector<art::Ptr<recob::Hit>> const& hits,
509  double& slope,
510  double& intercept);
511 
512  // FIXME: Is this necessary? Document or remove!
513  // 2022-04-18 CHG
514  friend class HoughTransformClus;
515 
516  private:
517  void HLSSaveBMPFile(char const*, unsigned char*, int, int);
518 
519  int fMaxLines;
520  int fMinHits;
521  int fSaveAccumulator;
524  float
529  float fMaxSlope;
530  int
532  int
535  int
537  int
540  float
543  float
545  };
546 
547 } // namespace
548 
549 #endif // HOUGHBASEALG_H
intermediate_table::iterator iterator
typename Traits_t::CounterBlock_t CounterBlock_t
Definition: CountersMap.h:154
float fRhoResolutionFactor
Factor determining the resolution in rho.
Definition: HoughBaseAlg.h:534
bool operator<(const houghCorner &houghCornerComp) const
Definition: HoughBaseAlg.h:167
KEY Key_t
type of counter key in the map
Definition: CountersMap.h:138
Declaration of signal hit object.
typename Base_t::CounterKey_t CounterKey_t
Definition: HoughBaseAlg.h:433
typename Traits_t::template BaseMap_t< typename std::allocator_traits< Allocator_t >::template rebind_alloc< typename Traits_t::MapValue_t >> BaseMap_t
Type of the map used in the implementation.
Definition: CountersMap.h:158
intermediate_table::const_iterator const_iterator
Cluster finding and building.
std::pair< const_iterator, SubCounter_t > PairValue_t
Pair identifying a counter and its current value.
Definition: HoughBaseAlg.h:295
mergedLines(double totalQTemp=0, double pMin0Temp=0, double pMin1Temp=0, double pMax0Temp=0, double pMax1Temp=0, double clusterNumberTemp=-999999, double showerLikenessTemp=0)
Definition: HoughBaseAlg.h:182
Data structure collecting charge information to be filled in cluster.
Definition: HoughBaseAlg.h:458
int fMaxLines
Max number of lines that can be found.
Definition: HoughBaseAlg.h:519
ChargeInfo_t(float in, float in_stdev, float sum, float sum_stdev)
Definition: HoughBaseAlg.h:464
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:289
float fMissedHitsDistance
Distance between hits in a hough line before a hit is considered missed.
Definition: HoughBaseAlg.h:542
int fNumAngleCells
that fall into the "correct" bin will be small and consistent with noise.
Definition: HoughBaseAlg.h:523
parameter set interface
PairValue_t increment_and_get_max(Key_t key_begin, Key_t key_end, SubCounter_t current_max)
Increments by 1 the specified counters and returns the maximum.
Definition: HoughBaseAlg.h:385
HoughTransformCounters(Allocator_t alloc)
Constructor, specifies an allocator.
Definition: HoughBaseAlg.h:301
float fMaxDistance
Max distance that a hit can be from a line to be considered part of that line.
Definition: HoughBaseAlg.h:528
float fMaxSlope
Max slope a line can have.
Definition: HoughBaseAlg.h:529
General LArSoft Utilities.
Declaration of cluster object.
SubCounter_t decrement(Key_t key)
Decrements by 1 the specified counter.
Definition: HoughBaseAlg.h:323
HoughTransformCounters()
Default constructor (empty map)
Definition: HoughBaseAlg.h:298
double value
Definition: spectrum.C:18
int fRhoZeroOutRange
Range in rho over which to zero out area around previously found lines in the accumulator.
Definition: HoughBaseAlg.h:531
double strength
Definition: HoughBaseAlg.h:157
CountersMap with access optimized for Hough Transform algorithm.
Definition: HoughBaseAlg.h:279
ALLOC Allocator_t
type of the single counter
Definition: CountersMap.h:140
Structure with the index of the counter, split as needed.
Definition: CountersMap.h:275
ifstream in
Definition: comparison.C:7
Contains all timing reference information for the detector.
std::vector< art::Ptr< recob::Hit > > hits
Definition: HoughBaseAlg.h:221
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
Definition: MVAAlg.h:12
Map storing counters in a compact way.
Definition: CountersMap.h:130
Counter_t SubCounter_t
Type of the subcounter (that is, the actual counter)
Definition: CountersMap.h:152
Map of counters, stored compactly.
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:533
typename Base_t::const_iterator const_iterator
Definition: HoughBaseAlg.h:292
Double_t sum
Definition: plot.C:31
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
houghCorner(double strengthTemp=0, double p0Temp=0, double p1Temp=0)
Definition: HoughBaseAlg.h:160
float fMissedHitsToLineSize
Ratio of missed hits to line size for a line to be considered a fake.
Definition: HoughBaseAlg.h:544