All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MorphologicalFilter_tool.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 /// \file MorphologicalFilter.cc
3 /// \author T. Usher
4 ////////////////////////////////////////////////////////////////////////
5 
6 #include <cmath>
7 #include "art/Utilities/ToolMacros.h"
8 #include "art/Utilities/make_tool.h"
9 #include "art_root_io/TFileService.h"
10 #include "messagefacility/MessageLogger/MessageLogger.h"
11 #include "cetlib_except/exception.h"
14 #include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom()
15 
17 #include "icarus_signal_processing/WaveformTools.h"
18 
19 #include "TH1F.h"
20 #include "TH2F.h"
21 #include "TProfile.h"
22 
23 #include <fstream>
24 
25 namespace caldata
26 {
27 
29 {
30 public:
31  explicit MorphologicalFilter(const fhicl::ParameterSet& pset);
32 
34 
35  void configure(const fhicl::ParameterSet& pset) override;
36  void initializeHistograms(art::TFileDirectory&) const override;
37  size_t plane() const override {return fPlane;}
38 
39  void FilterWaveform(RawDigitVector&, size_t, size_t, float = 0.) const override;
40 
41 private:
42 
43  using Waveform = std::vector<short>;
44 
45  // Average the input waveform
47 
48  // Actual histogram initialization...
49  enum HistogramType : int
50  {
52  };
53 
54  caldata::HistogramMap initializeHistograms(size_t, size_t, size_t) const;
55 
56  // Member variables from the fhicl file
57  size_t fPlane;
58  int fNumBinsToAve; ///< Controls the smoothing
59  int fStructuringElement; ///< The window size
60  bool fOutputHistograms; ///< Output histograms?
61 
62  std::vector<float> fAveWeightVec; ///< Weight vector for smoothing
63  float fWeightSum; ///< sum of weights for smoothing
64 
65  art::TFileDirectory* fHistDirectory;
66 
67  // Global histograms
69  TH1F* fDiffRmsHist;
70  TH1F* fDiffMaxHist;
75 
76  icarus_signal_processing::WaveformTools<short> fWaveformTool;
77 
78  // Services
79  const geo::GeometryCore* fGeometry = lar::providerFrom<geo::Geometry>();
80 };
81 
82 //----------------------------------------------------------------------
83 // Constructor.
84 MorphologicalFilter::MorphologicalFilter(const fhicl::ParameterSet& pset)
85 {
86  configure(pset);
87 }
88 
90 {
91 }
92 
93 void MorphologicalFilter::configure(const fhicl::ParameterSet& pset)
94 {
95  // Start by recovering the parameters
96  std::vector<unsigned short> zin;
97 
98  fPlane = pset.get<size_t> ("Plane" );
99  fNumBinsToAve = pset.get< int > ("NumBinsToAve" );
100  fStructuringElement = pset.get< int> ("StructuringElement" );
101  fOutputHistograms = pset.get< bool > ("OutputHistograms", false);
102 
103  // If asked, define the global histograms
104  if (fOutputHistograms)
105  {
106  // Access ART's TFileService, which will handle creating and writing
107  // histograms and n-tuples for us.
108  art::ServiceHandle<art::TFileService> tfs;
109 
110  fHistDirectory = tfs.get();
111 
112  // Make a directory for these histograms
113  art::TFileDirectory dir = fHistDirectory->mkdir(Form("MF/ROIPlane_%1zu",fPlane));
114 
115  fDiffMeanHist = dir.make<TH1F>("DiffMean", ";Diff Mean;", 100, -20., 20.);
116  fDiffRmsHist = dir.make<TH1F>("DiffRms", ";Diff RMS;", 100, 0., 5.);
117  fDiffMaxHist = dir.make<TH1F>("DiffMax", ";Diff Max;", 200, 0., 200.);
118  fNumSigmaHist = dir.make<TH1F>("NSigma", ";#sigma;", 200, 0., 50.);
119  fNumSigNextHist = dir.make<TH1F>("NSigNext", ";#sigma;", 200, 0., 50.);
120  fDeltaTicksHist = dir.make<TH1F>("DeltaTix", ";Delta t", 200, 0., 200.);
121 
122  fDTixVDiffHist = dir.make<TH2F>("DTixVDiff", ";Delta t;Max Diff", 200, 0., 200., 200, 0., 200.);
123  }
124 
125  // precalculate the weight vector to use in the smoothing
126  fAveWeightVec.resize(fNumBinsToAve);
127 
128  if (fNumBinsToAve > 1)
129  {
130  for(int idx = 0; idx < fNumBinsToAve/2; idx++)
131  {
132  float weight = idx + 1;
133 
134  fAveWeightVec.at(idx) = weight;
135  fAveWeightVec.at(fNumBinsToAve - idx - 1) = weight;
136  }
137 
138  // Watch for case of fNumBinsToAve being odd
139  if (fNumBinsToAve % 2 > 0) fAveWeightVec.at(fNumBinsToAve/2) = fNumBinsToAve/2 + 1;
140  }
141  else fAveWeightVec.at(0) = 1.;
142 
143  fWeightSum = std::accumulate(fAveWeightVec.begin(),fAveWeightVec.end(), 0.);
144 
145  return;
146 }
147 
148 void MorphologicalFilter::FilterWaveform(RawDigitVector& waveform, size_t channel, size_t cnt, float pedestal) const
149 {
150  // The plan here is to use a morphological filtering technique to find the slowly varying baseline
151  // movement and remove it
152 
153  // We make lots of vectors... erosion, dilation, average and difference
154  Waveform erosionVec;
155  Waveform dilationVec;
156  Waveform averageVec;
157  Waveform differenceVec;
158 
159  // Define histograms for this particular channel?
160  caldata::HistogramMap histogramMap = initializeHistograms(channel, cnt, waveform.size());
161 
162  // If histogramming, then keep track of the original input channel
163  if (!histogramMap.empty()) for(size_t idx = 0; idx < waveform.size(); idx++) histogramMap.at(caldata::WAVEFORM)->Fill(idx, waveform.at(idx), 1.);
164 
165  Waveform smoothWaveform = waveform;
166 
167  // If the input pedestal is non-zero then baseline correct
168  if (std::abs(pedestal) > std::numeric_limits<float>::epsilon())
169  std::transform(waveform.begin(),waveform.end(),smoothWaveform.begin(),[pedestal](const auto& val){return val - short(std::round(pedestal));});
170 
171  // Compute the morphological filter vectors
172  fWaveformTool.getErosionDilationAverageDifference(smoothWaveform, fStructuringElement, erosionVec, dilationVec, averageVec, differenceVec);
173 
174  // What we are really interested in here is the closing vector but compute both
175  Waveform openingVec;
176  Waveform closingVec;
177 
178  fWaveformTool.getOpeningAndClosing(erosionVec,dilationVec,fStructuringElement,openingVec,closingVec);
179 
180  // Ok, get an average of the two
181  std::transform(openingVec.begin(),openingVec.end(),closingVec.begin(),averageVec.begin(),[](const auto& left, const auto& right){return (left + right)/2;});
182 
183  // Now smooth
184  std::transform(smoothWaveform.begin(),smoothWaveform.end(),averageVec.begin(),waveform.begin(),std::minus<short>());
185 
186  // Keep track of the corrected waveform
187  if (!histogramMap.empty())
188  {
189  for(size_t idx = 0; idx < waveform.size(); idx++) histogramMap.at(CORWAVEFORM)->Fill(idx, waveform.at(idx), 1.);
190  }
191 
192  return;
193 }
194 
195 void MorphologicalFilter::smoothInputWaveform(const RawDigitVector& inputWaveform, RawDigitVector& outputWaveform) const
196 {
197  // Vector smoothing - take the 10 bin average
198  int halfBins = fNumBinsToAve / 2;
199 
200  outputWaveform.resize(inputWaveform.size());
201 
202  // To facilitate handling the bins at the ends of the input waveform we embed in a slightly larger
203  // vector which has zeroes on the ends
204  RawDigitVector tempWaveform(inputWaveform.size()+fNumBinsToAve);
205 
206  // Set the edge bins which can't be smoothed to zero
207  std::fill(tempWaveform.begin(),tempWaveform.begin()+halfBins,0.);
208  std::fill(tempWaveform.end()-halfBins,tempWaveform.end(),0.);
209 
210  // Copy in the input waveform
211  std::copy(inputWaveform.begin(),inputWaveform.end(),tempWaveform.begin()+halfBins);
212 
213  // Now do the smoothing
214  for(size_t idx = 0; idx < inputWaveform.size(); idx++)
215  {
216  float weightedSum(0.);
217 
218  for(int wIdx = 0; wIdx < fNumBinsToAve; wIdx++) weightedSum += fAveWeightVec.at(wIdx) * tempWaveform.at(idx + wIdx);
219 
220  outputWaveform.at(idx) = weightedSum / fWeightSum;
221  }
222 
223  return;
224 }
225 
226 void MorphologicalFilter::initializeHistograms(art::TFileDirectory& histDir) const
227 {
228  // It is assumed that the input TFileDirectory has been set up to group histograms into a common
229  // folder at the calling routine's level. Here we create one more level of indirection to keep
230  // histograms made by this tool separate.
231 /*
232  std::string dirName = "ROIFinderPlane_" + std::to_string(fPlane);
233 
234  art::TFileDirectory dir = histDir.mkdir(dirName.c_str());
235 
236  auto const* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
237  double samplingRate = detprop->SamplingRate();
238  double numBins = fROIFinderVec.size();
239  double maxFreq = 500. / samplingRate;
240  std::string histName = "ROIFinderPlane_" + std::to_string(fPlane);
241 
242  TH1D* hist = dir.make<TH1D>(histName.c_str(), "ROIFinder;Frequency(MHz)", numBins, 0., maxFreq);
243 
244  for(int bin = 0; bin < numBins; bin++)
245  {
246  double freq = maxFreq * double(bin + 0.5) / double(numBins);
247 
248  hist->Fill(freq, fROIFinderVec.at(bin).Re());
249  }
250 */
251 
252  return;
253 }
254 
255 caldata::HistogramMap MorphologicalFilter::initializeHistograms(size_t channel, size_t cnt, size_t waveformSize) const
256 {
257  HistogramMap histogramMap;
258 
259  if (fOutputHistograms)
260  {
261  // Try to limit to the wire number (since we are already segregated by plane)
262  std::vector<geo::WireID> wids = fGeometry->ChannelToWire(channel);
263  size_t cryo = wids[0].Cryostat;
264  size_t tpc = wids[0].TPC;
265  size_t plane = wids[0].Plane;
266  size_t wire = wids[0].Wire;
267 
268  // Make a directory for these histograms
269  art::TFileDirectory dir = fHistDirectory->mkdir(Form("MF/ROIPlane_%1zu/c%1zu/c%1zut%1zuwire_%05zu",plane,cnt,cryo,tpc,wire));
270 
271  // We keep track of four histograms:
272  try
273  {
274  // origWaveHist = dir.make<TProfile>(Form("Inp_%03zu_ctw%01zu/%01zu/%05zu",cnt,cryo,tpc,wire), "Waveform", waveform.size(), 0, waveform.size(), -500., 500.);
275  histogramMap[caldata::WAVEFORM] =
276  dir.make<TProfile>(Form("MFWfm_%03zu_ctw%01zu-%01zu-%01zu-%05zu",cnt,cryo,tpc,plane,wire), "Waveform", waveformSize, 0, waveformSize, -500., 500.);
277  histogramMap[caldata::EROSION] =
278  dir.make<TProfile>(Form("MFEro_%03zu_ctw%01zu-%01zu-%01zu-%05zu",cnt,cryo,tpc,plane,wire), "Erosion", waveformSize, 0, waveformSize, -500., 500.);
279  histogramMap[caldata::DILATION] =
280  dir.make<TProfile>(Form("MFDil_%03zu_ctw%01zu-%01zu-%01zu-%05zu",cnt,cryo,tpc,plane,wire), "Dilation", waveformSize, 0, waveformSize, -500., 500.);
281  histogramMap[caldata::AVERAGE] =
282  dir.make<TProfile>(Form("MFAve_%03zu_ctw%01zu-%01zu-%01zu-%05zu",cnt,cryo,tpc,plane,wire), "Average", waveformSize, 0, waveformSize, -500., 500.);
283  histogramMap[caldata::DIFFERENCE] =
284  dir.make<TProfile>(Form("MFDif_%03zu_ctw%01zu-%01zu-%01zu-%05zu",cnt,cryo,tpc,plane,wire), "Difference", waveformSize, 0, waveformSize, -500., 500.);
285  histogramMap[caldata::OPENING] =
286  dir.make<TProfile>(Form("MFOpe_%03zu_ctw%01zu-%01zu-%01zu-%05zu",cnt,cryo,tpc,plane,wire), "Opening", waveformSize, 0, waveformSize, -500., 500.);
287  histogramMap[caldata::CLOSING] =
288  dir.make<TProfile>(Form("MFClo_%03zu_ctw%01zu-%01zu-%01zu-%05zu",cnt,cryo,tpc,plane,wire), "Closing", waveformSize, 0, waveformSize, -500., 500.);
289 
290  // Also, if smoothing then we would like to keep track of the original waveform too
291  histogramMap[CORWAVEFORM] =
292  dir.make<TProfile>(Form("MFCor_%03zu_ctw%01zu-%01zu-%01zu-%05zu",cnt,cryo,tpc,plane,wire), "Corrected Waveform", waveformSize, 0, waveformSize, -500., 500.);
293  } catch(...)
294  {
295  std::cout << "Caught exception trying to make new hists, tpc,plane,cnt,wire: " << tpc << ", " << fPlane << ", " << cnt << ", " << wire << std::endl;
296  }
297  }
298 
299  return histogramMap;
300 }
301 
302 DEFINE_ART_CLASS_TOOL(MorphologicalFilter)
303 }
void configure(const fhicl::ParameterSet &pset) override
Utilities related to art service access.
bool fOutputHistograms
Output histograms?
static constexpr Sample_t transform(Sample_t sample)
walls no right
Definition: selectors.fcl:105
raw::RawDigit::ADCvector_t RawDigitVector
std::vector< geo::WireID > ChannelToWire(raw::ChannelID_t const channel) const
Returns a list of wires connected to the specified TPC channel.
void initializeHistograms(art::TFileDirectory &) const override
T abs(T value)
MorphologicalFilter(const fhicl::ParameterSet &pset)
void fill(const art::PtrVector< recob::Hit > &hits, int only_plane)
walls no left
Definition: selectors.fcl:105
Description of geometry of one entire detector.
void smoothInputWaveform(const RawDigitVector &, RawDigitVector &) const
int fNumBinsToAve
Controls the smoothing.
tuple dir
Definition: dropbox.py:28
icarus_signal_processing::WaveformTools< short > fWaveformTool
This provides an interface for tools which are tasked with filtering input waveforms.
void FilterWaveform(RawDigitVector &, size_t, size_t, float=0.) const override
T copy(T const &v)
art::ServiceHandle< art::TFileService > tfs
const geo::GeometryCore * fGeometry
std::map< int, TProfile * > HistogramMap
std::vector< float > fAveWeightVec
Weight vector for smoothing.
process_name can override from command line with o or output caldata
Definition: pid.fcl:40
art framework interface to geometry description
BEGIN_PROLOG could also be cout
float fWeightSum
sum of weights for smoothing