OpenMS
MorphologicalFilter.h
Go to the documentation of this file.
1 // --------------------------------------------------------------------------
2 // OpenMS -- Open-Source Mass Spectrometry
3 // --------------------------------------------------------------------------
4 // Copyright The OpenMS Team -- Eberhard Karls University Tuebingen,
5 // ETH Zurich, and Freie Universitaet Berlin 2002-2023.
6 //
7 // This software is released under a three-clause BSD license:
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of any author or any participating institution
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 // For a full list of authors, refer to the file AUTHORS.
17 // --------------------------------------------------------------------------
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL ANY OF THE AUTHORS OR THE CONTRIBUTING
22 // INSTITUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // --------------------------------------------------------------------------
31 // $Maintainer: Timo Sachsenberg $
32 // $Authors: $
33 // --------------------------------------------------------------------------
34 
35 #pragma once
36 
43 #include <algorithm>
44 #include <iterator>
45 
46 namespace OpenMS
47 {
48 
49  namespace Internal
50  {
57  template<typename IteratorT>
58  class /* OPENMS_DLLAPI */ IntensityIteratorWrapper
59  {
60  public:
61  typedef std::forward_iterator_tag iterator_category;
62  typedef typename IteratorT::value_type::IntensityType value_type;
63  typedef typename IteratorT::value_type::IntensityType& reference;
64  typedef typename IteratorT::value_type::IntensityType* pointer;
65  typedef typename IteratorT::difference_type difference_type;
66 
67  IntensityIteratorWrapper(const IteratorT& rhs) : base(rhs)
68  {
69  }
70 
72  {
73  return base->getIntensity();
74  }
75 
76  template<typename IndexT>
77  value_type operator[](const IndexT& index)
78  {
79  return base[index].getIntensity();
80  }
81 
83  {
84  return base - rhs.base;
85  }
86 
88  {
89  ++base;
90  return *this;
91  }
92 
94  {
95  IteratorT tmp = *this;
96  ++(*this);
97  return tmp;
98  }
99 
100  bool operator==(const IntensityIteratorWrapper& rhs) const
101  {
102  return base == rhs.base;
103  }
104 
105  bool operator!=(const IntensityIteratorWrapper& rhs) const
106  {
107  return base != rhs.base;
108  }
109 
110  protected:
111  IteratorT base;
112  };
113 
115  template<typename IteratorT>
117  {
119  }
120 
121  } // namespace Internal
122 
158  class OPENMS_DLLAPI MorphologicalFilter : public ProgressLogger, public DefaultParamHandler
159  {
160  public:
162  MorphologicalFilter() : ProgressLogger(), DefaultParamHandler("MorphologicalFilter"), struct_size_in_datapoints_(0)
163  {
164  // structuring element
165  defaults_.setValue("struc_elem_length", 3.0, "Length of the structuring element. This should be wider than the expected peak width.");
166  defaults_.setValue("struc_elem_unit", "Thomson", "The unit of the 'struct_elem_length'.");
167  defaults_.setValidStrings("struc_elem_unit", {"Thomson", "DataPoints"});
168  // methods
169  defaults_.setValue("method", "tophat",
170  "Method to use, the default is 'tophat'. Do not change this unless you know what you are doing. The other methods may be useful for tuning the parameters, see the class "
171  "documentation of MorpthologicalFilter.");
172  defaults_.setValidStrings("method", {"identity", "erosion", "dilation", "opening", "closing", "gradient", "tophat", "bothat", "erosion_simple", "dilation_simple"});
173 
174  defaultsToParam_();
175  }
176 
179  {
180  }
181 
193  template<typename InputIterator, typename OutputIterator>
194  void filterRange(InputIterator input_begin, InputIterator input_end, OutputIterator output_begin)
195  {
196  // the buffer is static only to avoid reallocation
197  static std::vector<typename InputIterator::value_type> buffer;
198  const UInt size = input_end - input_begin;
199 
200  // determine the struct size in data points if not already set
201  if (struct_size_in_datapoints_ == 0)
202  {
203  struct_size_in_datapoints_ = (UInt)(double)param_.getValue("struc_elem_length");
204  }
205 
206  // apply the filtering
207  std::string method = param_.getValue("method");
208  if (method == "identity")
209  {
210  std::copy(input_begin, input_end, output_begin);
211  }
212  else if (method == "erosion")
213  {
214  applyErosion_(struct_size_in_datapoints_, input_begin, input_end, output_begin);
215  }
216  else if (method == "dilation")
217  {
218  applyDilation_(struct_size_in_datapoints_, input_begin, input_end, output_begin);
219  }
220  else if (method == "opening")
221  {
222  if (buffer.size() < size)
223  buffer.resize(size);
224  applyErosion_(struct_size_in_datapoints_, input_begin, input_end, buffer.begin());
225  applyDilation_(struct_size_in_datapoints_, buffer.begin(), buffer.begin() + size, output_begin);
226  }
227  else if (method == "closing")
228  {
229  if (buffer.size() < size)
230  buffer.resize(size);
231  applyDilation_(struct_size_in_datapoints_, input_begin, input_end, buffer.begin());
232  applyErosion_(struct_size_in_datapoints_, buffer.begin(), buffer.begin() + size, output_begin);
233  }
234  else if (method == "gradient")
235  {
236  if (buffer.size() < size)
237  buffer.resize(size);
238  applyErosion_(struct_size_in_datapoints_, input_begin, input_end, buffer.begin());
239  applyDilation_(struct_size_in_datapoints_, input_begin, input_end, output_begin);
240  for (UInt i = 0; i < size; ++i)
241  output_begin[i] -= buffer[i];
242  }
243  else if (method == "tophat")
244  {
245  if (buffer.size() < size)
246  buffer.resize(size);
247  applyErosion_(struct_size_in_datapoints_, input_begin, input_end, buffer.begin());
248  applyDilation_(struct_size_in_datapoints_, buffer.begin(), buffer.begin() + size, output_begin);
249  for (UInt i = 0; i < size; ++i)
250  output_begin[i] = input_begin[i] - output_begin[i];
251  }
252  else if (method == "bothat")
253  {
254  if (buffer.size() < size)
255  buffer.resize(size);
256  applyDilation_(struct_size_in_datapoints_, input_begin, input_end, buffer.begin());
257  applyErosion_(struct_size_in_datapoints_, buffer.begin(), buffer.begin() + size, output_begin);
258  for (UInt i = 0; i < size; ++i)
259  output_begin[i] = input_begin[i] - output_begin[i];
260  }
261  else if (method == "erosion_simple")
262  {
263  applyErosionSimple_(struct_size_in_datapoints_, input_begin, input_end, output_begin);
264  }
265  else if (method == "dilation_simple")
266  {
267  applyDilationSimple_(struct_size_in_datapoints_, input_begin, input_end, output_begin);
268  }
269 
270  struct_size_in_datapoints_ = 0;
271  }
272 
287  void filter(MSSpectrum& spectrum)
288  {
289  // make sure the right peak type is set
291 
292  // Abort if there is nothing to do
293  if (spectrum.size() <= 1)
294  {
295  return;
296  }
297 
298  // Determine structuring element size in datapoints (depending on the unit)
299  if (param_.getValue("struc_elem_unit") == "Thomson")
300  {
301  const double struc_elem_length = (double)param_.getValue("struc_elem_length");
302  const double mz_diff = spectrum.back().getMZ() - spectrum.begin()->getMZ();
303  struct_size_in_datapoints_ = (UInt)(ceil(struc_elem_length * (double)(spectrum.size() - 1) / mz_diff));
304  }
305  else
306  {
307  struct_size_in_datapoints_ = (UInt)(double)param_.getValue("struc_elem_length");
308  }
309  // make it odd (needed for the algorithm)
310  if (!Math::isOdd(struct_size_in_datapoints_))
311  ++struct_size_in_datapoints_;
312 
313  // apply the filtering and overwrite the input data
314  std::vector<Peak1D::IntensityType> output(spectrum.size());
315  filterRange(Internal::intensityIteratorWrapper(spectrum.begin()), Internal::intensityIteratorWrapper(spectrum.end()), output.begin());
316 
317  // overwrite output with data
318  for (Size i = 0; i < spectrum.size(); ++i)
319  {
320  spectrum[i].setIntensity(output[i]);
321  }
322  }
323 
331  {
332  startProgress(0, exp.size(), "filtering baseline");
333  for (UInt i = 0; i < exp.size(); ++i)
334  {
335  filter(exp[i]);
336  setProgress(i);
337  }
338  endProgress();
339  }
340 
341  protected:
344 
349  template<typename InputIterator, typename OutputIterator>
350  void applyErosion_(Int struc_size, InputIterator input, InputIterator input_end, OutputIterator output)
351  {
352  typedef typename InputIterator::value_type ValueType;
353  const Int size = input_end - input;
354  const Int struc_size_half = struc_size / 2; // yes, integer division
355 
356  static std::vector<ValueType> buffer;
357  if (Int(buffer.size()) < struc_size)
358  buffer.resize(struc_size);
359 
360  Int anchor; // anchoring position of the current block
361  Int i; // index relative to anchor, used for 'for' loops
362  Int ii = 0; // input index
363  Int oi = 0; // output index
364  ValueType current; // current value
365 
366  // we just can't get the case distinctions right in these cases, resorting to simple method.
367  if (size <= struc_size || size <= 5)
368  {
369  applyErosionSimple_(struc_size, input, input_end, output);
370  return;
371  }
372  {
373  // lower margin area
374  current = input[0];
375  for (++ii; ii < struc_size_half; ++ii)
376  if (current > input[ii])
377  current = input[ii];
378  for (; ii < std::min(Int(struc_size), size); ++ii, ++oi)
379  {
380  if (current > input[ii])
381  current = input[ii];
382  output[oi] = current;
383  }
384  }
385  {
386  // middle (main) area
387  for (anchor = struc_size; anchor <= size - struc_size; anchor += struc_size)
388  {
389  ii = anchor;
390  current = input[ii];
391  buffer[0] = current;
392  for (i = 1; i < struc_size; ++i, ++ii)
393  {
394  if (current > input[ii])
395  current = input[ii];
396  buffer[i] = current;
397  }
398  ii = anchor - 1;
399  oi = ii + struc_size_half;
400  current = input[ii];
401  for (i = 1; i < struc_size; ++i, --ii, --oi)
402  {
403  if (current > input[ii])
404  current = input[ii];
405  output[oi] = std::min(buffer[struc_size - i], current);
406  }
407  if (current > input[ii])
408  current = input[ii];
409  output[oi] = current;
410  }
411  }
412  {
413  // higher margin area
414  ii = size - 1;
415  oi = ii;
416  current = input[ii];
417  for (--ii; ii >= size - struc_size_half; --ii)
418  if (current > input[ii])
419  current = input[ii];
420  for (; ii >= std::max(size - Int(struc_size), 0); --ii, --oi)
421  {
422  if (current > input[ii])
423  current = input[ii];
424  output[oi] = current;
425  }
426  anchor = size - struc_size;
427  ii = anchor;
428  current = input[ii];
429  buffer[0] = current;
430  for (i = 1; i < struc_size; ++i, ++ii)
431  {
432  if (current > input[ii])
433  current = input[ii];
434  buffer[i] = current;
435  }
436  ii = anchor - 1;
437  oi = ii + struc_size_half;
438  current = input[ii];
439  for (i = 1; (ii >= 0) && (i < struc_size); ++i, --ii, --oi)
440  {
441  if (current > input[ii])
442  current = input[ii];
443  output[oi] = std::min(buffer[struc_size - i], current);
444  }
445  if (ii >= 0)
446  {
447  if (current > input[ii])
448  current = input[ii];
449  output[oi] = current;
450  }
451  }
452  return;
453  }
454 
459  template<typename InputIterator, typename OutputIterator>
460  void applyDilation_(Int struc_size, InputIterator input, InputIterator input_end, OutputIterator output)
461  {
462  typedef typename InputIterator::value_type ValueType;
463  const Int size = input_end - input;
464  const Int struc_size_half = struc_size / 2; // yes, integer division
465 
466  static std::vector<ValueType> buffer;
467  if (Int(buffer.size()) < struc_size)
468  buffer.resize(struc_size);
469 
470  Int anchor; // anchoring position of the current block
471  Int i; // index relative to anchor, used for 'for' loops
472  Int ii = 0; // input index
473  Int oi = 0; // output index
474  ValueType current; // current value
475 
476  // we just can't get the case distinctions right in these cases, resorting to simple method.
477  if (size <= struc_size || size <= 5)
478  {
479  applyDilationSimple_(struc_size, input, input_end, output);
480  return;
481  }
482  {
483  // lower margin area
484  current = input[0];
485  for (++ii; ii < struc_size_half; ++ii)
486  if (current < input[ii])
487  current = input[ii];
488  for (; ii < std::min(Int(struc_size), size); ++ii, ++oi)
489  {
490  if (current < input[ii])
491  current = input[ii];
492  output[oi] = current;
493  }
494  }
495  {
496  // middle (main) area
497  for (anchor = struc_size; anchor <= size - struc_size; anchor += struc_size)
498  {
499  ii = anchor;
500  current = input[ii];
501  buffer[0] = current;
502  for (i = 1; i < struc_size; ++i, ++ii)
503  {
504  if (current < input[ii])
505  current = input[ii];
506  buffer[i] = current;
507  }
508  ii = anchor - 1;
509  oi = ii + struc_size_half;
510  current = input[ii];
511  for (i = 1; i < struc_size; ++i, --ii, --oi)
512  {
513  if (current < input[ii])
514  current = input[ii];
515  output[oi] = std::max(buffer[struc_size - i], current);
516  }
517  if (current < input[ii])
518  current = input[ii];
519  output[oi] = current;
520  }
521  }
522  {
523  // higher margin area
524  ii = size - 1;
525  oi = ii;
526  current = input[ii];
527  for (--ii; ii >= size - struc_size_half; --ii)
528  if (current < input[ii])
529  current = input[ii];
530  for (; ii >= std::max(size - Int(struc_size), 0); --ii, --oi)
531  {
532  if (current < input[ii])
533  current = input[ii];
534  output[oi] = current;
535  }
536  anchor = size - struc_size;
537  ii = anchor;
538  current = input[ii];
539  buffer[0] = current;
540  for (i = 1; i < struc_size; ++i, ++ii)
541  {
542  if (current < input[ii])
543  current = input[ii];
544  buffer[i] = current;
545  }
546  ii = anchor - 1;
547  oi = ii + struc_size_half;
548  current = input[ii];
549  for (i = 1; (ii >= 0) && (i < struc_size); ++i, --ii, --oi)
550  {
551  if (current < input[ii])
552  current = input[ii];
553  output[oi] = std::max(buffer[struc_size - i], current);
554  }
555  if (ii >= 0)
556  {
557  if (current < input[ii])
558  current = input[ii];
559  output[oi] = current;
560  }
561  }
562  return;
563  }
564 
566  template<typename InputIterator, typename OutputIterator>
567  void applyErosionSimple_(Int struc_size, InputIterator input_begin, InputIterator input_end, OutputIterator output_begin)
568  {
569  typedef typename InputIterator::value_type ValueType;
570  const int size = input_end - input_begin;
571  const Int struc_size_half = struc_size / 2; // yes integer division
572  for (Int index = 0; index < size; ++index)
573  {
574  Int start = std::max(0, index - struc_size_half);
575  Int stop = std::min(size - 1, index + struc_size_half);
576  ValueType value = input_begin[start];
577  for (Int i = start + 1; i <= stop; ++i)
578  if (value > input_begin[i])
579  value = input_begin[i];
580  output_begin[index] = value;
581  }
582  return;
583  }
584 
586  template<typename InputIterator, typename OutputIterator>
587  void applyDilationSimple_(Int struc_size, InputIterator input_begin, InputIterator input_end, OutputIterator output_begin)
588  {
589  typedef typename InputIterator::value_type ValueType;
590  const int size = input_end - input_begin;
591  const Int struc_size_half = struc_size / 2; // yes integer division
592  for (Int index = 0; index < size; ++index)
593  {
594  Int start = std::max(0, index - struc_size_half);
595  Int stop = std::min(size - 1, index + struc_size_half);
596  ValueType value = input_begin[start];
597  for (Int i = start + 1; i <= stop; ++i)
598  if (value < input_begin[i])
599  value = input_begin[i];
600  output_begin[index] = value;
601  }
602  return;
603  }
604 
605  private:
608  };
609 
610 } // namespace OpenMS
A base class for all classes handling default parameters.
Definition: DefaultParamHandler.h:92
An iterator wrapper to access peak intensities instead of the peak itself.
Definition: MorphologicalFilter.h:59
bool operator==(const IntensityIteratorWrapper &rhs) const
Definition: MorphologicalFilter.h:100
IteratorT base
Definition: MorphologicalFilter.h:111
std::forward_iterator_tag iterator_category
Definition: MorphologicalFilter.h:61
bool operator!=(const IntensityIteratorWrapper &rhs) const
Definition: MorphologicalFilter.h:105
IteratorT::difference_type difference_type
Definition: MorphologicalFilter.h:65
IteratorT::value_type::IntensityType value_type
Definition: MorphologicalFilter.h:62
IteratorT::value_type::IntensityType * pointer
Definition: MorphologicalFilter.h:64
difference_type operator-(IntensityIteratorWrapper &rhs) const
Definition: MorphologicalFilter.h:82
value_type operator*()
Definition: MorphologicalFilter.h:71
IntensityIteratorWrapper operator++(int)
Definition: MorphologicalFilter.h:93
IntensityIteratorWrapper & operator++()
Definition: MorphologicalFilter.h:87
IteratorT::value_type::IntensityType & reference
Definition: MorphologicalFilter.h:63
value_type operator[](const IndexT &index)
Definition: MorphologicalFilter.h:77
IntensityIteratorWrapper(const IteratorT &rhs)
Definition: MorphologicalFilter.h:67
In-Memory representation of a mass spectrometry run.
Definition: MSExperiment.h:72
Size size() const
The number of spectra.
Definition: MSExperiment.h:147
The representation of a 1D spectrum.
Definition: MSSpectrum.h:70
This class implements baseline filtering operations using methods from mathematical morphology.
Definition: MorphologicalFilter.h:159
void applyDilation_(Int struc_size, InputIterator input, InputIterator input_end, OutputIterator output)
Applies dilation. This implementation uses van Herk's method. Only 3 min/max comparisons are required...
Definition: MorphologicalFilter.h:460
MorphologicalFilter(const MorphologicalFilter &source)
copy constructor not implemented
void filterRange(InputIterator input_begin, InputIterator input_end, OutputIterator output_begin)
Applies the morphological filtering operation to an iterator range.
Definition: MorphologicalFilter.h:194
void filter(MSSpectrum &spectrum)
Applies the morphological filtering operation to an MSSpectrum.
Definition: MorphologicalFilter.h:287
void filterExperiment(PeakMap &exp)
Applies the morphological filtering operation to an MSExperiment.
Definition: MorphologicalFilter.h:330
UInt struct_size_in_datapoints_
Member for struct size in data points.
Definition: MorphologicalFilter.h:343
MorphologicalFilter()
Constructor.
Definition: MorphologicalFilter.h:162
void applyDilationSimple_(Int struc_size, InputIterator input_begin, InputIterator input_end, OutputIterator output_begin)
Applies dilation. Simple implementation, possibly faster if struc_size is very small,...
Definition: MorphologicalFilter.h:587
void applyErosion_(Int struc_size, InputIterator input, InputIterator input_end, OutputIterator output)
Applies erosion. This implementation uses van Herk's method. Only 3 min/max comparisons are required ...
Definition: MorphologicalFilter.h:350
void applyErosionSimple_(Int struc_size, InputIterator input_begin, InputIterator input_end, OutputIterator output_begin)
Applies erosion. Simple implementation, possibly faster if struc_size is very small,...
Definition: MorphologicalFilter.h:567
~MorphologicalFilter() override
Destructor.
Definition: MorphologicalFilter.h:178
Base class for all classes that want to report their progress.
Definition: ProgressLogger.h:53
void setType(SpectrumType type)
sets the spectrum type
@ PROFILE
profile data
Definition: SpectrumSettings.h:74
int Int
Signed integer type.
Definition: Types.h:102
unsigned int UInt
Unsigned integer type.
Definition: Types.h:94
size_t Size
Size type e.g. used as variable which can hold result of size()
Definition: Types.h:127
bool isOdd(UInt x)
Returns true if the given integer is odd.
Definition: MathFunctions.h:199
IntensityIteratorWrapper< IteratorT > intensityIteratorWrapper(const IteratorT &rhs)
make-function so that we need no write out all those type names to get the wrapped iterator.
Definition: MorphologicalFilter.h:116
Main OpenMS namespace.
Definition: FeatureDeconvolution.h:48