├── .ipynb_checkpoints ├── Untitled-checkpoint.ipynb └── resting_ica-checkpoint.ipynb ├── Brainlock.py ├── Experiment ├── N400 Experiment │ ├── CrossCor.py │ ├── LSL.py │ ├── README.md │ ├── acr │ │ └── acr.csv │ ├── acrselection.py │ ├── eeg │ │ ├── Known_1.csv │ │ ├── Known_2.csv │ │ ├── unKnown_1.csv │ │ └── unKnown_2.csv │ └── n400.py ├── P300 Target Stimuli │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── 7.jpg │ ├── 8.jpg │ ├── 9.jpg │ ├── README.md │ └── p300.py └── Resting State │ ├── Muse Aquisition.py │ └── preproc.py ├── MNI0041_MEGs0003_resting_20130416_03_AUX.ds └── MNI0041_MEGs0003_resting_20130416_03_AUX.newds ├── Pipeline_brainhack.ipynb ├── README.md ├── S001R02.edf ├── S002R02.edf ├── S003R02.edf └── resting_ica.ipynb /.ipynb_checkpoints/Untitled-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [], 3 | "metadata": {}, 4 | "nbformat": 4, 5 | "nbformat_minor": 0 6 | } 7 | -------------------------------------------------------------------------------- /.ipynb_checkpoints/resting_ica-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Importing Libraries" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 16, 13 | "metadata": { 14 | "collapsed": false 15 | }, 16 | "outputs": [], 17 | "source": [ 18 | "import mne\n", 19 | "import numpy as np\n", 20 | "import matplotlib.pyplot as plt\n", 21 | "from scipy import signal\n", 22 | "\n", 23 | "from sklearn.decomposition import FastICA, PCA\n", 24 | "#https://www.physionet.org/pn4/eegmmidb/\n" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "# Reading raw data and picking the relevant channels" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 18, 37 | "metadata": { 38 | "collapsed": false 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "#raw = mne.io.read_raw_ctf('MNI0041_MEGs0003_resting_20130416_03_AUX.ds', preload=True)\n" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 21, 48 | "metadata": { 49 | "collapsed": false 50 | }, 51 | "outputs": [ 52 | { 53 | "name": "stdout", 54 | "output_type": "stream", 55 | "text": [ 56 | "Extracting edf Parameters from S001R02.edf...\n", 57 | "Setting channel info structure...\n", 58 | "Creating Raw.info structure...\n", 59 | "Reading 0 ... 9759 = 0.000 ... 60.994 secs...\n", 60 | "Ready.\n" 61 | ] 62 | } 63 | ], 64 | "source": [ 65 | "raw = mne.io.read_raw_edf(\"S001R02.edf\", preload=True)" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": 22, 71 | "metadata": { 72 | "collapsed": false 73 | }, 74 | "outputs": [ 75 | { 76 | "name": "stdout", 77 | "output_type": "stream", 78 | "text": [ 79 | "[u'Fc5.', u'Fc3.', u'Fc1.', u'Fcz.', u'Fc2.', u'Fc4.', u'Fc6.', u'C5..', u'C3..', u'C1..', u'Cz..', u'C2..', u'C4..', u'C6..', u'Cp5.', u'Cp3.', u'Cp1.', u'Cpz.', u'Cp2.', u'Cp4.', u'Cp6.', u'Fp1.', u'Fpz.', u'Fp2.', u'Af7.', u'Af3.', u'Afz.', u'Af4.', u'Af8.', u'F7..', u'F5..', u'F3..', u'F1..', u'Fz..', u'F2..', u'F4..', u'F6..', u'F8..', u'Ft7.', u'Ft8.', u'T7..', u'T8..', u'T9..', u'T10.', u'Tp7.', u'Tp8.', u'P7..', u'P5..', u'P3..', u'P1..', u'Pz..', u'P2..', u'P4..', u'P6..', u'P8..', u'Po7.', u'Po3.', u'Poz.', u'Po4.', u'Po8.', u'O1..', u'Oz..', u'O2..', u'Iz..', 'STI 014']\n" 80 | ] 81 | } 82 | ], 83 | "source": [ 84 | "print(raw.ch_names)" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 23, 90 | "metadata": { 91 | "collapsed": false 92 | }, 93 | "outputs": [ 94 | { 95 | "name": "stdout", 96 | "output_type": "stream", 97 | "text": [ 98 | "\n" 99 | ] 100 | } 101 | ], 102 | "source": [ 103 | "print (raw)" 104 | ] 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "metadata": {}, 109 | "source": [ 110 | "# Checking what the actual data looks like" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 2, 116 | "metadata": { 117 | "collapsed": false 118 | }, 119 | "outputs": [ 120 | { 121 | "ename": "NameError", 122 | "evalue": "name 'raw' is not defined", 123 | "output_type": "error", 124 | "traceback": [ 125 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 126 | "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", 127 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mraw\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 128 | "\u001b[1;31mNameError\u001b[0m: name 'raw' is not defined" 129 | ] 130 | } 131 | ], 132 | "source": [ 133 | "raw.plot()" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 33, 139 | "metadata": { 140 | "collapsed": false 141 | }, 142 | "outputs": [ 143 | { 144 | "name": "stdout", 145 | "output_type": "stream", 146 | "text": [ 147 | "\n" 148 | ] 149 | } 150 | ], 151 | "source": [ 152 | "\n", 153 | "print(raw.pick_channels([u'O1..']))\n", 154 | "X = raw._data.T #Transposing the data to use it in the ICA" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "metadata": { 161 | "collapsed": false 162 | }, 163 | "outputs": [], 164 | "source": [] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "# Plotting Raw Data" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 10, 176 | "metadata": { 177 | "collapsed": false 178 | }, 179 | "outputs": [ 180 | { 181 | "data": { 182 | "text/plain": [ 183 | "" 184 | ] 185 | }, 186 | "execution_count": 10, 187 | "metadata": {}, 188 | "output_type": "execute_result" 189 | } 190 | ], 191 | "source": [ 192 | "raw.plot(n_channels=3, duration=1, start=60)" 193 | ] 194 | }, 195 | { 196 | "cell_type": "markdown", 197 | "metadata": {}, 198 | "source": [ 199 | "# Running FastICA and PCA on the transposed data" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": 34, 205 | "metadata": { 206 | "collapsed": false 207 | }, 208 | "outputs": [], 209 | "source": [ 210 | "ica = FastICA()\n", 211 | "S_ = ica.fit_transform(X)\n", 212 | "A_ = ica.mixing_" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 35, 218 | "metadata": { 219 | "collapsed": true 220 | }, 221 | "outputs": [], 222 | "source": [ 223 | "pca = PCA()\n", 224 | "H = pca.fit_transform(X)" 225 | ] 226 | }, 227 | { 228 | "cell_type": "markdown", 229 | "metadata": {}, 230 | "source": [ 231 | "# Plotting the ICA" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 38, 237 | "metadata": { 238 | "collapsed": false 239 | }, 240 | "outputs": [], 241 | "source": [ 242 | "plt.figure()\n", 243 | "plt.plot(S_,\"g\")\n", 244 | "plt.plot(X)\n", 245 | "plt.show()" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": 29, 251 | "metadata": { 252 | "collapsed": false 253 | }, 254 | "outputs": [ 255 | { 256 | "data": { 257 | "text/plain": [ 258 | "(9760L, 65L)" 259 | ] 260 | }, 261 | "execution_count": 29, 262 | "metadata": {}, 263 | "output_type": "execute_result" 264 | } 265 | ], 266 | "source": [ 267 | "S_.shape" 268 | ] 269 | } 270 | ], 271 | "metadata": { 272 | "kernelspec": { 273 | "display_name": "Python 2", 274 | "language": "python", 275 | "name": "python2" 276 | }, 277 | "language_info": { 278 | "codemirror_mode": { 279 | "name": "ipython", 280 | "version": 2 281 | }, 282 | "file_extension": ".py", 283 | "mimetype": "text/x-python", 284 | "name": "python", 285 | "nbconvert_exporter": "python", 286 | "pygments_lexer": "ipython2", 287 | "version": "2.7.13" 288 | } 289 | }, 290 | "nbformat": 4, 291 | "nbformat_minor": 2 292 | } 293 | -------------------------------------------------------------------------------- /Brainlock.py: -------------------------------------------------------------------------------- 1 | # IMPORT STATEMENTS 2 | 3 | from __future__ import division # so that 1/3=0.333 instead of 1/3=0 4 | from multiprocessing import Process, Queue 5 | from psychopy import locale_setup, visual, core, event, logging, sound, gui 6 | from psychopy.constants import * # things like STARTED, FINISHED 7 | import numpy as np # whole numpy lib is available, prepend 'np.' 8 | from numpy import sin, cos, tan, log, log10, pi, average, sqrt, std, deg2rad, rad2deg, linspace, asarray 9 | from numpy.random import random, randint, normal, shuffle 10 | import os # handy system and path functions 11 | import csv 12 | import sys # to get file system encoding 13 | import random 14 | import matplotlib.pyplot as plt 15 | from pylsl import StreamInlet, resolve_stream 16 | 17 | ### Make sure you are in the correct directory ### 18 | os.chdir('/home/sydney/Brainlock') 19 | 20 | 21 | 22 | ####### Functions ######## 23 | 24 | def lslstream(): 25 | print("looking for an EEG stream...") 26 | streams = resolve_stream('type','EEG') 27 | print("Found it!") 28 | return StreamInlet(streams[0]) 29 | 30 | def datastream(): 31 | data= [] 32 | return True 33 | 34 | def runlive(): 35 | 36 | for frameN in range(180): 37 | 38 | if frameN ==0: 39 | stim=True 40 | print stim 41 | win.flip() 42 | 43 | if 0 <= frameN < 60: 44 | print("0-60: Time - %f",(clock.getTime())) 45 | fixation.draw() 46 | 47 | if frameN == 60: 48 | stim=False 49 | if 60 <= frameN < 180: 50 | word.draw() 51 | print("60-180: Time - %f",(clock.getTime())) 52 | if frameN ==179: 53 | acrdone= True 54 | psydone=True 55 | 56 | ####### end Functions ######## 57 | 58 | 59 | 60 | 61 | #####Getting the CSV File ready 62 | 63 | 64 | 65 | 66 | #IMPORT CUSTOM MODULES 67 | #from Password_Setup import * 68 | 69 | #PRE-LOAD STIMS 70 | 71 | knownA= range(10) 72 | unknowA= range(10) 73 | 74 | 75 | #### Acronym List ####### 76 | with open('acr.csv','r') as acr: 77 | 78 | reader=csv.reader(acr) 79 | knownA = reader.next() 80 | unknownA= reader.next() 81 | 82 | 83 | ##### Determine the User. If he presses 1, it will run openBCI 84 | usern = raw_input("Enter your username:") 85 | live = int(raw_input("Enter 1 for streaming, 2 for data stream")) 86 | 87 | if live == 1: 88 | inlet= lslstream() 89 | 90 | else: 91 | inlet= datastream() 92 | 93 | ##### 94 | 95 | 96 | 97 | acrLknown.extend(acrlunknown) 98 | acrL=acrLknown 99 | print acrL 100 | 101 | random.shuffle(acrL) 102 | 103 | #if lSize is not 0: 104 | # acrL=acrL[:lSize] 105 | ######/end Acronym List ############## 106 | 107 | 108 | 109 | #INTRO SCREEN 110 | win = visual.Window(size=(1366, 768), fullscr=True, screen=0, allowGUI=False, allowStencil=False, 111 | monitor='testMonitor', color=[-1,-1,-1], colorSpace='rgb', 112 | blendMode='avg', useFBO=True, 113 | ) 114 | 115 | #CREATE NONTEXT OBJECTS 116 | 117 | 118 | fixation = visual.GratingStim(win=win, mask='cross', size=0.5, pos=[0,0], sf=0.1) 119 | 120 | sync = visual.ShapeStim(win, units='', lineWidth=1.5, lineColor='white', 121 | lineColorSpace='rgb', fillColor='white', fillColorSpace='rgb', 122 | vertices=((0.8, 0.9), (0.9, 0.9), (0.9, 0.8), (0.8,0.8)), 123 | closeShape=True, pos=(0, 0), size=1, ori=0.0, opacity=1.0, 124 | contrast=1.0, depth=0, interpolate=True, name=None, 125 | autoLog=None, autoDraw=False) 126 | 127 | 128 | 129 | ####Variables For the Loop##### 130 | clock=core.Clock() 131 | stim= True 132 | acrdone= False 133 | psydone= False 134 | i= 0 135 | j=0 136 | 137 | ###/End of Variables For the Loop##### 138 | 139 | def fix(val1,val2): 140 | var=True 141 | if val1==0: 142 | return var 143 | if val1==1: 144 | var=val2 145 | print var 146 | 147 | 148 | def lsl(q): 149 | with open(usern+'.csv','wb') as f: 150 | writer = csv.writer(f) 151 | power=1 152 | stim= "+" 153 | #if not q.empty(): 154 | # power,stim =q.get() 155 | 156 | while power==1: 157 | if q.empty(): 158 | sample,timestamp = inlet.pull_sample() 159 | data=[stim,timestamp] 160 | data.extend(sample) 161 | writer.writerow(data) 162 | else: 163 | power,stim =q.get() 164 | 165 | 166 | 167 | ###Code Below is all the Main Loop ##### 168 | 169 | if __name__ == "__main__": 170 | q= Queue() 171 | # q.put([1,"+"]) 172 | l= Process(target=lsl,args=(q,)) 173 | l.start() 174 | for k in range(len(acrL)): 175 | word = visual.TextStim(win=win, ori=0, name='word', 176 | text=acrL[k],font=u'Arial', 177 | pos=[0, 0], height=0.5, wrapWidth=300, 178 | color=u'white', colorSpace='rgb', opacity=1, 179 | depth=0.0) 180 | i=k 181 | for frameN in range(180): 182 | win.flip() 183 | if frameN ==0: 184 | q.put([1,"+"]) 185 | if 0 <= frameN < 60: 186 | ## print("0-60: Time - %f",(clock.getTime())) 187 | fixation.draw() 188 | 189 | if frameN == 60: 190 | q.put([1,acrL[k]]) 191 | if 60 <= frameN < 180: 192 | word.draw() 193 | ## print("60-180: Time - %f",(clock.getTime())) 194 | 195 | q.put([0,""]) 196 | l.terminate() 197 | win.close() 198 | 199 | 200 | ###Code Cemetary 201 | 202 | 203 | ## sample,timestamp = inlet.pull_sample() 204 | ## nTime=timestamp+3.0 205 | ## while timestamp " 215 | ] 216 | }, 217 | "metadata": {}, 218 | "output_type": "display_data" 219 | } 220 | ], 221 | "source": [ 222 | "cm_normalized = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n", 223 | "\n", 224 | "plt.figure(figsize=(8,6))\n", 225 | "plot_confusion_matrix(cm_normalized)" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": null, 231 | "metadata": { 232 | "collapsed": true 233 | }, 234 | "outputs": [], 235 | "source": [] 236 | } 237 | ], 238 | "metadata": { 239 | "kernelspec": { 240 | "display_name": "Python 3", 241 | "language": "python", 242 | "name": "python3" 243 | }, 244 | "language_info": { 245 | "codemirror_mode": { 246 | "name": "ipython", 247 | "version": 3 248 | }, 249 | "file_extension": ".py", 250 | "mimetype": "text/x-python", 251 | "name": "python", 252 | "nbconvert_exporter": "python", 253 | "pygments_lexer": "ipython3", 254 | "version": "3.5.2" 255 | } 256 | }, 257 | "nbformat": 4, 258 | "nbformat_minor": 0 259 | } 260 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Brainlock 2 | 3 | Brainlock is a N400 based EEG biometric authentication system. In this design we flash different acronyms to the user with the expectation that they will known some and depending on the person. This acts as their password. 4 | 5 | Depending on the person, you may need to test whether they know the acronym or not (there is a script called acrselection). Run the 6 | 7 | ##Requirements: 8 | - Python 2.7+ (https://www.python.org/) 9 | - Psychopy (http://www.psychopy.org/) 10 | - Python OpenBCI (https://github.com/OpenBCI/OpenBCI_Python) 11 | - Octave (If you prefer, a Matlab script will also be provided) 12 | - EEGLab (https://sccn.ucsd.edu/eeglab) 13 | - an OpenBCI 14 | - A EEg cap or something to keep the electrodes from not moving. 15 | - 16 | 17 | ## Protocol 18 | 19 | protocol has 3 steps that are associated with it: 20 | 21 | 1) Data Aquisition (Using Psychopy and OpenBCI) 22 | 2) Biometric Template (Using EEGLab and Octave) 23 | 3) Testing 24 | 25 | 26 | 27 | ### Data Aquisition 28 | 29 | This project is really early in its development and as such, you will need to run multiple scripts 30 | 31 | To build the eeg For the Aquisition Script of the eeg template , 32 | 33 | For the OpenBCI, you will need to have your pins configured properly. Depending on the person, the suggested amount of electrodes to work with would be around 5: 3 would be your points of eeg aquisition, 1 would be your reference and 1 would be your ground. 34 | 35 | ### Signal Processing and Classification 36 | 37 | In order to do this, all that is required is to run octave with EEGlab. Once the script is complete, you should be able to take your csv then run the BrainLock script 38 | 39 | 40 | ### Testing 41 | 42 | In order to dertmine if you have a good EEG template, you can run the Brainlock script. You can select if you want to do a live or csv test, where you compare an eeg session with the template. This will inform you how good your reading is. 43 | 44 | # TODO: 45 | 46 | * Improving Robustness is critical to make this something liable to be used in the field. Whether it is expanding for it to use other hardware or improve the signal processing and classification, This should be improved first before anything else. 47 | * Update scripts to match the style conventions of Pythno 48 | 49 | -------------------------------------------------------------------------------- /S001R02.edf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeuroTechX/Brainlock/d4dbf173c969c7e5a2861675a3c37e6dfce47890/S001R02.edf -------------------------------------------------------------------------------- /S002R02.edf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeuroTechX/Brainlock/d4dbf173c969c7e5a2861675a3c37e6dfce47890/S002R02.edf -------------------------------------------------------------------------------- /S003R02.edf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeuroTechX/Brainlock/d4dbf173c969c7e5a2861675a3c37e6dfce47890/S003R02.edf --------------------------------------------------------------------------------