LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
EndPointAlg.cxx
Go to the documentation of this file.
1 //
3 // EndPointAlg class
4 //
5 // joshua.spitz@yale.edu
6 //
7 // This algorithm is designed to find (weak) vertices from hits after deconvolution and hit finding.
8 // A weak end point is a end point that has been found using a dedicated end point finding algorithm only. A
9 // strong end point is a end point that has been found using a dedicated end point finding algorithm and matched
10 // to a crossing of two or more HoughLineFinder lines. The End PointMatch module finds strong vertices.
17 //Thanks to B. Morgan of U. of Warwick for comments and suggestions
18 
19 #include <iostream>
20 #include <vector>
21 extern "C" {
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 }
25 #include <sstream>
26 #include <fstream>
27 #include <math.h>
28 #include <algorithm>
29 
30 #include "TMath.h"
31 
32 // Framework includes
38 
49 
50 //-----------------------------------------------------------------------------
52 {
53  this->reconfigure(pset);
54 }
55 
56 //-----------------------------------------------------------------------------
58 {
59 }
60 
61 //-----------------------------------------------------------------------------
63 {
64  fTimeBins = p.get< int >("TimeBins");
65  fMaxCorners = p.get< int >("MaxCorners");
66  fGsigma = p.get< double >("Gsigma");
67  fWindow = p.get< int >("Window");
68  fThreshold = p.get< double >("Threshold");
69  fSaveVertexMap = p.get< int >("SaveVertexMap");
70 }
71 
72 //-----------------------------------------------------------------------------
73 double cluster::EndPointAlg::Gaussian(int x, int y, double sigma)
74 {
75  double Norm=1./std::sqrt(2*TMath::Pi()*pow(sigma,2));
76  double value=Norm*exp(-(pow(x,2)+pow(y,2))/(2*pow(sigma,2)));
77  return value;
78 }
79 
80 //-----------------------------------------------------------------------------
82 {
83  double Norm=1./(std::sqrt(2*TMath::Pi())*pow(fGsigma,3));
84  double value=Norm*(-x)*exp(-(pow(x,2)+pow(y,2))/(2*pow(fGsigma,2)));
85  return value;
86 }
87 
88 //-----------------------------------------------------------------------------
90 {
91  double Norm=1./(std::sqrt(2*TMath::Pi())*pow(fGsigma,3));
92  double value=Norm*(-y)*exp(-(pow(x,2)+pow(y,2))/(2*pow(fGsigma,2)));
93  return value;
94 }
95 
96 
97 //-----------------------------------------------------------------------------
98 //this method saves a BMP image of the vertex map space, which can be viewed with gimp
99 void cluster::EndPointAlg::VSSaveBMPFile(const char *fileName, unsigned char *pix, int dx, int dy)
100 {
101  std::ofstream bmpFile(fileName, std::ios::binary);
102  bmpFile.write("B", 1);
103  bmpFile.write("M", 1);
104  int bitsOffset = 54 +256*4;
105  int size = bitsOffset + dx*dy; //header plus 256 entry LUT plus pixels
106  bmpFile.write((const char *)&size, 4);
107  int reserved = 0;
108  bmpFile.write((const char *)&reserved, 4);
109  bmpFile.write((const char *)&bitsOffset, 4);
110  int bmiSize = 40;
111  bmpFile.write((const char *)&bmiSize, 4);
112  bmpFile.write((const char *)&dx, 4);
113  bmpFile.write((const char *)&dy, 4);
114  short planes = 1;
115  bmpFile.write((const char *)&planes, 2);
116  short bitCount = 8;
117  bmpFile.write((const char *)&bitCount, 2);
118  int i, temp = 0;
119  for (i=0; i<6; i++)
120  bmpFile.write((const char *)&temp, 4); // zero out optional color info
121  // write a linear LUT
122  char lutEntry[4]; // blue,green,red
123  lutEntry[3] = 0; // reserved part
124  for (i=0; i<256; i++)
125  {
126  lutEntry[0] =i;
127  lutEntry[1] =i+1;
128  lutEntry[2] = i+2;
129  bmpFile.write(lutEntry, sizeof lutEntry);
130  }
131  // write the actual pixels
132  bmpFile.write((const char *)pix, dx*dy);
133 }
134 
135 //......................................................
137  std::vector<recob::EndPoint2D> & vtxcol,
138  std::vector< art::PtrVector<recob::Hit> > & vtxHitsOut,
139  art::Event const& evt,
140  std::string const& label)
141 {
142 
144  const detinfo::DetectorProperties* detp = lar::providerFrom<detinfo::DetectorPropertiesService>();
145 
146  //Point to a collection of vertices to output.
147  std::vector< art::Ptr<recob::Hit> > hit;
148 
149  art::FindManyP<recob::Hit> fmh(clusIn, evt, label);
150 
151  int flag = 0;
152  int windex = 0;//the wire index to make sure the end point finder does not fall off the edge of the hit map
153  int tindex = 0;//the time index to make sure the end point finder does not fall off the edge of the hit map
154  int n = 0; //index of window cell. There are 49 cells in the 7X7 Gaussian and Gaussian derivative windows
155  unsigned int numberwires = 0;
156  const unsigned int numbertimesamples = detp->ReadOutWindowSize();
157  const float TicksPerBin = numbertimesamples / fTimeBins;
158 
159  double MatrixAAsum = 0.;
160  double MatrixBBsum = 0.;
161  double MatrixCCsum = 0.;
162  std::vector<double> Cornerness2;
163  //gaussian window definitions. The cell weights are calculated here to help the algorithm's speed
164  double w[49] = {0.};
165  double wx[49] = {0.};
166  double wy[49] = {0.};
167  int ctr = 0;
168  for(int i = -3; i < 4; ++i){
169  for(int j = 3; j > -4; --j){
170  w[ctr] = Gaussian(i, j, fGsigma);
171  wx[ctr] = GaussianDerivativeX(i,j);
172  wy[ctr] = GaussianDerivativeY(i,j);
173  ++ctr;
174  }
175  }
176 
177  unsigned int wire = 0;
178  unsigned int wire2 = 0;
179  for(auto view : geom->Views()){
182  hit.clear();
183  size_t cinctr = 0;
184  while(clusterIter!= clusIn.end() ) {
185  if((*clusterIter)->View() == view){
186  hit = fmh.at(cinctr);
187  }//end if cluster is in the correct view
188  clusterIter++;
189  ++cinctr;
190  }
191 
192  if(hit.size() == 0) continue;
193 
194  geo::WireID wid = hit[0]->WireID();
195  numberwires = geom->Cryostat(wid.Cryostat).TPC(wid.TPC).Plane(wid.Plane).Nwires();
196  mf::LogInfo("EndPointAlg") << " --- endpoints check "
197  << numberwires << " "
198  << numbertimesamples << " "
199  << fTimeBins;
200 
201  std::vector < std::vector < double > > MatrixAsum(numberwires);
202  std::vector < std::vector < double > > MatrixBsum(numberwires);
203  std::vector < std::vector < double > > hit_map(numberwires); //the map of hits
204 
205  //the index of the hit that corresponds to the potential corner
206  std::vector < std::vector < int > > hit_loc(numberwires);
207 
208  std::vector < std::vector < double > > Cornerness(numberwires); //the "weight" of a corner
209 
210  for(unsigned int wi = 0; wi < numberwires; ++wi){
211  hit_map[wi].resize(fTimeBins,0);
212  hit_loc[wi].resize(fTimeBins,-1);
213  Cornerness[wi].resize(fTimeBins,0);
214  MatrixAsum[wi].resize(fTimeBins,0);
215  MatrixBsum[wi].resize(fTimeBins,0);
216  }
217  for(unsigned int i = 0; i < hit.size(); ++i){
218  wire = hit[i]->WireID().Wire;
219  //pixelization using a Gaussian
220  // for(int j = 0; j <= (int)(hit[i]->EndTime()-hit[i]->StartTime()+.5); ++j)
221  // hit_map[wire][(int)((hit[i]->StartTime()+j)*(fTimeBins/numbertimesamples)+.5)] += Gaussian((int)(j-((hit[i]->EndTime()-hit[i]->StartTime())/2.)+.5),0,hit[i]->EndTime()-hit[i]->StartTime());
222  const float center = hit[i]->PeakTime(), sigma = hit[i]->RMS();
223  const int iFirstBin = int((center - 3*sigma) / TicksPerBin),
224  iLastBin = int((center + 3*sigma) / TicksPerBin);
225  for (int iBin = iFirstBin; iBin <= iLastBin; ++iBin) {
226  const float bin_center = iBin * TicksPerBin;
227  hit_map[wire][iBin] += Gaussian(bin_center, center, sigma);
228  }
229  }
230 
231  // Gaussian derivative convolution
232  for(unsigned int wire = 1; wire < numberwires-1; ++wire){
233 
234  for(int timebin = 1; timebin < fTimeBins-1; ++timebin){
235  MatrixAsum[wire][timebin] = 0.;
236  MatrixBsum[wire][timebin] = 0.;
237  n = 0;
238  for(int i = -3; i <= 3; ++i) {
239  windex = wire+i;
240  if(windex < 0 ) windex = 0;
241  // this is ok, because the line before makes sure it's not negative
242  else if ((unsigned int)windex >= numberwires) windex = numberwires-1;
243 
244  for(int j = -3; j <= 3; ++j){
245  tindex = timebin+j;
246  if(tindex < 0) tindex=0;
247  else if(tindex >= fTimeBins) tindex = fTimeBins-1;
248 
249  MatrixAsum[wire][timebin] += wx[n]*hit_map[windex][tindex];
250  MatrixBsum[wire][timebin] += wy[n]*hit_map[windex][tindex];
251  ++n;
252  } // end loop over j
253  } // end loop over i
254  } // end loop over time bins
255  }// end loop over wires
256 
257  //calculate the cornerness of each pixel while making sure not to fall off the hit map.
258  for(unsigned int wire = 1; wire < numberwires-1; ++wire){
259 
260  for(int timebin = 1; timebin < fTimeBins-1; ++timebin){
261  MatrixAAsum = 0.;
262  MatrixBBsum = 0.;
263  MatrixCCsum = 0.;
264  //Gaussian smoothing convolution
265  n = 0;
266  for(int i = -3; i <= 3; ++i){
267  windex = wire+i;
268  if(windex<0) windex = 0;
269  // this is ok, because the line before makes sure it's not negative
270  else if((unsigned int)windex >= numberwires) windex = numberwires-1;
271 
272  for(int j = -3; j <= 3; ++j){
273  tindex = timebin+j;
274  if(tindex < 0) tindex = 0;
275  else if(tindex >= fTimeBins) tindex = fTimeBins-1;
276 
277  MatrixAAsum += w[n]*pow(MatrixAsum[windex][tindex],2);
278  MatrixBBsum += w[n]*pow(MatrixBsum[windex][tindex],2);
279  MatrixCCsum += w[n]*MatrixAsum[windex][tindex]*MatrixBsum[windex][tindex];
280  ++n;
281  }// end loop over j
282  }// end loop over i
283 
284  if((MatrixAAsum + MatrixBBsum) > 0)
285  Cornerness[wire][timebin] = (MatrixAAsum*MatrixBBsum-pow(MatrixCCsum,2))/(MatrixAAsum+MatrixBBsum);
286  else
287  Cornerness[wire][timebin] = 0;
288 
289  if(Cornerness[wire][timebin] > 0){
290  for(unsigned int i = 0;i < hit.size(); ++i){
291  wire2 = hit[i]->WireID().Wire;
292  //make sure the end point candidate coincides with an actual hit.
293  if(wire == wire2
294  && std::abs(hit[i]->TimeDistanceAsRMS(timebin*(numbertimesamples/fTimeBins))) < 1.){
295  //this index keeps track of the hit number
296  hit_loc[wire][timebin] = i;
297  Cornerness2.push_back(Cornerness[wire][timebin]);
298  break;
299  }
300  }// end loop over hits
301  }// end if cornerness > 0
302  } // end loop over time bins
303  } // end wire loop
304 
305  std::sort(Cornerness2.rbegin(), Cornerness2.rend());
306 
307  for(int vertexnum = 0; vertexnum < fMaxCorners; ++vertexnum){
308  flag = 0;
309  for(unsigned int wire = 0; wire < numberwires && flag == 0; ++wire){
310  for(int timebin = 0; timebin < fTimeBins && flag == 0; ++timebin){
311  if(Cornerness2.size() > (unsigned int)vertexnum)
312  if(Cornerness[wire][timebin] == Cornerness2[vertexnum]
313  && Cornerness[wire][timebin] > 0.
314  && hit_loc[wire][timebin] > -1){
315  ++flag;
316 
317  //thresholding
318  if(Cornerness2.size())
319  if(Cornerness[wire][timebin] < (fThreshold*Cornerness2[0]))
320  vertexnum = fMaxCorners;
321  vHits.push_back(hit[hit_loc[wire][timebin]]);
322 
323  // get the total charge from the associated hits
324  double totalQ = 0.;
325  for(size_t vh = 0; vh < vHits.size(); ++vh) totalQ += vHits[vh]->Integral();
326 
327  recob::EndPoint2D endpoint(hit[hit_loc[wire][timebin]]->PeakTime(),
328  hit[hit_loc[wire][timebin]]->WireID(),
329  Cornerness[wire][timebin],
330  vtxcol.size(),
331  view,
332  totalQ);
333  vtxcol.push_back(endpoint);
334  vtxHitsOut.push_back(vHits);
335  vHits.clear();
336 
337  // non-maximal suppression on a square window. The wire coordinate units are
338  // converted to time ticks so that the window is truly square.
339  // Note that there are 1/0.0743=13.46 time samples per 4.0 mm (wire pitch in ArgoNeuT),
340  // assuming a 1.5 mm/us drift velocity for a 500 V/cm E-field
341 
342  double drifttick = detp->DriftVelocity(detp->Efield(),detp->Temperature());
343  drifttick *= detp->SamplingRate()*1.e-3;
344  double wirepitch = geom->WirePitch();
345  double corrfactor = drifttick/wirepitch;
346 
347  for(int wireout=(int)wire-(int)((fWindow*(numbertimesamples/fTimeBins)*corrfactor)+.5);
348  wireout <= (int)wire+(int)((fWindow*(numbertimesamples/fTimeBins)*corrfactor)+.5); ++wireout){
349  for(int timebinout=timebin-fWindow;timebinout <= timebin+fWindow; timebinout++){
350  if(std::sqrt(pow(wire-wireout,2)+pow(timebin-timebinout,2))<fWindow)//circular window
351  Cornerness[wireout][timebinout]=0;
352  }
353  }
354  }
355  }
356  }
357  }
358  Cornerness2.clear();
359  hit.clear();
360  if(clusterIter != clusIn.end()) clusterIter++;
361 
362  if((int)wid.Plane == fSaveVertexMap){
363  unsigned char *outPix = new unsigned char [fTimeBins*numberwires];
364  //finds the maximum cell in the map for image scaling
365  int cell = 0;
366  int pix = 0;
367  int maxCell = 0;
368  //int xmaxx, ymaxx;
369  for (int y = 0; y < fTimeBins; ++y){
370  for (unsigned int x = 0; x < numberwires; ++x){
371  cell = (int)(hit_map[x][y]*1000);
372  if (cell > maxCell){
373  maxCell = cell;
374  // xmaxx=x;
375  // ymaxx=y;
376  }
377  }
378  }
379  for (int y = 0; y < fTimeBins; ++y){
380  for (unsigned int x = 0; x<numberwires; ++x){
381  //scales the pixel weights based on the maximum cell value
382  if(maxCell>0)
383  pix = (int)((1500000*hit_map[x][y])/maxCell);
384  outPix[y*numberwires + x] = pix;
385  }
386  }
387 
388  // add 3x3 pixel squares to with the harris vertex finders to the .bmp file
389 
390  for(unsigned int ii = 0; ii < vtxcol.size(); ++ii){
391  if(vtxcol[ii].View() == (unsigned int)view){
392  pix = (int)(255);
393  outPix[(int)(vtxcol[ii].DriftTime()*(fTimeBins/numbertimesamples))*numberwires + vtxcol[ii].WireID().Wire] = pix;
394  outPix[(int)(vtxcol[ii].DriftTime()*(fTimeBins/numbertimesamples))*numberwires + vtxcol[ii].WireID().Wire-1] = pix;
395  outPix[(int)(vtxcol[ii].DriftTime()*(fTimeBins/numbertimesamples))*numberwires + vtxcol[ii].WireID().Wire+1] = pix;
396  outPix[(int)((vtxcol[ii].DriftTime()*(fTimeBins/numbertimesamples))-1)*numberwires + vtxcol[ii].WireID().Wire] = pix;
397  outPix[(int)((vtxcol[ii].DriftTime()*(fTimeBins/numbertimesamples))+1)*numberwires + vtxcol[ii].WireID().Wire] = pix;
398  outPix[(int)((vtxcol[ii].DriftTime()*(fTimeBins/numbertimesamples))-1)*numberwires + vtxcol[ii].WireID().Wire-1] = pix;
399  outPix[(int)((vtxcol[ii].DriftTime()*(fTimeBins/numbertimesamples))-1)*numberwires + vtxcol[ii].WireID().Wire-2] = pix;
400  outPix[(int)((vtxcol[ii].DriftTime()*(fTimeBins/numbertimesamples))+1)*numberwires + vtxcol[ii].WireID().Wire+1] = pix;
401  outPix[(int)((vtxcol[ii].DriftTime()*(fTimeBins/numbertimesamples))+1)*numberwires + vtxcol[ii].WireID().Wire+2] = pix;
402  }
403  }
404 
405  VSSaveBMPFile(Form("harrisvertexmap_%d_%d.bmp",(*clusIn.begin())->ID(),wid.Plane), outPix, numberwires, fTimeBins);
406  delete [] outPix;
407  }
408  } // end loop over views
409 
410  return vtxcol.size();
411 }
412 
Float_t x
Definition: compare.C:6
algorithm to find 2D endpoints
virtual unsigned int ReadOutWindowSize() const =0
void reconfigure(fhicl::ParameterSet const &pset)
Definition: EndPointAlg.cxx:62
double GaussianDerivativeX(int x, int y)
Definition: EndPointAlg.cxx:81
Encapsulate the construction of a single cyostat.
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
iterator begin()
Definition: PtrVector.h:223
Declaration of signal hit object.
Float_t y
Definition: compare.C:6
std::set< geo::View_t > const & Views() const
Returns a list of possible views in the detector.
virtual double SamplingRate() const =0
Returns the period of the TPC readout electronics clock.
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:130
double Gaussian(int x, int y, double sigma)
Definition: EndPointAlg.cxx:73
geo::Length_t WirePitch(geo::PlaneID const &planeid) const
Returns the distance between two consecutive wires.
size_t EndPoint(const art::PtrVector< recob::Cluster > &clusIn, std::vector< recob::EndPoint2D > &vtxcol, std::vector< art::PtrVector< recob::Hit > > &vtxHitsOut, art::Event const &evt, std::string const &label)
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
EndPointAlg(fhicl::ParameterSet const &pset)
Definition: EndPointAlg.cxx:51
void push_back(Ptr< U > const &p)
Definition: PtrVector.h:441
virtual double Temperature() const =0
T get(std::string const &key) const
Definition: ParameterSet.h:231
iterator end()
Definition: PtrVector.h:237
CryostatGeo const & Cryostat(geo::CryostatID const &cryoid) const
Returns the specified cryostat.
double GaussianDerivativeY(int x, int y)
Definition: EndPointAlg.cxx:89
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:258
Declaration of cluster object.
size_type size() const
Definition: PtrVector.h:308
Detector simulation of raw signals on wires.
data_t::const_iterator const_iterator
Definition: PtrVector.h:61
std::string value(boost::any const &)
Utility object to perform functions of association.
Encapsulate the construction of a single detector plane.
const TPCGeo & TPC(unsigned int itpc) const
Return the itpc&#39;th TPC in the cryostat.
virtual double DriftVelocity(double efield=0., double temperature=0.) const =0
unsigned int Nwires() const
Number of wires in this plane.
Definition: PlaneGeo.h:250
Char_t n[5]
PlaneGeo const & Plane(geo::View_t view) const
Return the plane in the tpc with View_t view.
Definition: TPCGeo.cxx:299
virtual double Efield(unsigned int planegap=0) const =0
Returns the nominal electric field in the specified volume.
TCEvent evt
Definition: DataStructs.cxx:5
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:203
WireID()=default
Default constructor: an invalid TPC ID.
void clear()
Definition: PtrVector.h:537
Float_t w
Definition: plot.C:23
void VSSaveBMPFile(const char *fileName, unsigned char *pix, int dx, int dy)
Definition: EndPointAlg.cxx:99
art framework interface to geometry description
Encapsulate the construction of a single detector plane.