IRootLab
An Open-Source MATLAB toolbox for vibrational biospectroscopy
detect_peaks.m
Go to the documentation of this file.
1 %>@ingroup maths
2 %>@file
3 %> @brief Peak detector.
4 %>
5 %> This algorithm is a simplified version of [1]. What is referred to as "noise" in the publication can be considered as the @c minhei parameter.
6 %>
7 %> <h3>References</h3>
8 %> [1] K. R. Coombes et al., Quality control and peak finding for proteomics data collected from nipple aspirate fluid by surface-enhanced
9 %> laser desorption and ionization.,” Clinical chemistry, vol. 49, no. 10, pp. 1615-23, Oct. 2003.
10 %>
11 %
12 %> @param y Vector of values.
13 %> @param minalt=0 Minimum altitude (distance from zero to montain top).
14 %> @param minhei=0 Minimum height (distance from highest foot (either left or right)) to mountain top.
15 %> @param mindist=1 Minimum horizontal distance between two peaks. Peak pruning based on this is done in an iterative
16 %> way to keep the highest peak of a set of peaks too close to each other.
17 %> @return Indexes of detected peaks
18 function idxs = detect_peaks(y, minalt, minhei, mindist)
19 if ~exist('mindist', 'var')
20  mindist = 1;
21 end;
22 if ~exist('minalt', 'var')
23  minalt = 0;
24 end;
25 if ~exist('minhei', 'var')
26  minhei = 0;
27 end;
28 
29 no_peaks = 0;
30 x_peaks = [];
31 y_peaks = [];
32 
33 
34 y = abs(y);
35 if sum(y == Inf) == length(y)
36  y(y == Inf) = 0;
37 else
38  y(y == Inf) = max(y(y ~= Inf));
39 end;
40 y = [0 y 0 Inf];
41 
42 
43 step = 1;
44 idxs = [0];
45 nf = length(y);
46 
47 idx_foot1 = 1;
48 flag_maybe = 0;
49 flag_new = 0;
50 cnt0 = 0;
51 for i = 2:nf
52  trend_ = y(i)-y(i-1);
53  if trend_ == 0 && i > 2
54  trend = trend_last;
55  cnt0 = cnt0+1;
56  else
57  trend = trend_;
58  end;
59 
60  if i > 2
61  if trend == 0
62  else
63  if trend > 0 && trend_last < 0
64  % new uptrend
65  if flag_maybe
66  if y(idx_maybe)-y(i-1) > minhei
67  % last maximum will be a peak (unless not enough altitude, see below) because both feet are low
68  % enough (left foot already checked below).
69  flag_new = 1;
70  % right foot becomes left foot for next peak. Left foot is only re-assigned when a peak is found.
71  % It is reassigned regardless of the peak being at enough altitude.
72  idx_foot1 = i-1;
73  else
74  flag_maybe = 0; % last maximum found is not going to be a peak because right foot is too high
75  end;
76  end;
77  elseif trend < 0 && trend_last > 0
78  % new downtrend
79  if y(i-1)-y(idx_foot1) > minhei
80  idx_maybe = i-1-floor(cnt0/2);
81  flag_maybe = 1; % the previous point may be a peak
82  end;
83  cnt0 = 0;
84  end;
85  end;
86  end;
87 
88  if flag_new
89  if y(idx_maybe) >= minalt
90  no_peaks = no_peaks+1;
91  if no_peaks > step
92  % efficient way to make idxs grow
93  step = step*2;
94  idxs(step) = 0;
95  end;
96  idxs(no_peaks) = idx_maybe;
97  end;
98  flag_new = 0;
99  end;
100 
101  if trend_ ~= 0
102  cnt0 = 0;
103  end;
104 
105  trend_last = trend;
106 end;
107 
108 idxs = idxs(1:no_peaks);
109 
110 
111 
112 if mindist > 1
113  %eliminates redundant peaks (too close to each other)
114 
115  while 1
116  % idxs2(i) contains the distance between the i-th index and its predecessor
117  % idid contains the indexes of the indexes of the peaks that need to be checked
118  idxs2 = [Inf, diff(idxs)];
119  idid = find(idxs2 < mindist);
120  if numel(idid) == 0
121  break;
122  end;
123  idid = union(idid, idid-1);
124 
125 
126  % Peaks will be checked per block of adjacend peaks too close to each other
127  flag_block = 0;
128  for i = numel(idid):-1:1
129  if idxs2(idid(i)) >= mindist
130  if flag_block
131  i1 = i;
132  len = i2-i1+1;
133 
134  [dummy, idxmax] = max(y(idxs(idid(i1:i2))));
135  % once maximum is found, it will engulf its left and right peaks
136  if idxmax < len
137  idxs(idid(i1-1+idxmax+1)) = [];
138  end;
139  if idxmax > 1
140  idxs(idid(i1-1+idxmax-1)) = [];
141  end;
142 
143  flag_block = 0;
144  end;
145  else
146  if ~flag_block
147  flag_block = 1;
148  i2 = i;
149  end;
150  end;
151  end;
152  end;
153 end;
154 idxs = idxs-1; % A zero element has been added at the beginning, hence the "-1"
Base Block class.
Definition: block.m:2
function detect_peaks(in y, in minalt, in minhei, in mindist)
Analysis Session (AS) base class.
Definition: as.m:6