OpenMS
Loading...
Searching...
No Matches
MorphologicalFilter.h
Go to the documentation of this file.
1// Copyright (c) 2002-present, OpenMS Inc. -- EKU Tuebingen, ETH Zurich, and FU Berlin
2// SPDX-License-Identifier: BSD-3-Clause
3//
4// --------------------------------------------------------------------------
5// $Maintainer: Timo Sachsenberg $
6// $Authors: $
7// --------------------------------------------------------------------------
8
9#pragma once
10
17#include <algorithm>
18#include <iterator>
19
20namespace OpenMS
21{
22
23 namespace Internal
24 {
31 template<typename IteratorT>
32 class /* OPENMS_DLLAPI */ IntensityIteratorWrapper
33 {
34 public:
35 typedef std::forward_iterator_tag iterator_category;
36 typedef typename IteratorT::value_type::IntensityType value_type;
37 typedef typename IteratorT::value_type::IntensityType& reference;
38 typedef typename IteratorT::value_type::IntensityType* pointer;
39 typedef typename IteratorT::difference_type difference_type;
40
41 IntensityIteratorWrapper(const IteratorT& rhs) : base(rhs)
42 {
43 }
44
46 {
47 return base->getIntensity();
48 }
49
50 template<typename IndexT>
51 value_type operator[](const IndexT& index)
52 {
53 return base[index].getIntensity();
54 }
55
57 {
58 return base - rhs.base;
59 }
60
62 {
63 ++base;
64 return *this;
65 }
66
68 {
69 IteratorT tmp = *this;
70 ++(*this);
71 return tmp;
72 }
73
74 bool operator==(const IntensityIteratorWrapper& rhs) const
75 {
76 return base == rhs.base;
77 }
78
79 bool operator!=(const IntensityIteratorWrapper& rhs) const
80 {
81 return base != rhs.base;
82 }
83
84 protected:
85 IteratorT base;
86 };
87
89 template<typename IteratorT>
94
95 } // namespace Internal
96
135 class OPENMS_DLLAPI MorphologicalFilter : public ProgressLogger, public DefaultParamHandler
136 {
137 public:
139 MorphologicalFilter() : ProgressLogger(), DefaultParamHandler("MorphologicalFilter"), struct_size_in_datapoints_(0)
140 {
141 // structuring element
142 defaults_.setValue("struc_elem_length", 3.0, "Length of the structuring element. This should be wider than the expected peak width.");
143 defaults_.setValue("struc_elem_unit", "Thomson", "The unit of the 'struct_elem_length'.");
144 defaults_.setValidStrings("struc_elem_unit", {"Thomson", "DataPoints"});
145 // methods
146 defaults_.setValue("method", "tophat",
147 "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 "
148 "documentation of MorpthologicalFilter.");
149 defaults_.setValidStrings("method", {"identity", "erosion", "dilation", "opening", "closing", "gradient", "tophat", "bothat", "erosion_simple", "dilation_simple"});
150
151 defaultsToParam_();
152 }
153
156 {
157 }
158
170 template<typename InputIterator, typename OutputIterator>
171 void filterRange(InputIterator input_begin, InputIterator input_end, OutputIterator output_begin)
172 {
173 // Reuse scratch storage without sharing it across threads.
174 static thread_local std::vector<typename InputIterator::value_type> buffer;
175 const UInt size = input_end - input_begin;
176
177 // determine the struct size in data points if not already set
178 if (struct_size_in_datapoints_ == 0)
179 {
180 struct_size_in_datapoints_ = (UInt)(double)param_.getValue("struc_elem_length");
181 }
182
183 // apply the filtering
184 std::string method = param_.getValue("method");
185 if (method == "identity")
186 {
187 std::copy(input_begin, input_end, output_begin);
188 }
189 else if (method == "erosion")
190 {
191 applyErosion_(struct_size_in_datapoints_, input_begin, input_end, output_begin);
192 }
193 else if (method == "dilation")
194 {
195 applyDilation_(struct_size_in_datapoints_, input_begin, input_end, output_begin);
196 }
197 else if (method == "opening")
198 {
199 if (buffer.size() < size)
200 buffer.resize(size);
201 applyErosion_(struct_size_in_datapoints_, input_begin, input_end, buffer.begin());
202 applyDilation_(struct_size_in_datapoints_, buffer.begin(), buffer.begin() + size, output_begin);
203 }
204 else if (method == "closing")
205 {
206 if (buffer.size() < size)
207 buffer.resize(size);
208 applyDilation_(struct_size_in_datapoints_, input_begin, input_end, buffer.begin());
209 applyErosion_(struct_size_in_datapoints_, buffer.begin(), buffer.begin() + size, output_begin);
210 }
211 else if (method == "gradient")
212 {
213 if (buffer.size() < size)
214 buffer.resize(size);
215 applyErosion_(struct_size_in_datapoints_, input_begin, input_end, buffer.begin());
216 applyDilation_(struct_size_in_datapoints_, input_begin, input_end, output_begin);
217 for (UInt i = 0; i < size; ++i)
218 output_begin[i] -= buffer[i];
219 }
220 else if (method == "tophat")
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 for (UInt i = 0; i < size; ++i)
227 output_begin[i] = input_begin[i] - output_begin[i];
228 }
229 else if (method == "bothat")
230 {
231 if (buffer.size() < size)
232 buffer.resize(size);
233 applyDilation_(struct_size_in_datapoints_, input_begin, input_end, buffer.begin());
234 applyErosion_(struct_size_in_datapoints_, buffer.begin(), buffer.begin() + size, output_begin);
235 for (UInt i = 0; i < size; ++i)
236 output_begin[i] = input_begin[i] - output_begin[i];
237 }
238 else if (method == "erosion_simple")
239 {
240 applyErosionSimple_(struct_size_in_datapoints_, input_begin, input_end, output_begin);
241 }
242 else if (method == "dilation_simple")
243 {
244 applyDilationSimple_(struct_size_in_datapoints_, input_begin, input_end, output_begin);
245 }
246
247 struct_size_in_datapoints_ = 0;
248 }
249
264 void filter(MSSpectrum& spectrum)
265 {
266 // make sure the right peak type is set
267 spectrum.setType(SpectrumSettings::SpectrumType::PROFILE);
268
269 // Abort if there is nothing to do
270 if (spectrum.size() <= 1)
271 {
272 return;
273 }
274
275 // Determine structuring element size in datapoints (depending on the unit)
276 if (param_.getValue("struc_elem_unit") == "Thomson")
277 {
278 const double struc_elem_length = (double)param_.getValue("struc_elem_length");
279 const double mz_diff = spectrum.back().getMZ() - spectrum.begin()->getMZ();
280 struct_size_in_datapoints_ = (UInt)(ceil(struc_elem_length * (double)(spectrum.size() - 1) / mz_diff));
281 }
282 else
283 {
284 struct_size_in_datapoints_ = (UInt)(double)param_.getValue("struc_elem_length");
285 }
286 // make it odd (needed for the algorithm)
287 if (!Math::isOdd(struct_size_in_datapoints_))
288 ++struct_size_in_datapoints_;
289
290 // apply the filtering and overwrite the input data
291 std::vector<Peak1D::IntensityType> output(spectrum.size());
292 filterRange(Internal::intensityIteratorWrapper(spectrum.begin()), Internal::intensityIteratorWrapper(spectrum.end()), output.begin());
293
294 // overwrite output with data
295 for (Size i = 0; i < spectrum.size(); ++i)
296 {
297 spectrum[i].setIntensity(output[i]);
298 }
299 }
300
308 {
309 startProgress(0, exp.size(), "filtering baseline");
310 for (UInt i = 0; i < exp.size(); ++i)
311 {
312 filter(exp[i]);
313 setProgress(i);
314 }
315 endProgress();
316 }
317
318 protected:
321
326 template<typename InputIterator, typename OutputIterator>
327 void applyErosion_(Int struc_size, InputIterator input, InputIterator input_end, OutputIterator output)
328 {
329 typedef typename InputIterator::value_type ValueType;
330 const Int size = input_end - input;
331 const Int struc_size_half = struc_size / 2; // yes, integer division
332
333 // Reuse scratch storage without sharing it across threads.
334 static thread_local std::vector<ValueType> buffer;
335 if (Int(buffer.size()) < struc_size)
336 buffer.resize(struc_size);
337
338 Int anchor; // anchoring position of the current block
339 Int i; // index relative to anchor, used for 'for' loops
340 Int ii = 0; // input index
341 Int oi = 0; // output index
342 ValueType current; // current value
343
344 // we just can't get the case distinctions right in these cases, resorting to simple method.
345 if (size <= struc_size || size <= 5)
346 {
347 applyErosionSimple_(struc_size, input, input_end, output);
348 return;
349 }
350 {
351 // lower margin area
352 current = input[0];
353 for (++ii; ii < struc_size_half; ++ii)
354 if (current > input[ii])
355 current = input[ii];
356 for (; ii < std::min(Int(struc_size), size); ++ii, ++oi)
357 {
358 if (current > input[ii])
359 current = input[ii];
360 output[oi] = current;
361 }
362 }
363 {
364 // middle (main) area
365 for (anchor = struc_size; anchor <= size - struc_size; anchor += struc_size)
366 {
367 ii = anchor;
368 current = input[ii];
369 buffer[0] = current;
370 for (i = 1; i < struc_size; ++i, ++ii)
371 {
372 if (current > input[ii])
373 current = input[ii];
374 buffer[i] = current;
375 }
376 ii = anchor - 1;
377 oi = ii + struc_size_half;
378 current = input[ii];
379 for (i = 1; i < struc_size; ++i, --ii, --oi)
380 {
381 if (current > input[ii])
382 current = input[ii];
383 output[oi] = std::min(buffer[struc_size - i], current);
384 }
385 if (current > input[ii])
386 current = input[ii];
387 output[oi] = current;
388 }
389 }
390 {
391 // higher margin area
392 ii = size - 1;
393 oi = ii;
394 current = input[ii];
395 for (--ii; ii >= size - struc_size_half; --ii)
396 if (current > input[ii])
397 current = input[ii];
398 for (; ii >= std::max(size - Int(struc_size), 0); --ii, --oi)
399 {
400 if (current > input[ii])
401 current = input[ii];
402 output[oi] = current;
403 }
404 anchor = size - struc_size;
405 ii = anchor;
406 current = input[ii];
407 buffer[0] = current;
408 for (i = 1; i < struc_size; ++i, ++ii)
409 {
410 if (current > input[ii])
411 current = input[ii];
412 buffer[i] = current;
413 }
414 ii = anchor - 1;
415 oi = ii + struc_size_half;
416 current = input[ii];
417 for (i = 1; (ii >= 0) && (i < struc_size); ++i, --ii, --oi)
418 {
419 if (current > input[ii])
420 current = input[ii];
421 output[oi] = std::min(buffer[struc_size - i], current);
422 }
423 if (ii >= 0)
424 {
425 if (current > input[ii])
426 current = input[ii];
427 output[oi] = current;
428 }
429 }
430 return;
431 }
432
437 template<typename InputIterator, typename OutputIterator>
438 void applyDilation_(Int struc_size, InputIterator input, InputIterator input_end, OutputIterator output)
439 {
440 typedef typename InputIterator::value_type ValueType;
441 const Int size = input_end - input;
442 const Int struc_size_half = struc_size / 2; // yes, integer division
443
444 // Reuse scratch storage without sharing it across threads.
445 static thread_local std::vector<ValueType> buffer;
446 if (Int(buffer.size()) < struc_size)
447 buffer.resize(struc_size);
448
449 Int anchor; // anchoring position of the current block
450 Int i; // index relative to anchor, used for 'for' loops
451 Int ii = 0; // input index
452 Int oi = 0; // output index
453 ValueType current; // current value
454
455 // we just can't get the case distinctions right in these cases, resorting to simple method.
456 if (size <= struc_size || size <= 5)
457 {
458 applyDilationSimple_(struc_size, input, input_end, output);
459 return;
460 }
461 {
462 // lower margin area
463 current = input[0];
464 for (++ii; ii < struc_size_half; ++ii)
465 if (current < input[ii])
466 current = input[ii];
467 for (; ii < std::min(Int(struc_size), size); ++ii, ++oi)
468 {
469 if (current < input[ii])
470 current = input[ii];
471 output[oi] = current;
472 }
473 }
474 {
475 // middle (main) area
476 for (anchor = struc_size; anchor <= size - struc_size; anchor += struc_size)
477 {
478 ii = anchor;
479 current = input[ii];
480 buffer[0] = current;
481 for (i = 1; i < struc_size; ++i, ++ii)
482 {
483 if (current < input[ii])
484 current = input[ii];
485 buffer[i] = current;
486 }
487 ii = anchor - 1;
488 oi = ii + struc_size_half;
489 current = input[ii];
490 for (i = 1; i < struc_size; ++i, --ii, --oi)
491 {
492 if (current < input[ii])
493 current = input[ii];
494 output[oi] = std::max(buffer[struc_size - i], current);
495 }
496 if (current < input[ii])
497 current = input[ii];
498 output[oi] = current;
499 }
500 }
501 {
502 // higher margin area
503 ii = size - 1;
504 oi = ii;
505 current = input[ii];
506 for (--ii; ii >= size - struc_size_half; --ii)
507 if (current < input[ii])
508 current = input[ii];
509 for (; ii >= std::max(size - Int(struc_size), 0); --ii, --oi)
510 {
511 if (current < input[ii])
512 current = input[ii];
513 output[oi] = current;
514 }
515 anchor = size - struc_size;
516 ii = anchor;
517 current = input[ii];
518 buffer[0] = current;
519 for (i = 1; i < struc_size; ++i, ++ii)
520 {
521 if (current < input[ii])
522 current = input[ii];
523 buffer[i] = current;
524 }
525 ii = anchor - 1;
526 oi = ii + struc_size_half;
527 current = input[ii];
528 for (i = 1; (ii >= 0) && (i < struc_size); ++i, --ii, --oi)
529 {
530 if (current < input[ii])
531 current = input[ii];
532 output[oi] = std::max(buffer[struc_size - i], current);
533 }
534 if (ii >= 0)
535 {
536 if (current < input[ii])
537 current = input[ii];
538 output[oi] = current;
539 }
540 }
541 return;
542 }
543
545 template<typename InputIterator, typename OutputIterator>
546 void applyErosionSimple_(Int struc_size, InputIterator input_begin, InputIterator input_end, OutputIterator output_begin)
547 {
548 typedef typename InputIterator::value_type ValueType;
549 const int size = input_end - input_begin;
550 const Int struc_size_half = struc_size / 2; // yes integer division
551 for (Int index = 0; index < size; ++index)
552 {
553 Int start = std::max(0, index - struc_size_half);
554 Int stop = std::min(size - 1, index + struc_size_half);
555 ValueType value = input_begin[start];
556 for (Int i = start + 1; i <= stop; ++i)
557 if (value > input_begin[i])
558 value = input_begin[i];
559 output_begin[index] = value;
560 }
561 return;
562 }
563
565 template<typename InputIterator, typename OutputIterator>
566 void applyDilationSimple_(Int struc_size, InputIterator input_begin, InputIterator input_end, OutputIterator output_begin)
567 {
568 typedef typename InputIterator::value_type ValueType;
569 const int size = input_end - input_begin;
570 const Int struc_size_half = struc_size / 2; // yes integer division
571 for (Int index = 0; index < size; ++index)
572 {
573 Int start = std::max(0, index - struc_size_half);
574 Int stop = std::min(size - 1, index + struc_size_half);
575 ValueType value = input_begin[start];
576 for (Int i = start + 1; i <= stop; ++i)
577 if (value < input_begin[i])
578 value = input_begin[i];
579 output_begin[index] = value;
580 }
581 return;
582 }
583
584 private:
587 };
588
589} // namespace OpenMS
A base class for all classes handling default parameters.
Definition DefaultParamHandler.h:66
An iterator wrapper to access peak intensities instead of the peak itself.
Definition MorphologicalFilter.h:33
bool operator==(const IntensityIteratorWrapper &rhs) const
Definition MorphologicalFilter.h:74
IteratorT base
Definition MorphologicalFilter.h:85
std::forward_iterator_tag iterator_category
Definition MorphologicalFilter.h:35
bool operator!=(const IntensityIteratorWrapper &rhs) const
Definition MorphologicalFilter.h:79
IteratorT::difference_type difference_type
Definition MorphologicalFilter.h:39
IteratorT::value_type::IntensityType value_type
Definition MorphologicalFilter.h:36
IteratorT::value_type::IntensityType * pointer
Definition MorphologicalFilter.h:38
difference_type operator-(IntensityIteratorWrapper &rhs) const
Definition MorphologicalFilter.h:56
value_type operator*()
Definition MorphologicalFilter.h:45
IntensityIteratorWrapper & operator++()
Definition MorphologicalFilter.h:61
IntensityIteratorWrapper operator++(int)
Definition MorphologicalFilter.h:67
IteratorT::value_type::IntensityType & reference
Definition MorphologicalFilter.h:37
value_type operator[](const IndexT &index)
Definition MorphologicalFilter.h:51
IntensityIteratorWrapper(const IteratorT &rhs)
Definition MorphologicalFilter.h:41
In-Memory representation of a mass spectrometry run.
Definition MSExperiment.h:49
Size size() const noexcept
The number of spectra.
The representation of a 1D spectrum.
Definition MSSpectrum.h:44
This class implements baseline filtering operations using methods from mathematical morphology.
Definition MorphologicalFilter.h:136
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:438
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:171
void filter(MSSpectrum &spectrum)
Applies the morphological filtering operation to an MSSpectrum.
Definition MorphologicalFilter.h:264
void filterExperiment(PeakMap &exp)
Applies the morphological filtering operation to an MSExperiment.
Definition MorphologicalFilter.h:307
UInt struct_size_in_datapoints_
Member for struct size in data points.
Definition MorphologicalFilter.h:320
MorphologicalFilter()
Constructor.
Definition MorphologicalFilter.h:139
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:566
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:327
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:546
~MorphologicalFilter() override
Destructor.
Definition MorphologicalFilter.h:155
Base class for all classes that want to report their progress.
Definition ProgressLogger.h:27
void setType(SpectrumType type)
sets the spectrum type
int Int
Signed integer type.
Definition Types.h:72
unsigned int UInt
Unsigned integer type.
Definition Types.h:64
size_t Size
Size type e.g. used as variable which can hold result of size()
Definition Types.h:97
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:90
Main OpenMS namespace.
Definition openswathalgo/include/OpenMS/OPENSWATHALGO/DATAACCESS/ISpectrumAccess.h:19