Vespucci  1.0.0
kernelpeakfinding.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright (C) 2014-2016 Wright State University - All Rights Reserved
3  Daniel P. Foose - Maintainer/Lead Developer
4 
5  This file is part of Vespucci.
6 
7  Vespucci is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  Vespucci is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with Vespucci. If not, see <http://www.gnu.org/licenses/>.
19 *******************************************************************************/
21 using namespace std;
22 using namespace arma;
23 uvec Vespucci::Math::PeakFinding::FindIntercepts(const vec &signal)
24 {
25  vec intercepts = zeros(signal.n_rows);
26  for (uword i = 1; i < signal.n_rows; ++i){
27  if ((signal(i-1) > 0) && (signal(i) < 0))
28  intercepts(i) = 1;
29  }
30  return find(intercepts);
31 }
32 
33 vec Vespucci::Math::PeakFinding::DerivGaussKernel(double abscissa_step, uword window_size, uword width, double height)
34 {
35  double range = abscissa_step * double(window_size) / 2.0;
36  vec x = linspace(-range, range, window_size);
37  return -height*(x / (std::pow(width, 3) * std::sqrt(2 * datum::pi)))
38  % arma::exp(-(arma::pow(x,2)/2 * std::pow(width, 2)));
39 }
40 
41 uvec Vespucci::Math::PeakFinding::FindPeakCenters(const vec &signal, const vec &abscissa, list<umat> &ridges, uword first_scale, uword last_scale, uword search_window, uword min_length, uword max_gap, const string &max_method)
42 {
43  using namespace Vespucci::Math::PeakFinding;
44  if (!(last_scale - first_scale))
45  throw invalid_argument("Invalid last_scale must be larger than first_scale!");
46  if ((last_scale - first_scale) < min_length)
47  throw invalid_argument("Invalid scale or minimum length");
48  if (max_method != "signal" && max_method != "mexh")
49  throw invalid_argument("Invalid max_method");
50  field<uvec> centers(last_scale - first_scale);
51  ridges.clear();
52  double abscissa_step = (abscissa.max() - abscissa.min()) / double(abscissa.n_elem);
53  uword window_size = 3*last_scale;
54  //Finding step
55  for (uword i = first_scale; i < last_scale; ++i){
56  vec kernel = DerivGaussKernel(abscissa_step, window_size, i, signal.max());
57  vec derivative = conv(signal, kernel, "same");
58  //fix end of vector
59  uword end = derivative.n_rows - window_size/2;
60  derivative.rows(end, derivative.n_rows - 1).fill(derivative(end));
61  centers(i-first_scale) = FindIntercepts(derivative);
62  }
63 
64  //Linking step
65  uword i = 0; //row to start search
66  uword j = 0; //row of centers(i)
67  uword k = 0; //row of centers searching from centers(i)
68  uword l = 0; //row of centers(k)
69  for (i = 0; i < centers.n_rows - min_length; ++i){
70  for (j = 0; j < centers(i).n_rows; ++j){
71  uword initial_center = centers(i)(j);
72  umat current_ridge = {{i, initial_center}};
73  uword min = initial_center - (search_window / 2);
74  uword max = initial_center + (search_window / 2);
75  uword current_gap = 0;
76  for (k = i+1; (k < centers.n_elem && current_gap < max_gap); ++k){
77  uvec current_centers = centers(k);
78  for (l = 0; (l < current_centers.n_rows && current_centers(l) <= max); ++l){
79  Row<uword> current_pair;
80  if ((current_centers.n_rows > l) && (current_centers(l) >= min)){
81  if (!current_pair.n_elem){
82  current_pair = {k, current_centers(l)};
83  }
84  else{ //select the closest in case two are found that satisfy criteria
85  Row<uword> candidate_pair = {k, current_centers(l)};
86  uword dist1 = std::abs(int(current_centers(l)) - int(initial_center));
87  uword dist2 = std::abs(int(current_pair(1)) - int(initial_center));
88  current_pair = (dist1 < dist2 ? candidate_pair : current_pair);
89  }
90  //remove a point if it has been resolved
91 
92  uvec query = find(current_centers == current_pair(1));
93  if (query.n_rows){
94  current_centers.shed_row(query(0));
95  centers(k) = current_centers;
96  }
97  current_ridge.insert_rows(current_ridge.n_rows, current_pair);
98  }//if statement for valid center found
99  else{
100  current_gap++;
101  }
102 
103  }//for values in centers(j)
104 
105  }//for vectors in centers (compared to centers(i))
106  if (current_ridge.n_rows >= min_length) ridges.push_back(current_ridge);
107  }//for values in centers(i);
108  }//for vectors in centers
109  //collect peak centers
110  uvec peak_centers;
111  for (umat &ridge : ridges){
112  vec values;
113  uvec max_indices;
114  if (max_method == "signal"){
115  for (uword i = 0; i < ridge.n_rows; ++i){
116  if (!values.n_rows) values = {signal(ridge(i, 1))};
117  else values = join_vert(values, vec({signal(ridge(i, 1))}) );
118  }
119 
120  }
121  else if (max_method == "mexh"){
122  mat smoothed_signal(signal.n_rows, (last_scale - first_scale) + 1);
123  for (uword i = first_scale; i <= last_scale; ++i){
124  vec kernel = MexicanHatKernel(abscissa_step, window_size, i, signal.max());
125  smoothed_signal.col(i-1) = conv(signal, kernel, "same");
126  }
127  for (uword i = 0; i < ridge.n_rows; ++i){
128  if (!values.n_rows) values = vec({smoothed_signal(ridge(i, 1), ridge(i, 0))});
129  else values = join_vert(values, vec({smoothed_signal(ridge(i, 1), ridge(i, 0))}) );
130  }
131  }
132  else{
133  cerr << "should have thrown invalid_argument earlier!\n";
134 
135  return uvec();
136  }
137 
138  max_indices = find(values == values.max());
139  if (!peak_centers.n_rows) peak_centers = {ridge(max_indices(0), 1)};
140  else peak_centers = join_vert(peak_centers, uvec({ridge(max_indices(0), 1)}));
141  }
142  return peak_centers;
143 }
144 
145 vec Vespucci::Math::PeakFinding::MexicanHatKernel(double abscissa_step, uword window_size, uword width, double height)
146 {
147  uword k = window_size / 2;
148  vec x = linspace(-k*abscissa_step, k*abscissa_step, window_size);
149  return height * (2 / (sqrt(3*width) * pow(datum::pi, 0.25)))
150  * (ones(x.n_rows) - (pow(x, 2)/pow(width, 2)))
151  % exp(-(pow(x, 2) / 2*pow(width, 2)));
152 }
VESPUCCI_EXPORT arma::vec MexicanHatKernel(double abscissa_step, arma::uword window_size, arma::uword width, double height)
VESPUCCI_EXPORT arma::vec DerivGaussKernel(double abscissa_step, arma::uword window_size, arma::uword width, double height)
VESPUCCI_EXPORT arma::uword max(arma::uword a, arma::uword b)
Vespucci::Math::max.
Definition: accessory.cpp:237
VESPUCCI_EXPORT arma::uword min(arma::uword a, arma::uword b)
Vespucci::Math::min.
Definition: accessory.cpp:249
A namespace for math functions relating to peak detection and counting.
VESPUCCI_EXPORT arma::uvec FindPeakCenters(const arma::vec &signal, const arma::vec &abscissa, std::list< arma::umat > &ridges, arma::uword first_scale, arma::uword last_scale, arma::uword search_window, arma::uword min_length, arma::uword max_gap, const std::string &max_method)
VESPUCCI_EXPORT arma::uvec FindIntercepts(const arma::vec &signal)