60981
Comment:
|
← Revision 155 as of 2024-11-28 12:34:50 ⇥
84564
|
Deletions are marked like this. | Additions are marked like this. |
Line 2: | Line 2: |
'''[TUTORIAL UNDER DEVELOPMENT: NOT READY FOR PUBLIC USE] ''' ''Authors: Francois Tadel, Elizabeth Bock, Sylvain Baillet'' |
''Authors: Francois Tadel, Elizabeth Bock, Matthias Sure, Sylvain Baillet'' |
Line 8: | Line 6: |
Requirements: You need a license for the Matlab environment in order to use these tools and execute custom scripts. If you are running the compiled version of Brainstorm with the MCR library, the only custom code you can run is through the menu File > Matlab console and the process "Run Matlab command". |
|
Line 12: | Line 8: |
{{{#!wiki warning '''Warning''': Never call '''`clear`''', '''`clear functions`''', '''`clear classes`''' or '''`clear all`''' while Brainstorm is running. It will clear variables, functions and classes that are needed by Brainstorm.. }}} |
|
Line 13: | Line 14: |
The easiest way to get started with a new Brainstorm script is to use the script generator, already introduced in the tutorial [[http://neuroimage.usc.edu/brainstorm/Tutorials/PipelineEditor#Saving_a_pipeline|Select files and run processes]]. Select some files in the Process1 or Process2 tabs, select a list of processes, and use the menu '''Generate .m script'''. The example below should work on the the protocol "TutorialIntroduction" created during the introduction tutorials. | The easiest way to get started with a new Brainstorm script is to use the script generator, already introduced in the tutorial [[http://neuroimage.usc.edu/brainstorm/Tutorials/PipelineEditor#Saving_a_pipeline|Select files and run processes]]. Select some files in the Process1 or Process2 tabs, select a list of processes, and use the menu '''Generate .m script'''. The example below should work with the protocol "TutorialIntroduction" created during the introduction tutorials. |
Line 16: | Line 17: |
* Select process '''File > Select files: Recordings''' (do not execute immediately)<<BR>>Subject='''Subject01''', Condition='''[Empty]''', File comment='''Avg: deviant''' (the space is important). <<BR>><<BR>> {{attachment:start1.gif||height="334",width="400"}} * This process selects all the recordings with a comment including the string "Avg: deviant", from all the folders in Subject01 (except "Intra-subject" and "Common files"). We expect to get two files: the averages of the deviant condition for both runs. * Add process '''Pre-process > Band-pass filter''': Lower cutoff='''0Hz''', Upper cutoff='''30Hz''', Mirror. <<BR>> '''Add process File > Save snapshot''': Recordings time series, Sensors=MEG. <<BR>> <<BR>> {{attachment:start2.gif||height="294",width="518"}} |
* Select process '''File > Select files: Recordings''' (do not execute immediately)<<BR>>Subject='''Subject01''', Condition='''[Empty]''', File name='''Avg: deviant''' (the space is important). <<BR>><<BR>> {{attachment:start1.gif||width="400",height="334"}} * This process selects all the recordings with a comment including the string "Avg: deviant", from all the folders in Subject01 (except for "Intra-subject" and "Common files"). We expect to get two files: the averages of the deviant condition for both runs. * Add process '''Pre-process > Band-pass filter''': Lower cutoff='''0Hz''', Upper cutoff='''30Hz''', 60dB. <<BR>> '''Add process File > Save snapshot''': Recordings time series, Sensors=MEG. <<BR>> <<BR>> {{attachment:start2.gif||width="536",height="353"}} |
Line 20: | Line 23: |
* Do not run the pipeline, select the menu '''Generate .m script '''instead. It saves a new .m file and opens it in the Matlab editor. Close the pipeline editor window and look at the script.<<BR>><<BR>> {{attachment:start3.gif||height="272",width="673"}} | * Do not run the pipeline, select the menu '''Generate .m script '''instead. It saves a new .m file and opens it in the Matlab editor. Close the pipeline editor window and look at the script.<<BR>><<BR>> {{attachment:start3.gif||width="673",height="272"}} |
Line 37: | Line 41: |
* '''sFiles''': The list of files in input. Currently empty because we did not select anything in input in the Process1 list. If we had selected files, it would contain a cell array of strings with relative file paths. * '''SubjectNames''': List of subject names that are used in the script. Most of the times, the generated script would contain only one entry, but it written as a cell array so that it is easier to extend it to multiple subjects with a loop (described further in this tutorial). |
* '''sFiles''': The list of files in input. Currently empty because we did not select anything in the Process1 list. If we had selected files, it would contain a cell array of strings with relative file paths. * '''SubjectNames''': List of subject names that are used in the script. Most of the times, the generated scripts contain only one entry, but it is written as a cell array to make it easier to extend to multiple subjects with a loop (described further in this tutorial). |
Line 44: | Line 49: |
Start a new report of activity: Clears all the previous logs and gets ready to record new messages. The report will collect all the messages that are generated during the execution of the script by the various processes. You can explicitely add screen captures and additional messages to the current report with the function bst_report. This report will remain open until the function bst_report('Start') is called again. To display the current report, use the menu [[http://neuroimage.usc.edu/brainstorm/Tutorials/PipelineEditor#Report_viewer|File > Report viewer]]. The syntax '''function_name'''(''''SubFunction'''', arguments) is used a lot in Brainstorm, to call a subfunction available inside a .m file. This line above calls the function Report() in the file brainstorm3/toolbox/process/bst_report.m. This is made possible with the use of the short script "macro_methodcall" you will see at the top of many files. Many of the Brainstorm .m files are actually libraries of functions, rather than simple "scripts" or "functions". |
Starts a new report of activity: Clears all the previous logs and gets ready to record new messages. The report will collect all the messages that are generated during the execution of the script by the various processes. You can explicitly add screen captures and additional messages to the current report with the function bst_report. This report will remain open until the function bst_report('Start') is called again. To display the current report, use the menu [[http://neuroimage.usc.edu/brainstorm/Tutorials/PipelineEditor#Report_viewer|File > Report viewer]]. The syntax '''function_name'''(''''SubFunction'''', arguments) is used a lot in Brainstorm: it calls a subfunction available inside a .m file. This line above calls the function Start() in the file brainstorm3/toolbox/process/bst_report.m. This is made possible with the use of the short script "macro_method". Many of the Brainstorm .m files are actually libraries of functions, rather than simple "scripts" or "functions". |
Line 61: | Line 66: |
'sensortypes', 'MEG', ... | |
Line 63: | Line 69: |
'mirror', 1, ... 'sensortypes', 'MEG', ... |
'attenuation', 'strict', ... % 60dB 'mirror', 0, ... 'useold', 0, ... |
Line 80: | Line 87: |
* '''process_name''': String indicating the function corresponding to the process to execute. To know from the pipeline editor what is the path to the process function: hover your mouse over the selected process, as illustrated in [[http://neuroimage.usc.edu/brainstorm/Tutorials/PipelineEditor#Saving_a_pipeline|this tutorial]]. * '''input_files_A''': List of input files in Process1, or FilesA in Process2. It can be a cell array of files names (full path, or relative path from the protocol folder), or an array of structures describing the files in the database (returned by a previous call to bst_process). * '''input_files_B''': Empty for Process1, or FilesB in Process2. Cell array of strings or array of struct. * '''options_list''': Pairs of (option_name, option_values), one for each option of the process. * '''output_files''': Array of structures describing the files in output of the process. If the process created new files, this variable contains the new files. If the process didn't create new files or was modifying exiting files, most of the time this variable would contain the same files as the input list. |
* '''process_name''': String indicating the function corresponding to the process to execute. To know from the pipeline editor what is the path to the process function: hover your mouse over the selected process, as illustrated in [[http://neuroimage.usc.edu/brainstorm/Tutorials/PipelineEditor#Plugin_structure|this tutorial]]. * '''input_files_A''': List of input files in Process1, or FilesA in Process2. It can be a cell array of file names (full path, or relative path from the protocol folder), or an array of structures describing the files in the database (returned by a previous call to bst_process). * '''input_files_B''': Empty for Process1, or FilesB in Process2. Cell array of strings, or array of struct. * '''options_list''': Pairs of (option_name, option_values), one pair for each option of the process. * '''output_files''': Array of structures describing the files in output of the process. If the process created new files, this variable points at them. If the process didn't create new files or was modifying exiting files, this variable points at the input files. |
Line 91: | Line 102: |
Closes the current report and saves it in the user Brainstorm report folder ($HOME/.brainstorm/reports). These reports are in .mat format and contain all the information necessary to re-run the execution exactly in the same way, but they not easy to read. | Closes the current report and saves it in the user report folder ($HOME/.brainstorm/reports). These reports are in .mat format and contain all the information necessary to re-run the execution exactly in the same way, but they are not easy to read. |
Line 103: | Line 114: |
This function exports the report in readable format, as an HTML that includes all the screen captures embedded in it. It is disabled by default. If you want to use this feature: remove the "%" at the beginning of the line, and define the variable ExportDir. ExportDir must be a string that defines where to save the HTML report. It can be wither the absolute path to a HTML file (eg. 'C:\Users\myuser\Documents\report_example.html') or just a folder (eg. 'C:\Users\myuser\Documents'). If you enter only a path to a folder, a default file name including the protocol name and a date tag is generated (report_ProtocolName_YYMMDD_HHMMSS.html). |
This function exports the report in readable format, as an HTML file that includes all the screen captures embedded in it. It is disabled by default. If you want to use this feature: remove the "%" at the beginning of the line, and define the variable ExportDir. ExportDir must be a string that defines where to save the HTML report. It can be either the absolute path to a HTML file (eg. 'C:\Users\myuser\Documents\report_example.html') or just a folder (eg. 'C:\Users\myuser\Documents'). If you enter only a path to a folder, a default file name including the protocol name and a date tag is generated (report_ProtocolName_YYMMDD_HHMMSS.html). {{{ % bst_report('Email', ReportFile, username, to, subject, isFullReport); }}} Sends the report by email, as explained in this later section: [[https://neuroimage.usc.edu/brainstorm/Tutorials/Scripting#Send_report_by_email|Send report by email]]. {{{ % gui_brainstorm('EmptyTempFolder'); }}} Delete all the files in the [[https://neuroimage.usc.edu/brainstorm/Tutorials/Scripting#Temporary_files|Brainstorm temporary folder]]. |
Line 108: | Line 129: |
This script you generated is like any Matlab script: you can edit it, rename the variables, add tests and loops, etc. The first important thing to understand is how to edit the options or change the inputs/outputs of a process. The script generator uses only one variable for all the file lists ('''sFiles''') and the output process is always the input of the following process. This is usually too restrictive to write a full analysis script: we commonly need to have multiple lists of files or to run two different operations on the same file. | The script you generated is like any Matlab script: you can edit it, rename the variables, add tests and loops, etc. The first important thing to understand is how to edit the options and change the inputs/outputs. The script generator uses only one variable for all the file lists ('''sFiles''') and the output process is always the input of the following process. This is usually too restrictive to write a full analysis script: we commonly need to have multiple lists of files or to run two different operations on the same file. |
Line 121: | Line 142: |
There is no need to set the parameter sFiles, because there is no input, you can replace it with an empty matrix []. You can therefore remove the line "sFiles = [];". We can also rename the output variable "sAvgData", to be a bit more specific. | There is no need to set the parameter sFiles, because there is no input, you can replace it with an empty matrix []. You can therefore remove the line "sFiles = [];". We can also rename the output variable "sAvgData", to be more specific. |
Line 133: | Line 154: |
Edit low-pass filter call: Change the input to sAvgData and the output to sAvgDataLow, this way you will be able to keep track of the two files if you need to use them independently later in the script. | Edit the call to the low-pass filter: Change the input to sAvgData and the output to sAvgDataLow, this way you will be able to keep track of the two files if you need to use them independently later. |
Line 137: | Line 158: |
'sensortypes', 'MEG', ... | |
Line 139: | Line 161: |
'sensortypes', 'MEG'); }}} Edit the call to the snapshot process: Change the input to sAvgDataLow, and remove the output parameter (we are not expecting output from it). |
'attenuation', 'strict'); % 60dB }}} Edit the call to the snapshot process: Change the input to sAvgDataLow, and remove the output parameter (we are not expecting any output file from it). |
Line 148: | Line 170: |
Replace the last lines with the following, to export the report instead of opening in the report viewer (edit the file path to point at your own user folder instead). | Replace the last lines with the following code, in order to export the report instead of opening in the report viewer (edit the file path to point at your own user folder instead). |
Line 152: | Line 174: |
bst_report('Export', ReportFile, 'C:\Users\franc\Documents\report_test.html'); | bst_report('Export', ReportFile, 'C:\Users\myuser\Documents\report_test.html'); |
Line 164: | Line 186: |
The variable SubjectNames is not defined yet: Execute the first two lines "SubjectNames = {'Subject01'}", then try again. You should now have a new variable in your Matlab workspace, which points at the two average files. Type "sAvgData(1)" in your command window to display the first element: | The variable SubjectNames is not defined yet: Execute the first lines "SubjectNames = {'Subject01'}", then try again. You should now have a new variable in your Matlab workspace, which points at the two average files. Type "sAvgData(1)" in your command window to display the first element: |
Line 171: | Line 193: |
FileName: 'Subject01/S01_AEF_20131218_01_600Hz_notch/data_deviant_average_160513_1329.mat' | FileName: '..._01_600Hz_notch/data_deviant_average_160513_1329.mat' |
Line 179: | Line 201: |
ChannelTypes: {'ADC A' 'ADC V' 'DAC' 'ECG' 'EOG' 'FitErr' 'HLU' 'MEG' 'MEG REF' ...} | ChannelTypes: {'ADC A' 'ADC V' 'DAC' 'ECG' 'EOG' 'MEG' 'MEG REF' ...} |
Line 184: | Line 206: |
Line 185: | Line 208: |
* '''Comment''': Comment field of the file (what is displayed in the database explorer) * '''Condition''': Name of the condition/folder in which the file is located * '''SubjectFile''': Relative path to the subject file (brainstormsubject.mat) * '''SubjectName''': Name of the subject * '''DataFile''': For types 'results' or 'timefreq', path of the parent file in the database explorer * '''ChannelFile''': Relative path to the channel file * '''ChannelTypes''': Cell array of channel types available for the input file |
* '''Comment''': Comment/Name field of the file (what is displayed in the database explorer). * '''Condition''': Name of the condition/folder in which the file is located. * '''SubjectFile''': Relative path to the subject file (brainstormsubject.mat). * '''SubjectName''': Name of the subject (must be the same as the folder name). * '''DataFile''': For types 'results' or 'timefreq', path of the parent file in the database explorer. * '''ChannelFile''': Relative path to the channel file. * '''ChannelTypes''': Cell array of channel types available for the input file. |
Line 194: | Line 224: |
To help you navigate in the Brainstorm code, here are some naming conventions: | To help you navigate in the Brainstorm code, here are some, naming conventions (aka nomenclature): |
Line 197: | Line 227: |
Line 198: | Line 229: |
Line 199: | Line 231: |
* '''Graphic handles''': Matlab graphic objects, name starting with a "h" (eg. hFig, hAxes, hLine, hText). |
|
Line 200: | Line 235: |
Line 201: | Line 237: |
* '''Graphic handles''': Matlab graphic objects, name starting with a "h" (eg. hFig, hAxes, hLine, hText). | |
Line 218: | Line 253: |
'sensortypes', 'MEG', ... | |
Line 220: | Line 256: |
'sensortypes', 'MEG'); | 'attenuation', 'strict'); % 60dB |
Line 236: | Line 272: |
At the end of the execution, nothing happens, because we indicated we wanted to just export the report instead of opening it. To check out the report of execution: use the menu '''File > Report viewer''' from the Brainstorm window, or open the '''report_test.html''' that was saved somewhere on your computer. On this page, you can get review everything that happened in the script: when it was executed, how long it took, what are the processes that were executed, some produced messages (two files were selected with the first process), and at the end you have the screen captures taken by process_snapshot. . {{attachment:report1.gif||height="455",width="593"}} |
At the end of the execution, nothing happens, because we indicated we wanted to export the report instead of opening it. To check out the report of execution: use the menu '''File > Report viewer''' from the Brainstorm window, or open the file '''report_test.html''' that was saved somewhere on your computer. In this page, you can review everything that happened in the script: when it was executed, how long it took, what are the processes that were executed, some additional messages (two files were selected with the first process) and the screen captures taken by process_snapshot. . {{attachment:report1.gif||width="593",height="455"}} |
Line 243: | Line 279: |
If you execute the script again, it will not behave as expected anymore. The selection process we used assumes that there is only one file per folder with a comment that includes "Avg: deviant". This is not the case anymore after the execution, because the low-pass filtered files also contain the same tags. The execution of the first process of the script now '''returns 4 files'''. | If you execute the script again, it will not behave as expected anymore. The selection process we used assumes that there is only one file per folder with a name that includes "Avg: deviant". This is not the case anymore after the execution, because the low-pass filtered files also contain the same string. The execution of the first process of the script now '''returns 4 files'''. |
Line 259: | Line 295: |
* Select process '''File > Select files: By tag''': Search="low", Search the comment, '''Ignore '''the files. <<BR>><<BR>> {{attachment:select_tag.gif||height="252",width="261"}} | * Select process '''File > Select files: By tag''': Search="low", Search the file name, '''Ignore '''the files. <<BR>><<BR>> {{attachment:select_tag.gif}} |
Line 261: | Line 298: |
Line 270: | Line 308: |
% Process: Ignore file comments with tag: low | % Process: Ignore file names with tag: low |
Line 273: | Line 311: |
'search', 2, ... % Search the file comments | 'search', 2, ... % Search the file names |
Line 276: | Line 314: |
With this last modification, your script is more robust. It can be executed multiple times without completely changing its behavior. When you are fetching files from the database using comment tags or file names, always pay attention to this aspect: the database grows and the further you go, the more specific your requests have to be. A good practice can be to tag explicitely the output files your script generates if you need to fetch them later. You can use the process '''File > Add tag''' and '''File > Set comment'''. . {{attachment:add_tag.gif||height="203",width="456"}} |
With this last modification, your script is more robust. It can be executed multiple times without completely changing its behavior. When you are fetching files from the database using tags or file names, always pay attention to this aspect: the database grows and the further you go, the more specific your requests have to be. A good practice can be to tag explicitly the output files your script generates if you need to fetch them later. You can use the process '''File > Add tag''' and '''File > Set name'''. . {{attachment:add_tag.gif}} |
Line 283: | Line 321: |
Brainstorm must be running in the background for these scripts to run properly. The interface doesn't have to be visible on the screen, but the database engine must be running for processing requests. At the beginning of your script, you can explicitely start or restart Brainstorm. '''brainstorm''': Start Brainstorm with the regular GUI. '''brainstorm nogui''': Start in silent mode, without the GUI, without progress bar, without user interactions. If you want to start Brainstorm only if Brainstorm is not already running, you can use the following code: |
Brainstorm must be running in the background for these scripts to run properly. The interface doesn't have to be visible on the screen, but the database engine must be running for processing requests. At the beginning of your script, you can explicitly start or restart Brainstorm. * '''brainstorm''': Start Brainstorm with the regular GUI. * '''brainstorm nogui''': Start in silent mode. The Java GUI is created but hidden, the progress bar is not shown, all the processes run without user interactions, using default options instead of asking interactively. Visualization figures opened in the processing scripts are still created and made visible. * '''brainstorm server''': Start in headless mode, for execution on a distant computation server that does not have any graphical capability or display attached to it. In this mode, none of the Java GUI elements are created, and the Matlab figures are not displayed. Whether you would be able to add screen captures to your execution reports depends mostly on the Matlab version available of your server (see section below [[https://neuroimage.usc.edu/brainstorm/Tutorials/Scripting#Running_scripts_on_a_cluster|Running scripts on a cluster]]). * '''brainstorm <script.m> <parameters>''': Start Brainstorm in server mode, execute a script and quit Brainstorm. This allows executing any Matlab or Brainstorm script from the command line. This works also from the compiled version of Brainstorm, executed with the free [[https://fr.mathworks.com/products/compiler/matlab-runtime.html|MATLAB Runtime]] (see [[https://neuroimage.usc.edu/brainstorm/Installation|installation instructions]], section "without Matlab"). Add the full path to the script and parameters to the command line: * Windows: '''brainstorm3.bat <script.m>''' '''<parameters>''' * Linux/MacOS: '''brainstorm3.command <MATLABROOT> <script.m>''' '''<parameters>'''<<BR>>''MATLABROOT: Matlab Runtime installation folder, eg. /usr/local/MATLAB_Runtime/v98/'' * '''brainstorm ... local''': Instead of using a user defined brainstorm_db folder, it would use the default folder for the database: ''$HOME/.brainstorm/local_db'' If you want to start Brainstorm only if it is not already running, you can use the following code: |
Line 316: | Line 363: |
Additional command line options: {{{ brainstorm stop % Quit Brainstorm brainstorm update % Download and install latest Brainstorm update (see bst_update) brainstorm reset % Re-initialize Brainstorm database and preferences brainstorm digitize % Digitize electrodes positions and head shape using a Polhemus brainstorm setpath % Add Brainstorm subdirectories to current path brainstorm info % Open Brainstorm website brainstorm forum % Open Brainstorm forum brainstorm license % Display license }}} |
|
Line 317: | Line 376: |
The functions '''bst_get''' and '''bst_set''' allow you to query the database, access the configuration of the software and some display parameters. The complete reference documentation of these functions is included directly in their code (brainstorm3/toolbox/core/bst_get.m and bst_set.m). | The functions '''bst_get''' and '''bst_set''' allow you to query the database, access the configuration of the software and modify some display parameters. The complete reference documentation of these functions is included directly in their code (brainstorm3/toolbox/core/bst_get.m and bst_set.m). |
Line 322: | Line 381: |
>> ProtocolInfo = bst_get('ProtocolInfo') % Returns the configuration of the current protocol | >> ProtocolInfo = bst_get('ProtocolInfo') % Configuration of the current protocol |
Line 331: | Line 390: |
>> isGUI = bst_get('isGUI') % Is the Brainstorm interface currently displayed (0=no, 1=yes) >> bst_set('FlipYAxis', 1) % The new figures will have the Y axis flipped >> bst_set('TSDisplayMode', 'butterfly') % The new figures will use a "butterfly" view >> bst_set('FieldTripDir', FieldTripDir) % Set the path where the FieldTrip toolbox is installed }}} To reference the files in the database, each protocol is subdivided in '''Subjects''' (the "anat" folder and containing the MRI, surfaces and atlases) and '''Studies''' (the "data" folder, including the recordings, channel files and all the analyses). Each Study corresponds to a sub-folder (eg. protocol/data/subject01/run01/), and is attached to only one subject. Subjects and Studies are referenced in the protocol with a unique index most of the time kept in variables named '''iSubject''' and '''iStudy'''. The files available in them are also referenced with indices, with variables such as iAnatomy, iSurface, iData, iHeadModel, iResults, iTimefreq. You can see the indices in the database explorer by hovering your mouse over the nodes of the tree: {{attachment:db_indices.gif||height="64",width="715"}} |
>> isGUI = bst_get('isGUI') % Is the Brainstorm interface displayed (0=no, 1=yes) >> bst_set('FlipYAxis', 1) % New figures will have the Y axis flipped >> bst_set('TSDisplayMode', 'butterfly') % New figures will use a "butterfly" view }}} To reference the files in the database, each protocol is subdivided in '''Subjects''' (the "anat" folder, containing the MRI, surfaces and atlases) and '''Studies''' (the "data" folder, including the recordings, channel files and all the analyses). Each Study corresponds to a sub-folder (eg. protocol/data/subject01/run01/), and is attached to only one subject. Subjects and Studies are referenced in the protocol with a unique index, most of the time kept in variables named '''iSubject''' and '''iStudy'''. The files available in them are also referenced with indices, with variables such as iAnatomy, iSurface, iData, iHeadModel, iResults or iTimefreq. You can see the indices in the database explorer by hovering your mouse over the nodes files and folders: {{attachment:db_indices.gif||width="715",height="64"}} |
Line 352: | Line 410: |
>> sStudy = bst_get('Study', sAvgData(1).iStudy) % Get a study structure with its index | >> sStudy = bst_get('Study', sAvgData(1).iStudy) % Get study struct with its index |
Line 357: | Line 415: |
BrainStormSubject: 'Subject01/brainstormsubject.mat' % Corresponding subject filename Condition: {'S01_AEF_20131218_01_600Hz_notch'} % Name of the condition (=folder) Channel: [1x1 struct] % Channel file for the folder iChannel: [] % Not used anymore Data: [1x242 struct] % List of "data" files in the folder HeadModel: [1x1 struct] % List of head models in the folder iHeadModel: 1 % Default head model (file in green) Result: [1x244 struct] % List of source files and source links Stat: [1x0 struct] % List of statistical results Image: [0x0 struct] % List of images NoiseCov: [1x2 struct] % Noise(1) and data(2) covariance files Dipoles: [0x0 struct] % List of dipole files in the folder Timefreq: [1x247 struct] % List of time-frequency files Matrix: [0x0 struct] % List of "matrix" files in the folder % If 1, the trial is marked as bad in the explorer |
BrainStormSubject: 'Subject01/brainstormsubject.mat' % Subject filename Condition: {'S01_AEF_20131218_01_600Hz_notch'} % Name of the folder Channel: [1x1 struct] % Channel file iChannel: [] % Not used anymore Data: [1x242 struct] % List of "data" files in the folder HeadModel: [1x1 struct] % List of head models in the folder iHeadModel: 1 % Default head model (file in green) Result: [1x244 struct] % List of source files and links Stat: [1x0 struct] % List of statistical results Image: [0x0 struct] % List of images NoiseCov: [1x2 struct] % Noise(1) and data(2) covariance files Dipoles: [0x0 struct] % List of dipole files in the folder Timefreq: [1x247 struct] % List of time-frequency files Matrix: [0x0 struct] % List of "matrix" files in the folder |
Line 376: | Line 433: |
>> sData = sStudy.Data(sAvgData(1).iItem) % Get the structure representing the file from sStudy | % Get the structure representing the file from sStudy >> sData = sStudy.Data(sAvgData(1).iItem) |
Line 378: | Line 436: |
FileName: 'Subject01/S01_AEF_20131218_01_600Hz_notch/data_deviant_average_160513_1329.mat' Comment: 'Avg: deviant (39 files)' % File comment, displayed in the database explorer DataType: 'recordings' % Type of data in the file (mostly "recordings") BadTrial: 0 |
FileName: '..._01_600Hz_notch/data_deviant_average_160513_1329.mat' Comment: 'Avg: deviant (39 files)' % File name DataType: 'recordings' % Type of data in the file BadTrial: 0 % If 1, the trial is marked as bad |
Line 386: | Line 444: |
>> sSubject = bst_get('Subject', sStudy.BrainStormSubject) % Get subject structure from filename | % Get subject structure from filename (lists the files in the subject folder) >> sSubject = bst_get('Subject', sStudy.BrainStormSubject) |
Line 388: | Line 447: |
Name: 'Subject01' % Subject name, same as folder name Comments: '' % Not used much |
Name: 'Subject01' % Subject name, same as folder name Comments: '' % Not used much |
Line 391: | Line 450: |
DateOfAcquisition: '' % Not used anymore Anatomy: [1x1 struct] % List of MRI volumes in the subject folder Surface: [1x9 struct] % List of surfaces in the subject folder iAnatomy: 1 % Default MRI volume (in green, index of array Anatomy) iScalp: 9 % Default head surface (index of array Surface) iCortex: 4 % Default cortex surface (index of array Surface) iInnerSkull: [] % Default inner skull surface (index of array Surface) iOuterSkull: [] % Default outer skull surface (index of array Surface) iOther: [] % Not used anymore UseDefaultAnat: 0 % If 1: Subject uses the default anatomy UseDefaultChannel: 0 % 0=one channel/folder, 1=one/subject, 2=one global |
DateOfAcquisition: '' % Not used anymore Anatomy: [1x1 struct] % List of MRI volumes Surface: [1x9 struct] % List of surfaces iAnatomy: 1 % Index of default MRI iScalp: 9 % Index of default head surface iCortex: 4 % Index of default cortex surface iInnerSkull: [] % Index of default inner skull surface iOuterSkull: [] % Index of default outer skull surface iOther: [] % Not used anymore UseDefaultAnat: 0 % If 1: Use the default anatomy UseDefaultChannel: 0 % 0=one/folder, 1=one/subject, 2=one global |
Line 408: | Line 467: |
Subject01/S01_AEF_20131218_01_600Hz_notch/data_deviant_average_160513_1329.mat | Subject01/..._01_600Hz_notch/data_deviant_average_160513_1329.mat |
Line 413: | Line 472: |
FileName: 'Subject01/S01_AEF_20131218_01_600Hz_notch/brainstormstudy.mat' | FileName: '..._01_600Hz_notch/brainstormstudy.mat' |
Line 443: | Line 502: |
The structures of the different types of files functions were described in the sections "On the hard drive" of the introduction tutorials. Here is a directory of all these sections: | The structures of the different types of files were described in the sections "On the hard drive" of the introduction tutorials. Here is a summary of all these sections: |
Line 446: | Line 505: |
Line 447: | Line 507: |
* '''Power spectrum''': File type "timefreq", [[http://neuroimage.usc.edu/brainstorm/Tutorials/ArtifactsFilter#On_the_hard_drive|Power sprectum and frequency filters]]. | * '''Power spectrum''': File type "timefreq", [[http://neuroimage.usc.edu/brainstorm/Tutorials/ArtifactsFilter#On_the_hard_drive|Power spectrum and frequency filters]]. |
Line 449: | Line 511: |
Line 450: | Line 513: |
Line 451: | Line 515: |
Line 452: | Line 517: |
Line 453: | Line 519: |
Line 454: | Line 521: |
Line 455: | Line 523: |
Line 456: | Line 525: |
Line 457: | Line 527: |
Line 478: | Line 549: |
==== Matlab ==== | |
Line 480: | Line 552: |
. {{attachment:export_matlab.gif||height="118",width="355"}} | . {{attachment:export_matlab.gif||width="355",height="118"}} |
Line 507: | Line 579: |
{{attachment:import_matlab.gif||height="74",width="708"}} | {{attachment:import_matlab.gif||width="708",height="74"}} |
Line 510: | Line 582: |
==== Files ==== In addtion to export to Matlab, files in the database explorer can be exported as files. Right-click on the Deviant average in the database explorer > '''File > Export to File'''. Indicate the filename of the file format for the destination folder. The available file formats depend on the type of file to export. . {{attachment:export_file.gif}} Also, 'data' (raw and imported/trial recordings), 'results' (sources), 'timefreq' (time-frequency, spectrum and connectivity), or 'matrix' (any time series extracted from other files) files can be also exported to files using the process: '''File > Export to file '''. . {{attachment:process_export_file.gif}} |
|
Line 514: | Line 595: |
* '''file_fullpath''': Converts a relative file path from the Brainstorm database to an absolute path. | * '''file_fullpath''': Converts a relative file path to an absolute path. |
Line 516: | Line 598: |
Line 520: | Line 603: |
* '''in_tess_bst'''(TessFile): Read a surface file. * '''in_mri_bst'''(MriFile): Read a MRI/volume file. |
|
Line 521: | Line 608: |
Line 522: | Line 610: |
* '''in_bst_channel'''(ChannelFile): Read a channel file. |
|
Line 523: | Line 614: |
Line 524: | Line 616: |
Line 525: | Line 618: |
Line 526: | Line 620: |
Line 527: | Line 622: |
Line 528: | Line 624: |
Line 529: | Line 626: |
* '''in_fread'''(sFile, ChannelMat, iEpoch, SamplesBounds, iChannels, ImportOptions): Low-level function for reading blocks from continuous files. Require a call to in_fopen first to get the sFile structure. | * '''in_fread'''(sFile, ChannelMat, iEpoch, SamplesBounds, iChannels, ImportOptions): Low-level function for reading blocks from continuous files. Requires a call to in_fopen first to get the sFile structure. |
Line 534: | Line 632: |
* FileName: Must be an absolute path, use in combination with file_fullpath to use relative paths. * FileMat: Must be a valid Brainstorm structure, corresponding to the file type. |
* FileName: Absolute path to a .mat file, use in combination with file_fullpath for relative paths. * FileMat: Valid Brainstorm structure, corresponding to the file type. |
Line 537: | Line 637: |
Line 538: | Line 639: |
Line 539: | Line 641: |
Line 543: | Line 646: |
* '''db_add'''(iStudy/iSubject, FileMat): Add a new file in an anatomy folder or a functional data folder. This function saves and then registers it in the database. FileMat must be a structure, not a filename. You should not save the file manually before calling this function. | * '''db_add'''(iStudy/iSubject, FileMat): Add a new file in an anatomy folder or a functional data folder. This function saves the file and then reloads the folder. FileMat must be a structure, not a filename. You should not save the file manually before calling this function. |
Line 545: | Line 649: |
Line 550: | Line 655: |
Line 551: | Line 657: |
Line 552: | Line 659: |
Line 557: | Line 665: |
Line 558: | Line 667: |
Line 559: | Line 669: |
Line 560: | Line 671: |
* '''db_group_conditions''': Merge two analysis folders from the same subject. | * '''db_group_conditions''': Merge two folders from the same subject. |
Line 562: | Line 675: |
* '''db_rename_subject''': Rename a subject | * '''db_rename_subject''': Rename a subject. |
Line 564: | Line 679: |
* '''db_set_headmodel''': Copy a head model to other folders |
|
Line 565: | Line 683: |
Line 592: | Line 711: |
Line 593: | Line 713: |
Line 594: | Line 715: |
Line 595: | Line 717: |
Line 596: | Line 719: |
Line 597: | Line 721: |
Line 598: | Line 723: |
* '''view_spectrum''': Display power spectrum (PSD or time-frequency files). | * '''view_spectrum''': Display a power spectrum (PSD or time-frequency files). |
Line 600: | Line 727: |
Line 601: | Line 729: |
Line 602: | Line 731: |
Line 603: | Line 733: |
Line 604: | Line 735: |
Line 605: | Line 737: |
Line 606: | Line 739: |
Line 607: | Line 741: |
Line 608: | Line 743: |
Line 609: | Line 745: |
* '''view_surface_matrix''': View a custorm surface (user defines vertices, faces, color, transparency). | * '''view_surface_matrix''': View a custom surface (user defines vertices, faces, color, transparency). |
Line 611: | Line 749: |
Line 612: | Line 751: |
Line 613: | Line 753: |
Line 618: | Line 759: |
Line 619: | Line 761: |
Line 620: | Line 763: |
* panel_record(''''SetDisplayMode'''', hFig, DisplayMode): 'column' or 'butterfly' | * panel_record(''''SetDisplayMode'''', hFig, DisplayMode): 'column' or 'butterfly'. |
Line 632: | Line 777: |
Line 633: | Line 779: |
* panel_surface(''''SetShowSulci'''', hFig, iTess, 1): Enable/Display the display of sulci. * panel_surface(''''SetSurfaceColor'''', hFig, iTess, [1 0 0]): Set surface color. * panel_surface(''''SetSurfaceSmooth'''', hFig, iTess, Smooth, 0): Set amount of smoothing (0-1). * panel_surface(''''SetSurfaceTransparency'''', hFig, iTess, Transp): Set transparency (0-1). * panel_surface(''''SetDataThreshold'''', hFig, iTess, Thresh): Set amplitude threshold. |
* panel_surface(''''SetShowSulci'''', hFig, iTess, 1): Show/hide the sulci (darker color for deeper areas). * panel_surface(''''SetSurfaceColor'''', hFig, iTess, [1 0 0]): Set the surface color. * panel_surface(''''SetSurfaceSmooth'''', hFig, iTess, Smooth, 0): Set the amount of smoothing (0-1). * panel_surface(''''SetSurfaceTransparency'''', hFig, iTess, Transp): Set the surface transparency (0-1). * panel_surface(''''SetDataThreshold'''', hFig, iTess, Thresh): Set the amplitude threshold. |
Line 639: | Line 791: |
* panel_surface(''''SelectHemispheres'''', target): Equivalent to clicking on the buttons in the ''Resect'' panel of the ''Surface'' tab. Possible ''target'' values: 'left', 'right', 'struct', 'none' |
|
Line 643: | Line 798: |
* panel_freq(''''SetCurrentFreq'''', iFreq, 1): Set the the current frequency index (from the Freqs vector) * panel_freq(''''SetCurrentFreq'''', Freq, 0): Set the the current frequency (in Hz). |
* panel_freq(''''SetCurrentFreq'''', iFreq, 1): Set the current frequency index (from the Freqs vector) * panel_freq(''''SetCurrentFreq'''', Freq, 0): Set the current frequency (in Hz). |
Line 647: | Line 803: |
Line 648: | Line 805: |
Line 649: | Line 807: |
Line 650: | Line 809: |
* sOptions.'''RowName''': Controls the signal that is currently displayed (for 'SingleSensor' display mode) | * sOptions.'''RowName''': Controls the signal that is currently displayed (for 'SingleSensor' display mode). |
Line 656: | Line 817: |
Line 657: | Line 819: |
Line 658: | Line 821: |
Line 659: | Line 823: |
Line 660: | Line 825: |
Line 661: | Line 827: |
Line 666: | Line 833: |
Line 667: | Line 835: |
* StatThreshOptions.'''pThreshold''': Current p-Value threshold. | * StatThreshOptions.'''pThreshold''': Current significance level <<HTML(α)>> (ie. p-value threshold) |
Line 669: | Line 839: |
Line 674: | Line 845: |
* '''out_figure_movie''': Save a movie from one or multitple Brainstorm figures. | * '''out_figure_movie''': Save a movie from one or multiple Brainstorm figures. |
Line 676: | Line 849: |
Line 679: | Line 853: |
This section illustrates how to add new files to the database. We will create a sinusoidal signal and save it in a "matrix" file, in a new folder of subject Test. {{{ % Create two sinsuoidal signals (20Hz,30Hz), over 1 second with a sampling frequency of 1000Hz |
This section illustrates how to add new files to the database. We will create a sinusoidal signal and save it in a "matrix" file, in a new folder of the subject "Test". {{{ % Time: 1 second with a sampling frequency of 1000Hz |
Line 684: | Line 858: |
% Generate two sinsuoidal signals (20Hz,30Hz) | |
Line 699: | Line 874: |
There are many options to add a new file to the database, with various levels of requirements. You can call the db_add function (reloads the destination folder, therefore slow if you save many files), save the file in the corresponding folder and reload the protocol, or register the file in the database manually (more complicated but faster). | There are many options to add a new file to the database, with various levels of requirements. You can call the db_add function (reloads the destination folder, therefore slow if you save many files), save the file in the corresponding folder and reload the protocol (slow as well), or register the file in the database manually (more complicated but faster). |
Line 709: | Line 884: |
% Another to generate a unique filename (without a timestamp) MatrixFile = file_unique(bst_fullfile(OutputFolder, 'matrix_test.mat')); % Save file bst_save(MatrixFile, sMat, 'v6'); % Reload the folder in which the new file was saved db_reload_studies(iStudy); }}} '''Option #3: bst_save / '''db_add_data {{{ % Get the full path to the new folder (same folder as the brainstormstudy.mat file for this study) |
% Get the full path to the new folder % (same folder as the brainstormstudy.mat file for this study) |
Line 725: | Line 891: |
% Reload the folder in which the new file was saved db_reload_studies(iStudy); }}} '''Option #3: bst_save / '''db_add_data {{{ % Another way to generate a unique filename (without a timestamp) MatrixFile = file_unique(bst_fullfile(OutputFolder, 'matrix_test.mat')); % Save file bst_save(MatrixFile, sMat, 'v6'); |
|
Line 727: | Line 903: |
% Update tree | % Update the database explorer display |
Line 730: | Line 906: |
{{attachment:newfile.gif}} | . {{attachment:newfile.gif||width="521",height="166"}} |
Line 733: | Line 909: |
A step that commonly requires manual changes is the definition of the event markers. Many time we have to combine external triggers or behavioral information with the existing events. This example illustrates how to load the events, modify them and save them back. For the continuous recordings, the events are saved in the .mat file corresponding to the "Link to raw file" available in the database explorer. These structures contain only meta-data and information created with Brainstorm, the EEG/MEG recordings are available in a separate binary file. First, we need to load this link. {{{ % Right-click on a "Link to raw file" in the database explorer > File > Copy file path to clipboard RawFile = '/.../@rawS01_AEF_20131218_01_600Hz_notch/data_0raw_S01_AEF_20131218_01_600Hz_notch.mat' % Load the "sFile" structure, contained in the .F structure of the link .mat file |
A step that commonly requires manual changes is the definition of the event markers. For example, we have to combine external triggers or behavioral information with the existing events. This example illustrates how to load the events, modify them and save them back. For the continuous recordings, the events are saved in the .mat file corresponding to the "Link to raw file". These structures contain only meta-data and information created with Brainstorm, the EEG/MEG recordings are available in a separate binary file. First, we need to load this link. {{{ % Right-click on a "Link to raw file" in the database explorer % > File > Copy file path to clipboard RawFile = '/.../@rawS01.../data_0raw_S01_..._01_600Hz_notch.mat' % Load the "sFile" structure, contained in the .F structure % of the link file (data_0raw...mat) |
Line 749: | Line 928: |
samples | |
Line 753: | Line 931: |
}}} For example, let's say we want to add 30ms to all the events in the category "button", to compensate for some hardware delay, and create a new event category with the modified timing. We need first to identify what is the '''index of the category "button"''', in this array of 7 events structures. |
channels notes }}} For example, let's say we want to add 30ms to all the events in the category "button" in order to compensate for some hardware delay, and create a new event category with the modified timing. We need first to identify what is the '''index of the category "button"''', in this array of 7 event structures. |
Line 769: | Line 949: |
'standard' 'deviant' 'button' 'cardiac' 'blink' 'bad_1-7Hz' 'bad_40-240Hz' }}} If you wanted to search instead all the events containing a specific tag, for example "bad", you can use the '''cellfun''' function (function that applies the same function sequentially to all the elements in a cell array and concatenates the results) in combination with the '''strfind''' function (search for a substring). The final call to the '''find''' function returns at which indices the list of tags found in the event label is not empty. {{{ >> iEvtBad = find(~cellfun(@(c)isempty(strfind(c,'bad')), {sRaw.F.events.label})) |
'standard' 'deviant' 'button' 'cardiac' 'blink' 'bad_1-7Hz' 'bad_40-240Hz' }}} If you want to search instead all the events containing a specific tag, for example "bad", you can use the '''cellfun''' function (applies the same function sequentially to all the elements in a cell array and concatenates the results) in combination with the '''strfind''' function (search for a substring). The final call to the '''find''' function returns at which indices the list of tags found in the event label is not empty. {{{ >> iEvtBad = find( ~cellfun( @(c)isempty(strfind(c,'bad')), ... {sRaw.F.events.label})) |
Line 778: | Line 960: |
The code below copies the existing event category "button", renames it and add a 30ms offset. Note that '''times''' and '''samples''' field must always match. If you modify one, you must also modify the others (times = samples/sfreq). If you add or remove events, you must adjust the size of the '''epochs''' field (always 1 for most file formats). {{{ % Copy the event category "button" to a new cagory |
The code below copies the existing event category "button", renames it and add a 30ms offset. If you add or remove events, you must adjust the size of the other fields: '''epochs''' (always 1 for most file formats), '''channels''' and '''notes''' (cell array of empty matrices in most cases). {{{ % Copy the event category "button" to a new category |
Line 784: | Line 966: |
% Rename new event to "button_offset" | % Rename the new event to "button_offset" |
Line 787: | Line 969: |
% Compute how many samples correspond to 30ms (18 samples at 600Hz = 30ms) | % How many samples in 30ms (0.030s * 600Hz = 18 samples) |
Line 789: | Line 971: |
% Apply this offset to the events in the "button_offset" category sRaw.F.events(iEvtNew).samples = sRaw.F.events(iEvtNew).samples + offsetSample; % Re-compute the corresponding times sRaw.F.events(iEvtNew).times = sRaw.F.events(iEvtNew).samples ./ sRaw.F.prop.sfreq; % Re-generate an epochs field with only ones (optional here, as we didn't change the number of evt) sRaw.F.events(iEvtNew).epochs = ones(1, size(sRaw.F.events(iEvtNew).times, 2)); |
% Apply offset to the events in the "button_offset" category sRaw.F.events(iEvtNew).times = sRaw.F.events(iEvtNew).times + 0.03 % Round new time values to the nearest sample sRaw.F.events(iEvtNew).times = ... round(sRaw.F.events(iEvtNew).times .* sRaw.F.prop.sfreq) ./ sRaw.F.prop.sfreq; % Re-generate an epochs field with only ones, and empty notes and channels fields % (optional here, as we didn't change the number of evt) nTimes = size(sRaw.F.events(iEvtNew).times, 2); sRaw.F.events(iEvtNew).epochs = ones(1, nTimes); sRaw.F.events(iEvtNew).channels = cell(1, nTimes); sRaw.F.events(iEvtNew).notes = cell(1, nTimes); |
Line 802: | Line 989: |
epochs: [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1] samples: [1x40 double] |
epochs: [1x40 double] |
Line 807: | Line 993: |
}}} The last step is to '''save the modifications''' back to the "Link to raw file". Here the call to '''file_fullpath''' is optional because the variable RawFile was already containing the absolute path to the file. |
channels: {1x40 cell} notes: {1x40 cell} }}} The last step is to '''save the modifications''' back to the "Link to raw file". Here the call to '''file_fullpath''' is optional because the variable RawFile already contains the absolute path to the file. |
Line 816: | Line 1004: |
. {{attachment:edit_events.gif||height="146",width="461"}} | . {{attachment:edit_events.gif||width="461",height="146"}} |
Line 819: | Line 1007: |
The easier way to understand how to use a function is to search the code with the "'''Find files'''" interface in Matlab. Go to the brainstorm3 folder, click on "File files" (or Ctrl+Shift+F), enter the name of function your are looking for in "Find files containing text", Include subfolders, Match case. It will return all the lines that include the string you entered across all the files in Brainstorm. Just double-click on a line to jump to the code in the Matlab editor. {{attachment:find_files.gif||height="307",width="638"}} |
The easier way to understand how to use a function is to search the code with the "'''Find files'''" interface in Matlab. Go to the brainstorm3 folder, click on "Find files" (or Ctrl+Shift+F), enter the name of a function in "Find files containing text", Include subfolders, Match case. It will return all the lines that include the string you entered across all the files in the Brainstorm distribution. Just double-click on a line to jump to the code in the Matlab editor. {{attachment:find_files.gif||width="638",height="307"}} |
Line 825: | Line 1013: |
{{attachment:find_files2.gif||height="95",width="726"}} If you have trouble understanding how to set some input parameters, you can use the '''debugger''' to explore a real use case. Place a breakpoint at the begging of your function of interest (watch [[https://www.youtube.com/watch?v=PdNY9n8lV1Y|this tutorial]] if you don't know how to do it), for in example view_timeseries.m. Then click on the corresponding menus in the Brainstorm interface (eg. double-click on on a data file). When the execution reaches the line you selected, it stops and give you back the commands. You can explore the values in all the variables, modify them, and execute the code step by step (many options available in the Editor tab of Matlab). {{attachment:debugger.gif||height="268",width="509"}} == Additional quality control [TODO] == You cann add in the reports all the information that will help you control the quality of the analysis, or even figures you want to include in publications or clinical reports. Create figures and snapshot them {{{ % Display raster plot hFigRaster = view_erpimage({sFilesEpochs1.FileName}, 'erpimage', 'MEG'); bst_report('Snapshot', hFigRaster, sFilesEpochs1(1).FileName, 'ERP image'); }}} Add messages: warnings and errors: == Loop over subject and runs [TODO] == Creating loops is not supported yet by the script generator, but relatively easy to do from a script without having to know too much about Matlab programming. 1) Fill the cell array SubjectNames with all your subjects names, with the same dimensions as the list of input raw files (sFiles) . 2) Add a "for" loop that includes all the bst_process() calls (leave the bst_report() calls and input definition outside) 3) Inside the loop, replace SubjectNames with SubjectNames{i} and sFiles with sFiles(i) |
{{attachment:find_files2.gif||width="726",height="95"}} If you have trouble understanding how to set some input parameters, you can use the Matlab's '''[[https://www.mathworks.com/help/matlab/matlab_prog/debugging-process-and-features.html | debugger]]''' to explore a real use case. Place a breakpoint at the begging of your function of interest (watch [[https://www.youtube.com/watch?v=PdNY9n8lV1Y|this tutorial]] if you don't know how to do this), for example in view_timeseries.m. Then click on the corresponding menus in the Brainstorm interface (eg. double-click on a data file). When the execution reaches the line you selected, it stops and gives you back the commands. You can explore the values in all the variables, modify them, and execute the code step by step (many options available in the Editor tab of Matlab). {{attachment:debugger.gif||width="509",height="268"}} == Find interface callback functions == If you are looking for the function called by a menu or a button in the interface: * In Matlab, go to the "brainstorm3" folder * Click on the button "Find file" in the ribbon "Editor" of Matlab interface (Ctrl+Shift+F) * Select the option "Include subfolders" * Search for the text of the menu or button you are looking for (eg. 'Set as default'); you can do a case-sensitive or a case-insensitive search (option "Match case") * It gives you the list of all the places where this text appears in the Brainstorm code. In the previous example, in tree_callbacks.m, you’d find a call to function SetDefaultSurf. * Double-click on the corresponding line to jump to the code * Right-click on "SetDefaultSurf" > Open "SetDefaultSurf" * The editor should jump to the code of function SetDefaultSurf(), and there you’d find your call to "db_surface_default" * Right-click on it > Open "db_surface_default" to open the function and read the header that should explain its usage (this example is not very detailed, sorry) * Sometimes it helps to look at other examples of calls to this function: Use the window "Find files" again to search for text "db_surface_default"<<BR>> == Additional quality control == You can add in the reports all the information that may help you control the quality of the analysis, or figures you want to include in publications or clinical reports. The process "File > Save snapshot" lets you save some predefined views, but you can also custom screen captures. The example below shows how to add a "[[http://neuroimage.usc.edu/brainstorm/Tutorials/Epoching#Raster_plot|raster plot]]" for all the deviant trials from Run#01 in the report. {{{ % Get all the deviant trials in Run#01 (the list includes the deviant average) sDeviant = bst_process('CallProcess', 'process_select_files_data', [], [], ... 'subjectname', 'Subject01', ... 'condition', 'S01_AEF_20131218_01_600Hz_notch', ... 'tag', 'deviant'); % Open raster plot hFig = view_erpimage({sDeviant.FileName}, 'erpimage', 'MEG'); % Select the channel MRT34 sOptions = panel_display('GetDisplayOptions'); sOptions.RowName = 'MRT34'; panel_display('SetDisplayOptions', sOptions); % Screen capture of this figure % bst_report('Snapshot', hFig, FileName, Comment, WindowPosition); bst_report('Snapshot', hFig, [], 'ERP image: MRT34', [300 100 600 400]); % Close figure close(hFig); }}} You can also add messages in the reports (information, warning or errors). {{{ % Function call: bst_report(MsgType, sProcess, sInputs, Message) bst_report('Info', [], sDeviant, 'This is an information message.'); bst_report('Warning', [], sDeviant, 'This is a warning.'); bst_report('Error', [], sDeviant, 'This is an error.'); % Open the report viewer to show the current report (not saved yet) bst_report('Open', 'Current'); }}} Report generated with the code above: . {{attachment:report2.gif||width="510",height="349"}} == Loop over subjects == Creating loops is not supported yet by the script generator, but relatively easy to do from a script without knowing too much about Matlab programming. The example below shows how to create a loop over subjects to import their anatomy. The dataset used here is from the tutorial [[Tutorials/VisualSingle|MEG visual: single subject]]. With the Process1 box empty, select the process "Import > Import anatomy > '''Import anatomy folder'''" and generate a script. Simplify if using the guidelines presented in the previous sections: {{{ % Input files SubjectNames = {'sub001'}; RawFiles = {... '/.../Tutorials/sample_group/freesurfer/sub001'}; % Process: Import anatomy folder bst_process('CallProcess', 'process_import_anatomy', [], [], ... 'subjectname', SubjectNames{1}, ... 'mrifile', {RawFiles{1}, 'FreeSurfer'}, ... 'nvertices', 15000); }}} Add the other subject names and corresponding FreeSurfer folders in the script header: {{{ SubjectNames = {'sub001', 'sub002', 'sub003', 'sub004'}; RawFiles = {... '/.../Tutorials/sample_group/freesurfer/sub001', ... '/.../Tutorials/sample_group/freesurfer/sub002', ... '/.../Tutorials/sample_group/freesurfer/sub003', ... '/.../Tutorials/sample_group/freesurfer/sub004'}; }}} Add a [[http://www.mathworks.com/help/matlab/ref/for.html|for loop]] around all the steps to repeat on each subject ("for" before, and "end" after the code), and replace the indices "1" with the loop variable: {{{ % Loop on subjects for iSubject = 1:length(SubjectNames) % Process: Import anatomy folder bst_process('CallProcess', 'process_import_anatomy', [], [], ... 'subjectname', SubjectNames{iSubject}, ... 'mrifile', {RawFiles{iSubject}, 'FreeSurfer'}, ... 'nvertices', 15000); end }}} == Loop over acquisition runs == If you have multiple subjects for which the anatomy is already imported, and multiple runs to process for each subject, you can add two nested for loops to link all the runs to the database in the same script. The dataset used here is from the tutorial [[Tutorials/VisualSingle|MEG visual: single subject]]. With the Process1 box empty, select the process "Import > Import recordings > '''Create link to raw file'''" and generate a script. Simplify if using the guidelines presented in the previous sections: {{{ % Input files SubjectNames = {'sub001'}; RawFiles = {... '/.../sample_group/ds117/sub001/MEG/run_01_sss.fif'}; % Process: Create link to raw file sFileRaw = bst_process('CallProcess', 'process_import_data_raw', [], [], ... 'subjectname', SubjectNames{1}, ... 'datafile', {RawFiles{1}, 'FIF'}, ... 'channelreplace', 0, ... 'channelalign', 0, ... 'evtmode', 'value'); }}} Add the other subject names and all the runs for all the subjects (array of cell arrays) in the script header: {{{ SubjectNames = {'sub001', 'sub002'}; RawFiles = {... {'/.../sample_group/ds117/sub001/MEG/run_01_sss.fif', ... '/.../sample_group/ds117/sub001/MEG/run_02_sss.fif', ... '/.../sample_group/ds117/sub001/MEG/run_03_sss.fif'}, ... {'/.../sample_group/ds117/sub002/MEG/run_01_sss.fif', ... '/.../sample_group/ds117/sub002/MEG/run_02_sss.fif', ... '/.../sample_group/ds117/sub002/MEG/run_03_sss.fif'}}; }}} Add two for loops around the code to repeat on all the runs: {{{ % Loop on subjects for iSubject = 1:length(SubjectNames) % Loop on runs for each subject for iRun = 1:length(RawFiles{iSubject}) % Process: Create link to raw file sFileRaw = bst_process('CallProcess', 'process_import_data_raw', [], [], ... 'subjectname', SubjectNames{iSubject}, ... 'datafile', {RawFiles{iSubjects}{iRun}, 'FIF'}, ... 'channelreplace', 0, ... 'channelalign', 0, ... 'evtmode', 'value'); end end }}} |
Line 855: | Line 1166: |
* '''Anatomical fiducials''': Set NAS/LPA/RPA and compute the MNI transformation for each subject. | * '''Anatomical fiducials''': Set NAS/LPA/RPA and compute the MNI normalization for each subject. |
Line 857: | Line 1169: |
Line 858: | Line 1171: |
Line 859: | Line 1173: |
Line 863: | Line 1178: |
Line 864: | Line 1180: |
Line 865: | Line 1182: |
Line 866: | Line 1184: |
Line 867: | Line 1186: |
Line 868: | Line 1188: |
Line 869: | Line 1190: |
Line 870: | Line 1192: |
Line 871: | Line 1194: |
Line 872: | Line 1196: |
Line 873: | Line 1198: |
Line 875: | Line 1201: |
Line 876: | Line 1203: |
Line 877: | Line 1205: |
Line 878: | Line 1207: |
Line 879: | Line 1209: |
Line 880: | Line 1211: |
Line 882: | Line 1214: |
Line 883: | Line 1216: |
Line 884: | Line 1218: |
Line 885: | Line 1220: |
Line 886: | Line 1222: |
Line 887: | Line 1224: |
Line 888: | Line 1226: |
Line 889: | Line 1228: |
Line 891: | Line 1231: |
Line 892: | Line 1233: |
Line 894: | Line 1236: |
Line 895: | Line 1238: |
Line 897: | Line 1241: |
== Final script == | == Final scripts == |
Line 905: | Line 1249: |
== Temporary files == Some processes need to create temporary files on the hard drive. For example, when epoching MEG/EEG recordings, Brainstorm would first create a temporary folder '''import_yymmdd_hhmmss''', store all the epochs in it, then move them to the database when the epoching process is completed. The name of the temporary folder indicates its creation time (year/month/day_hour_minutes_seconds). The default folder where Brainstorm stores its temporary files is located in the user folder ('''$HOME/.brainstorm/tmp/'''). This can be changed as explained in the introduction tutorial [[https://neuroimage.usc.edu/brainstorm/Tutorials/CreateProtocol#Changing_the_temporary_folder|Create protocol]]. At the end of each process, all the temporary files should be deleted automatically. However, in some cases, the process crashes or is killed before it can delete its temporary files. When starting the Brainstorm interface, users gets offered to delete the remaining temporary files. However, when running Brainstorm only from scripts, these files may never be deleted or made explicitly visible. To avoid these leftover temporary files to pile up indefinitely, it can be useful to add an explicit call to the function below, which deletes all of them. {{{ gui_brainstorm('EmptyTempFolder'); }}} '''Warning''': Do not call this function in scripts that are supposed to be executed in parallel from the same user, as it might delete the temporary files from another session of Brainstorm. == Parallel processing == At the moment, Brainstorm is not capable of safely handling multiple sessions executed on the same computer, from the same user or on the same database folder. We are working on a new version of the databasing system that will support concurrent accesses. Until this is released, you should not have multiple people working at the same time with the same database, or multiple Matlab/Brainstorm instances running in parallel. In some specific cases, however, it is possible to start multiple long processes simultaneously without much risk. For example, starting the BrainSuite MRI segmentation for multiple subjects within the same protocol can make sense. Some recommendations to run this safely: * The current protocol should never be changed * The temporary files from the other processes should not be deleted * Each instance of Matlab/Brainstorm must process a different subject * Reload the database after all the executions are completed Note that parallel processing does not always improve global performances. Many Matlab functions are already optimized to distribute their computation load over multiple CPUs. Running these in parallel from multiple instances of Matlab could be slower and less efficient in terms of memory usage, than running them sequentially from the same instance. Before deciding on running tasks in parallel, unless they are clearly not optimized to work on multiple cores (e.g. FreeSurfer segmentation), you should always run some tests to make sure it improves the global computation time. == Running scripts on a cluster == === With Matlab === Matlab, including the toolboxes you need, needs to be installed on the server and a Brainstorm version needs to be located on the server. Start Brainstorm with the command "'''brainstorm server'''". For the database: the folder "brainstorm_db" needs to be registered at the beginning of the script. You can either upload a protocol folder from your local computer, or create an empty brainstorm_db folder and a new protocol on the distance server. At the end of this initialization phase, you need to have something like the structure below: * '''/home/user/brainstorm3''' * '''/home/user/brainstorm_db/ProtocolName/anat''' * '''/home/user/brainstorm_db/'''ProtocolName/'''data''' Your processing script could start with some of the elements below, depending on what you need: {{{ % Start Brainstorm cd /home/user/brainstorm3; if ~brainstorm('status') brainstorm server end % Path to a Brainstorm database (= a folder that contains one or more Brainstorm protocols) BrainstormDbDir = '/home/user/brainstorm_db'; % Load a new uploaded database (sets BrainstormDbDir and load all the protocols it contains) db_import(BrainstormDbDir); % Alternative: Set the Brainstorm DB folder % (defines where the new protocols are going to be created, but does not load anything) bst_set('BrainstormDbDir', BrainstormDbDir); % Get the protocol index of an existing protocol (already loaded previously in Brainstorm) iProtocol = bst_get('Protocol', ProtocolName); % Create a new protocol if needed if isempty(iProtocol) UseDefaultAnat = 0; UseDefaultChannel = 0; gui_brainstorm('CreateProtocol', ProtocolName, UseDefaultAnat, UseDefaultChannel); end % Delete an existing protocol gui_brainstorm('DeleteProtocol', ProtocolName); }}} Execution: You should check that there is a Matlab license available on the server, and that RAM and disk quotas are large enough for the data you will create while processing. The Matlab version might differ from the version installed on your local computer or toolboxes might be missing, causing some errors. A Brainstorm script can be executed either directly (it needs to start Brainstorm itself, like in the example script above), or started using the syntax '''brainstorm <script.m> <parameters>''' (starts Brainstorm in server mode, executes the script and quit Brainstorm). See sections [[https://neuroimage.usc.edu/brainstorm/Tutorials/Scripting#Starting_Brainstorm|Starting Brainstorm]] and [[https://neuroimage.usc.edu/brainstorm/Tutorials/Scripting#Parameters|Parameters]]. === External Matlab call === To call a Brainstorm script directly from the command line of a terminal and avoid the graphical environment of Matlab to get started, on Linux or MacOS, you can use the syntax: {{{ matlab -nosplash -nodesktop -r "run('/path/to/script.m');" }}} === Without Matlab === Brainstorm scripts can also be executed from the '''compiled version of Brainstorm''', therefore not requiring a Matlab license, only the installation of the free [[https://fr.mathworks.com/products/compiler/matlab-runtime.html|MATLAB Runtime]] (see [[https://neuroimage.usc.edu/brainstorm/Installation|installation instructions]], section "without Matlab"). Add the full path to the script and the parameters to the command line: * Windows: '''brainstorm3.bat <script.m>''' '''<parameters>''' * Linux/MacOS: '''brainstorm3.command <MATLABROOT> <script.m>''' '''<parameters>'''<<BR>>''MATLABROOT: Matlab Runtime installation folder, eg. /usr/local/MATLAB_Runtime/v98/'' * To avoid being asked for "brainstorm_db", add the argument: '''local'''<<BR>>''Example: brainstorm3.command /usr/local/MATLAB/MATLAB_Runtime/v98 main.m '''local''''' In this configuration, Brainstorm is started using the command '''brainstorm server''' and then the script is executed. Therefore, your script should '''not '''start brainstorm again: remove the line "brainstorm server" from the example script in the section above. In this configuration, the Matlab Runtime is not allowed to compile and execute .m scripts as a regular Matlab installation would. To work around this limitation, Brainstorm reads the script.m file and executes its contents with the Matlab '''eval '''function. Therefore this approach does not support the definition of functions or classes within the script.m. The compiled version of Brainstorm can only run code that can be executed from the Matlab Command Window. If you need more flexibility, you need to recompile a modified version of Brainstorm including your custom code ([[https://neuroimage.usc.edu/brainstorm/Tutorials/Scripting#How_to_compile_Brainstorm|see below]]). === Parameters === The syntax of the script changes depending on the input parameters it accepts from the command line. ==== No extra parameters ==== The .m file must have the structure of a simple script, with no additional function definition. Example script.m: {{{ disp(['SCRIPT> Script start.']); sStudy = bst_get('Study'); disp(['SCRIPT> Current study path: ' sStudy.FileName 10]); }}} Execution from the command line: {{{ >> brainstorm 'C:\Users\franc\Downloads\GetStudyPath.m' BST> Starting Brainstorm: BST> ================================= BST> Version: 15-Jun-2022 BST> Deleting old process reports... BST> Loading configuration file... BST> Reading process folder... BST> Loading current protocol... BST> ================================= SCRIPT> Script start. SCRIPT> Current study path: Subject01/S01_AEF_20131218_01_600Hz_notch/brainstormstudy.mat BST> Emptying temporary directory... BST> Brainstorm stopped. }}} ==== Extra command line parameters ==== The .m file must declare a function on its first line. The number and names of parameters are parsed by Brainstorm from this first line, matched with the parameters passed from the command line, then the first line is removed and the rest of the code is executed with '''eval'''. Avoid including the "end" statement for the function, as it would result in more work to identify and remove it from the code. No other functions, sub-functions or classes can be declared in the same .m file. Note that parameters are received as '''char arrays''' in the code: if you are expecting to pass numerical values, then the values must be parsed from the string (eg. with str2num). Example script.m: {{{ function GetStudyPath(iStudy) disp([10 'SCRIPT> Script start.']); sStudy = bst_get('Study', str2num(iStudy)); disp(['SCRIPT> Study path: ' sStudy.FileName 10]); }}} Execution from the command line: {{{ >> brainstorm 'C:\Users\franc\Downloads\GetStudyPath.m' 1 BST> Starting Brainstorm: BST> ================================= BST> Version: 15-Jun-2022 BST> Deleting old process reports... BST> Loading configuration file... BST> Reading process folder... BST> Loading current protocol... BST> ================================= WARNING: This file is a function, trying to convert to a script... SCRIPT> Script start. SCRIPT> Study path: Test/@default_study/brainstormstudy.mat BST> Emptying temporary directory... BST> Brainstorm stopped. }}} == Send report by email == When running some long computation on a distant server, it can be convenient to receive an email when the processing is over. Two solutions for sending yourself an email from Brainstorm: from the pipeline editor, or directly from a script. Use the process '''File > Send report by email''' to send the current execution report: * '''Brainstorm username''': The user name you use to download Brainstorm from the website, or to post messages on the user forum. The email associated with this account is the primary recipient of the email. * '''Send copy to''': Optional, sends a copy to an additional email address. Use this to notify collaborator, or receive the notification on an email address that is not registered on the Brainstorm website. * '''Subject''': Subject of the email. * '''Send full report''': If selected, sends the full HTML execution report as displayed by the Report viewer. Be careful when working with sensitive patient data, as it would send with no encryption the full file names of all the files. If not selected, only the name of the processes and execution times are sent, in plain text. <<BR>><<BR>> {{attachment:email.gif}} Alternatively, use one of the following options (isFullReport can be set to 0 or 1): {{{ % At the end of your script ReportFile = bst_report('Save', sFiles); bst_report('Email', ReportFile, username, to, subject, isFullReport); % Anywhere in your script bst_report('Email', 'current', username, to, subject, isFullReport); }}} == How to compile Brainstorm == Brainstorm can be compiled as a JAR application using the function [[https://github.com/brainstorm-tools/brainstorm3/blob/master/deploy/bst_compile.m|bst_compile.m]]. You would need this in order to execute without Matlab a modified version of Brainstorm. Software requirements: * MATLAB >= 2020a * MATLAB Compiler toolbox * MATLAB Compiler SDK toolbox * OpenJDK 8: * Download: https://adoptium.net/temurin/releases/?version=8 * Install Ubuntu package: sudo apt install openjdk-8-jdk * Set the environment variable '''JAVA_HOME''' to the installation folder of the JDK. * Windows 10: Right-click on "This PC" > Advanced system settings > Environment variables * Linux & MacOS: The syntax depends on your shell, look it up on Google. * From Matlab: setenv('JAVA_HOME', '/path/to/jdk-8.0.../') * Check the environment variable from Matlab: getenv('JAVA_HOME') * The Matlab "bin" folder must be in the system path. Open a terminal and run "mcc", if you get an error message ("command not found"), you should add to the system path the "bin" folder located in the Matlab installation folder before starting Matlab. To compile Brainstorm, run from the command line: * '''brainstorm compile''': Compiles Brainstorm together with many [[https://neuroimage.usc.edu/brainstorm/Tutorials/Plugins|plugins]]: SPM12, FieldTrip, Brain2mesh, Iso2mesh, BrainEntropy, LibSVM, NIRSTORM... At the moment, this works only from a Windows 10 computer (but will be extended later to other OS). * '''brainstorm compile noplugs''': Compile Brainstorm without the extra plugins. This should work on all the operating systems. |
|
Line 909: | Line 1459: |
* Tutorial: [[Java|Java in Brainstorm]] * Forum: [[https://neuroimage.usc.edu/forums/t/out-of-memory-error-when-computing-tf-on-sources-when-previously-it-worked-fine/12166/2|Memory management]] * Forum: [[https://neuroimage.usc.edu/forums/t/running-brainstorm-on-a-cluster/26522|Running Brainstorm on a cluster]] * Forum: [[https://neuroimage.usc.edu/forums/t/how-to-compile-brainstorm/17756|How to compile Brainstorm]] (outdated, see compilation section above instead) |
Tutorial 28: Scripting
Authors: Francois Tadel, Elizabeth Bock, Matthias Sure, Sylvain Baillet
The previous tutorials explained how to use Brainstorm in an interactive way to process one subject with two acquisition runs. In the context of a typical neuroimaging study, you may have tens or hundreds of subjects to process in the same way, it is unrealistic to do everything manually. Some parts of the analysis can be processed in batches with no direct supervision, others require more attention. This tutorial introduces tools and tricks that will help you assemble an efficient analysis pipeline.
Contents
- Starting a new script
- Line by line: Header
- Line by line: Body
- Line by line: Footer
- Simplify the calls
- Evaluate in Matlab
- Naming conventions
- Running the script
- Running the script again
- Starting Brainstorm
- Database requests
- File structures
- Custom processing
- Reference: File manipulation
- Reference: Display functions
- Example: Creating a new file
- Example: Editing events
- Find examples in the code
- Find interface callback functions
- Additional quality control
- Loop over subjects
- Loop over acquisition runs
- How to process an entire study
- Final scripts
- Temporary files
- Parallel processing
- Running scripts on a cluster
- Send report by email
- How to compile Brainstorm
- Additional documentation
Warning: Never call clear, clear functions, clear classes or clear all while Brainstorm is running. It will clear variables, functions and classes that are needed by Brainstorm..
Starting a new script
The easiest way to get started with a new Brainstorm script is to use the script generator, already introduced in the tutorial Select files and run processes. Select some files in the Process1 or Process2 tabs, select a list of processes, and use the menu Generate .m script. The example below should work with the protocol "TutorialIntroduction" created during the introduction tutorials.
- In the Process1 tab, leave the selection box empty and click on [Run]. Instead of selecting the files from the Brainstorm interface, we will select them directly from the database using a script.
Select process File > Select files: Recordings (do not execute immediately)
Subject=Subject01, Condition=[Empty], File name=Avg: deviant (the space is important).
- This process selects all the recordings with a comment including the string "Avg: deviant", from all the folders in Subject01 (except for "Intra-subject" and "Common files"). We expect to get two files: the averages of the deviant condition for both runs.
Add process Pre-process > Band-pass filter: Lower cutoff=0Hz, Upper cutoff=30Hz, 60dB.
Add process File > Save snapshot: Recordings time series, Sensors=MEG.
- This will apply a low-pass filter at 30Hz and save a screen capture of the signals in the report.
Do not run the pipeline, select the menu Generate .m script instead. It saves a new .m file and opens it in the Matlab editor. Close the pipeline editor window and look at the script.
- The script you just generated can be the starting point to your own custom script. The following sections explain line by line how they work and how to edit them.
Line by line: Header
% Script generated by Brainstorm (19-Jul-2016)
All the lines starting with a "%" are comments, they are never executed.
% Input files sFiles = []; SubjectNames = {... 'Subject01'};
These lines define the script inputs:
sFiles: The list of files in input. Currently empty because we did not select anything in the Process1 list. If we had selected files, it would contain a cell array of strings with relative file paths.
SubjectNames: List of subject names that are used in the script. Most of the times, the generated scripts contain only one entry, but it is written as a cell array to make it easier to extend to multiple subjects with a loop (described further in this tutorial).
% Start a new report bst_report('Start', sFiles);
Starts a new report of activity: Clears all the previous logs and gets ready to record new messages. The report will collect all the messages that are generated during the execution of the script by the various processes. You can explicitly add screen captures and additional messages to the current report with the function bst_report. This report will remain open until the function bst_report('Start') is called again. To display the current report, use the menu File > Report viewer.
The syntax function_name('SubFunction', arguments) is used a lot in Brainstorm: it calls a subfunction available inside a .m file. This line above calls the function Start() in the file brainstorm3/toolbox/process/bst_report.m. This is made possible with the use of the short script "macro_method". Many of the Brainstorm .m files are actually libraries of functions, rather than simple "scripts" or "functions".
Line by line: Body
% Process: Select data files in: Subject01/*/Avg: deviant sFiles = bst_process('CallProcess', 'process_select_files_data', sFiles, [], ... 'subjectname', SubjectNames{1}, ... 'condition', '', ... 'tag', 'Avg: deviant', ... 'includebad', 0, ... 'includeintra', 0, ... 'includecommon', 0); % Process: Low-pass:30Hz sFiles = bst_process('CallProcess', 'process_bandpass', sFiles, [], ... 'sensortypes', 'MEG', ... 'highpass', 0, ... 'lowpass', 30, ... 'attenuation', 'strict', ... % 60dB 'mirror', 0, ... 'useold', 0, ... 'overwrite', 0); % Process: Snapshot: Recordings time series sFiles = bst_process('CallProcess', 'process_snapshot', sFiles, [], ... 'target', 5, ... % Recordings time series 'modality', 1, ... % MEG (All) 'orient', 4, ... % bottom 'time', 0.11, ... 'contact_time', [0, 0.1], ... 'contact_nimage', 12, ... 'threshold', 20, ... 'Comment', 'Run');
You will find one block per process you selected in the pipeline editor. They all have the same syntax:
output_files = bst_process('CallProcess', process_name, input_files_A, input_files_B, options_list);
process_name: String indicating the function corresponding to the process to execute. To know from the pipeline editor what is the path to the process function: hover your mouse over the selected process, as illustrated in this tutorial.
input_files_A: List of input files in Process1, or FilesA in Process2. It can be a cell array of file names (full path, or relative path from the protocol folder), or an array of structures describing the files in the database (returned by a previous call to bst_process).
input_files_B: Empty for Process1, or FilesB in Process2. Cell array of strings, or array of struct.
options_list: Pairs of (option_name, option_values), one pair for each option of the process.
output_files: Array of structures describing the files in output of the process. If the process created new files, this variable points at them. If the process didn't create new files or was modifying exiting files, this variable points at the input files.
Line by line: Footer
% Save and display report ReportFile = bst_report('Save', sFiles);
Closes the current report and saves it in the user report folder ($HOME/.brainstorm/reports). These reports are in .mat format and contain all the information necessary to re-run the execution exactly in the same way, but they are not easy to read.
The parameter "sFiles" is optional, it indicates what are the files that are considered as the final results of the script. You can remove it without breaking your script: ReportFile = bst_report('Save');
bst_report('Open', ReportFile);
Opens the report viewer to display what happened during the execution. This is equivalent to using the menu File > Report viewer. You can comment this line (ie. add a "%" at the beginning of the line) if you don't want to show the report at the end of the execution.
% bst_report('Export', ReportFile, ExportDir);
This function exports the report in readable format, as an HTML file that includes all the screen captures embedded in it. It is disabled by default. If you want to use this feature: remove the "%" at the beginning of the line, and define the variable ExportDir.
ExportDir must be a string that defines where to save the HTML report. It can be either the absolute path to a HTML file (eg. 'C:\Users\myuser\Documents\report_example.html') or just a folder (eg. 'C:\Users\myuser\Documents'). If you enter only a path to a folder, a default file name including the protocol name and a date tag is generated (report_ProtocolName_YYMMDD_HHMMSS.html).
% bst_report('Email', ReportFile, username, to, subject, isFullReport);
Sends the report by email, as explained in this later section: Send report by email.
% gui_brainstorm('EmptyTempFolder');
Delete all the files in the Brainstorm temporary folder.
Simplify the calls
The script you generated is like any Matlab script: you can edit it, rename the variables, add tests and loops, etc. The first important thing to understand is how to edit the options and change the inputs/outputs. The script generator uses only one variable for all the file lists (sFiles) and the output process is always the input of the following process. This is usually too restrictive to write a full analysis script: we commonly need to have multiple lists of files or to run two different operations on the same file.
Let's consider the first process call, which selects the averages for the Deviant condition in both runs.
sFiles = bst_process('CallProcess', 'process_select_files_data', sFiles, [], ... 'subjectname', SubjectNames{1}, ... 'condition', '', ... 'tag', 'Avg: deviant', ... 'includebad', 0, ... 'includeintra', 0, ... 'includecommon', 0);
There is no need to set the parameter sFiles, because there is no input, you can replace it with an empty matrix []. You can therefore remove the line "sFiles = [];". We can also rename the output variable "sAvgData", to be more specific.
sAvgData = bst_process('CallProcess', 'process_select_files_data', [], [], ...
You can omit all the options that are not defined, not used, or kept to their default values:
sAvgData = bst_process('CallProcess', 'process_select_files_data', [], [], ... 'subjectname', SubjectNames{1}, ... 'tag', 'Avg: deviant');
Edit the call to the low-pass filter: Change the input to sAvgData and the output to sAvgDataLow, this way you will be able to keep track of the two files if you need to use them independently later.
sAvgDataLow = bst_process('CallProcess', 'process_bandpass', sAvgData, [], ... 'sensortypes', 'MEG', ... 'highpass', 0, ... 'lowpass', 30, ... 'attenuation', 'strict'); % 60dB
Edit the call to the snapshot process: Change the input to sAvgDataLow, and remove the output parameter (we are not expecting any output file from it).
bst_process('CallProcess', 'process_snapshot', sAvgDataLow, [], ... 'target', 5, ... % Recordings time series 'modality', 1); % MEG (All)
Replace the last lines with the following code, in order to export the report instead of opening in the report viewer (edit the file path to point at your own user folder instead).
ReportFile = bst_report('Save'); bst_report('Export', ReportFile, 'C:\Users\myuser\Documents\report_test.html');
Evaluate in Matlab
Select the code for the first process in the Matlab editor, right-click > Evaluate selection (or press F9).
If you haven't executed your script yet, you will get the following error in the Matlab command window:
Undefined variable "SubjectNames" or class "SubjectNames".
The variable SubjectNames is not defined yet: Execute the first lines "SubjectNames = {'Subject01'}", then try again. You should now have a new variable in your Matlab workspace, which points at the two average files. Type "sAvgData(1)" in your command window to display the first element:
>> sAvgData(1) ans = iStudy: 6 iItem: 1 FileName: '..._01_600Hz_notch/data_deviant_average_160513_1329.mat' FileType: 'data' Comment: 'Avg: deviant (39 files)' Condition: 'S01_AEF_20131218_01_600Hz_notch' SubjectFile: 'Subject01/brainstormsubject.mat' SubjectName: 'Subject01' DataFile: '' ChannelFile: 'Subject01/S01_AEF_20131218_01_600Hz_notch/channel_ctf_acc1.mat' ChannelTypes: {'ADC A' 'ADC V' 'DAC' 'ECG' 'EOG' 'MEG' 'MEG REF' ...}
The field "sAvgData(1).FileName" contains the relative path to the to the Deviant average in the first run. This structure sAvgData contains also a lot of information that can be helpful in your script:
iStudy / iItem: Reference of the file in the database (described later in this tutorial).
FileType: 'raw' (continuous files), 'data' (recordings), 'results' (sources), 'timefreq' (time-frequency, spectrum and connectivity), or 'matrix' (any time series extracted from other files).
Comment: Comment/Name field of the file (what is displayed in the database explorer).
Condition: Name of the condition/folder in which the file is located.
SubjectFile: Relative path to the subject file (brainstormsubject.mat).
SubjectName: Name of the subject (must be the same as the folder name).
DataFile: For types 'results' or 'timefreq', path of the parent file in the database explorer.
ChannelFile: Relative path to the channel file.
ChannelTypes: Cell array of channel types available for the input file.
Naming conventions
To help you navigate in the Brainstorm code, here are some, naming conventions (aka nomenclature):
Structures: Name starting with a "s" followed by a capital letter (eg. sFiles, sStudy, sSubject).
Indices: Either loop variables or array indices, name starting with a "i" (eg. iSubject, iStudy, iTime).
Counts: Number of elements in a group, name starting with a "n" (eg. nAvg, nTrials, nSubjects).
Graphic handles: Matlab graphic objects, name starting with a "h" (eg. hFig, hAxes, hLine, hText).
File names: Scripts and functions, only lower case, separation with "_" (eg. process_fft, bst_get).
Sub-functions: Inside a .m file, name starting with a capital, CamelCase (eg. CallProcess, Start).
Running the script
The simplified script looks like this:
% Input files SubjectNames = {'Subject01'}; % Start a new report bst_report('Start'); % Process: Select data files in: Subject01/*/Avg: deviant sAvgData = bst_process('CallProcess', 'process_select_files_data', [], [], ... 'subjectname', SubjectNames{1}, ... 'tag', 'Avg: deviant'); % Process: Low-pass:30Hz sAvgDataLow = bst_process('CallProcess', 'process_bandpass', sAvgData, [], ... 'sensortypes', 'MEG', ... 'highpass', 0, ... 'lowpass', 30, ... 'attenuation', 'strict'); % 60dB % Process: Snapshot: Recordings time series bst_process('CallProcess', 'process_snapshot', sAvgDataLow, [], ... 'target', 5, ... % Recordings time series 'modality', 1); % MEG (All) % Save and display report ReportFile = bst_report('Save'); bst_report('Export', ReportFile, 'C:\Users\franc\Documents\report_test.html');
You have three ways to execute it:
- Select all the lines (Ctrl+A) and evaluate it in Matlab (F9).
- In the Editor toolbar of the Matlab environment, click on the button [Run].
- Save the file, go to this folder with Matlab (or add it to your path) and type the name of the script in the command window (without the ".m" at the end).
At the end of the execution, nothing happens, because we indicated we wanted to export the report instead of opening it. To check out the report of execution: use the menu File > Report viewer from the Brainstorm window, or open the file report_test.html that was saved somewhere on your computer.
In this page, you can review everything that happened in the script: when it was executed, how long it took, what are the processes that were executed, some additional messages (two files were selected with the first process) and the screen captures taken by process_snapshot.
Running the script again
If you execute the script again, it will not behave as expected anymore. The selection process we used assumes that there is only one file per folder with a name that includes "Avg: deviant". This is not the case anymore after the execution, because the low-pass filtered files also contain the same string. The execution of the first process of the script now returns 4 files.
>> sAvgData = bst_process('CallProcess', 'process_select_files_data', [], [], ... 'subjectname', SubjectNames{1}, ... 'tag', 'Avg: deviant') sAvgData = 1x4 struct array with fields: iStudy iItem ...
In order to exclude the low-pass filtered files from this selection, you can add another process that will refine the selection. Use the script generator again to create a template call for another process, then copy-paste it in your script.
- In Process1: Select any recordings file (we will not run anything, just generate a script).
Select process File > Select files: By tag: Search="low", Search the file name, Ignore the files.
Select the menu Generate .m script (make sure you do not overwrite the script you are currently working on), then close the pipeline editor.
- Copy-paste and edit the call to process_select_tag to your main script.
Now the file selection part of your script should look like this, and should return only two files:
% Process: Select data files in: Subject01/*/Avg: Deviant sAvgData = bst_process('CallProcess', 'process_select_files_data', [], [], ... 'subjectname', SubjectNames{1}, ... 'tag', 'Avg: deviant'); % Process: Ignore file names with tag: low sAvgData = bst_process('CallProcess', 'process_select_tag', sAvgData, [], ... 'tag', 'low', ... 'search', 2, ... % Search the file names 'select', 2); % Ignore the files with the tag
With this last modification, your script is more robust. It can be executed multiple times without completely changing its behavior. When you are fetching files from the database using tags or file names, always pay attention to this aspect: the database grows and the further you go, the more specific your requests have to be.
A good practice can be to tag explicitly the output files your script generates if you need to fetch them later. You can use the process File > Add tag and File > Set name.
Starting Brainstorm
Brainstorm must be running in the background for these scripts to run properly. The interface doesn't have to be visible on the screen, but the database engine must be running for processing requests. At the beginning of your script, you can explicitly start or restart Brainstorm.
brainstorm: Start Brainstorm with the regular GUI.
brainstorm nogui: Start in silent mode. The Java GUI is created but hidden, the progress bar is not shown, all the processes run without user interactions, using default options instead of asking interactively. Visualization figures opened in the processing scripts are still created and made visible.
brainstorm server: Start in headless mode, for execution on a distant computation server that does not have any graphical capability or display attached to it. In this mode, none of the Java GUI elements are created, and the Matlab figures are not displayed. Whether you would be able to add screen captures to your execution reports depends mostly on the Matlab version available of your server (see section below Running scripts on a cluster).
brainstorm <script.m> <parameters>: Start Brainstorm in server mode, execute a script and quit Brainstorm. This allows executing any Matlab or Brainstorm script from the command line. This works also from the compiled version of Brainstorm, executed with the free MATLAB Runtime (see installation instructions, section "without Matlab"). Add the full path to the script and parameters to the command line:
Windows: brainstorm3.bat <script.m> <parameters>
Linux/MacOS: brainstorm3.command <MATLABROOT> <script.m> <parameters>
MATLABROOT: Matlab Runtime installation folder, eg. /usr/local/MATLAB_Runtime/v98/
brainstorm ... local: Instead of using a user defined brainstorm_db folder, it would use the default folder for the database: $HOME/.brainstorm/local_db
If you want to start Brainstorm only if it is not already running, you can use the following code:
if ~brainstorm('status') brainstorm nogui end
To select a specific protocol at the beginning of your script:
ProtocolName = 'TutorialIntroduction'; % Get the protocol index iProtocol = bst_get('Protocol', ProtocolName); if isempty(iProtocol) error(['Unknown protocol: ' ProtocolName]); end % Select the current procotol gui_brainstorm('SetCurrentProtocol', iProtocol);
To delete the protocol and start over:
% Delete existing protocol gui_brainstorm('DeleteProtocol', ProtocolName); % Create new protocol gui_brainstorm('CreateProtocol', ProtocolName, 0, 0);
Additional command line options:
brainstorm stop % Quit Brainstorm brainstorm update % Download and install latest Brainstorm update (see bst_update) brainstorm reset % Re-initialize Brainstorm database and preferences brainstorm digitize % Digitize electrodes positions and head shape using a Polhemus brainstorm setpath % Add Brainstorm subdirectories to current path brainstorm info % Open Brainstorm website brainstorm forum % Open Brainstorm forum brainstorm license % Display license
Database requests
The functions bst_get and bst_set allow you to query the database, access the configuration of the software and modify some display parameters. The complete reference documentation of these functions is included directly in their code (brainstorm3/toolbox/core/bst_get.m and bst_set.m).
Let's start with a few simple examples:
>> ProtocolInfo = bst_get('ProtocolInfo') % Configuration of the current protocol ProtocolInfo = Comment: 'TutorialIntroduction' STUDIES: 'C:\Work\Protocols\TutorialIntroduction\data' SUBJECTS: 'C:\Work\Protocols\TutorialIntroduction\anat' iStudy: 6 UseDefaultAnat: 0 UseDefaultChannel: 0 >> isGUI = bst_get('isGUI') % Is the Brainstorm interface displayed (0=no, 1=yes) >> bst_set('FlipYAxis', 1) % New figures will have the Y axis flipped >> bst_set('TSDisplayMode', 'butterfly') % New figures will use a "butterfly" view
To reference the files in the database, each protocol is subdivided in Subjects (the "anat" folder, containing the MRI, surfaces and atlases) and Studies (the "data" folder, including the recordings, channel files and all the analyses). Each Study corresponds to a sub-folder (eg. protocol/data/subject01/run01/), and is attached to only one subject.
Subjects and Studies are referenced in the protocol with a unique index, most of the time kept in variables named iSubject and iStudy. The files available in them are also referenced with indices, with variables such as iAnatomy, iSurface, iData, iHeadModel, iResults or iTimefreq. You can see the indices in the database explorer by hovering your mouse over the nodes files and folders:
Example: Getting the study structure from the variable sAvgData, defined in the script:
>> sAvgData(1) ans = iStudy: 6 iItem: 1 ... >> sStudy = bst_get('Study', sAvgData(1).iStudy) % Get study struct with its index sStudy = Name: 'S01_AEF_20131218_01_600Hz_notch' FileName: 'Subject01/S01_AEF_20131218_01_600Hz_notch/brainstormstudy.mat' DateOfStudy: '13-May-2016' BrainStormSubject: 'Subject01/brainstormsubject.mat' % Subject filename Condition: {'S01_AEF_20131218_01_600Hz_notch'} % Name of the folder Channel: [1x1 struct] % Channel file iChannel: [] % Not used anymore Data: [1x242 struct] % List of "data" files in the folder HeadModel: [1x1 struct] % List of head models in the folder iHeadModel: 1 % Default head model (file in green) Result: [1x244 struct] % List of source files and links Stat: [1x0 struct] % List of statistical results Image: [0x0 struct] % List of images NoiseCov: [1x2 struct] % Noise(1) and data(2) covariance files Dipoles: [0x0 struct] % List of dipole files in the folder Timefreq: [1x247 struct] % List of time-frequency files Matrix: [0x0 struct] % List of "matrix" files in the folder
Example: Getting the data structure.
% Get the structure representing the file from sStudy >> sData = sStudy.Data(sAvgData(1).iItem) sData = FileName: '..._01_600Hz_notch/data_deviant_average_160513_1329.mat' Comment: 'Avg: deviant (39 files)' % File name DataType: 'recordings' % Type of data in the file BadTrial: 0 % If 1, the trial is marked as bad
Example: Getting the subject structure.
% Get subject structure from filename (lists the files in the subject folder) >> sSubject = bst_get('Subject', sStudy.BrainStormSubject) sSubject = Name: 'Subject01' % Subject name, same as folder name Comments: '' % Not used much FileName: 'Subject01/brainstormsubject.mat' DateOfAcquisition: '' % Not used anymore Anatomy: [1x1 struct] % List of MRI volumes Surface: [1x9 struct] % List of surfaces iAnatomy: 1 % Index of default MRI iScalp: 9 % Index of default head surface iCortex: 4 % Index of default cortex surface iInnerSkull: [] % Index of default inner skull surface iOuterSkull: [] % Index of default outer skull surface iOther: [] % Not used anymore UseDefaultAnat: 0 % If 1: Use the default anatomy UseDefaultChannel: 0 % 0=one/folder, 1=one/subject, 2=one global
Example: Getting the study structure and data index from a file name.
>> DataFile = sAvgData(1).FileName DataFile = Subject01/..._01_600Hz_notch/data_deviant_average_160513_1329.mat >> [sStudy, iStudy, iData] = bst_get('DataFile', DataFile) sStudy = Name: 'S01_AEF_20131218_01_600Hz_notch' FileName: '..._01_600Hz_notch/brainstormstudy.mat' DateOfStudy: '13-May-2016' BrainStormSubject: 'Subject01/brainstormsubject.mat' Condition: {'S01_AEF_20131218_01_600Hz_notch'} Channel: [1x1 struct] iChannel: [] Data: [1x242 struct] HeadModel: [1x1 struct] iHeadModel: 1 Result: [1x244 struct] Stat: [1x0 struct] Image: [0x0 struct] NoiseCov: [1x2 struct] Dipoles: [0x0 struct] Timefreq: [1x247 struct] Matrix: [0x0 struct] iStudy = 6 iData = 1
Many other options are available for searching files in the database with bst_get. We cannot list them all in this page, but you can refer to the code of bst_get.m for more information.
To change parameters or database structures: bst_set.m.
File structures
The structures of the different types of files were described in the sections "On the hard drive" of the introduction tutorials. Here is a summary of all these sections:
Continuous recordings: File type "raw", Event markers.
Imported recordings: File type "data", Import epochs and Average response.
Power spectrum: File type "timefreq", Power spectrum and frequency filters.
Channel files: File type "channel", Channel file / MEG-MRI coregistration.
SSP/ICA projectors: Saved in the channel files, Artifact cleaning with SSP.
Head models: File type "headmodel", Head model.
Noise covariance: File types "noisecov" and "ndatacov", Noise and data covariance matrices.
Source files: File types "results" and "link", Source estimation.
Time-frequency: File type "timefreq", Time-frequency.
Statistics: File types "pdata", "presults", "ptimefreq" and "pmatrix", Statistics.
MRI volumes: File type "subjectimage", Display the anatomy.
Surfaces: File type "tess", Display the anatomy.
Scouts and atlases: Saved in the surface files, Scouts.
Custom processing
In many situations, you will find useful to read the files available in the database, and maybe modify them. The easiest approaches do not require any scripting, we will start by reviewing them quickly.
Process: Run Matlab command
If you want to modify the values saved in a file (eg. the field "F" from a "data" file), the easiest way is probably to use the process1 File > Run Matlab command. It is also available from Process2 in the category "Other".
It loads the files in input and run them through a piece of Matlab code that you can edit freely. It can extend a lot the flexibility of the Brainstorm pipeline manager, providing an easy access to any Matlab function or script.
The corresponding script looks like this:
sFiles = bst_process('CallProcess', 'process_matlab_eval', sFiles, [], ... 'matlab', 'Data = Data.^2;', ... 'sensortypes', 'MEG', ... 'overwrite', 0);
Export/Import with the database explorer
Matlab
Right-click on the Deviant average in the database explorer > File > Export to Matlab > "DataMat".
Data exported as "DataMat" >> DataMat DataMat = ChannelFlag: [340x1 double] ColormapType: [] Comment: 'Avg: deviant (39 files)' DataType: 'recordings' Device: 'CTF' DisplayUnits: [] F: [340x361 double] History: {45x3 cell} Std: [] Time: [1x361 double] nAvg: 39 Events: [1x1 struct]
DataMat is the exact content of the corresponding .mat file, as loaded with Matlab's load() function. Edit some of the fields of this structure from the Matlab command window:
>> DataMat.Comment = 'Test square'; >> DataMat.F = DataMat.F .^ 2;
Now right-click on the folder containing the original file > File > Import from Matlab > DataMat:
If instead, you right-click on the original file and select the menu File > Import from Matlab, it overwrites the selected file instead of creating a new one with the selected structure.
Files
In addtion to export to Matlab, files in the database explorer can be exported as files. Right-click on the Deviant average in the database explorer > File > Export to File. Indicate the filename of the file format for the destination folder. The available file formats depend on the type of file to export.
Also, 'data' (raw and imported/trial recordings), 'results' (sources), 'timefreq' (time-frequency, spectrum and connectivity), or 'matrix' (any time series extracted from other files) files can be also exported to files using the process: File > Export to file .
Reference: File manipulation
Useful functions for manipulating file names and paths (read the code of the functions for help):
file_fullpath: Converts a relative file path to an absolute path.
file_short: Converts an absolute file path to a short path, relative to the current protocol folder.
file_gettype: Returns the type of a file.
Reading files from a script (all the functions take relative paths in input):
in_tess_bst(TessFile): Read a surface file.
in_mri_bst(MriFile): Read a MRI/volume file.
in_bst_data(DataFile): Read an imported epoch.
in_bst_timefreq(TimefreqFile): Read a power spectrum, time-frequency or connectivity file.
in_bst_channel(ChannelFile): Read a channel file.
in_bst_headmodel(HeadmodelFile, ApplyOrient): Read a head model file and apply orientations.
in_bst_results(ResultsFile, LoadFull): Load a source file and optionally reconstruct the full source time series on the fly (ImagingKernel * recordings).
in_bst_matrix(MatrixFile): Read a file with the type "matrix".
in_bst(FileName, TimeWindow): Read any Brainstorm data file with the possibility to load only a specific part of the file. "TimeWindow" is a range of time values in seconds: [tStart, tStop].
bst_process('LoadInputFile', FileName, Target, TimeWindow): The most high-level function for reading data files. "Target" is a string with the list of signal names or types to load.
bst_memory('GetConnectMatrix', TimefreqMat): Rebuild a full connectivity matrix.
in_fopen(DataFile, FileFormat, ImportOptions): Low-level function for opening continuous files.
in_fread(sFile, ChannelMat, iEpoch, SamplesBounds, iChannels, ImportOptions): Low-level function for reading blocks from continuous files. Requires a call to in_fopen first to get the sFile structure.
Saving files:
bst_save(FileName, FileMat, Version, isAppend): Save a file but does not register it in the database.
FileName: Absolute path to a .mat file, use in combination with file_fullpath for relative paths.
FileMat: Valid Brainstorm structure, corresponding to the file type.
Version: Defines which version of the Matlab .mat format is used to store the data:
- 'v6': Fastest option, bigger files, no compression, no files >2Gb
- 'v7': Slower option, compressed, no files >2Gb
- 'v7.3': Much slower than the others, compressed, but only way to save files > 2Gb.isAppend: If set to 1, updates only the fields defined in FileMat, keep the others untouched.
file_unique: Produces a unique file name by adding a number tag to it.
bst_process('GetNewFilename', OutputFolder, BaseFilename): Generate a new unique file name based on the beginning of the file name (eg. BaseFilename='data_average_test_').
Registering new files in the database:
db_add(iStudy/iSubject, FileMat): Add a new file in an anatomy folder or a functional data folder. This function saves the file and then reloads the folder. FileMat must be a structure, not a filename. You should not save the file manually before calling this function.
db_add_data(iStudy, FileName, FileMat): Register in the database a structure FileMat that has already been saved in file FileName. You should call bst_save manually before calling this function.
db_add_data(iStudy, FileName, FileMat, iItem): Overwrites the existing file #iItem.
Reload folders (if you saved or deleted files without registering correctly the modification in the database):
db_reload_studies(iStudies): Reload only the select data folders (aka "studies").
db_reload_conditions(iSubjects): Reload all the data folders for a subject.
db_reload_subjects(iSubjects): Reload the anatomy of the selected subjects.
db_reload_database('current'): Reload the entire protocol (anatomy and functional data).
Other useful database functions:
db_add_condition: Create a new folder in a subject.
db_add_subject: Create a new subject.
db_delete_studies: Delete a list of folders.
db_delete_subjects: Delete a list of subjects.
db_group_conditions: Merge two folders from the same subject.
db_rename_condition: Rename a folder.
db_rename_subject: Rename a subject.
db_set_channel: Set the channel file for a folder.
db_set_headmodel: Copy a head model to other folders
db_set_noisecov: Set the noise/data covariance for a folder, or copy to other folders/subjects.
db_set_template: Copy an anatomy template to a subject or use it as the default anatomy.
Export a file from the database to other file formats (read the comments in the functions for help):
- export_channel
- export_data
- export_events
- export_result
- export_timefreq
- export_matrix
- export_mri
- export_surfaces
- export_protocol: Export a subject or an entire protocol as a .zip file.
Convert Brainstorm structures to FieldTrip structures:
- out_fieldtrip_channel
- out_fieldtrip_data
- out_fieldtrip_timefreq
- out_fieldtrip_headmodel
- out_fieldtrip_results
- out_fieldtrip_matrix
Reference: Display functions
Create new visualization figures:
view_channels: Display sensors in a 3D figure.
view_helmet: Display the inner surface of the MEG helmet in a 3D figure.
view_timeseries: Display a data file as time series.
view_timeseries_matrix: Display a custom matrix as time series.
view_topography: Display a data file as a spatial topography.
view_erpimage: Display multiple data files as an image, signal by signal.
view_timefreq: Open a time-frequency file (various display modes available).
view_spectrum: Display a power spectrum (PSD or time-frequency files).
view_connect: Open a connectivity matrix (various display modes available).
view_matrix: Open a "matrix" file (various display modes available).
view_contactsheet: Create a contact sheet in time or across a volume from an existing figure.
view_noisecov: Display a noise or data covariance file.
view_dipoles: Open a dipoles file (various display modes available).
view_pac: Open PAC results (various display modes available).
view_mri: View a MRI file in the MRI viewer (with or without a functional overlay from a source file).
view_mri_3d: View a MRI file in a 3D figure (with or without a functional overlay from a source file).
view_surface: View a surface.
view_surface_data: View a surface file with a source file as its texture.
view_surface_matrix: View a custom surface (user defines vertices, faces, color, transparency).
view_image_reg: Display a 3D or 4D matrix as an image with time and/or frequency dimensions.
view_struct: Display a structure as with the popup menu "File > View file contents".
script_view_sources: Shortcut script to display source files.
bst_memory('UnloadAll', 'Forced'): Close all the existing figures.
Configure time-series figures:
panel_time('SetCurrentTime', t): Set current time.
panel_record('SetTimeLength', duration): Set the length of the current display page, in seconds.
panel_record('SetStartTime', t): Set the start of the current page, in seconds.
panel_record('SetDisplayMode', hFig, DisplayMode): 'column' or 'butterfly'.
panel_filter('SetFilters', isLowPass, LowPass, isHighPass, HighPass, isSinRem, SinRem, isMirror)
panel_montage('SetCurrentMontage', hFig, MontageName): Change the montage of channels.
bst_figures('SetSelectedRows', SelectedChannels): Set selected channels (cell array of strings).
figure_timeseries('SetTimeSelectionManual', hFig, TimeWindow): Select a time segment.
Configure 3D figures:
figure_3d('SetStandardView', hFig, 'left'): Change camera view (top,bottom,left,right,front,back).
figure_3d('ViewSensors', hFig, isMarkers, isLabels): Enable the view of sensor markers and labels.
panel_surface('SetShowSulci', hFig, iTess, 1): Show/hide the sulci (darker color for deeper areas).
panel_surface('SetSurfaceColor', hFig, iTess, [1 0 0]): Set the surface color.
panel_surface('SetSurfaceSmooth', hFig, iTess, Smooth, 0): Set the amount of smoothing (0-1).
panel_surface('SetSurfaceTransparency', hFig, iTess, Transp): Set the surface transparency (0-1).
panel_surface('SetDataThreshold', hFig, iTess, Thresh): Set the amplitude threshold.
panel_surface('SetSizeThreshold', hFig, iTess, MinSize): Set size threshold (min size slider).
panel_surface('SelectHemispheres', target): Equivalent to clicking on the buttons in the Resect panel of the Surface tab. Possible target values: 'left', 'right', 'struct', 'none'
figure_mri('SetLocation', CsName, hFig, [], xyz): CsName=voxel/mri/scs/mni, xyz=[x,y,z]
Configure time-frequency figures:
panel_freq('SetCurrentFreq', iFreq, 1): Set the current frequency index (from the Freqs vector)
panel_freq('SetCurrentFreq', Freq, 0): Set the current frequency (in Hz).
sOptions = panel_display('GetDisplayOptions'): Get display options selected in the Display tab.
panel_display('SetDisplayOptions', sOptions): Change the options selected in the Display tab.
sOptions.HideEdgeEffects: Controls the checkbox "Hide edge effects" (0 or 1).
sOptions.HighResolution: Controls the checkbox "Smooth display" (0 or 1).
sOptions.RowName: Controls the signal that is currently displayed (for 'SingleSensor' display mode).
sOptions.Function: Controls the display function (magnitude, power, log, phase).
Configure colormaps:
bst_colormaps('SetColormapName', ColormapType, ColormapName): 'jet', 'parula', 'cmap_rbw', ...
bst_colormaps('SetMaxMode', ColormapType, MaxMode): Colorbar range ('global', 'local', 'custom').
bst_colormaps('SetMaxCustom', ColormapType, [], Min, Max): Set a custom colorbar range.
bst_colormaps('SetColormapAbsolute', ColormapType, isAbsolute): Show positive or relative values
bst_colormaps('SetDisplayColorbar', ColormapType, isDisplay): Show colorbar in the figures.
bst_colormaps('RestoreDefaults', ColormapType): Restore the default configuration for a colormap.
ColormapType: anatomy, meg, eeg, sources, stat1, stat2, time, timefreq, connect1, connectn, image
Configure statistical thresholding:
StatThreshOptions = bst_get('StatThreshOptions'): Get display options selected in the Stat tab.
bst_set('StatThreshOptions', StatThreshOptions): Change the options selected in the Stat tab.
StatThreshOptions.pThreshold: Current significance level α (ie. p-value threshold)
StatThreshOptions.Correction: Correction for multiple comparisons ('none', 'fdr', 'bonferroni')
StatThreshOptions.Control: List of dimensions to correct for multiple comparisons (default = [1 2 3])
Export the contents of a figure to a file:
out_figure_image: Screen capture of any Brainstorm figure.
out_figure_movie: Save a movie from one or multiple Brainstorm figures.
out_figure_timefreq: Extract some of the data displayed in a time-frequency figure.
out_figure_timeseries: Extract some of the data displayed in a time series figure.
Example: Creating a new file
This section illustrates how to add new files to the database. We will create a sinusoidal signal and save it in a "matrix" file, in a new folder of the subject "Test".
% Time: 1 second with a sampling frequency of 1000Hz t = 0:0.001:1; % Generate two sinsuoidal signals (20Hz,30Hz) F = [sin(20*2*pi*t); 0.5*sin(30*2*pi*t)]; % Initialize an empty "matrix" structure sMat = db_template('matrixmat'); % Fill the required fields of the structure sMat.Value = F; sMat.Comment = 'Test sinusoids'; sMat.Description = {'Signal #1: 20Hz'; 'Signal #2: 30Hz'}; sMat.Time = t; % Create a new folder "Script" in subject "Test" iStudy = db_add_condition('Test', 'Script'); % Get the corresponding study structure sStudy = bst_get('Study', iStudy);
There are many options to add a new file to the database, with various levels of requirements. You can call the db_add function (reloads the destination folder, therefore slow if you save many files), save the file in the corresponding folder and reload the protocol (slow as well), or register the file in the database manually (more complicated but faster).
Option #1: db_add
OutputFile = db_add(iStudy, sMat);
Option #2: bst_save / db_reload_studies
% Get the full path to the new folder % (same folder as the brainstormstudy.mat file for this study) OutputFolder = bst_fileparts(file_fullpath(sStudy.FileName)); % Get a new unique filename (including a timestamp) MatrixFile = bst_process('GetNewFilename', OutputFolder, 'matrix_test'); % Save file bst_save(MatrixFile, sMat, 'v6'); % Reload the folder in which the new file was saved db_reload_studies(iStudy);
Option #3: bst_save / db_add_data
% Another way to generate a unique filename (without a timestamp) MatrixFile = file_unique(bst_fullfile(OutputFolder, 'matrix_test.mat')); % Save file bst_save(MatrixFile, sMat, 'v6'); % Reference saved file in the database db_add_data(iStudy, MatrixFile, sMat); % Update the database explorer display panel_protocols('UpdateNode', 'Study', iStudy);
Example: Editing events
A step that commonly requires manual changes is the definition of the event markers. For example, we have to combine external triggers or behavioral information with the existing events. This example illustrates how to load the events, modify them and save them back.
For the continuous recordings, the events are saved in the .mat file corresponding to the "Link to raw file". These structures contain only meta-data and information created with Brainstorm, the EEG/MEG recordings are available in a separate binary file. First, we need to load this link.
% Right-click on a "Link to raw file" in the database explorer % > File > Copy file path to clipboard RawFile = '/.../@rawS01.../data_0raw_S01_..._01_600Hz_notch.mat' % Load the "sFile" structure, contained in the .F structure % of the link file (data_0raw...mat) sRaw = in_bst_data(RawFile, 'F'); >> sRaw.F.events ans = 1x7 struct array with fields: label color epochs times reactTimes select channels notes
For example, let's say we want to add 30ms to all the events in the category "button" in order to compensate for some hardware delay, and create a new event category with the modified timing. We need first to identify what is the index of the category "button", in this array of 7 event structures.
% Find the index of the event category "button" iEvtButton = find(strcmpi({sRaw.F.events.label}, 'button')); >> iEvtButton iEvtButton = 3
In the code above, note this special Matlab syntax that allows the concatenation of the values of one field across multiple structures, in an array of structures:
>> {sRaw.F.events.label} ans = 'standard' 'deviant' 'button' 'cardiac' 'blink' 'bad_1-7Hz' 'bad_40-240Hz'
If you want to search instead all the events containing a specific tag, for example "bad", you can use the cellfun function (applies the same function sequentially to all the elements in a cell array and concatenates the results) in combination with the strfind function (search for a substring). The final call to the find function returns at which indices the list of tags found in the event label is not empty.
>> iEvtBad = find( ~cellfun( @(c)isempty(strfind(c,'bad')), ... {sRaw.F.events.label})) iEvtBad = 6 7
The code below copies the existing event category "button", renames it and add a 30ms offset. If you add or remove events, you must adjust the size of the other fields: epochs (always 1 for most file formats), channels and notes (cell array of empty matrices in most cases).
% Copy the event category "button" to a new category iEvtNew = length(sRaw.F.events) + 1; sRaw.F.events(iEvtNew) = sRaw.F.events(iEvtButton); % Rename the new event to "button_offset" sRaw.F.events(iEvtNew).label = 'button_offset'; % How many samples in 30ms (0.030s * 600Hz = 18 samples) offsetSample = round(0.030 .* sRaw.F.prop.sfreq); % Apply offset to the events in the "button_offset" category sRaw.F.events(iEvtNew).times = sRaw.F.events(iEvtNew).times + 0.03 % Round new time values to the nearest sample sRaw.F.events(iEvtNew).times = ... round(sRaw.F.events(iEvtNew).times .* sRaw.F.prop.sfreq) ./ sRaw.F.prop.sfreq; % Re-generate an epochs field with only ones, and empty notes and channels fields % (optional here, as we didn't change the number of evt) nTimes = size(sRaw.F.events(iEvtNew).times, 2); sRaw.F.events(iEvtNew).epochs = ones(1, nTimes); sRaw.F.events(iEvtNew).channels = cell(1, nTimes); sRaw.F.events(iEvtNew).notes = cell(1, nTimes); % Change the event color to yellow (red=1, green=1, blue=0) sRaw.F.events(iEvtNew).color = [1 1 0]; >> sRaw.F.events(iEvtNew) ans = label: 'button_offset' color: [1 1 0] epochs: [1x40 double] times: [1x40 double] reactTimes: [] select: 1 channels: {1x40 cell} notes: {1x40 cell}
The last step is to save the modifications back to the "Link to raw file". Here the call to file_fullpath is optional because the variable RawFile already contains the absolute path to the file.
% Update the sRaw structure to the RawFile file (the last parameter appends to the existing struct) bst_save(file_fullpath(RawFile), sRaw, 'v6', 1);
Open the recordings to make sure your transformation worked the way you expected.
Find examples in the code
The easier way to understand how to use a function is to search the code with the "Find files" interface in Matlab. Go to the brainstorm3 folder, click on "Find files" (or Ctrl+Shift+F), enter the name of a function in "Find files containing text", Include subfolders, Match case. It will return all the lines that include the string you entered across all the files in the Brainstorm distribution. Just double-click on a line to jump to the code in the Matlab editor.
You can use the same interface to find what function is called when you click on a button or menu in the interface. Search for the label or the tooltip of the interface element in the same way. The example below shows how to track what happens when you click on the headmodel popup menu "Check spheres".
If you have trouble understanding how to set some input parameters, you can use the Matlab's debugger to explore a real use case. Place a breakpoint at the begging of your function of interest (watch this tutorial if you don't know how to do this), for example in view_timeseries.m. Then click on the corresponding menus in the Brainstorm interface (eg. double-click on a data file). When the execution reaches the line you selected, it stops and gives you back the commands. You can explore the values in all the variables, modify them, and execute the code step by step (many options available in the Editor tab of Matlab).
Find interface callback functions
If you are looking for the function called by a menu or a button in the interface:
- In Matlab, go to the "brainstorm3" folder
- Click on the button "Find file" in the ribbon "Editor" of Matlab interface (Ctrl+Shift+F)
- Select the option "Include subfolders"
- Search for the text of the menu or button you are looking for (eg. 'Set as default'); you can do a case-sensitive or a case-insensitive search (option "Match case")
It gives you the list of all the places where this text appears in the Brainstorm code. In the previous example, in tree_callbacks.m, you’d find a call to function SetDefaultSurf.
- Double-click on the corresponding line to jump to the code
Right-click on "SetDefaultSurf" > Open "SetDefaultSurf"
The editor should jump to the code of function SetDefaultSurf(), and there you’d find your call to "db_surface_default"
Right-click on it > Open "db_surface_default" to open the function and read the header that should explain its usage (this example is not very detailed, sorry)
Sometimes it helps to look at other examples of calls to this function: Use the window "Find files" again to search for text "db_surface_default"
Additional quality control
You can add in the reports all the information that may help you control the quality of the analysis, or figures you want to include in publications or clinical reports. The process "File > Save snapshot" lets you save some predefined views, but you can also custom screen captures. The example below shows how to add a "raster plot" for all the deviant trials from Run#01 in the report.
% Get all the deviant trials in Run#01 (the list includes the deviant average) sDeviant = bst_process('CallProcess', 'process_select_files_data', [], [], ... 'subjectname', 'Subject01', ... 'condition', 'S01_AEF_20131218_01_600Hz_notch', ... 'tag', 'deviant'); % Open raster plot hFig = view_erpimage({sDeviant.FileName}, 'erpimage', 'MEG'); % Select the channel MRT34 sOptions = panel_display('GetDisplayOptions'); sOptions.RowName = 'MRT34'; panel_display('SetDisplayOptions', sOptions); % Screen capture of this figure % bst_report('Snapshot', hFig, FileName, Comment, WindowPosition); bst_report('Snapshot', hFig, [], 'ERP image: MRT34', [300 100 600 400]); % Close figure close(hFig);
You can also add messages in the reports (information, warning or errors).
% Function call: bst_report(MsgType, sProcess, sInputs, Message) bst_report('Info', [], sDeviant, 'This is an information message.'); bst_report('Warning', [], sDeviant, 'This is a warning.'); bst_report('Error', [], sDeviant, 'This is an error.'); % Open the report viewer to show the current report (not saved yet) bst_report('Open', 'Current');
Report generated with the code above:
Loop over subjects
Creating loops is not supported yet by the script generator, but relatively easy to do from a script without knowing too much about Matlab programming. The example below shows how to create a loop over subjects to import their anatomy. The dataset used here is from the tutorial MEG visual: single subject.
With the Process1 box empty, select the process "Import > Import anatomy > Import anatomy folder" and generate a script. Simplify if using the guidelines presented in the previous sections:
% Input files SubjectNames = {'sub001'}; RawFiles = {... '/.../Tutorials/sample_group/freesurfer/sub001'}; % Process: Import anatomy folder bst_process('CallProcess', 'process_import_anatomy', [], [], ... 'subjectname', SubjectNames{1}, ... 'mrifile', {RawFiles{1}, 'FreeSurfer'}, ... 'nvertices', 15000);
Add the other subject names and corresponding FreeSurfer folders in the script header:
SubjectNames = {'sub001', 'sub002', 'sub003', 'sub004'}; RawFiles = {... '/.../Tutorials/sample_group/freesurfer/sub001', ... '/.../Tutorials/sample_group/freesurfer/sub002', ... '/.../Tutorials/sample_group/freesurfer/sub003', ... '/.../Tutorials/sample_group/freesurfer/sub004'};
Add a for loop around all the steps to repeat on each subject ("for" before, and "end" after the code), and replace the indices "1" with the loop variable:
% Loop on subjects for iSubject = 1:length(SubjectNames) % Process: Import anatomy folder bst_process('CallProcess', 'process_import_anatomy', [], [], ... 'subjectname', SubjectNames{iSubject}, ... 'mrifile', {RawFiles{iSubject}, 'FreeSurfer'}, ... 'nvertices', 15000); end
Loop over acquisition runs
If you have multiple subjects for which the anatomy is already imported, and multiple runs to process for each subject, you can add two nested for loops to link all the runs to the database in the same script. The dataset used here is from the tutorial MEG visual: single subject.
With the Process1 box empty, select the process "Import > Import recordings > Create link to raw file" and generate a script. Simplify if using the guidelines presented in the previous sections:
% Input files SubjectNames = {'sub001'}; RawFiles = {... '/.../sample_group/ds117/sub001/MEG/run_01_sss.fif'}; % Process: Create link to raw file sFileRaw = bst_process('CallProcess', 'process_import_data_raw', [], [], ... 'subjectname', SubjectNames{1}, ... 'datafile', {RawFiles{1}, 'FIF'}, ... 'channelreplace', 0, ... 'channelalign', 0, ... 'evtmode', 'value');
Add the other subject names and all the runs for all the subjects (array of cell arrays) in the script header:
SubjectNames = {'sub001', 'sub002'}; RawFiles = {... {'/.../sample_group/ds117/sub001/MEG/run_01_sss.fif', ... '/.../sample_group/ds117/sub001/MEG/run_02_sss.fif', ... '/.../sample_group/ds117/sub001/MEG/run_03_sss.fif'}, ... {'/.../sample_group/ds117/sub002/MEG/run_01_sss.fif', ... '/.../sample_group/ds117/sub002/MEG/run_02_sss.fif', ... '/.../sample_group/ds117/sub002/MEG/run_03_sss.fif'}};
Add two for loops around the code to repeat on all the runs:
% Loop on subjects for iSubject = 1:length(SubjectNames) % Loop on runs for each subject for iRun = 1:length(RawFiles{iSubject}) % Process: Create link to raw file sFileRaw = bst_process('CallProcess', 'process_import_data_raw', [], [], ... 'subjectname', SubjectNames{iSubject}, ... 'datafile', {RawFiles{iSubjects}{iRun}, 'FIF'}, ... 'channelreplace', 0, ... 'channelalign', 0, ... 'evtmode', 'value'); end end
How to process an entire study
This section proposes a standard workflow for processing a full group study with Brainstorm. It contains the same steps of analysis as the introduction tutorials, but separating what can be done automatically from what should be done manually. This workflow can be adapted to most ERP studies (stimulus-based).
Prototype: Start by processing one or two subjects completely interactively (exactly like in the introduction tutorials). Use the few pilot subjects that you have for your study to prototype the analysis pipeline and check manually all the intermediate stages. Take notes of what you're doing along the way, so that you can later write a script that reproduces the same operations.
Anatomical fiducials: Set NAS/LPA/RPA and compute the MNI normalization for each subject.
Segmentation: Run FreeSurfer/BrainSuite to get surfaces and atlases for all the subjects.
File > Batch MRI fiducials: This menu prompts for the selection of the fiducials for all the subjects and saves a file fiducials.m in each segmentation folder. You will not have to redo this even if you have to start over your analysis from the beginning.
Script: Write a loop that calls the process "Import anatomy folder" for all the subjects.
Alternatives: Create and import the subjects one by one and set the fiducials at the import time. Or use the default anatomy for all the subjects (or use warped templates).
Script #1: Pre-processing: Loop on the subjects and the acquisition runs.
Create link to raw files: Link the subject and noise recordings to the database.
Event markers: Read and group triggers from digital and analog channel, fix stimulation delays
Evaluation: Power spectrum density of the recordings to evaluate their quality.
Pre-processing: Notch filter, sinusoid removal, band-pass filter.
Evaluation: Power spectrum density of the recordings to make sure the filters worked well.
Cleanup: Delete the links to the original files (the filtered ones are copied in the database).
Detect artifacts: Detect heartbeats, Detect eye blinks, Remove simultaneous.
Compute SSP: Heartbeats, Blinks (this selects the first component of each decomposition)
Compute ICA: If you have some artifacts you'd like to remove with ICA (no default selection).
Screenshots: Check the MRI/sensors registration, PSD before and after corrections, SSP.
Export the report: One report per subject, or one report for all the subjects, saved in HTML.
Manual inspection #1:
Check the reports: Information messages (number of events, errors and warnings) and screen captures (registration problems, obvious noisy channels, incorrect SSP topographies).
Mark bad channels: Open the recordings, select the channels and mark them as bad. Or use the process "Set bad channels" to mark the same bad channels in multiple files.
Fix the SSP/ICA: For the suspicious runs: Open the file, adjust the list of blink and cardiac events, remove and recompute the SSP decompositions, manually select the components.
Detect other artifacts: Run the process on all the runs of all the subjects at once (select all the files in Process1 and run the process, or generate the equivalent script).
Mark bad segments: Review the artifacts detected in 1-7Hz and 40-240Hz, keep only the ones you really want to remove, then mark the event categories as bad. Review quickly the rest of the file and check that there are no other important artifacts.
Additional SSP: If you find one type of artifact that repeats (typically saccades and SQUID jumps), you can create additional SSP projectors, either with the process "SSP: Generic" or directly from a topography figure (right-click on the figure > Snapshot> Use as SSP projector).
Script #2: Subject-level analysis: Epoching, averaging, sources, time-frequency.
Importing: Process "Import MEG/EEG: Events" and "Pre-process > Remove DC offset".
Averaging: Average trials by run, average runs by subject (registration problem in MEG).
Noise covariance: Compute from empty room or resting recordings, copy to other folders.
Head model: Compute for each run, or compute once and copy if the runs are co-registered.
Sources: Compute for each run, average across runs and subjects in source space for MEG.
Time-frequency: Computation with Hilbert transform or Morlet wavelets, then normalize.
Screenshots: Check the quality of all the averages (time series, topographies, sources).
Export the report: One report per subject, or one report for all the subjects, saved in HTML.
Manual inspection #2:
Check the reports: Check the number of epochs imported and averaged in each condition, check the screen capture of the averages (all the primary responses should be clearly visible).
Regions of interest: If not using predefined regions from an atlas, define the scouts on the anatomy of each subject (or on the template and then project them to the subjects).
Script #3: Group analysis, ROI-based analysis, etc.
Averaging: Group averages for the sensor data, the sources and the time-frequency maps.
Statistics: Contrast between conditions or groups of subjects.
Regions of interest: Any operation that involve scouts.
Final scripts
The following script from the Brainstorm distribution reproduces the introduction tutorials ("Get started"): brainstorm3/toolbox/script/tutorial_introduction.m - Report: report_TutorialIntroduction.html
For an example of a script illustrating how to create loops, look at the tutorial MEG visual: single subject. brainstorm3/toolbox/script/tutorial_visual_single.m - Report: report_TutorialVisual_sub001.html
Temporary files
Some processes need to create temporary files on the hard drive. For example, when epoching MEG/EEG recordings, Brainstorm would first create a temporary folder import_yymmdd_hhmmss, store all the epochs in it, then move them to the database when the epoching process is completed. The name of the temporary folder indicates its creation time (year/month/day_hour_minutes_seconds).
The default folder where Brainstorm stores its temporary files is located in the user folder ($HOME/.brainstorm/tmp/). This can be changed as explained in the introduction tutorial Create protocol.
At the end of each process, all the temporary files should be deleted automatically. However, in some cases, the process crashes or is killed before it can delete its temporary files. When starting the Brainstorm interface, users gets offered to delete the remaining temporary files. However, when running Brainstorm only from scripts, these files may never be deleted or made explicitly visible. To avoid these leftover temporary files to pile up indefinitely, it can be useful to add an explicit call to the function below, which deletes all of them.
gui_brainstorm('EmptyTempFolder');
Warning: Do not call this function in scripts that are supposed to be executed in parallel from the same user, as it might delete the temporary files from another session of Brainstorm.
Parallel processing
At the moment, Brainstorm is not capable of safely handling multiple sessions executed on the same computer, from the same user or on the same database folder. We are working on a new version of the databasing system that will support concurrent accesses. Until this is released, you should not have multiple people working at the same time with the same database, or multiple Matlab/Brainstorm instances running in parallel.
In some specific cases, however, it is possible to start multiple long processes simultaneously without much risk. For example, starting the BrainSuite MRI segmentation for multiple subjects within the same protocol can make sense. Some recommendations to run this safely:
- The current protocol should never be changed
- The temporary files from the other processes should not be deleted
- Each instance of Matlab/Brainstorm must process a different subject
- Reload the database after all the executions are completed
Note that parallel processing does not always improve global performances. Many Matlab functions are already optimized to distribute their computation load over multiple CPUs. Running these in parallel from multiple instances of Matlab could be slower and less efficient in terms of memory usage, than running them sequentially from the same instance. Before deciding on running tasks in parallel, unless they are clearly not optimized to work on multiple cores (e.g. FreeSurfer segmentation), you should always run some tests to make sure it improves the global computation time.
Running scripts on a cluster
With Matlab
Matlab, including the toolboxes you need, needs to be installed on the server and a Brainstorm version needs to be located on the server. Start Brainstorm with the command "brainstorm server".
For the database: the folder "brainstorm_db" needs to be registered at the beginning of the script. You can either upload a protocol folder from your local computer, or create an empty brainstorm_db folder and a new protocol on the distance server. At the end of this initialization phase, you need to have something like the structure below:
/home/user/brainstorm3
/home/user/brainstorm_db/ProtocolName/anat
/home/user/brainstorm_db/ProtocolName/data
Your processing script could start with some of the elements below, depending on what you need:
% Start Brainstorm cd /home/user/brainstorm3; if ~brainstorm('status') brainstorm server end % Path to a Brainstorm database (= a folder that contains one or more Brainstorm protocols) BrainstormDbDir = '/home/user/brainstorm_db'; % Load a new uploaded database (sets BrainstormDbDir and load all the protocols it contains) db_import(BrainstormDbDir); % Alternative: Set the Brainstorm DB folder % (defines where the new protocols are going to be created, but does not load anything) bst_set('BrainstormDbDir', BrainstormDbDir); % Get the protocol index of an existing protocol (already loaded previously in Brainstorm) iProtocol = bst_get('Protocol', ProtocolName); % Create a new protocol if needed if isempty(iProtocol) UseDefaultAnat = 0; UseDefaultChannel = 0; gui_brainstorm('CreateProtocol', ProtocolName, UseDefaultAnat, UseDefaultChannel); end % Delete an existing protocol gui_brainstorm('DeleteProtocol', ProtocolName);
Execution: You should check that there is a Matlab license available on the server, and that RAM and disk quotas are large enough for the data you will create while processing. The Matlab version might differ from the version installed on your local computer or toolboxes might be missing, causing some errors.
A Brainstorm script can be executed either directly (it needs to start Brainstorm itself, like in the example script above), or started using the syntax brainstorm <script.m> <parameters> (starts Brainstorm in server mode, executes the script and quit Brainstorm). See sections Starting Brainstorm and Parameters.
External Matlab call
To call a Brainstorm script directly from the command line of a terminal and avoid the graphical environment of Matlab to get started, on Linux or MacOS, you can use the syntax:
matlab -nosplash -nodesktop -r "run('/path/to/script.m');"
Without Matlab
Brainstorm scripts can also be executed from the compiled version of Brainstorm, therefore not requiring a Matlab license, only the installation of the free MATLAB Runtime (see installation instructions, section "without Matlab"). Add the full path to the script and the parameters to the command line:
Windows: brainstorm3.bat <script.m> <parameters>
Linux/MacOS: brainstorm3.command <MATLABROOT> <script.m> <parameters>
MATLABROOT: Matlab Runtime installation folder, eg. /usr/local/MATLAB_Runtime/v98/To avoid being asked for "brainstorm_db", add the argument: local
Example: brainstorm3.command /usr/local/MATLAB/MATLAB_Runtime/v98 main.m local
In this configuration, Brainstorm is started using the command brainstorm server and then the script is executed. Therefore, your script should not start brainstorm again: remove the line "brainstorm server" from the example script in the section above.
In this configuration, the Matlab Runtime is not allowed to compile and execute .m scripts as a regular Matlab installation would. To work around this limitation, Brainstorm reads the script.m file and executes its contents with the Matlab eval function. Therefore this approach does not support the definition of functions or classes within the script.m. The compiled version of Brainstorm can only run code that can be executed from the Matlab Command Window. If you need more flexibility, you need to recompile a modified version of Brainstorm including your custom code (see below).
Parameters
The syntax of the script changes depending on the input parameters it accepts from the command line.
No extra parameters
The .m file must have the structure of a simple script, with no additional function definition.
Example script.m:
disp(['SCRIPT> Script start.']); sStudy = bst_get('Study'); disp(['SCRIPT> Current study path: ' sStudy.FileName 10]);
Execution from the command line:
>> brainstorm 'C:\Users\franc\Downloads\GetStudyPath.m' BST> Starting Brainstorm: BST> ================================= BST> Version: 15-Jun-2022 BST> Deleting old process reports... BST> Loading configuration file... BST> Reading process folder... BST> Loading current protocol... BST> ================================= SCRIPT> Script start. SCRIPT> Current study path: Subject01/S01_AEF_20131218_01_600Hz_notch/brainstormstudy.mat BST> Emptying temporary directory... BST> Brainstorm stopped.
Extra command line parameters
The .m file must declare a function on its first line. The number and names of parameters are parsed by Brainstorm from this first line, matched with the parameters passed from the command line, then the first line is removed and the rest of the code is executed with eval. Avoid including the "end" statement for the function, as it would result in more work to identify and remove it from the code. No other functions, sub-functions or classes can be declared in the same .m file.
Note that parameters are received as char arrays in the code: if you are expecting to pass numerical values, then the values must be parsed from the string (eg. with str2num).
Example script.m:
function GetStudyPath(iStudy) disp([10 'SCRIPT> Script start.']); sStudy = bst_get('Study', str2num(iStudy)); disp(['SCRIPT> Study path: ' sStudy.FileName 10]);
Execution from the command line:
>> brainstorm 'C:\Users\franc\Downloads\GetStudyPath.m' 1 BST> Starting Brainstorm: BST> ================================= BST> Version: 15-Jun-2022 BST> Deleting old process reports... BST> Loading configuration file... BST> Reading process folder... BST> Loading current protocol... BST> ================================= WARNING: This file is a function, trying to convert to a script... SCRIPT> Script start. SCRIPT> Study path: Test/@default_study/brainstormstudy.mat BST> Emptying temporary directory... BST> Brainstorm stopped.
Send report by email
When running some long computation on a distant server, it can be convenient to receive an email when the processing is over. Two solutions for sending yourself an email from Brainstorm: from the pipeline editor, or directly from a script.
Use the process File > Send report by email to send the current execution report:
Brainstorm username: The user name you use to download Brainstorm from the website, or to post messages on the user forum. The email associated with this account is the primary recipient of the email.
Send copy to: Optional, sends a copy to an additional email address. Use this to notify collaborator, or receive the notification on an email address that is not registered on the Brainstorm website.
Subject: Subject of the email.
Send full report: If selected, sends the full HTML execution report as displayed by the Report viewer. Be careful when working with sensitive patient data, as it would send with no encryption the full file names of all the files. If not selected, only the name of the processes and execution times are sent, in plain text.
Alternatively, use one of the following options (isFullReport can be set to 0 or 1):
% At the end of your script ReportFile = bst_report('Save', sFiles); bst_report('Email', ReportFile, username, to, subject, isFullReport); % Anywhere in your script bst_report('Email', 'current', username, to, subject, isFullReport);
How to compile Brainstorm
Brainstorm can be compiled as a JAR application using the function bst_compile.m. You would need this in order to execute without Matlab a modified version of Brainstorm.
Software requirements:
MATLAB >= 2020a
- MATLAB Compiler toolbox
- MATLAB Compiler SDK toolbox
- OpenJDK 8:
- Install Ubuntu package: sudo apt install openjdk-8-jdk
Set the environment variable JAVA_HOME to the installation folder of the JDK.
Windows 10: Right-click on "This PC" > Advanced system settings > Environment variables
Linux & MacOS: The syntax depends on your shell, look it up on Google.
- From Matlab: setenv('JAVA_HOME', '/path/to/jdk-8.0.../')
- Check the environment variable from Matlab: getenv('JAVA_HOME')
- The Matlab "bin" folder must be in the system path. Open a terminal and run "mcc", if you get an error message ("command not found"), you should add to the system path the "bin" folder located in the Matlab installation folder before starting Matlab.
To compile Brainstorm, run from the command line:
brainstorm compile: Compiles Brainstorm together with many plugins: SPM12, FieldTrip, Brain2mesh, Iso2mesh, BrainEntropy, LibSVM, NIRSTORM... At the moment, this works only from a Windows 10 computer (but will be extended later to other OS).
brainstorm compile noplugs: Compile Brainstorm without the extra plugins. This should work on all the operating systems.
Additional documentation
Tutorial: How to write your own process
Tutorial: Java in Brainstorm
Forum: Memory management
Forum: How to compile Brainstorm (outdated, see compilation section above instead)