Noninvasive Fetal ECG: The PhysioNet/Computing in Cardiology Challenge 2013 1.0.0
(8,618 bytes)
function Xr=FecgQRSmCanc(X,qrsM,fs,cName,graph,dbFlag,saveFig,qrsAfs)
% ---------------------------------------------------------------------------------------------
% Fecg: "Mother" ECG canceling using PQRST approximation obtained by
% Singular Value Decomposition (SVD).
% A trapezoidal window is used to select and weight the signal around each detected mother QRS.
%
% Author: Maurizio Varanini, Clinical Physiology Institute, CNR, Pisa, Italy
% For any comment or bug report, please send e-mail to: maurizio.varanini@ifc.cnr.it
% ---------------------------------------------------------------------------------------------
% This program is free software; you can redistribute it and/or modify it under the terms
% of the GNU General Public License as published by the Free Software Foundation; either
% version 2 of the License, or (at your option) any later version.
%
% This program is distributed "as is" and "as available" in the hope that it will be useful,
% but WITHOUT ANY WARRANTY of any kind; without even the implied warranty of MERCHANTABILITY
% or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
% ---------------------------------------------------------------------------------------------
if(nargin<4), cName=''; end
if(nargin<5), graph=1; end
if(nargin<6), dbFlag=1; end
if(nargin<7), saveFig=0; end
if(nargin<8), qrsAfs=[]; end
grafEigD=0;
fprintf('\n --------------------------------------------------------- \n');
[progpath, progname] = fileparts(which(mfilename));
fprintf('Program: %s, record name: %s\n', progname, cName);
if(~isempty(qrsAfs));
learning=1; % learning set, annotations are available
else
learning=0; % test set
end
%-------------------------------------------------------------
% recording duration
[ndt, ns]=size(X);
if(dbFlag && learning), PlotSgnMrkNc(X, qrsAfs*fs, fs, [cName, ' - fetal QRS ann.']); end
vtime= [1:ndt]/fs;
% -----
% qrsM(1,:) QRSref (reference point, max signed derivative )
% qrsM(2,:) QRSonset (derivative overcomes half threshold)
% qrsM(3,:) supThDer (derivative overcomes threshold)
% qrsM(4,:) maxAbsDer (max absolute derivative)
% qrsM(5,:) infThDer (derivative becomes lower than threshold)
% qrsM(5,:) QRSoffset (derivative decreses below half threshold)
qrsR=qrsM(1,:); % the reference point is the point of max signed derivative
if(dbFlag), PlotSgnMrkNc(X, qrsR, fs, [cName, ' - mother QRS det.']); end
nQRS=length(qrsR);
fprintf('Number of QRSs= %d\n', nQRS);
RRc= diff(qrsR);
RRs= RRc/fs;
RRmean=meansc(RRs,4,4);
%RRmean= mean(RRs);
RRstd= std(RRs);
fprintf('RR mean= %d, stdev=%d\n', RRmean, RRstd);
% the reference point is the point of max signed derivative
npp=fix(0.2*fs); % number of samples before the qrs reference
npd=fix(min(0.5, 0.8*(RRmean-0.1))*fs); % number of samples after the qrs reference
npt=1+npp+npd;
% extend the signals in order to manage the first and the last QRS
Xx=X;
npqp=fix(0.12*fs);
if(qrsR(1)-npqp < 1), qi=2;
npxp=0;
Xx(1:npqp,:)=repmat(Xx(npqp+1,:),npqp,1);
else qi=1;
npxp=max(0,npp+1-qrsR(1)); % number of samples added to left of each signal
Xx=[repmat(X(1,:),npxp,1);X];
end
npqd=fix(0.14*fs);
qf=nQRS;
if(qrsR(end)+npqd > size(X,1)), qf=qf-1;
Xx(end-npqd+1:end,:)=repmat(Xx(end-npqd-1,:),npqd,1);
end
if(qrsR(qf)+0.85*fs*median(RRs(end-4:end)) < size(X,1))
npep=fix(max(0.1*fs, 0.15*fs*mean(RRs(end-4:end))));
Xx(end-npep+1:end,:)=repmat(Xx(end-npep-1,:),npep,1);
end
npxd=max(0,qrsR(qf)+npd-ndt); % number of samples added to right of each signal
Xx=[Xx;repmat(X(end,:),npxd,1)];
ndtx=size(Xx,1);
nqe= qf-qi+1;
vtimex= [1-npxp:ndt+npxd]/fs;
% built vectors of indexes (start and end) of QRS window
iqw=qrsR(qi:qf);
iw=npxp+iqw-npp; % start of QRS window
fw=npxp+iqw+npd; % end of QRS window
A=zeros(npt,nqe);
wwg=weightFun2(npp,npd,fs);
Xc=zeros(ndtx,ns);
for is=1:ns
for iq=1:nqe
iwq=iw(iq); fwq=fw(iq);
A(:,iq)=Xx(iwq:fwq,is).*wwg;
end
% Singular value decomposition
% S = diagonal matrix containing the singular values
% U and V = unitary matrices so that X = U*S*V'.
% tic;
[U,S,V] = svd(A,0);
% fprintf('SVD (%d,%d) computation time = %3.2f\n', size(A), toc);
% fprintf('%f, ', diag(S)); fprintf('\n');
if(grafEigD), figure; plot(diag(S),'r-*');
title([num2str(is),'- Singular values']); drawnow;
end
mt=nqe;
nds=3; % select the number of singular values <===
sv=diag(S);
pownds(is)=sum(sv(1:nds).^2)/sum(sv(1:nqe).^2);
if(dbFlag), fprintf('is=%d, pownds=%f\n', is, pownds(is)); end
% tic
Sr=S; for k=1:mt-nds-1, Sr(mt-k,mt-k)=0; end
Ar=U*Sr*V';
% fprintf('Ar (%d,%d) rebuilding time = %3.2f\n', size(Ar), toc);
for iq=1:nqe
iwq=iw(iq); fwq=fw(iq);
% Xc(iwq:fwq,is)=Ar(:,iq);
Xc(iwq:fwq,is)=Ar(:,iq)./wwg; % .*windLR;
end
% smoothing connections between successive windows
for iq=1:nqe-1
fwq=fw(iq); iwqs=iw(iq+1);
if(iwqs>fwq)
dv=Xc(iwqs,is)-Xc(fwq,is);
pv=dv/(iwqs-fwq);
Xc(fwq+1:iwqs-1,is)=Xc(fwq,is)+pv*(1:iwqs-fwq-1)';
end
end
end
if(dbFlag)
for is=1:ns,
figure
set(gcf,'Color','white');
subplot(2,1,1), plot(vtimex,[Xx(:,is),Xc(:,is)]);
wgmi1= min(Xx(:,is)) -2;
wgma1= max(Xx(:,is)) +2;
ylim([wgmi1, wgma1]);
set(gca,'YTick',[-5 0 5])
DrawVertMarker(iw'/fs,'r',':','none');
DrawVertMarker(qrsM(1,:)'/fs,'g',':','none');
DrawVertMarker(fw'/fs,'m',':','none');
title([cName,' - ',num2str(is),': ecg & ecgM']);
subplot(2,1,2), plot(vtimex,[Xx(:,is)-Xc(:,is)]);
ylim([wgmi1, wgma1]);
set(gca,'YTick',[-5 0 5])
DrawVertMarker(iw'/fs,'r',':','none');
DrawVertMarker(qrsM(1,:)'/fs,'g',':','none');
DrawVertMarker(fw'/fs,'m',':','none');
title([cName,' - ',num2str(is),': ecg - ecgM']);
shg
end
end
Xc= Xc(npxp+1:end-npxd,:);
Xx= Xx(npxp+1:end-npxd,:);
Xe=Xx-Xc;
iw=iw-npxp; fw=fw-npxp;
if(graph)
for is=1:ns,
figure, set(gcf,'Color','white');
subplot(2,1,1), plot(vtime,[X(:,is),Xc(:,is)]);
wgmi1= min(X(:,is)) -2;
wgma1= max(X(:,is)) +2;
ylim([wgmi1, wgma1]);
set(gca,'YTick',[-5 0 5])
DrawVertMarker(iw'/fs,'r',':','none');
DrawVertMarker(qrsM(1,:)'/fs,'g',':','none');
DrawVertMarker(fw'/fs,'m',':','none');
title([cName,' - ',num2str(is),': ecg & ecgM']);
subplot(2,1,2), plot(vtime,Xe(:,is));
ylim([wgmi1, wgma1]);
set(gca,'YTick',[-5 0 5])
DrawVertMarker(iw'/fs,'r',':','none');
DrawVertMarker(qrsM(1,:)'/fs,'g',':','none');
DrawVertMarker(fw'/fs,'m',':','none');
title([cName,' - ',num2str(is),': ecg - ecgM']);
shg
end
end
%--------------------------------------------------------------------------------------------
if(graph || saveFig)
figure, set(gcf,'Color','white');
for is=1:ns,
subplot(ns,1,is), plot(vtime,Xe(:,is));
wgmi1= min(Xe(:,is)) -2;
wgma1= max(Xe(:,is)) +2;
ylim([wgmi1, wgma1]);
DrawVertMarker(iw'/fs,'r',':','none');
DrawVertMarker(qrsM(1,:)'/fs,'g',':','none');
DrawVertMarker(fw'/fs,'m',':','none');
set(gca,'YTick',[-5 0 5])
% if(is~=ns), set(gca,'XTickLabel',''); end
if(is==1), title([cName,': mixed foetal ECG']); end
end
shg
if(saveFig), figFmt='png';
figPath=fullfile('../Figure/',progname);
if(~exist(figPath,'dir')), mkdir(figPath); end
figName=fullfile(figPath,[cName,'_mCanc']);
print(gcf, ['-d',figFmt],figName);
% saveas(gcf, figName,'fig');
end
end
%-------------------------------------------------------------
Xr=Xe;
return
end % = function ===============================================================
% ------------------------------------------------------------------
function wwg=weightFun2(npp,npd,fs)
nppc1=fix(0.06*fs); npdc1=fix(0.06*fs);
nppc2=fix(0.08*fs); npdc2=min(fix(0.2*fs),npd-npdc1);
ii1=1; ie1=npp-nppc1-nppc2;
ii2=ie1+1; ie2=ie1+nppc2;
ii3=ie2+1; ie3=ie2+nppc1+npdc1+1;
ii4=ie3+1; ie4=ie3+npdc2;
ii5=ie4+1; ie5=npp+npd+1;
wwg(ii1:ie1)=0.20;
wwg(ii2:ie2)=0.20+0.8*(1:nppc2)/nppc2;
wwg(ii3:ie3)=1;
wwg(ii4:ie4)=1-0.8*(1:npdc2)/npdc2;
wwg(ii5:ie5)=0.20;
wwg=wwg';
% figure, plot(wwg);
return
end % = function ===============================================================
%