|
a |
|
b/featurebased-approach/subfunctions/get_poincare.m |
|
|
1 |
function feat=get_poincare(QRS,fs) |
|
|
2 |
% Obtain poincare plots for HRV analysis |
|
|
3 |
% |
|
|
4 |
% |
|
|
5 |
% |
|
|
6 |
% -- |
|
|
7 |
% ECG classification from single-lead segments using Deep Convolutional Neural |
|
|
8 |
% Networks and Feature-Based Approaches - December 2017 |
|
|
9 |
% |
|
|
10 |
% Released under the GNU General Public License |
|
|
11 |
% |
|
|
12 |
% Copyright (C) 2017 Fernando Andreotti, Oliver Carr |
|
|
13 |
% University of Oxford, Insitute of Biomedical Engineering, CIBIM Lab - Oxford 2017 |
|
|
14 |
% fernando.andreotti@eng.ox.ac.uk |
|
|
15 |
% |
|
|
16 |
% |
|
|
17 |
% For more information visit: https://github.com/fernandoandreotti/cinc-challenge2017 |
|
|
18 |
% |
|
|
19 |
% Referencing this work |
|
|
20 |
% |
|
|
21 |
% Andreotti, F., Carr, O., Pimentel, M.A.F., Mahdi, A., & De Vos, M. (2017). |
|
|
22 |
% Comparing Feature Based Classifiers and Convolutional Neural Networks to Detect |
|
|
23 |
% Arrhythmia from Short Segments of ECG. In Computing in Cardiology. Rennes (France). |
|
|
24 |
% |
|
|
25 |
% Last updated : December 2017 |
|
|
26 |
% |
|
|
27 |
% This program is free software: you can redistribute it and/or modify |
|
|
28 |
% it under the terms of the GNU General Public License as published by |
|
|
29 |
% the Free Software Foundation, either version 3 of the License, or |
|
|
30 |
% (at your option) any later version. |
|
|
31 |
% |
|
|
32 |
% This program is distributed in the hope that it will be useful, |
|
|
33 |
% but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
34 |
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
35 |
% GNU General Public License for more details. |
|
|
36 |
% |
|
|
37 |
% You should have received a copy of the GNU General Public License |
|
|
38 |
% along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
39 |
|
|
|
40 |
i=1; |
|
|
41 |
% Set the bins for the histogram |
|
|
42 |
edges{1}=0:100:2000; % <----------- SET BINS |
|
|
43 |
edges{2}=-1000:100:1000; % <----------- SET BINS |
|
|
44 |
|
|
|
45 |
% Get the RR interval series from the QRS detection |
|
|
46 |
RR=diff(QRS./fs)*1000; |
|
|
47 |
|
|
|
48 |
% If there are less than 5 r peaks, enter NaN instead of finding |
|
|
49 |
% histogram |
|
|
50 |
|
|
|
51 |
% Find the difference in RR interval series |
|
|
52 |
dRR=diff(RR); |
|
|
53 |
|
|
|
54 |
RR2=RR(2:end); |
|
|
55 |
X=[RR2,dRR]; |
|
|
56 |
|
|
|
57 |
x_mid=median(RR2); |
|
|
58 |
y_mid=median(dRR); |
|
|
59 |
|
|
|
60 |
r=[50,100,200,300]; % <--------- SET NUMBER AND VALUES OF RADIUS (IF >4 CHANGE ALL FEATURE INDICES) |
|
|
61 |
for j=1:4 |
|
|
62 |
ang=0:0.01:2*pi; |
|
|
63 |
xc1=r(j)*cos(ang)+x_mid; |
|
|
64 |
yc1=r(j)*sin(ang)+y_mid; |
|
|
65 |
|
|
|
66 |
in = inpolygon(RR2,dRR,xc1,yc1); |
|
|
67 |
|
|
|
68 |
% Features 1-4: Percentage of points inside circle (centred on median) |
|
|
69 |
feat(i,j)=sum(in)/length(in); |
|
|
70 |
end |
|
|
71 |
% Features 5-8: median and iqr of x and y coordinates |
|
|
72 |
feat(i,5)=x_mid; |
|
|
73 |
feat(i,6)=y_mid; |
|
|
74 |
feat(i,7)=iqr(RR2); |
|
|
75 |
feat(i,8)=iqr(dRR); |
|
|
76 |
|
|
|
77 |
[N,~]=hist3(X,'Edges',edges); |
|
|
78 |
[N2,~]=hist3([x_mid,y_mid],'Edges',edges); |
|
|
79 |
[row,col]=find(N2); |
|
|
80 |
|
|
|
81 |
% Features 9-14: Bins with at least 1 and greater than 1 values (and |
|
|
82 |
% normalised) |
|
|
83 |
feat(i,9)=sum(sum(N>0)); |
|
|
84 |
feat(i,10)=sum(sum(N>1)); |
|
|
85 |
feat(i,11)=sum(sum(N>0))/length(RR); |
|
|
86 |
feat(i,12)=sum(sum(N>1))/length(RR); |
|
|
87 |
feat(i,13)=sum(sum(N>0))/sum(RR); |
|
|
88 |
feat(i,14)=sum(sum(N>1))/sum(RR); |
|
|
89 |
|
|
|
90 |
[a1,a2]=meshgrid(1:length(edges{1}),1:length(edges{2})); |
|
|
91 |
C = sqrt((a1-row).^2+(a2-col).^2)<=3; % <-----------------SET THIS RADIUS |
|
|
92 |
|
|
|
93 |
M=N; |
|
|
94 |
M(C)=0; |
|
|
95 |
|
|
|
96 |
% Features 15-20: Bins with at least 1 and greater than 1 values (and |
|
|
97 |
% normalised) excluding centre circle |
|
|
98 |
feat(i,15)=sum(sum(M>0)); |
|
|
99 |
feat(i,16)=sum(sum(M>1)); |
|
|
100 |
feat(i,17)=sum(sum(M>0))/length(RR); |
|
|
101 |
feat(i,18)=sum(sum(M>1))/length(RR); |
|
|
102 |
feat(i,19)=sum(sum(M>0))/sum(RR); |
|
|
103 |
feat(i,20)=sum(sum(M>1))/sum(RR); |
|
|
104 |
|
|
|
105 |
k = boundary(RR2,dRR); |
|
|
106 |
|
|
|
107 |
A = polyarea(RR2(k),dRR(k)); |
|
|
108 |
% Features 21-23: Minimum area enclosing all points (normalised) |
|
|
109 |
feat(i,21)=A; |
|
|
110 |
feat(i,22)=A/length(RR); |
|
|
111 |
feat(i,23)=A/sum(RR); |
|
|
112 |
|
|
|
113 |
k2 = boundary(RR2,dRR,0); |
|
|
114 |
A2 = polyarea(RR2(k2),dRR(k2)); |
|
|
115 |
|
|
|
116 |
% Features 24-26: Minimum convex area enclosing all points (normalised) |
|
|
117 |
feat(i,24)=A2; |
|
|
118 |
feat(i,25)=A2/length(RR); |
|
|
119 |
feat(i,26)=A2/sum(RR); |
|
|
120 |
|
|
|
121 |
% Features 27-29: Perimeter of minimum area |
|
|
122 |
feat(i,27)=sqrt(sum(diff(RR2(k)).^2+diff(dRR(k)).^2)); |
|
|
123 |
feat(i,28)=feat(i,27)/length(RR); |
|
|
124 |
feat(i,29)=feat(i,27)/sum(RR); |
|
|
125 |
|
|
|
126 |
% Features 30-32: Perimeter of minimum area |
|
|
127 |
feat(i,30)=sqrt(sum(diff(RR2(k)).^2+diff(dRR(k)).^2)); |
|
|
128 |
feat(i,31)=feat(i,27)/length(RR); |
|
|
129 |
feat(i,32)=feat(i,27)/sum(RR); |
|
|
130 |
|
|
|
131 |
% Features 33-35: Average distance to centre |
|
|
132 |
feat(i,33)=mean(sqrt((RR2-x_mid).^2+(dRR-y_mid).^2)); |
|
|
133 |
feat(i,34)=feat(i,33)/length(RR); |
|
|
134 |
feat(i,35)=feat(i,33)/sum(RR); |
|
|
135 |
|
|
|
136 |
B = bsxfun(@minus,X(:,1)',X(:,1)) + ... |
|
|
137 |
bsxfun(@minus,X(:,2)',X(:,2)); |
|
|
138 |
B=B.^2; |
|
|
139 |
B(1:length(B)+1:numel(B)) = Inf; |
|
|
140 |
minB=min(B); |
|
|
141 |
% Features 36-38: Average distance to nearest point |
|
|
142 |
feat(i,36)=mean(minB); |
|
|
143 |
feat(i,37)=feat(i,36)/length(RR); |
|
|
144 |
feat(i,38)=feat(i,36)/sum(RR); |
|
|
145 |
|
|
|
146 |
% Features 39-41: Distance to next point |
|
|
147 |
feat(i,39)=sqrt(sum(diff(RR2).^2+diff(dRR).^2)); |
|
|
148 |
feat(i,40)=feat(i,39)/length(RR); |
|
|
149 |
feat(i,41)=feat(i,39)/sum(RR); |
|
|
150 |
|
|
|
151 |
[idx,Cent,sumd] = kmeans(X,4); |
|
|
152 |
Dist=pdist(Cent); |
|
|
153 |
|
|
|
154 |
% Features 42-46: Cluster distances |
|
|
155 |
feat(i,42)=max(Dist); |
|
|
156 |
feat(i,43)=min(Dist); |
|
|
157 |
feat(i,44)=mean(Dist); |
|
|
158 |
feat(i,45)=std(Dist); |
|
|
159 |
feat(i,46)=median(Dist); |
|
|
160 |
|
|
|
161 |
vertA2=[RR2(k2),dRR(k2)]; |
|
|
162 |
DistA2=pdist(vertA2); |
|
|
163 |
% Features 47-49: Area major minor axes |
|
|
164 |
feat(i,47)=max(DistA2); |
|
|
165 |
|
|
|
166 |
% Features 50-55: Smaller percentage in circle (for other rhythms) |
|
|
167 |
r2=[3,5,10,20,30,40]; % <--------- SET NUMBER AND VALUES OF RADIUS (IF >4 CHANGE ALL FEATURE INDICES) |
|
|
168 |
for j=1:6 |
|
|
169 |
ang=0:0.01:2*pi; |
|
|
170 |
xc1=r2(j)*cos(ang)+x_mid; |
|
|
171 |
yc1=r2(j)*sin(ang)+y_mid; |
|
|
172 |
|
|
|
173 |
in = inpolygon(RR2,dRR,xc1,yc1); |
|
|
174 |
feat(i,47+j)=sum(in)/length(in); |
|
|
175 |
end |
|
|
176 |
|
|
|
177 |
|
|
|
178 |
P=[RR2(k2),dRR(k2)]; |
|
|
179 |
[A , c] = MinVolEllipse(P', 0.1); |
|
|
180 |
[U Q V]=svd(A); |
|
|
181 |
r1=1/sqrt(Q(1,1)); |
|
|
182 |
r2=1/sqrt(Q(2,2)); |
|
|
183 |
theta=acos(V(1,1)); |
|
|
184 |
% Features 56-61: Ellipse features |
|
|
185 |
feat(i,54)=c(1); |
|
|
186 |
feat(i,55)=c(2); |
|
|
187 |
feat(i,56)=r1; |
|
|
188 |
feat(i,57)=r2; |
|
|
189 |
feat(i,58)=r1/r2; |
|
|
190 |
feat(i,59)=theta; |
|
|
191 |
|
|
|
192 |
% features 62-68: Number of clusters |
|
|
193 |
thresh_dist=[100,200,300,400,500,750,1000]; |
|
|
194 |
for th=1:length(thresh_dist) |
|
|
195 |
if max(Dist)<thresh_dist(th) |
|
|
196 |
clust=1; |
|
|
197 |
elseif min(Dist)>thresh_dist(th) |
|
|
198 |
clust=4; |
|
|
199 |
else |
|
|
200 |
indD=Dist<thresh_dist(th); |
|
|
201 |
group1=1; |
|
|
202 |
group2=2; |
|
|
203 |
group3=3; |
|
|
204 |
group4=4; |
|
|
205 |
if indD(1)==1 |
|
|
206 |
group1=[group1, 2]; |
|
|
207 |
group2=[]; |
|
|
208 |
end |
|
|
209 |
if indD(2)==1 |
|
|
210 |
group1=[group1, 3]; |
|
|
211 |
group3=[]; |
|
|
212 |
end |
|
|
213 |
if indD(3)==1 |
|
|
214 |
group1=[group1, 4]; |
|
|
215 |
group4=[]; |
|
|
216 |
end |
|
|
217 |
if indD(4)==1 && indD(1)==1 |
|
|
218 |
group1=[group1, 3]; |
|
|
219 |
group3=[]; |
|
|
220 |
end |
|
|
221 |
if indD(4)==1 && indD(1)==0 |
|
|
222 |
group2=[group2, 3]; |
|
|
223 |
group3=[]; |
|
|
224 |
end |
|
|
225 |
if indD(5)==1 && indD(1)==1 |
|
|
226 |
group1=[group1, 4]; |
|
|
227 |
group4=[]; |
|
|
228 |
end |
|
|
229 |
if indD(5)==1 && indD(1)==0 |
|
|
230 |
group2=[group2, 4]; |
|
|
231 |
group4=[]; |
|
|
232 |
end |
|
|
233 |
if indD(6)==1 && indD(2)==1 |
|
|
234 |
group1=[group1, 4]; |
|
|
235 |
group4=[]; |
|
|
236 |
end |
|
|
237 |
if indD(6)==1 && indD(1)==0 && indD(4)==1 |
|
|
238 |
group2=[group2, 4]; |
|
|
239 |
group4=[]; |
|
|
240 |
end |
|
|
241 |
if indD(6)==1 && indD(1)==0 && indD(4)==0 |
|
|
242 |
group3=[group3, 4]; |
|
|
243 |
group4=[]; |
|
|
244 |
end |
|
|
245 |
clust=1; |
|
|
246 |
if ~isempty(group2) |
|
|
247 |
clust=clust+1; |
|
|
248 |
end |
|
|
249 |
if ~isempty(group3) |
|
|
250 |
clust=clust+1; |
|
|
251 |
end |
|
|
252 |
if ~isempty(group4) |
|
|
253 |
clust=clust+1; |
|
|
254 |
end |
|
|
255 |
end |
|
|
256 |
feat(i,59+th)=clust; |
|
|
257 |
end |
|
|
258 |
|
|
|
259 |
|
|
|
260 |
|