Skip to content

Commit

Permalink
implement channel map creation and saving
Browse files Browse the repository at this point in the history
  • Loading branch information
nsteinme committed Feb 22, 2019
1 parent 106f144 commit 10aaaf6
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 33 deletions.
Binary file modified configFiles/neuropixPhase3A_kilosortChanMap.mat
Binary file not shown.
Binary file modified configFiles/neuropixPhase3B1_kilosortChanMap.mat
Binary file not shown.
Binary file modified configFiles/neuropixPhase3B2_kilosortChanMap.mat
Binary file not shown.
70 changes: 70 additions & 0 deletions gui/createValidChanMap.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@


function cm = createValidChanMap(q, varargin)
cm = [];
if isfield(q, 'chanMap') && ...
isfield(q, 'xcoords') && ...
isfield(q, 'ycoords') && ...
numel(q.chanMap)==numel(q.xcoords) &&...
numel(q.chanMap)==numel(q.ycoords)

if min(q.chanMap)==0
q.chanMap = q.chanMap+1;
end

% has required fields - we accept it

cm.chanMap = q.chanMap(:);
cm.xcoords = q.xcoords(:);
cm.ycoords = q.ycoords(:);
cm.chanMap0ind = q.chanMap(:)-1;

if isfield(q, 'connected')
if numel(q.connected)==numel(cm.chanMap)
cm.connected = q.connected(:);
else
warning('Invalid chanMap variable ''connected'': must be the same size as chanMap. Using default.');
end

else
cm.connected = true(size(cm.chanMap));
end

cm.kcoords = ones(size(cm.chanMap));
if isfield(q, 'kcoords')
if numel(q.kcoords)==numel(cm.chanMap)
cm.kcoords = q.kcoords(:);
else
warning('Invalid chanMap variable ''kcoords'': must be the same size as chanMap. Using default.');
end
elseif isfield(q, 'shankInd')
if numel(q.shankInd)==numel(cm.chanMap)
cm.kcoords = q.shankInd(:);
else
warning('Invalid chanMap variable ''shankInd'': must be the same size as chanMap. Using default.');
end
end

if isfield(q, 'name')
cm.name = q.name;
else
if nargin>1
filename = varargin{1};
x = strfind(filename, 'kilosortChanMap');
if ~isempty(x)
cm.name = filename(1:x-2);
else
cm.name = filename;
end
else
cm.name = 'unknown';
end
end

if isfield(q, 'siteSize')
cm.siteSize = siteSize;
end

else
warning('Invalid channel map: A valid channel map must have chanMap, xcoords, and ycoords, and they must all be the same size.')
end
124 changes: 91 additions & 33 deletions gui/ksGUI.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@
% GUI by N. Steinmetz
%
% TODO: (* = before release)
% - test that path adding and compilation work on a fresh install
% - allow better setting of probe site shape/size
% - auto-load number of channels from meta file when possible
% - update time plot when scrolling in dataview
% - show RMS noise level of channels to help selecting ones to drop?
% - implement builder for new probe channel maps (cm, xc, yc, name,
% site size)
% - *advanced option setting
% - saving of probe layouts
% - plotting bug: zoom out on probe view should allow all the way out
% in x
Expand All @@ -28,9 +26,6 @@
% - find way to run ks in the background so gui is still usable(?)
% - quick way to set working/output directory when selecting a new file
% - when selecting a new file, reset everything
% - when re-loading old file and whitening matrix already exists, use
% it rather than re-compute
% - button to easily match folders to the source
% - why doesn't computeWhitening run on initial load?

properties
Expand Down Expand Up @@ -85,12 +80,12 @@ function init(obj)
fprintf(1, 'Success!\n');
cd(oldDir);
catch ex
fprintf(1, 'Compilation failed. Check installation instructions at https://github.com/cortex-lab/Kilosort\n');
fprintf(1, 'Compilation failed. Check installation instructions at https://github.com/MouseLand/Kilosort2\n');
rethrow(ex);
end
end


obj.P.allChanMaps = loadChanMaps();

