Skip to content

Commit

Permalink
New process to import clusters
Browse files Browse the repository at this point in the history
  • Loading branch information
ftadel committed Jan 18, 2023
1 parent 0bf18d5 commit b57db7e
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 33 deletions.
2 changes: 1 addition & 1 deletion doc/license.html
Expand Up @@ -5,7 +5,7 @@
<body alink="#fff000" link="#fff000" vlink="#fff000">
<h4><span style="font-family: Arial Black; color: #ffffff;"><strong>THERE IS NO UNDO BUTTON!<BR>SET UP A <FONT color=red>BACKUP</FONT> OF YOUR DATABASE</strong></span></h4>
<HR>
<!-- LICENCE_START -->Version: 3.230117 (17-Jan-2023)<br>
<!-- LICENCE_START -->Version: 3.230118 (18-Jan-2023)<br>
<span style="font-style: italic;">COPYRIGHT &copy; 2000-2023
USC &amp; McGill University.<br>
</span>
Expand Down
1 change: 1 addition & 0 deletions doc/updates.txt
Expand Up @@ -6,6 +6,7 @@ January 2023
- IO: Export matrix files as EDF+
- Inverse: Display scouts on source-level PSD as PSD figures
- Clusters: Save clusters in channel file, load automatically
- Clusters: Import from process
--------------------------------------------------------------
November 2022
- Forward: Display leadfield sensitivity (surface, MRI, isosurface)
Expand Down
2 changes: 1 addition & 1 deletion doc/version.txt
@@ -1,2 +1,2 @@
% Brainstorm
% v. 3.230117 (17-Jan-2023)
% v. 3.230118 (18-Jan-2023)
7 changes: 5 additions & 2 deletions toolbox/core/bst_get.m
Expand Up @@ -3801,13 +3801,16 @@
{'.sel'}, 'MNE selection files (*.sel)', 'MNE'; ...
{'.mon'}, 'Text montage files (*.mon)', 'MON'; ...
{'_montage'}, 'Brainstorm montage files (montage_*.mat)', 'BST';
{'.csv'}, 'Comma-separated montage files (*.csv)', 'CSV'; ...
{'.xml'}, 'Compumedics ProFusion montages (*.xml)', 'EEG-COMPUMEDICS-PFS'};
{'.csv'}, 'Comma-separated montage files (*.csv)', 'CSV'};
case 'montageout'
argout1 = {...
{'.sel'}, 'MNE selection files (*.sel)', 'MNE'; ...
{'.mon'}, 'Text montage files (*.mon)', 'MON'; ...
{'_montage'}, 'Brainstorm montage files (montage_*.mat)', 'BST'};
case 'clusterin'
argout1 = {...
{'_cluster'}, 'Brainstorm clusters file (*cluster*.mat)', 'BST'; ...
{'.sel'}, 'MNE selection files (*.sel)', 'MNE'};
case 'fibers'
argout1 = {...
{'.trk'}, 'TrackVis (*.trk)', 'TRK'; ...
Expand Down
37 changes: 10 additions & 27 deletions toolbox/gui/panel_cluster.m
Expand Up @@ -781,46 +781,29 @@ function LoadClusters(varargin)
% Get default cluster folder
ClusterDir = GetDefaultClusterDir();
% Ask user which are the files to be loaded
ClusterFiles = java_getfile('open', 'Import clusters', ClusterDir, ...
'multiple', 'files', ...
{{'_cluster'}, 'Sensor clusters (*cluster*.mat)', 'BST'}, 1);
[ClusterFiles, FileFormat] = java_getfile(...
'open', 'Import clusters', ClusterDir, ...
'multiple', 'files', ...
bst_get('FileFilters', 'clusterin'), 1);
if isempty(ClusterFiles)
return
end

% ==== CREATE AND DISPLAY ====
iNewClustersList = [];
iNewClusters = [];
bst_progress('start', 'Load clusters', 'Load cluster file');
% Load all files selected by user
for iFile = 1:length(ClusterFiles)
% Try to load cluster file
ClusterMat = load(ClusterFiles{iFile});
if ~isfield(ClusterMat, 'Clusters') || isempty(ClusterMat.Clusters)
continue;
end
% Create standardized structure
sClustersNew = repmat(db_template('cluster'), 1, length(ClusterMat.Clusters));
% Loop on all the new clusters
for i = 1:length(ClusterMat.Clusters)
sClustersNew(i).Sensors = ClusterMat.Clusters(i).Sensors;
if isfield(ClusterMat.Clusters(i), 'Label') && ~isempty(ClusterMat.Clusters(i).Label)
sClustersNew(i).Label = ClusterMat.Clusters(i).Label;
end
if isfield(ClusterMat.Clusters(i), 'Color') && ~isempty(ClusterMat.Clusters(i).Color)
sClustersNew(i).Color = ClusterMat.Clusters(i).Color;
end
if isfield(ClusterMat.Clusters(i), 'Function') && ~isempty(ClusterMat.Clusters(i).Function)
sClustersNew(i).Function = ClusterMat.Clusters(i).Function;
end
end
% Load clusters
sClusters = in_clusters(ClusterFiles{iFile}, FileFormat);
% Add to current clusters
iNewClustersList = [iNewClustersList, SetClusters('Add', sClustersNew)];
iNewClusters = [iNewClusters, SetClusters('Add', sClusters)];
end
% Update cluster list
UpdateClustersList();
% Select first cluster
if isempty(iNewClustersList)
SetSelectedClusters(iNewClustersList(1));
if isempty(iNewClusters)
SetSelectedClusters(iNewClusters(1));
end
bst_progress('stop');
end
Expand Down
2 changes: 0 additions & 2 deletions toolbox/gui/panel_montage.m
Expand Up @@ -1871,8 +1871,6 @@ function CreateMontageMenu(jButton, hFig)
sMon = in_montage_mne(FileNames{iFile});
case 'MON'
sMon = in_montage_mon(FileNames{iFile});
case 'EEG-COMPUMEDICS-PFS'
sMon = in_montage_compumedics(FileNames{iFile});
case 'BST'
DataMat = load(FileNames{iFile});
sMon = DataMat.Montages;
Expand Down
60 changes: 60 additions & 0 deletions toolbox/io/in_clusters.m
@@ -0,0 +1,60 @@
function sClusters = in_clusters(ClusterFile, FileFormat)
% IN_CLUSTERS: Read clusters of channels from a file

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2023

% Read file
switch (FileFormat)
case 'BST'
ClusterMat = load(ClusterFile);
if ~isfield(ClusterMat, 'Clusters')
error('Invalid Brainstorm clusters file: missing field "Clusters".');
end
case 'MNE'
sMontage = in_montage_mne(FileName);
for i = 1:length(sMontage)
ClusterMat.Clusters(i).Label = sMontage(i).Name;
ClusterMat.Clusters(i).Sensors = sMontage(i).ChanNames;
end
otherwise
error('Invalid file format.');
end
if isempty(ClusterMat.Clusters)
error(['No clusters available in file: ' FileName]);
end

% Create standardized Brainstorm structure
sClusters = repmat(db_template('cluster'), 1, length(ClusterMat.Clusters));
% Loop on all the new clusters
for i = 1:length(ClusterMat.Clusters)
sClusters(i).Sensors = ClusterMat.Clusters(i).Sensors;
if isfield(ClusterMat.Clusters(i), 'Label') && ~isempty(ClusterMat.Clusters(i).Label)
sClusters(i).Label = ClusterMat.Clusters(i).Label;
end
if isfield(ClusterMat.Clusters(i), 'Color') && ~isempty(ClusterMat.Clusters(i).Color)
sClusters(i).Color = ClusterMat.Clusters(i).Color;
end
if isfield(ClusterMat.Clusters(i), 'Function') && ~isempty(ClusterMat.Clusters(i).Function)
sClusters(i).Function = ClusterMat.Clusters(i).Function;
end
end


116 changes: 116 additions & 0 deletions toolbox/process/functions/process_channel_addcluster.m
@@ -0,0 +1,116 @@
function varargout = process_channel_addcluster( varargin )
% PROCESS_CHANNEL_ADDCLUSTER: Import clusters in the selected channel files.

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2023

eval(macro_method);
end


%% ===== GET DESCRIPTION =====
function sProcess = GetDescription()
% Description the process
sProcess.Comment = 'Import clusters of channels';
sProcess.Category = 'Custom';
sProcess.SubGroup = {'Import', 'Channel file'};
sProcess.Index = 90;
sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/ChannelClusters';
% Definition of the input accepted by this process
sProcess.InputTypes = {'data', 'raw'};
sProcess.OutputTypes = {'data', 'raw'};
sProcess.nInputs = 1;
sProcess.nMinFiles = 1;
% File selection options
SelectOptions = {...
'', ... % Filename
'', ... % FileFormat
'open', ... % Dialog type: {open,save}
'Import clusters...', ... % Window title
'ImportChannel', ... % LastUsedDir: {ImportData,ImportChannel,ImportAnat,ExportChannel,ExportData,ExportAnat,ExportProtocol,ExportImage,ExportScript}
'single', ... % Selection mode: {single,multiple}
'files', ... % Selection mode: {files,dirs,files_and_dirs}
bst_get('FileFilters', 'clusterin'), ... % Available file formats
'ClusterIn'}; % DefaultFormats: {ChannelIn,DataIn,DipolesIn,EventsIn,MriIn,NoiseCovIn,ResultsIn,SspIn,SurfaceIn,TimefreqIn
% Option: Event file
sProcess.options.clusterfile.Comment = 'Cluster file:';
sProcess.options.clusterfile.Type = 'filename';
sProcess.options.clusterfile.Value = SelectOptions;
end


%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess)
Comment = sProcess.Comment;
end


%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputs)
% Get options
ClusterFile = sProcess.options.clusterfile.Value{1};
FileFormat = sProcess.options.clusterfile.Value{2};
% Load input cluster file
sClusters = in_clusters(ClusterFile, FileFormat);
% Get unique channel files
AllChannelFiles = unique({sInputs.ChannelFile});
% Process each channel file
for iFile = 1:length(AllChannelFiles)
Compute(AllChannelFiles{iFile}, sClusters);
end
% Return all the files in input
OutputFiles = {sInputs.FileName};
end


