diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b25c15b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/2015-sarasola-sanz-EMG-based multi-joint kinematics decoding for robot-aided rehabilitation therapies.pdf b/2015-sarasola-sanz-EMG-based multi-joint kinematics decoding for robot-aided rehabilitation therapies.pdf new file mode 100644 index 0000000..077f0c6 --- /dev/null +++ b/2015-sarasola-sanz-EMG-based multi-joint kinematics decoding for robot-aided rehabilitation therapies.pdf Binary files differ diff --git a/2015-shiman-Towards Decoding of Functional Movements from the Same Limb using EEG.pdf b/2015-shiman-Towards Decoding of Functional Movements from the Same Limb using EEG.pdf new file mode 100644 index 0000000..ac26fcf --- /dev/null +++ b/2015-shiman-Towards Decoding of Functional Movements from the Same Limb using EEG.pdf Binary files differ diff --git a/2016-spueler-Comparing Methods for Decoding Movement Trajectory from ECoG in Chronic Stroke Patients.pdf b/2016-spueler-Comparing Methods for Decoding Movement Trajectory from ECoG in Chronic Stroke Patients.pdf new file mode 100644 index 0000000..33278f5 --- /dev/null +++ b/2016-spueler-Comparing Methods for Decoding Movement Trajectory from ECoG in Chronic Stroke Patients.pdf Binary files differ diff --git a/analyze.m b/analyze.m new file mode 100644 index 0000000..dbcc954 --- /dev/null +++ b/analyze.m @@ -0,0 +1,24 @@ +[signal, states, params] = load_bcidat('/home/jph/Uni/Masterarbeit/Block1_ReachingMovements/AO/AO_B1001/AO_B1S001R01.dat'); + +% [ splitSignals, code ] = cutAll( states, signal ); +[ splitSignals, code ] = cutWindows( states, signal, params, 1); + +NFFT=2048; % should be 2^n + +[ filteredSig, pwMeanMove, pwMeanRest,hz ] = myFilter( splitSignals,code,NFFT ); + +%plotChannelProzDiff(pwMeanMove,pwMeanRest,hz,params); +%plotChannels(pwMeanMove,hz,params); +plotImgScProzDiff( pwMeanMove, pwMeanRest) + +% figure +% subplot(2,1,1) +% plot(log(pwMeanMove)) +% xlim([0 150]) +% %ylim([-2 6]) +% subplot(2,1,2) +% plot(log(pwMeanRest)) +% xlim([0 150]) +% %ylim([-2 6]) + + \ No newline at end of file diff --git a/balance2Classes.m b/balance2Classes.m new file mode 100644 index 0000000..f4bae25 --- /dev/null +++ b/balance2Classes.m @@ -0,0 +1,14 @@ +function [trainClasses,trainData]=balance2Classes(trainClasses,trainData) + %balances two classes (0,1) + eps=0.00001; + while mean(trainClasses)> 0.5+eps || mean(trainClasses) < 0.5-eps + r=floor(0.5*size(trainClasses)*rand(1)+1); + if mean(trainClasses) < 0.5 + more=find(trainClasses==0); + else + more=find(trainClasses==1); + end + trainClasses=trainClasses([1:more(r)-1,more(r)+1:end]); + trainData=trainData([1:more(r)-1,more(r)+1:end],:); + end +end \ No newline at end of file diff --git a/balanceClasses.m b/balanceClasses.m new file mode 100644 index 0000000..e389592 --- /dev/null +++ b/balanceClasses.m @@ -0,0 +1,10 @@ +function [trainClasses,trainData]=balanceClasses(trainClasses,trainData,eps) + while sum(abs(diff(histcounts(trainClasses,size(unique(trainClasses),2)))))> 0+eps + mostFrequent=find(trainClasses==mode(trainClasses)); + + r=floor(size(mostFrequent,2)*rand(1)+1); + delIndex=mostFrequent(r); + trainClasses=trainClasses([1:delIndex-1,delIndex+1:end]); + trainData=trainData([1:delIndex-1,delIndex+1:end]); + end +end \ No newline at end of file diff --git a/chooseForCode.m b/chooseForCode.m new file mode 100644 index 0000000..aa15422 --- /dev/null +++ b/chooseForCode.m @@ -0,0 +1,12 @@ +function [ splitSamplesCode ] = chooseForCode(code,codes, splitSamples) +%chooseForCode chooses the samples for given code +% input the code to choose, the list of codes, the number of Samples and +% the list of Samples to get the list with the given code + splitSamplesCode=[]; + for i=1:size(codes) + if code == codes(i) + splitSamplesCode=[splitSamplesCode,splitSamples(:,:,i)]; + end + end +end + diff --git a/cutAll.m b/cutAll.m new file mode 100644 index 0000000..123d7b6 --- /dev/null +++ b/cutAll.m @@ -0,0 +1,16 @@ +function [ splitSignals, code ] = cutAll( states, signal ) +% cut all in same length by dropping the tail + +changeIndices=[find(diff(double(states.StimulusCode)));size(states.StimulusCode,1)]; +codeCount=[diff(changeIndices); size(states.StimulusCode,1)-sum(diff(changeIndices))]; + +splitSignals=[]; +code =zeros(size(codeCount,1)); +c=codeCount(1); + +for i=2:size(codeCount,1) + splitSignals=cat(3,splitSignals,signal(c:c+4998,:)); + code(i)=states.StimulusCode(c); + c=c+codeCount(i); +end +end \ No newline at end of file diff --git a/cutTrials.m b/cutTrials.m new file mode 100644 index 0000000..18b87bd --- /dev/null +++ b/cutTrials.m @@ -0,0 +1,22 @@ +function [ splitSignals, code ] = cutTrials( states, signal ) +%cut Trials only, ignore resting phases + +noSamplesPerTrial=15000; +% count how many recorded values belong to one trial +changeIndices=[find(diff(double(states.StimulusCode)));size(states.StimulusCode,1)]; +codeCount=[diff(changeIndices); size(states.StimulusCode,1)-sum(diff(changeIndices))]; + +splitSignals=[]; +code=[]; + +c=1; + +for i=1:size(codeCount,1) + if(codeCount(i)==noSamplesPerTrial) + splitSignals=cat(3,splitSignals,signal(c:c+codeCount(i)-1,:)); + code=cat(1,code,states.StimulusCode(c)); + end + c=c+codeCount(i); +end +end + diff --git a/cutWindows.m b/cutWindows.m new file mode 100644 index 0000000..44b50d6 --- /dev/null +++ b/cutWindows.m @@ -0,0 +1,21 @@ +function [ splitSignals, code ] = cutWindows( states, signal, params, t ) + changeIndices=cat(1,0,find(diff(double(states.StimulusCode)))); + codeCount=diff(changeIndices); + interval=t*params.SamplingRate.NumericValue; + + splitSignals=[]; + code=[]; + + c=codeCount(1)+1; + %cut windows of 1s + for i=2:(size(codeCount,1)) %exclude beginning and end (stimulus 0) + tempc=0; + while codeCount(i)-tempc >= interval + splitSignals=cat(3,splitSignals,signal(c+tempc:c+tempc+2499,:)); + code=cat(1,code,double(states.StimulusCode(c+tempc))); + tempc=tempc+interval; + end + c=c+codeCount(i); + end +end + diff --git a/data/accurancys15.mat b/data/accurancys15.mat new file mode 100644 index 0000000..b9bb82b --- /dev/null +++ b/data/accurancys15.mat Binary files differ diff --git a/data/accurancysInCV.mat b/data/accurancysInCV.mat new file mode 100644 index 0000000..7884903 --- /dev/null +++ b/data/accurancysInCV.mat Binary files differ diff --git a/fileInfo.txt b/fileInfo.txt new file mode 100644 index 0000000..a4a7f43 --- /dev/null +++ b/fileInfo.txt @@ -0,0 +1,11 @@ +Stimuli Sequence +12 3 8 1 10 4 7 2 15 4 12 1 14 2 9 3 11 2 13 4 10 3 7 3 12 1 9 1 15 1 6 3 13 2 8 4 11 1 14 4 6 4 9 1 14 2 7 4 6 1 8 3 10 3 7 2 15 4 12 1 14 2 9 4 11 3 13 4 10 2 7 1 12 3 9 2 15 2 6 3 13 + +Pre & Post Run +3s + +Stimulus duration +4s + +Sampling Rate +2500 diff --git a/generateTrainingData.m b/generateTrainingData.m new file mode 100644 index 0000000..a467afc --- /dev/null +++ b/generateTrainingData.m @@ -0,0 +1,27 @@ +function [trainingDataEEG,trainingDataEMG] = generateTrainingData(splitSignals,NFFT,window,shift,params) + frequency=params.SamplingRate.NumericValue; + tempEEG2=zeros([32,size(splitSignals,3),NFFT/2+1]); + tempEMG2=zeros([size(splitSignals,2)-3-32,size(splitSignals,3),(floor(size(splitSignals,1)/(frequency*window))-1)*window/shift+1]); + + %Filter around 50Hz and below 2 Hz + [A,B]= butter(2,[48 52]/(frequency/2),'stop'); + [C,D]= butter(2,2/(frequency/2),'high'); + + parfor i=1:32 %filter single channel, w/o HEOG, Synchro and 0s + tempEEG=zeros(size(splitSignals,3),NFFT/2+1); + for j=1:size(splitSignals,3) %filter single trial + tempEEG(j,:)=pwelch(filter(C,D,filter(A,B,splitSignals(:,i,j))),[],[],NFFT,frequency); + end + tempEEG2(i,:,:)=tempEEG; + end + trainingDataEEG=permute(tempEEG2,[2 1 3]); + + parfor i=33:size(splitSignals,2)-3 + tempEMG=zeros([size(splitSignals,3),(floor(size(splitSignals,1)/(frequency*window))-1)*window/shift+1]); + for j=1:size(splitSignals,3) %filter single trial + tempEMG(j,:)=waveformLength(filter(C,D,filter(A,B,splitSignals(:,i,j))),frequency,window,shift); + end + tempEMG2(i,:,:)=tempEMG; + end + trainingDataEMG=permute(tempEMG2,[2 1 3]); +end \ No newline at end of file diff --git a/kfoldCV.m b/kfoldCV.m new file mode 100644 index 0000000..9deb989 --- /dev/null +++ b/kfoldCV.m @@ -0,0 +1,32 @@ +function [model] = kfoldCV(classification,trainingData,k,cExpMax) + + randMap=randperm(size(trainingData,1)); + leaveOut=trainingData(mod(randMap,k+1)==0,:,:); + leaveClasses=classification(mod(randMap,k+1)==0); + remaining=trainingData(mod(randMap,k+1)~=0,:,:); + remainingClasses=classification(mod(randMap,k+1)~=0); + cvAccurancy=zeros(2*cExpMax+1,1); + for cExp=-cExpMax:1:cExpMax + c=2^cExp; + + randomMapping=randperm(size(remaining,1)); + accurancy=zeros(k,3); + + parfor i=1:k + trainData=remaining(mod(randomMapping,k)+1~=i,:,:); + testData=remaining(mod(randomMapping,k)+1==i,:,:); + trainClasses=remainingClasses(mod(randomMapping,k)+1~=i); + testClasses=remainingClasses(mod(randomMapping,k)+1==i); + [trainClasses,trainData]=balanceClasses(trainClasses,trainData,0.00001); + model=svmtrain(trainClasses,trainData(:,:),sprintf('-t 0 -c %f -q',c)); + [~, accurancy(i,:), ~]=svmpredict(testClasses, testData(:,:), model,'-q'); + end + + cvAccurancy=mean(accurancy(:,1)); + end + [~,i]=max(cvAccurancy); + bestC=2^(i-cExpMax-1); + [balanceLeftClasses, balanceLeftOut]=balanceClasses(leaveClasses,leaveOut,0.00001); + model=svmtrain(balanceLeftClasses, balanceLeftOut(:,:),sprintf('-t 0 -c %f -q',bestC)); + +end \ No newline at end of file diff --git a/myFilter.m b/myFilter.m new file mode 100644 index 0000000..8f6c13d --- /dev/null +++ b/myFilter.m @@ -0,0 +1,26 @@ +function [ filteredSig, pwMeanMove, pwMeanRest, hz ] = myFilter( splitSignals,code,NFFT ) + %Filter around 50Hz and below 2 Hz + [A,B]= butter(2,[48 52]/1250,'stop'); + [C,D]= butter(2,2/1250,'high'); + + + filteredSig=zeros(size(splitSignals)); + pwMeanMove=zeros(NFFT/2+1,size(splitSignals,2)); + pwMeanRest=zeros(NFFT/2+1,size(splitSignals,2)); + + for i=1:size(splitSignals,2) %filter single channel + for j=1:size(splitSignals,3) %filter single trial + filteredSig(:,i,j)=filter(C,D,filter(A,B,splitSignals(:,i,j))); + if code(j)<=5 + pwMeanMove(:,i)=pwMeanMove(:,i)+pwelch(filteredSig(:,i,j),[],[],NFFT,2500); + elseif code(j) > 5 + pwMeanRest(:,i)=pwMeanRest(:,i)+pwelch(filteredSig(:,i,j),[],[],NFFT,2500); + end + %s=cat(1,s,size(pwelch(filteredSig(:,i,j),[],[],[],2500))); + end + pwMeanMove(:,i)=pwMeanMove(:,i) /size(find(code <=5),1); + pwMeanRest(:,i)=pwMeanRest(:,i) /size(find(code > 5),1); + end + [a,hz]=pwelch(filteredSig(:,1,1),[],[],NFFT,2500); +end + diff --git a/plotChannelDiff.m b/plotChannelDiff.m new file mode 100644 index 0000000..e3e0e01 --- /dev/null +++ b/plotChannelDiff.m @@ -0,0 +1,17 @@ +function [] = plotChannelDiff( pwMeanMove, pwMeanRest,hz, params ) + +for i=1:size(params.ChannelNames.Value,1) + n=mod(i,9); + if n==1 + figure + elseif n==0; + n=9; + end + subplot(3,3,n); + plot(hz,pwMeanMove(:,i)-pwMeanRest(:,i)) + xlim([0 150]) + title(params.ChannelNames.Value(i)) +end + +end + diff --git a/plotChannelProzDiff.m b/plotChannelProzDiff.m new file mode 100644 index 0000000..b57bd79 --- /dev/null +++ b/plotChannelProzDiff.m @@ -0,0 +1,17 @@ +function [] = plotChannelProzDiff( pwMeanMove, pwMeanRest,hz, params ) + +for i=1:size(params.ChannelNames.Value,1) + n=mod(i,9); + if n==1 + figure + elseif n==0; + n=9; + end + subplot(3,3,n); + plot(hz,(pwMeanMove(:,i)-pwMeanRest(:,i))./pwMeanRest(:,i)) + xlim([0 150]) + title(params.ChannelNames.Value(i)) +end + +end + diff --git a/plotChannels.m b/plotChannels.m new file mode 100644 index 0000000..9126ec1 --- /dev/null +++ b/plotChannels.m @@ -0,0 +1,17 @@ +function [] = plotChannels( pwMean, hz, params ) + +for i=1:size(params.ChannelNames.Value,1) + n=mod(i,9); + if n==1 + figure + elseif n==0; + n=9; + end + subplot(3,3,n); + plot(log(hz),log(pwMean(:,i,:))) + xlim([0 150]) + title(params.ChannelNames.Value(i)) +end + +end + diff --git a/plotImgScProzDiff.m b/plotImgScProzDiff.m new file mode 100644 index 0000000..a1e6b34 --- /dev/null +++ b/plotImgScProzDiff.m @@ -0,0 +1,8 @@ +function [] = plotImgScProzDiff( pwMeanMove, pwMeanRest) + +prozDiff=(pwMeanMove-pwMeanRest)./pwMeanRest; +imagesc(1:1.22:100,1:32,transpose(prozDiff(1:82,1:32))) +colorbar(); + +end + diff --git a/readme.m b/readme.m new file mode 100644 index 0000000..13fd3d6 --- /dev/null +++ b/readme.m @@ -0,0 +1,55 @@ +[signal, states, params] = load_bcidat('/home/jph/Uni/Masterarbeit/Block1_ReachingMovements/AO/AO_B1001/AO_B1S001R01.dat'); +params.SamplingRate.NumericValue +plot(states.StimulusCode) + +% digitale Filter +help butter +help cheby1 +help cheby2 + +[A,B]= butter(2,[48 52]/1250,'stop'); + +filteredSig = filter(A,B,signal(:,14)); + +% Frequenzspektrum +pwelch(signal(:,14),[],[],[],2500) +pburg + +%----------- +% 1. EEG schneiden: Trials, Labels (Ruhe und Bewegung) +% (optinal) Filter (50 Hz, highpass (0.1Hz?) +% 2. Frequenzspektrum +% 3. Mittelwerte Spektrum �ber Trials: Ruhe gegen Bewegung +% auf C3, C4 konzentrieren (10-20 elektroden system nachschauen) + +%PSDC3=pwelch(filteredSig(:,:,1),[],[],[],2500); +%pwelch(filteredSig(:,13,16),[],[],[],2500); +%xlim([0 0.100]) + +% % plot all +% for i=1:size(filteredSig,3) +% if mod(i,4)==0 +% figure +% end +% subplot(2,2,mod(i,4)+1); +% pwelch(filteredSig(:,13,i),[],[],[],2500); +% xlim([0 0.100]); +% end + +%TODO +%1. filtern in zwei for-Schleifen um sicher zu gehen +%2. anschauen, evtl Window (1s) +%3. Differenz (bzw. prozentualer Unterschied) Trial vs. Ruhe + +%Tipps +% find, diff, xlim([0 0.1]) + +%imagesc + +%libsvm +% - svmTrain +%trial x ch x power (1-50) | bewegung / ruhe +% cross validation (selber impelmentieren) + +% Aufteilung EMG xor EEG (done) +% danach dann Bewegungen aufteilen \ No newline at end of file diff --git a/stimuliMatrix.txt b/stimuliMatrix.txt new file mode 100644 index 0000000..091d456 --- /dev/null +++ b/stimuliMatrix.txt @@ -0,0 +1,4 @@ +BlueGo RedGo GreenGo BrownGo PinkGo REST REST REST REST REST REST REST REST REST REST % +% % % % % % % % % % % % % % % % +sounds\BlueGo.wav sounds\RedGo.wav sounds\GreenGo.wav sounds\BrownGo.wav sounds\PinkGo.wav sounds\Rest.wav sounds\Rest.wav sounds\Rest.wav sounds\Rest.wav sounds\Rest.wav sounds\Rest.wav sounds\Rest.wav sounds\Rest.wav sounds\Rest.wav sounds\Rest.wav % +6000ms 6000ms 6000ms 6000ms 6000ms 2000ms 2100ms 2200ms 2300ms 2400ms 2500ms 2600ms 2700ms 2800ms 2900ms 2000ms diff --git a/svm.m b/svm.m new file mode 100644 index 0000000..f30be1f --- /dev/null +++ b/svm.m @@ -0,0 +1,36 @@ +%params +NFFT=2048; +cutWindow=2; +window=0.2; +shift=0.05; +k=10; +rounds=1; +maxExpC=5; + +%svm +[signal, states, params] = load_bcidat('/home/jph/Uni/masterarbeit/Block1_ReachingMovements/AO/AO_B1001/AO_B1S001R01.dat'); +% [ splitSignals, code ] = cutTrials( states, signal ); +[ splitSignals, code ] = cutWindows( states, signal, params, cutWindow ); + +%trainingData has format trial x ch x powerSpectrum +[trainingDataEEG,trainingDataEMG]=generateTrainingData(splitSignals,NFFT,window,shift,params); + +classification=code;%double(code<=5); + + +accurancy=zeros(rounds,3); +means=zeros(rounds,1); +for i=1:rounds + randMap=randperm(size(trainingDataEEG,1)); + leaveOut=trainingDataEEG(mod(randMap,k+2)==0,:,:); + leaveClasses=classification(mod(randMap,k+2)==0); + remaining=trainingDataEEG(mod(randMap,k+2)~=0,:,:); + remainingClasses=classification(mod(randMap,k+2)~=0); + + model=kfoldCV(remainingClasses,remaining,k,maxExpC); + + [predictions,accurancy(i,:),pvalues]=svmpredict(leaveClasses,leaveOut(:,:),model); + means(i)=mean(leaveClasses-predictions); +end +m=mean(accurancy(:,1)) +mm=mean(means) \ No newline at end of file diff --git a/waveformLength.m b/waveformLength.m new file mode 100644 index 0000000..cfc0298 --- /dev/null +++ b/waveformLength.m @@ -0,0 +1,12 @@ +function [WL]=waveformLength(signal,frequency,window,windowShift) +%waveformLength computes waveformLength of signal with given frequency, window and +%shift + signalShift=frequency*windowShift; + signalWindow=frequency*window; + shiftProp=window/windowShift; + WL=zeros((floor(size(signal,1)/signalWindow)-1)*shiftProp+1,1); + parfor j=1:(size(signal,1)/signalWindow-1)*shiftProp+1 + WL(j)=sum(diff(signal((j-1)*signalShift+1:(j-1)*signalShift+signalWindow))); + end +end +