end

Expand Down Expand Up @@ -134,7 +129,13 @@ function build(obj, f)
'String', 'Help', 'FontSize', 24,...
'Callback', @(~,~)obj.help);

obj.H.titleHBox.Sizes = [-5 -1];
obj.H.resetButton = uicontrol(...
'Parent', obj.H.titleHBox,...
'Style', 'pushbutton', ...
'String', 'Reset GUI', 'FontSize', 24,...
'Callback', @(~,~)obj.reset);

obj.H.titleHBox.Sizes = [-5 -1 -1];

% -- Main section
obj.H.setRunVBox = uiextras.VBox(...
Expand Down Expand Up @@ -260,16 +261,13 @@ function build(obj, f)
'Style', 'edit', 'HorizontalAlignment', 'left', ...
'String', '...', 'Callback', @(~,~)obj.updateFileSettings());

% TODO: get list of probes
probeNames = {obj.P.allChanMaps.name};
probeNames{end+1} = '[new]';
probeNames{end+1} = 'other...';
obj.H.settings.setProbeEdt = uicontrol(...
'Parent', obj.H.settingsGrid,...
'Style', 'popupmenu', 'HorizontalAlignment', 'left', ...
'String', {...
'Neuropixels Phase3A', ...
'Neuropixels Phase3B1 (staggered)',...
'Neuropixels Phase3B2 (aligned)',...
'[new]', ...
'other...'}, ...
'String', probeNames, ...
'Callback', @(~,~)obj.updateProbeView('reset'));
obj.H.settings.setnChanEdt = uicontrol(...
'Parent', obj.H.settingsGrid,...
Expand Down Expand Up @@ -449,8 +447,9 @@ function initPars(obj)
obj.restoreGUIsettings();
obj.updateProbeView('new');
obj.updateFileSettings();
catch
catch ex
obj.log('Failed to initialize last file.');
keyboard
end
end
else
Expand Down Expand Up @@ -1087,30 +1086,69 @@ function updateProbeView(obj, varargin)

cm = [];
switch selProbe
case 'Neuropixels Phase3A'
cm = load('neuropixPhase3A_kilosortChanMap.mat');
case 'Neuropixels Phase3B1 (staggered)'
cm = load('neuropixPhase3B1_kilosortChanMap.mat');
case 'Neuropixels Phase3B2 (aligned)'
cm = load('neuropixPhase3B2_kilosortChanMap.mat');
case '[new]'
obj.log('New probe creator not yet implemented.');
return;
%obj.log('New probe creator not yet implemented.');
answer = inputdlg({'Name for new channel map:', ...
'X-coordinates of each site (can use matlab expressions):',...
'Y-coordinates of each site:',...
'Shank index (''kcoords'') for each site (blank for single shank):',...
'Channel map (the list of rows in the data file for each site):',...
'List of disconnected/bad site numbers (blank for none):'});
if isempty(answer)
return;
else
cm.name = answer{1};
cm.xcoords = str2num(answer{2});
cm.ycoords = str2num(answer{3});
if ~isempty(answer{4})
cm.kcoords = str2num(answer{4});
end
cm.chanMap = str2num(answer{5});
if ~isempty(answer{6})
q = str2num(answer{6});
if numel(q) == numel(cm.chanMap)
cm.connected = q;
else
cm.connected = true(size(cm.chanMap));
cm.connected(q) = false;
end
end
cm = createValidChanMap(cm);
if ~isempty(cm)
answer = questdlg('Save this channel map for later?');
if strcmp(answer, 'Yes')
saveNewChanMap(cm, obj);
end
else
obj.log('Channel map invalid. Must have chanMap, xcoords, and ycoords of same length');
end
end
case 'other...'
[filename, pathname] = uigetfile('*.mat', 'Pick a channel map file.');

if filename~=0 % 0 when cancel
%obj.log('choosing a different channel map not yet implemented.');
cm = load(fullfile(pathname, filename));
cm.chanMap = cm.chanMap(:);
cm.xcoords = cm.xcoords(:);
cm.ycoords = cm.ycoords(:);
cm.connected = logical(cm.connected(:));
% ** check for all the right data, get a name for
% it, add to defaults
cm = load(fullfile(pathname, filename));
cm = createValidChanMap(cm, filename);
if ~isempty(cm)
obj.P.allChanMaps(end+1) = cm;
currProbeList = obj.H.settings.setProbeEdt.String;
newProbeList = [{cm.name} currProbeList];
obj.H.settings.setProbeEdt.String = newProbeList;
answer = questdlg('Save this channel map for later?');
if strcmp(answer, 'Yes')
saveNewChanMap(cm, obj);
end
else
obj.log('Channel map invalid. Must have chanMap, xcoords, and ycoords of same length');
return;
end
else
return;
end
otherwise
probeNames = {obj.P.allChanMaps.name};
cm = obj.P.allChanMaps(strcmp(probeNames, selProbe));
end

nSites = numel(cm.chanMap);
Expand Down Expand Up @@ -1429,7 +1467,10 @@ function restoreGUIsettings(obj)
obj.H.settings.setProbeEdt.Value = saveDat.settings.setProbeEdt.Value;
obj.H.settings.setnChanEdt.String = saveDat.settings.setnChanEdt.String;
obj.H.settings.setFsEdt.String = saveDat.settings.setFsEdt.String;
obj.H.settings.setNfiltEdt.String = saveDat.settings.setNfiltEdt.String;
obj.H.settings.setThEdt.String = saveDat.settings.setThEdt.String;
obj.H.settings.setLambdaEdt.String = saveDat.settings.setLambdaEdt.String;
obj.H.settings.setCcsplitEdt.String = saveDat.settings.setCcsplitEdt.String;
obj.H.settings.setMinfrEdt.String = saveDat.settings.setMinfrEdt.String;

obj.ops = saveDat.ops;
obj.rez = saveDat.rez;
Expand Down Expand Up @@ -1462,8 +1503,25 @@ function help(obj)

end

function reset(obj)
% full reset: delete userSettings.mat and the settings file
% for current file. re-launch.

delete(obj.P.settingsPath);

[p,fn] = fileparts(obj.H.settings.ChooseFileEdt.String);
savePath = fullfile(p, [fn '_ksSettings.mat']);
delete(savePath);

obj.P.skipSave = true;
kilosort;

end

function cleanup(obj)
obj.saveGUIsettings();
if ~isfield(obj.P, 'skipSave')
obj.saveGUIsettings();
end
fclose('all');
end

Expand Down
21 changes: 21 additions & 0 deletions gui/loadChanMaps.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@


function chanMaps = loadChanMaps()


ksroot = fileparts(fileparts(mfilename('fullpath')));
chanMapFiles = dir(fullfile(ksroot, 'configFiles', '*.mat'));

idx = 1;
chanMaps = [];
for c = 1:numel(chanMapFiles)

q = load(fullfile(ksroot, 'configFiles', chanMapFiles(c).name));

cm = createValidChanMap(q, chanMapFiles(c).name);
if ~isempty(cm)
if idx==1; chanMaps = cm; else; chanMaps(idx) = cm; end;
idx = idx+1;
end

end
21 changes: 21 additions & 0 deletions gui/saveNewChanMap.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@


function saveNewChanMap(cm, obj)

newName = cm.name;
if strcmp(cm.name, 'unknown')
answer = inputdlg('Name for this channel map:');
if ~isempty(answer) && ~isempty(answer{1})
newName = answer{1};
else
newName = '';
end
end
if ~isempty(newName)
ksRoot = fileparts(fileparts(mfilename('fullpath')));
newFn = [answer{1} '_kilosortChanMap.mat'];
save(fullfile(ksRoot, 'configFiles', newFn), '-struct', 'cm');
obj.log(['Saved new channel map: ' fullfile(ksRoot, 'configFiles', newFn)]);
else
obj.log('Could not save new channel map without a name');
end

0 comments on commit 10aaaf6

Please sign in to comment.