LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
CVNImageUtils.cxx
Go to the documentation of this file.
1 #include <iostream>
2 #include <vector>
3 
5 
7 {
8  // Set a default image size
9  SetImageSize(500, 500, 3);
10  SetPixelMapSize(2880, 500);
11  // Defualt is to reverse the y view
12  fViewReverse = {false, true, false};
13 
14  fUseLogScale = false;
16 }
17 
18 lcvn::CVNImageUtils::CVNImageUtils(unsigned int nWires, unsigned int nTDCs, unsigned int nViews)
19 {
20  SetImageSize(nWires, nTDCs, nViews);
21  SetPixelMapSize(2880, 500);
22  fUseLogScale = false;
24 }
25 
27 {
29 }
30 
32 {
34 }
35 
36 unsigned char lcvn::CVNImageUtils::ConvertChargeToChar(float charge)
37 {
38 
39  float peCorrChunk;
40  float truncateCorr;
41  float centreScale = 0.7;
42  if (fUseLogScale) {
43  float scaleFrac = (log(charge) / log(1000));
44  truncateCorr = ceil(centreScale * scaleFrac * 255.0);
45  }
46  else {
47  peCorrChunk = (1000.) / 255.0;
48  truncateCorr = ceil((charge) / (peCorrChunk));
49  }
50  if (truncateCorr > 255)
51  return (unsigned char)255;
52  else
53  return (unsigned char)truncateCorr;
54 }
55 
56 void lcvn::CVNImageUtils::SetImageSize(unsigned int nWires, unsigned int nTDCs, unsigned int nViews)
57 {
58  fNWires = nWires;
59  fNTDCs = nTDCs;
60  fNViews = nViews;
61 }
62 
63 void lcvn::CVNImageUtils::SetViewReversal(bool reverseX, bool reverseY, bool reverseZ)
64 {
65  fViewReverse = {reverseX, reverseY, reverseZ};
66 }
67 
68 void lcvn::CVNImageUtils::SetViewReversal(std::vector<bool> reverseViews)
69 {
70  if (reverseViews.size() != 3) {
71  std::cout << "Expected three views for view reversals... using defaults." << std::endl;
72  }
73  else {
74  SetViewReversal(reverseViews[0], reverseViews[1], reverseViews[2]);
75  }
76  return;
77 }
78 
80 {
81  fUseLogScale = setLog;
82 }
83 
84 void lcvn::CVNImageUtils::SetPixelMapSize(unsigned int nWires, unsigned int nTDCs)
85 {
86  fPixelMapWires = nWires;
87  fPixelMapTDCs = nTDCs;
88 }
89 
91  std::vector<unsigned char>& pix)
92 {
93 
94  SetPixelMapSize(pm.NWire(), pm.NTdc());
95 
96  // Strip out the charge vectors and use these
97  std::vector<float> v0pe = pm.fPEX;
98  std::vector<float> v1pe = pm.fPEY;
99  std::vector<float> v2pe = pm.fPEZ;
100 
101  ConvertChargeVectorsToPixelArray(v0pe, v1pe, v2pe, pix);
102 }
103 
105  std::vector<float>& v1pe,
106  std::vector<float>& v2pe,
107  std::vector<unsigned char>& pix)
108 {
109 
110  // Get the vectors
111  lcvn::ViewVector view0;
112  lcvn::ViewVector view1;
113  lcvn::ViewVector view2;
114  ConvertChargeVectorsToViewVectors(v0pe, v1pe, v2pe, view0, view1, view2);
115 
116  // Actually write the values to the pixel array
117  for (unsigned int view = 0; view < fNViews; ++view) {
118  for (unsigned int wire = 0; wire < fNWires; ++wire) {
119  for (unsigned int time = 0; time < fNTDCs; ++time) {
120 
121  unsigned char val = 0;
122  // Get the index for the pixel map
123  if (view == 0) { val = view0[wire][time]; }
124  if (view == 1) { val = view1[wire][time]; }
125  if (view == 2) { val = view2[wire][time]; }
126 
127  // Get the index for the final image
128  unsigned int i = time + fNTDCs * (wire + fNWires * view);
129  pix[i] = val;
130  }
131  }
132  }
133 
134  return;
135 }
136 
138  lcvn::ImageVector& imageVec)
139 {
140 
141  SetPixelMapSize(pm.NWire(), pm.NTdc());
142 
143  // Strip out the charge vectors and use these
144  std::vector<float> v0pe = pm.fPEX;
145  std::vector<float> v1pe = pm.fPEY;
146  std::vector<float> v2pe = pm.fPEZ;
147 
148  ConvertChargeVectorsToImageVector(v0pe, v1pe, v2pe, imageVec);
149 }
150 
152  lcvn::ImageVectorF& imageVec)
153 {
154 
155  SetPixelMapSize(pm.NWire(), pm.NTdc());
156 
157  // Strip out the charge vectors and use these
158  std::vector<float> v0pe = pm.fPEX;
159  std::vector<float> v1pe = pm.fPEY;
160  std::vector<float> v2pe = pm.fPEZ;
161 
162  ConvertChargeVectorsToImageVectorF(v0pe, v1pe, v2pe, imageVec);
163 }
164 
166  std::vector<float>& v1pe,
167  std::vector<float>& v2pe,
168  lcvn::ImageVector& imageVec)
169 {
170 
171  lcvn::ViewVector view0;
172  lcvn::ViewVector view1;
173  lcvn::ViewVector view2;
174 
175  ConvertChargeVectorsToViewVectors(v0pe, v1pe, v2pe, view0, view1, view2);
176 
177  lcvn::ImageVector newImage = BuildImageVector(view0, view1, view2);
178 
179  imageVec = newImage;
180 }
181 
183  std::vector<float>& v1pe,
184  std::vector<float>& v2pe,
185  lcvn::ImageVectorF& imageVec)
186 {
187 
188  lcvn::ViewVector view0;
189  lcvn::ViewVector view1;
190  lcvn::ViewVector view2;
191 
192  ConvertChargeVectorsToViewVectors(v0pe, v1pe, v2pe, view0, view1, view2);
193 
194  // Convert the ViewVector to ViewVectorF
195  lcvn::ViewVectorF floatView0 = ConvertViewVecToViewVecF(view0);
196  lcvn::ViewVectorF floatView1 = ConvertViewVecToViewVecF(view1);
197  lcvn::ViewVectorF floatView2 = ConvertViewVecToViewVecF(view2);
198 
199  lcvn::ImageVectorF newImage = BuildImageVectorF(floatView0, floatView1, floatView2);
200 
201  imageVec = newImage;
202 }
203 
205  std::vector<float>& v1pe,
206  std::vector<float>& v2pe,
207  lcvn::ViewVector& view0,
208  lcvn::ViewVector& view1,
209  lcvn::ViewVector& view2)
210 {
211 
212  // Reverse requested views
213  if (fViewReverse[0]) ReverseView(v0pe);
214  if (fViewReverse[1]) ReverseView(v1pe);
215  if (fViewReverse[2]) ReverseView(v2pe);
216 
217  // Get the integrated charge for each wire
218  std::vector<std::vector<float>> wireCharges;
219  for (unsigned int view = 0; view < fNViews; ++view) {
220 
221  std::vector<float> tempChargeVec;
222  for (unsigned int wire = 0; wire < fPixelMapWires; ++wire) {
223 
224  float totCharge = 0;
225  for (unsigned int time = 0; time < fPixelMapTDCs; ++time) {
226  float val = 0.;
227  unsigned int element = time + fPixelMapTDCs * wire;
228  if (view == 0) { val = v0pe[element]; }
229  if (view == 1) { val = v1pe[element]; }
230  if (view == 2) { val = v2pe[element]; }
231  totCharge += val;
232  }
233  tempChargeVec.push_back(totCharge);
234  }
235  wireCharges.push_back(tempChargeVec);
236  }
237 
238  // Get the integrated charge for each tdc
239  std::vector<std::vector<float>> tdcCharges;
240  for (unsigned int view = 0; view < fNViews; ++view) {
241 
242  std::vector<float> tempChargeVec;
243  for (unsigned int time = 0; time < fPixelMapTDCs; ++time) {
244 
245  float totCharge = 0;
246  for (unsigned int wire = 0; wire < fPixelMapWires; ++wire) {
247 
248  float val = 0.;
249  unsigned int element = time + fPixelMapTDCs * wire;
250  if (view == 0) { val = v0pe[element]; }
251  if (view == 1) { val = v1pe[element]; }
252  if (view == 2) { val = v2pe[element]; }
253  totCharge += val;
254  }
255  tempChargeVec.push_back(totCharge);
256  }
257  tdcCharges.push_back(tempChargeVec);
258  }
259 
260  // The output image consists of a rectangular region of the pixel map
261  // We want to find the start and end wires for each view
262  std::vector<unsigned int> imageStartWire(3, 0);
263  std::vector<unsigned int> imageEndWire(3, 0);
264  // And the same for TDCs
265  std::vector<unsigned int> imageStartTDC(3, 0);
266  std::vector<unsigned int> imageEndTDC(3, 0);
267 
269  // Do a rough vertex-based selection of the region for each view
270  for (unsigned int view = 0; view < wireCharges.size(); ++view) {
271  GetMinMaxWires(wireCharges[view], imageStartWire[view], imageEndWire[view]);
272  GetMinMaxTDCs(tdcCharges[view], imageStartTDC[view], imageEndTDC[view]);
273  }
274  }
275  else {
276  // Just use the number of wires and TDCs as the maximum values if we want to
277  // use a fixed range of wires and TDC for protoDUNE's APA 3
278  for (unsigned int i = 0; i < imageStartWire.size(); ++i) {
279  imageStartWire[i] = 0;
280  imageEndWire[i] = fNWires;
281  imageStartTDC[i] = 0;
282  imageEndTDC[i] = fNTDCs;
283  }
284  }
285 
286  // Write the values to the three vectors
287  for (unsigned int view = 0; view < fNViews; ++view) {
288  lcvn::ViewVector viewChargeVec;
289  for (unsigned int wire = imageStartWire[view]; wire <= imageEndWire[view]; ++wire) {
290  std::vector<unsigned char> wireTDCVec;
291  for (unsigned int time = imageStartTDC[view]; time <= imageEndTDC[view]; ++time) {
292 
293  // Get the index for the pixel map
294  unsigned int element = time + fPixelMapTDCs * wire;
295 
296  // We have to convert to char and then convert back to a float
297  unsigned char val = 0;
298  if (view == 0) { val = ConvertChargeToChar(v0pe[element]); }
299  if (view == 1) { val = ConvertChargeToChar(v1pe[element]); }
300  if (view == 2) { val = ConvertChargeToChar(v2pe[element]); }
301  wireTDCVec.push_back(val);
302  }
303  viewChargeVec.push_back(wireTDCVec);
304  }
305  if (view == 0) view0 = viewChargeVec;
306  if (view == 1) view1 = viewChargeVec;
307  if (view == 2) view2 = viewChargeVec;
308  }
309 
310  return;
311 }
312 
314  const std::vector<unsigned char>& pixelArray,
315  lcvn::ImageVectorF& imageVec)
316 {
317 
318  // The pixel arrays is built with indices i = tdc + nTDCs(wire + nWires*view)
319 
320  lcvn::ViewVectorF view0;
321  lcvn::ViewVectorF view1;
322  lcvn::ViewVectorF view2;
323 
324  for (unsigned int v = 0; v < fNViews; ++v) {
325  for (unsigned int w = 0; w < fNWires; ++w) {
326  std::vector<float> wireVec;
327  for (unsigned int t = 0; t < fNTDCs; ++t) {
328  unsigned int index = t + fNTDCs * (w + fNWires * v);
329  wireVec.push_back(pixelArray[index]);
330  }
331  if (v == 0) view0.push_back(wireVec);
332  if (v == 1) view1.push_back(wireVec);
333  if (v == 2) view2.push_back(wireVec);
334  }
335  }
336 
337  imageVec = BuildImageVectorF(view0, view1, view2);
338 }
339 
340 void lcvn::CVNImageUtils::GetMinMaxWires(std::vector<float>& wireCharges,
341  unsigned int& minWire,
342  unsigned int& maxWire)
343 {
344 
345  minWire = 0;
346  maxWire = fNWires;
347 
348  for (unsigned int wire = 0; wire < wireCharges.size(); ++wire) {
349 
350  // If we have got to fNWires from the end, the start needs to be this wire
351  if (wireCharges.size() - wire == fNWires) { break; }
352 
353  // For a given plane, look to see if the next 20 planes are empty. If not, this can be out start plane.
354  int nEmpty = 0;
355  for (unsigned int nextWire = wire + 1; nextWire <= wire + 20; ++nextWire) {
356  if (wireCharges[nextWire] == 0.0) ++nEmpty;
357  }
358  if (nEmpty < 5) {
359  minWire = wire;
360  maxWire = wire + fNWires - 1;
361  return;
362  }
363  }
364  // If we don't find a region where we have fewer than 5 empty planes then we just want to select the fNWires wires containing
365  // the most charge
366  float maxCharge = 0.;
367  unsigned int firstWire = 0;
368  for (unsigned int wire = 0; wire < wireCharges.size() - fNWires; ++wire) {
369  float windowCharge = 0.;
370  for (unsigned int nextwire = wire; nextwire < wire + fNWires; ++nextwire) {
371  windowCharge += wireCharges[nextwire];
372  }
373  if (windowCharge > maxCharge) {
374  maxCharge = windowCharge;
375  firstWire = wire;
376  }
377  }
378  minWire = firstWire;
379  maxWire = firstWire + fNWires - 1;
380 
381  std::cout
382  << "Used alternate method to get min and max wires due to vertex determination failure: "
383  << minWire << ", " << maxWire << std::endl;
384 }
385 
386 void lcvn::CVNImageUtils::GetMinMaxTDCs(std::vector<float>& tdcCharges,
387  unsigned int& minTDC,
388  unsigned int& maxTDC)
389 {
390 
391  minTDC = 0;
392  maxTDC = fNTDCs;
393 
394  for (unsigned int tdc = 0; tdc < tdcCharges.size(); ++tdc) {
395 
396  // If we have got to fNWires from the end, the start needs to be this wire
397  if (tdcCharges.size() - tdc == fNTDCs) { break; }
398 
399  // For a given tdc, look to see if the next 20 tdcs are empty. If not, this can be out start tdc.
400  int nEmpty = 0;
401  for (unsigned int nextTDC = tdc + 1; nextTDC <= tdc + 20; ++nextTDC) {
402  if (tdcCharges[nextTDC] == 0.0) ++nEmpty;
403  }
404  if (nEmpty < 5) {
405  minTDC = tdc;
406  maxTDC = tdc + fNTDCs - 1;
407  return;
408  }
409  }
410 
411  // If we don't find a region where we have fewer than 5 empty tdcs then we just want to select the fNTDCs tdcs containing
412  // the most charge
413  float maxCharge = 0.;
414  unsigned int firstTDC = 0;
415  for (unsigned int tdc = 0; tdc < tdcCharges.size() - fNTDCs; ++tdc) {
416  float windowCharge = 0.;
417  for (unsigned int nexttdc = tdc; nexttdc < tdc + fNTDCs; ++nexttdc) {
418  windowCharge += tdcCharges[nexttdc];
419  }
420  if (windowCharge > maxCharge) {
421  maxCharge = windowCharge;
422  firstTDC = tdc;
423  }
424  }
425  minTDC = firstTDC;
426  maxTDC = firstTDC + fNTDCs - 1;
427 
428  std::cout << "Used alternate method to get min and max tdcs due to vertex determination failure: "
429  << minTDC << ", " << maxTDC << std::endl;
430 }
431 
432 void lcvn::CVNImageUtils::ReverseView(std::vector<float>& peVec)
433 {
434 
435  std::vector<float> vecCopy(peVec.size(), 0.);
436 
437  for (unsigned int w = 0; w < fPixelMapWires; ++w) {
438  // Get our new plane number
439  unsigned int newPlane = fPixelMapWires - w - 1;
440 
441  for (unsigned int t = 0; t < fPixelMapTDCs; ++t) {
442  float val = peVec[t + fPixelMapTDCs * w];
443  vecCopy[t + fPixelMapTDCs * newPlane] = val;
444  }
445  }
446 
447  // Copy the values back into the original vector
448  peVec = move(vecCopy);
449 }
450 
452 {
453 
454  lcvn::ViewVectorF newVec;
455  newVec.reserve(view.size());
456  for (size_t w = 0; w < view.size(); ++w) {
457  std::vector<float> thisWire;
458  thisWire.reserve(view[w].size());
459  for (size_t t = 0; t < view[w].size(); ++t) {
460  float chargeSC = static_cast<float>(view[w][t]);
461  thisWire.push_back(chargeSC);
462  }
463  newVec.push_back(move(thisWire));
464  }
465  return newVec;
466 }
467 
469 {
470 
471  lcvn::ImageVectorF newImage;
472  newImage.reserve(image.size());
473  for (size_t w = 0; w < image.size(); ++w) {
474  lcvn::ViewVectorF thisWire;
475  thisWire.reserve(image[w].size());
476  for (size_t t = 0; t < image[w].size(); ++t) {
477  std::vector<float> thisTime;
478  thisTime.reserve(image[w][t].size());
479  for (size_t v = 0; v < image[w][t].size(); ++v) {
480  float chargeSC = static_cast<float>(image[w][t][v]);
481  thisTime.push_back(chargeSC);
482  }
483  thisWire.push_back(move(thisTime));
484  }
485  newImage.push_back(move(thisWire));
486  }
487  return newImage;
488 }
489 
491  lcvn::ViewVector& v1,
492  lcvn::ViewVector& v2)
493 {
494 
495  // Tensorflow wants things in the arrangement <wires, TDCs, views>
496  lcvn::ImageVector image;
497  image.reserve(v0.size());
498  for (unsigned int w = 0; w < v0.size(); ++w) {
499  std::vector<std::vector<unsigned char>> wireVec;
500  wireVec.reserve(v0[0].size());
501  for (unsigned int t = 0; t < v0[0].size(); ++t) {
502  std::vector<unsigned char> timeVec;
503  timeVec.push_back(v0[w][t]);
504  timeVec.push_back(v1[w][t]);
505  timeVec.push_back(v2[w][t]);
506  wireVec.push_back(move(timeVec));
507  } // Loop over tdcs
508  image.push_back(move(wireVec));
509  } // Loop over wires
510 
511  return image;
512 }
513 
515  lcvn::ViewVectorF& v1,
516  lcvn::ViewVectorF& v2)
517 {
518 
519  // Tensorflow wants things in the arrangement <wires, TDCs, views>
520  lcvn::ImageVectorF image;
521  image.reserve(v0.size());
522  for (unsigned int w = 0; w < v0.size(); ++w) {
523  std::vector<std::vector<float>> wireVec;
524  wireVec.reserve(v0[0].size());
525  for (unsigned int t = 0; t < v0[0].size(); ++t) {
526  std::vector<float> timeVec;
527  timeVec.push_back(v0[w][t]);
528  timeVec.push_back(v1[w][t]);
529  timeVec.push_back(v2[w][t]);
530  wireVec.push_back(move(timeVec));
531  } // Loop over tdcs
532  image.push_back(move(wireVec));
533  } // Loop over wires
534 
535  return image;
536 }
ImageVectorF BuildImageVectorF(ViewVectorF &v0, ViewVectorF &v1, ViewVectorF &v2)
void GetMinMaxWires(std::vector< float > &wireCharges, unsigned int &minWire, unsigned int &maxWire)
Get the minimum and maximum wires from the pixel map needed to make the image.
void ConvertChargeVectorsToPixelArray(std::vector< float > &v0pe, std::vector< float > &v1pe, std::vector< float > &v2pe, std::vector< unsigned char > &pix)
unsigned int fPixelMapTDCs
ViewVectorF ConvertViewVecToViewVecF(ViewVector &view)
Convert a ViewVector into a ViewVectorF.
void ConvertPixelArrayToImageVectorF(const std::vector< unsigned char > &pixelArray, ImageVectorF &imageVec)
Convert a pixel array into a ImageVectorF.
std::vector< std::vector< unsigned char > > ViewVector
Useful typedefs.
Definition: CVNImageUtils.h:17
void ReverseView(std::vector< float > &peVec)
Funtion to actually reverse the view.
PixelMap, basic input to CVN neural net.
Definition: PixelMap.h:20
void ConvertPixelMapToPixelArray(const PixelMap &pm, std::vector< unsigned char > &pix)
Convert a Pixel Map object into a single pixel array with an image size nWire x nTDC.
void ConvertPixelMapToImageVectorF(const PixelMap &pm, ImageVectorF &imageVec)
Convert a pixel map into an image vector (float version)
bool fDisableRegionSelection
Disable the region finding?
unsigned char ConvertChargeToChar(float charge)
Convert the hit charge into the range 0 to 255 required by the CVN.
void SetLogScale(bool setLog)
Set the log scale for charge.
void SetPixelMapSize(unsigned int nWires, unsigned int nTDCs)
Set the input pixel map size.
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:101
std::vector< float > fPEX
Vector of X PE measurements for pixels.
Definition: PixelMap.h:74
Utilities for producing images for the CVN.
bool fUseLogScale
Use a log scale for charge?
unsigned int NWire() const
Length in wires.
Definition: PixelMap.h:26
void ConvertChargeVectorsToViewVectors(std::vector< float > &v0pe, std::vector< float > &v1pe, std::vector< float > &v2pe, ViewVector &view0, ViewVector &view1, ViewVector &view2)
Base function for conversion of the Pixel Map to our required output format.
void GetMinMaxTDCs(std::vector< float > &tdcCharges, unsigned int &minTDC, unsigned int &maxTDC)
Get the minimum and maximum tdcs from the pixel map needed to make the image.
std::vector< ViewVector > ImageVector
Definition: CVNImageUtils.h:18
unsigned int fNViews
Number of views of each event.
unsigned int fNTDCs
Number of TDCs to use for the image height.
ImageVector BuildImageVector(ViewVector &v0, ViewVector &v1, ViewVector &v2)
Make the image vector from the view vectors.
std::vector< float > fPEZ
Vector of Y PE measurements for pixels.
Definition: PixelMap.h:76
void ConvertChargeVectorsToImageVector(std::vector< float > &v0pe, std::vector< float > &v1pe, std::vector< float > &v2pe, ImageVector &imageVec)
Convert three adc vectors into an image vector (contains all three views)
void EnableRegionSelection()
Enable the selection of the wire region.
ImageVectorF ConvertImageVecToImageVecF(ImageVector &image)
Convert a ImageVector into a ImageVectorF.
unsigned int fPixelMapWires
Input pixel map sizes.
std::vector< std::vector< float > > ViewVectorF
Definition: CVNImageUtils.h:19
void DisableRegionSelection()
Disable the selection of the wire region and just use the first 500 wires.
std::vector< bool > fViewReverse
Vector of bools to decide if any views need to be reversed.
void ConvertChargeVectorsToImageVectorF(std::vector< float > &v0pe, std::vector< float > &v1pe, std::vector< float > &v2pe, ImageVectorF &imageVec)
Float version of conversion for convenience of TF interface.
unsigned int NTdc() const
Width in tdcs.
Definition: PixelMap.h:29
std::vector< ViewVectorF > ImageVectorF
Definition: CVNImageUtils.h:20
unsigned int fNWires
Number of wires to use for the image width.
Float_t w
Definition: plot.C:20
void ConvertPixelMapToImageVector(const PixelMap &pm, ImageVector &imageVec)
Convert a pixel map into an image vector (contains all three views)
void SetImageSize(unsigned int nWires, unsigned int nTDCs, unsigned int nViews)
Set up the image size that we want to have.
std::vector< float > fPEY
Vector of Y PE measurements for pixels.
Definition: PixelMap.h:75
void SetViewReversal(bool reverseX, bool reverseY, bool reverseZ)
Function to set any views that need reversing.