%% ===== ADD CLUSTERS TO CHANNEL FILE =====
function Compute(ChannelFile, sClusters)
% Load file
ChannelFile = file_fullpath(ChannelFile);
ChannelMat = in_bst_channel(ChannelFile);
% Add or replace clusters
for i = 1:length(sClusters)
% If cluster already exists, update it, otherwise create a new entry
if ~isfield(ChannelMat, 'Clusters') || isempty(ChannelMat.Clusters)
ChannelMat.Clusters = repmat(db_template('cluster'), 0, 1);
iCluster = 1;
else
iCluster = find(strcmp(sClusters(i).Label, {ChannelMat.Clusters.Label}));
if isempty(iCluster)
iCluster = length(ChannelMat.Clusters) + 1;
end
end
% Copy all the fields
ChannelMat.Clusters(iCluster).Sensors = sClusters(i).Sensors;
ChannelMat.Clusters(iCluster).Label = sClusters(i).Label;
ChannelMat.Clusters(iCluster).Function = sClusters(i).Function;
% Add color if not defined yet
if ~isempty(sClusters(i).Color)
ChannelMat.Clusters(iCluster).Color = sClusters(i).Color;
else
ColorTable = panel_scout('GetScoutsColorTable');
iColor = mod(iCluster-1, length(ColorTable)) + 1;
ChannelMat.Clusters(i).Color = ColorTable(iColor,:);
end
end
% Save modified file
bst_save(ChannelFile, ChannelMat, 'v7');
end

0 comments on commit b57db7e

Please sign in to comment.