diff --git a/code/+nansen/+internal/+template/copyCss.m b/code/+nansen/+internal/+template/copyCss.m new file mode 100644 index 000000000..40a3d05a0 --- /dev/null +++ b/code/+nansen/+internal/+template/copyCss.m @@ -0,0 +1,4 @@ +function copyCss(htmlFolder) + cssFilepath = fullfile(nansen.toolboxdir, 'resources', 'templates', 'helpwin.css'); + copyfile(cssFilepath, htmlFolder) +end \ No newline at end of file diff --git a/code/+nansen/+internal/+template/fillTemplate.m b/code/+nansen/+internal/+template/fillTemplate.m new file mode 100644 index 000000000..3460a2c31 --- /dev/null +++ b/code/+nansen/+internal/+template/fillTemplate.m @@ -0,0 +1,154 @@ +function renderedHTML = fillTemplate(templatePath, outputPath, data) + % fillTemplate - Populates a template with data and writes the output to a file. + % + % Syntax: renderedHTML = fillTemplate(templatePath, outputPath, data) + % + % Inputs: + % templatePath - Path to the template file (string) + % outputPath - Path to save the rendered HTML output (string) + % data - Struct containing data to populate the template + % + % Outputs: + % renderedHTML - Rendered HTML as a string + + % Read the template file + template = fileread(templatePath); + + % Replace top-level fields + fields = fieldnames(data); + for i = 1:numel(fields) + if ~isstruct(data.(fields{i})) && ~iscell(data.(fields{i})) + placeholder = sprintf('{{ %s }}', fields{i}); + template = strrep(template, placeholder, string(data.(fields{i}))); + end + end + + % Process repeating sections for option presets + if isfield(data, 'option_presets') + template = processForLoop(template, 'option_presets', data.option_presets); + end + + % Process repeating sections for parameters + if isfield(data, 'parameters') + template = processForLoop(template, 'parameters', data.parameters); + end + + % Write the rendered HTML to the output file + renderedHTML = template; + renderedHTML = replaceUrlsWithHyperlinks(renderedHTML); + + pattern = upper(data.helptopic); + %patterns = sprintf('^%s | %s | %s\n', pattern, pattern, pattern); + %replacePattern = sprintf('%s', pattern); + %renderedHTML = regexprep(renderedHTML, patterns, replacePattern); + + % Define the pattern to match the target text without removing spaces + patterns = sprintf('(^|\\s)%s(\\s|$)', pattern); + + % Define the replacement pattern, ensuring the match is replaced without altering spaces + replacePattern = sprintf('$1%s$2', pattern); + + % Perform the replacement + renderedHTML = regexprep(renderedHTML, patterns, replacePattern); + + + fid = fopen(outputPath, 'w'); + if fid == -1 + error('Unable to open output file: %s', outputPath); + end + fprintf(fid, '%s', renderedHTML); + fclose(fid); +end + +function template = processForLoop(template, sectionName, loopParams) + % processForLoop - Replaces a for-loop section in the template with repeated items + % + % Inputs: + % template - Template string + % sectionName - Section name in the template + % dataList - Struct array containing the data + % itemTemplate - Template for individual items + % + % Output: + % template - Updated template string + + template = char(template); + template_lines = splitlines(template); + trimmed_lines = strtrim(template_lines); + + % Define loop directive placeholders + startPlaceholder = sprintf('{%% for .* %s %%}', sectionName); + endPlaceholder = sprintf('{%% endfor %%}'); + + % Locate the loop section + lineMatch = regexp(trimmed_lines, startPlaceholder, 'once'); + startIdx = find(cellfun(@(c) ~isempty(c), lineMatch)); + lineMatch = regexp(trimmed_lines(startIdx:end), endPlaceholder, 'once'); + endIdx = find(cellfun(@(c) ~isempty(c), lineMatch), 1, 'first') + startIdx - 1; + + if isempty(startIdx) || isempty(endIdx) + return; % No loop section found + end + + if isempty(loopParams) + if strcmp(sectionName, 'parameters') + loopContentFinal = ['', ... + 'No parameters are exposed for this method.', ... + '']; + else + loopContentFinal = ['', ... + 'No option presets are available for this method.', ... + '']; + end + else + % Extract everything between the loop directives + loopContent = template_lines(startIdx+1:endIdx-1); + loopContent = strjoin(loopContent, newline); + + placeholderPattern = '\{\{\s*([a-zA-Z0-9_\.]+)\s*\}\}'; + % Extract unique placeholders + tokens = regexp(loopContent, placeholderPattern, 'tokens'); + tokens = string(tokens); + placeholders = compose('{{ %s }}', tokens); + + loopTemplate = regexprep(loopContent, placeholders, '%s'); + + % Filter loopParams by detected tokens: + A = squeeze( split(tokens, '.') ); + fieldNames = A(:,2); + allFieldNames = fieldnames(loopParams); + loopParams = rmfield(loopParams, setdiff(fieldNames, allFieldNames)); + loopParams = orderfields(loopParams, fieldNames); + + strValues = squeeze( string( struct2cell(loopParams) ))'; + + % Build array of replace values for compose + loopContentFinal = compose(loopTemplate, strValues); + loopContentFinal = strjoin(loopContentFinal, newline); + end + + % Replace the loop section in the template + template = strjoin( cat(1, ... + template_lines(1:startIdx-1), ... + loopContentFinal, ... + template_lines(endIdx+1:end)), newline ); +end + +function result = replaceUrlsWithHyperlinks(inputText) + % replaceUrlsWithHyperlinks - Detects URLs in the input text and replaces them with HTML hyperlinks + % + % Inputs: + % inputText - A string containing the text with URLs + % + % Outputs: + % result - The input text with URLs replaced by HTML hyperlinks + + % Regular expression to detect URLs + urlPattern = '(https?://[^\s<]+)'; + + % Replacement pattern to create hyperlinks + replacementPattern = '$1'; + + % Replace URLs with HTML hyperlinks + result = regexprep(inputText, urlPattern, replacementPattern, 'ignorecase'); +end diff --git a/code/+nansen/+manage/OptionsManager.m b/code/+nansen/+manage/OptionsManager.m index ef9480dac..5bb163419 100644 --- a/code/+nansen/+manage/OptionsManager.m +++ b/code/+nansen/+manage/OptionsManager.m @@ -688,6 +688,40 @@ function updatePresets(obj) error('This method is not implemented yet.') end + + function metadata = getPresetMetadata(obj) + metadata = struct('name', {}, 'description', {}); + + options = [obj.PresetOptions_, obj.CustomOptions_]; + + if numel(options) == 1 + return + end + + defaultOptionsName = obj.getReferenceOptionsName('Default'); + + for i = 1:numel(options) + if strcmp(options(i).Name, defaultOptionsName) + metadata(i).name = obj.formatDefaultName(options(i).Name); + else + metadata(i).name = options(i).Name; + end + + metadata(i).description = options(i).Description; + end + end + + function optionsDescriptions = getOptionDescriptions(obj) + + if obj.FunctionTypeIdx == 1 || obj.FunctionTypeIdx == 2 + optionsDescriptions = obj.findOptionDescriptionsFromFunction(); + + elseif obj.FunctionTypeIdx == 3 || obj.FunctionTypeIdx == 4 + optionsDescriptions = []; + else + error('Something went wrong!') + end + end end methods % Set/get @@ -889,7 +923,7 @@ function updateOptionsFromDefault(obj) names = {optionsEntry.Name}; end end - + function optionsEntry = findPresetsFromFunction(obj) %findPresetsFromFunction Find preset options from a function % @@ -915,7 +949,6 @@ function updateOptionsFromDefault(obj) optionsEntry = obj.createOptionsStructForSaving(opts, name, ... sprintf('Default preset options for %s', obj.FunctionName) ); - end function optionsEntry = findPresetsFromOptionsMixinClass(obj) @@ -928,13 +961,20 @@ function updateOptionsFromDefault(obj) fcnName = strcat(obj.FunctionName, '.getDefaultOptions'); fcnHandle = str2func(fcnName); + methodNameFcnName = strcat(obj.FunctionName, '.MethodName'); + % Return as options entry (struct) opts = fcnHandle(); name = 'Preset Options'; + + try + methodName = eval(methodNameFcnName); + catch + methodName = obj.FunctionName; + end optionsEntry = obj.createOptionsStructForSaving(opts, name, ... - sprintf('Default preset options for %s', obj.FunctionName) ); - + sprintf('Default preset options for %s', methodName) ); end function tf = inheritOptionsFromSuperclass(obj) @@ -1066,6 +1106,12 @@ function updateOptionsFromDefault(obj) isMatch = strcmp(obj.PresetOptionNames, optionsName); S = obj.PresetOptions_(isMatch).Options; end + + function descriptions = findOptionDescriptionsFromFunction(obj) + + functionFilePath = which(obj.FunctionName); + descriptions = extractStructDescriptions(functionFilePath); + end end methods (Access = private) % Methods for listing option set names @@ -1116,6 +1162,7 @@ function updateOptionsFromDefault(obj) names(isPreferred) = obj.formatDefaultName(names(isPreferred)); end + end methods (Access = private) % Methods for file interaction @@ -1538,3 +1585,56 @@ function refreshCustomOptions(obj) end end end + +function descriptions = extractStructDescriptions(filePath) + % extractStructDescriptions - Extract descriptions for struct fields in + % the getDefaultParameters function from a MATLAB file. + % + % Inputs: + % filePath - Path to the MATLAB file containing the getDefaultParameters function. + % + % Outputs: + % descriptions - Struct array with fields 'name', 'default_value', and 'description'. + + % Read the file content + fileContent = fileread(filePath); + + % Locate the getDefaultParameters function + functionPattern = 'function\s+[^\=]+\=\s*getDefaultParameters\s*\(\)'; + startIdx = regexp(fileContent, functionPattern, 'start'); + if isempty(startIdx) + error('Function "getDefaultParameters" not found in the file.'); + end + + % Extract the content of the getDefaultParameters function + structStart = regexp(fileContent, 'params\s*=\s*struct\(\);', 'end', 'once'); + if isempty(structStart) + descriptions = struct('name', {}, 'default_value', {}, 'description', {}); + return; % Return an empty struct if the struct is not defined + end + + structContent = fileContent(structStart:end); + structEnd = regexp(structContent, 'end', 'start', 'once'); + if isempty(structEnd) + error('Function "getDefaultParameters" does not have an "end".'); + end + structContent = structContent(1:structEnd - 1); + + % Match struct field assignments with comments + pattern = 'params\.(\w+)\s*=\s*([^;]+);\s*%[^\n]*'; + matches = regexp(structContent, pattern, 'tokens', 'lineanchors'); + + % Parse matched tokens into a struct array + descriptions = struct('name', {}, 'default_value', {}, 'description', {}); + for i = 1:numel(matches) + descriptions(i).name = matches{i}{1}; + descriptions(i).default_value = strtrim(matches{i}{2}); + % Extract the comment (after %) + commentMatch = regexp(structContent, ['params\.' matches{i}{1} '.*?%\s*(.+?)\n'], 'tokens', 'once'); + if ~isempty(commentMatch) + descriptions(i).description = strtrim(commentMatch{1}); + else + descriptions(i).description = ''; + end + end +end diff --git a/code/+nansen/+session/SessionMethod.m b/code/+nansen/+session/SessionMethod.m index f6f23817f..83617e971 100644 --- a/code/+nansen/+session/SessionMethod.m +++ b/code/+nansen/+session/SessionMethod.m @@ -218,6 +218,7 @@ function usePreset(obj, presetName) S.BatchMode = 'serial'; S.IsQueueable = true; S.Alternatives = {}; + S.MethodName = ''; % Pick out default options from inputs or init to empty struct if ~isempty(varargin) && isstruct(varargin{1}) @@ -257,10 +258,12 @@ function usePreset(obj, presetName) % Get name of calling function: % Todo: Get this from varargin if provided. - fcnName = nansen.session.SessionMethod.getCallingFunction(); - fcnNameSplit = strsplit(fcnName, '.'); - S.MethodName = utility.string.varname2label(fcnNameSplit{end}); - + if isempty(S.MethodName) + fcnName = nansen.session.SessionMethod.getCallingFunction(); + fcnNameSplit = strsplit(fcnName, '.'); + S.MethodName = utility.string.varname2label(fcnNameSplit{end}); + end + attributes = S; end diff --git a/code/apps/+nansen/@App/App.m b/code/apps/+nansen/@App/App.m index 7ebec6eeb..94288bac1 100644 --- a/code/apps/+nansen/@App/App.m +++ b/code/apps/+nansen/@App/App.m @@ -165,6 +165,8 @@ app.Figure.Visible = 'on'; end + cleanupObject = onCleanup(@app.assertInitialized); + % % Start app construction app.configureWindow() app.lockWindowPosition() @@ -320,6 +322,13 @@ function forceQuit(app, message) ME = MException(errorId, message); throwAsCaller(ME) end + + function assertInitialized(app) + if ~app.isInitialized() + app.ApplicationState = nansen.enum.ApplicationState.ShuttingDown; + delete(app) + end + end end methods % Set/get methods @@ -503,6 +512,9 @@ function createMenu_SessionTaskMode(app) 'Text', modeInfo(i).MenuText, ... 'Tag', sprintf('core.session_task_mode.%s', lower(modeValue)), ... 'UserData', modeValue); + if isprop(mitem, 'Accelerator') && ~isempty(modeInfo(i).Accelerator) + mitem.Accelerator = modeInfo(i).Accelerator; + end mitem.MenuSelectedFcn = @(~, ~) app.setSessionTaskMode(modeValue); end @@ -596,7 +608,8 @@ function setSessionTaskMode(app, modeName) 'Label', {'Default', 'Preview', 'Queue', 'Edit', 'Restart', 'Help'}, ... 'MenuText', {'Default (Esc)', 'Preview (Shift)', 'Queue (q)', ... 'Edit (e)', 'Restart (r)', 'Help (h)'}, ... - 'Key', {'escape', 'shift', 'q', 'e', 'r', 'h'} ); + 'Key', {'escape', 'shift', 'q', 'e', 'r', 'h'}, ... + 'Accelerator', {'', '', 'q', 'e', 'r', 'h'} ); end function wasHandled = setSessionTaskModeFromKey(app, keyName) @@ -3628,8 +3641,17 @@ function onSessionTaskSelected(app, ~, evt) app.SessionTaskMenu.Mode = 'Default'; % Reset menu mode return elseif strcmp(evt.Mode, 'Help') - help(evt.TaskAttributes.FunctionName) - applify.helpDialog(evt.TaskAttributes.FunctionName) + try + titleStr = eval(sprintf('%s.MethodName', evt.TaskAttributes.FunctionName)); + catch + if ~isempty(evt.TaskAttributes.MethodName) + titleStr = evt.TaskAttributes.MethodName; + else + titleStr = strrep(evt.TaskAttributes.FunctionName, 'nansen.module.', ''); + end + end + %applify.helpDialog(evt.TaskAttributes.FunctionName, 'Title', titleStr) + applify.helpDialogNansenMethod(evt.TaskAttributes.FunctionName, 'Title', titleStr) return end @@ -3835,8 +3857,9 @@ function runTaskWithReset(~, sessionMethod, taskType, methodArgs) sMethod.RedoIfCompleted = true; sMethod.runMethod() case 'function' + warningMessage = "This method does not have reset mode"; + app.MessageDisplay.warn(warningMessage) sessionMethod(methodArgs{:}); - warning('Session method does not have reset mode') end end @@ -4558,6 +4581,12 @@ function saveMetatableColumnSettingsToProject(app) end %% User dialog - Display information, warning and error messages + function cleanupObj = displayRunningMethod(app) + % Todo: Make this non-modal + hDlg = app.MessageDisplay.inform('Method is running. Please wait...'); + cleanupObj = onCleanup(@(h) delete(hDlg)); + end + function throwSessionMethodFailedError(app, ME, taskName, methodName) % throwSessionMethodFailedError - Display error message if task fails if iscell(taskName) diff --git a/code/general/+utility/convertParamsToStructArray.m b/code/general/+utility/convertParamsToStructArray.m index fca44b41b..4cbd1fbd9 100644 --- a/code/general/+utility/convertParamsToStructArray.m +++ b/code/general/+utility/convertParamsToStructArray.m @@ -34,12 +34,26 @@ strBegin = varBeginInd(i); strEnd = strBegin + regexp(fSub(strBegin:end), '\n', 'once'); thisLine = fSub(strBegin:strEnd); - + + if startsWith(strtrim(thisLine), '%') + continue + end + % Split substring at = and % (Name = DefaltValue % Description) subStringB = strsplit(thisLine, '%'); % Split out comment first subStringA = strsplit(subStringB{1}, '='); % Split first part @ = - thisLineDivided = [subStringA, subStringB{2}]; + if numel(subStringB) == 1 + description = 'No description'; + else + if isempty(subStringB{2}) + description = 'No description'; + else + description = subStringB{2}; + end + end + + thisLineDivided = [subStringA, description]; thisLineDivided = strtrim(thisLineDivided); % Make sure that this line was divided in three parts. diff --git a/code/general/displayParameterTable.m b/code/general/displayParameterTable.m index 7100d1e19..3fa3fd1aa 100644 --- a/code/general/displayParameterTable.m +++ b/code/general/displayParameterTable.m @@ -1,4 +1,4 @@ -function displayParameterTable(mFilePath) +function T = displayParameterTable(mFilePath) S = utility.convertParamsToStructArray(mFilePath); T = struct2table(S); @@ -14,5 +14,8 @@ function displayParameterTable(mFilePath) fprintf('\nDefault parameters and descriptions for: %s\n\n', packageName) disp(T) - + + if ~nargout + clear T + end end diff --git a/code/graphics/+applify/helpDialog.m b/code/graphics/+applify/helpDialog.m index d9b7564b3..530bfbc52 100644 --- a/code/graphics/+applify/helpDialog.m +++ b/code/graphics/+applify/helpDialog.m @@ -1,4 +1,25 @@ -function helpDialog(functionName) +function helpDialog(functionName, options) + + arguments + functionName (1,1) string + options.Title (1,1) string = functionName + end + + if endsWith(functionName, 'Quicky') + functionFilepath = which(functionName); + + htmlFilepath = replace(functionFilepath, ... + '+sessionmethod/+process/+autoSegmentation/Quicky.m', ... + 'resources/documentation/process/autoSegmentation/Quicky.html'); + web(htmlFilepath, '-new', '-notoolbar') + return + end + + sectionHeaders = [... + "Summary", ... + "Description", ... + "Option Presets", ... + "Parameters"]; functionNameSplit = strsplit(functionName, '.'); functionNameShort = functionNameSplit{end}; @@ -8,21 +29,23 @@ function helpDialog(functionName) functionFilepath = which(functionName); functionContentStr = fileread(functionFilepath); - [idx, functions] = regexp(functionContentStr, 'function.*?end', 'start', 'match'); - + [idx, functions] = regexp(functionContentStr, 'classdef.*?end', 'start', 'match'); + if isempty(idx) + [idx, functions] = regexp(functionContentStr, 'function.*?end', 'start', 'match'); + end + function_def = functions{1}; function_def = regexprep(function_def, '\n ', '', 'once'); functionLines = strsplit(function_def, '\n', 'CollapseDelimiters', false); - + functionLines{1} = '% Summary:'; functionDoc = {}; - for i = 2:numel(functionLines) + for i = 1:numel(functionLines) if strncmp(functionLines{i}, '%', 1) functionDoc{end+1} = strrep( functionLines{i}, '%', ''); if i == 2 - functionDoc{end} = strrep( functionDoc{end}, functionNameShort, ''); - functionDoc{end} = strrep( functionDoc{end}, upper(functionNameShort), ''); + functionDoc{end} = regexprep( functionDoc{end}, functionNameShort, '', 'once', 'ignorecase'); end else break @@ -35,7 +58,7 @@ function helpDialog(functionName) helpfig.Color = theme.FigureBgColor; helpfig.MenuBar = 'none'; helpfig.NumberTitle = 'off'; - helpfig.Name = sprintf('Help for %s', functionName); + helpfig.Name = sprintf('Help for %s', options.Title); % Create an axes to plot text in ax = axes('Parent', helpfig, 'Position', [0,0,1,1]); @@ -62,10 +85,17 @@ function helpDialog(functionName) makeBold = contains(messages{i}, '\b'); messages{i} = strrep(messages{i}, '\b', ''); + if any(startsWith(strtrim(messages{i}), sectionHeaders)) + makeBold = true; + end + count = count + 1; hTxt(count) = text(0.05, y, sprintf(messages{i})); if makeBold; hTxt(count).FontWeight = 'bold'; end + if startsWith(strtrim(messages{i}), 'https://') + makeHyperlink(hTxt(count)) + end y = y + 0.04; end @@ -97,3 +127,19 @@ function helpDialog(functionName) set(jframe, 'WindowDeactivatedCallback', @(s, e) delete(helpfig)) end end + +function makeHyperlink(hText) + + hText.Color = 'blue'; + hText.FontWeight = 'bold'; + hText.Interpreter = 'none'; + + % Add an interactive callback to simulate a hyperlink + set(hText, 'ButtonDownFcn', @(src, event) web(hText.String, '-browser')); + + % Make the text object clickable + hText.HitTest = 'on'; + + % Set the axes to allow clicking on the text + % set(gca, 'ButtonDownFcn', []); +end \ No newline at end of file diff --git a/code/graphics/+applify/helpDialogNansenMethod.m b/code/graphics/+applify/helpDialogNansenMethod.m new file mode 100644 index 000000000..cb8941451 --- /dev/null +++ b/code/graphics/+applify/helpDialogNansenMethod.m @@ -0,0 +1,266 @@ +function helpDialogNansenMethod(functionName, options) + + arguments + functionName (1,1) string + options.Title (1,1) string = functionName + end + + % Extract description + + titleStr = sprintf('%s', options.Title); + functionNameSplit = split(functionName, '.'); + + functionFilepath = which(functionName); + [summary, description] = extractDocString(functionFilepath); + + data.title = titleStr; + data.main_title = titleStr; + data.helptext = char(formatInlineGuideText(summary)); + data.helptopic = functionNameSplit{end}; + data.description = char(formatGuideTextAsHtml(description)); + + data.parameters = struct.empty; + + try + optionsManager = nansen.OptionsManager(functionName); + data.option_presets = optionsManager.getPresetMetadata(); + + if strcmp(optionsManager.FunctionType,'Function') + data.parameters = optionsManager.getOptionDescriptions(); + else + S = optionsManager.getDefaultOptions; + data.parameters = flattenNestedStruct(S); + end + catch ME + data.option_presets = struct.empty; + data.parameters = struct(... + 'name', 'Options unavailable', ... + 'default_value', '', ... + 'description', ME.message); + end + + templateFile = fullfile(nansen.toolboxdir, 'resources', 'templates', 'session_method_help.html.template'); + htmlFolder = fullfile(tempdir, 'nansen-html'); + + if ~isfolder(htmlFolder) + mkdir(htmlFolder); + nansen.internal.template.copyCss(htmlFolder) + end + htmlFilepath = fullfile(htmlFolder, join(functionNameSplit(end-2:end), "_") + ".html"); + nansen.internal.template.fillTemplate(templateFile, htmlFilepath, data); + + web(htmlFilepath, '-new', '-notoolbar') +end + +function [summary, description] = extractDocString(filePath) + + summary = "No summary"; + description = "No description"; + + functionContent = fileread(filePath); + + docstringLines = string.empty; + + functionLines = splitlines(functionContent); + for i = 1:numel(functionLines) + thisLine = strtrim(functionLines(i)); + if startsWith(thisLine, 'function') || startsWith(thisLine, 'classdef') + continue + elseif startsWith(thisLine, '%') + thisLine = extractAfter(thisLine, '%'); + docstringLines(end+1) = thisLine; %#ok + else + break + end + end + + if ~isempty(docstringLines) + summary = strtrim(docstringLines{1}); + description = trimEmptyEdgeLines(docstringLines(2:end)); + description = strjoin(description, newline); + end +end + +function lines = trimEmptyEdgeLines(lines) + while ~isempty(lines) && strtrim(lines(1)) == "" + lines(1) = []; + end + while ~isempty(lines) && strtrim(lines(end)) == "" + lines(end) = []; + end +end + +function htmlText = formatGuideTextAsHtml(text) + lines = splitlines(string(text)); + htmlLines = strings(0, 1); + paragraphLines = strings(0, 1); + listItemLines = strings(0, 1); + isInList = false; + + for i = 1:numel(lines) + rawLine = string(lines(i)); + line = strtrim(rawLine); + + if line == "" + flushParagraph() + flushListItem() + closeList() + elseif isRawHtmlLine(line) + flushParagraph() + flushListItem() + closeList() + htmlLines(end+1, 1) = line; %#ok + elseif any(startsWith(line, ["- ", "* "])) + flushParagraph() + if ~isInList + htmlLines(end+1, 1) = ""; + isInList = false; + end + end +end + +function tf = isGuideHeading(line) + tf = endsWith(line, ":") && strlength(line) <= 80 && ~contains(line, "://"); +end + +function tf = isRawHtmlLine(line) + htmlPrefixes = ["$1'); +end + +function htmlText = escapeHtml(text) + htmlText = replace(text, "&", "&"); + htmlText = replace(htmlText, "<", "<"); + htmlText = replace(htmlText, ">", ">"); +end + +function flatStruct = flattenNestedStruct(nestedStruct, parentName) + % Recursively flattens a nested struct into a struct with 'name' and 'default_value' + % where nested fields are joined with "." + % + % Inputs: + % nestedStruct - The nested struct to flatten + % parentName - (Optional) The parent field name for recursion + % + % Outputs: + % flatStruct - The resulting flat struct with fields 'name' and 'default_value' + + if nargin < 2 + parentName = ''; + end + + flatStruct = struct('name', {}, 'default_value', {}, 'description', {}); + fieldNames = fieldnames(nestedStruct); + + for i = 1:numel(fieldNames) + if endsWith(fieldNames{i}, '_') + continue + end + + fieldName = fieldNames{i}; + fullName = fieldName; + if ~isempty(parentName) + fullName = sprintf('%s.%s', parentName, fieldName); + end + + value = nestedStruct.(fieldName); + if isstruct(value) + % Recurse into nested structs + nestedFlatStruct = flattenNestedStruct(value, fullName); + flatStruct = [flatStruct, nestedFlatStruct]; + else + if ischar(value) + % pass + elseif isempty(value) + value = ''; + elseif isscalar(value) + value = formatValueAsString(value); + else + if iscell(value) + value = cellfun(@(c) formatValueAsString(c), value, 'uni', false); + value = sprintf('{%s}', strjoin(value, ', ')); + else + value = arrayfun(@(c) formatValueAsString(c), value, 'uni', false); + value = sprintf('[%s]', strjoin(value, ', ')); + end + end + + % Add field to flatStruct + flatStruct(end + 1).name = fullName; + flatStruct(end).default_value = value; + flatStruct(end).description = 'No description available.'; + end + end +end + + +function value = formatValueAsString(value) + if isinteger(value) + value = sprintf('%d', value); + elseif isnumeric(value) + value = sprintf('%.2f', value); + elseif islogical(value) + if value + value = 'true'; + else + value = 'false'; + end + end +end diff --git a/code/modules/+nansen/+module/+general/+core/+sessionmethod/+data/+organize/changeDataLocationRoot.m b/code/modules/+nansen/+module/+general/+core/+sessionmethod/+data/+organize/changeDataLocationRoot.m index 3aeb23c73..4cb681e30 100644 --- a/code/modules/+nansen/+module/+general/+core/+sessionmethod/+data/+organize/changeDataLocationRoot.m +++ b/code/modules/+nansen/+module/+general/+core/+sessionmethod/+data/+organize/changeDataLocationRoot.m @@ -1,6 +1,10 @@ function varargout = changeDataLocationRoot(sessionObject, varargin) -%CHANGERAWDATAROOT Summary of this function goes here -% Detailed explanation goes here +%CHANGEDATALOCATIONROOT Change the root directory for the specified data location of a session. +% +% CHANGEDATALOCATIONROOT opens an interactive dialog for changing the root +% folder of the selected data location for a session. The dialog is a +% dropdown selector where you can select the root folder from all +% configured root folders of the data location. % % % % % % % % % % % % % % % INSTRUCTIONS % % % % % % % % % % % % % % % % - - - - - - - - - - You can remove this part - - - - - - - - - - - diff --git a/code/modules/+nansen/+module/+general/+core/+sessionmethod/+data/backup.m b/code/modules/+nansen/+module/+general/+core/+sessionmethod/+data/backup.m index 98348a124..390f03d72 100644 --- a/code/modules/+nansen/+module/+general/+core/+sessionmethod/+data/backup.m +++ b/code/modules/+nansen/+module/+general/+core/+sessionmethod/+data/backup.m @@ -1,7 +1,7 @@ function varargout = backup(sessionObject, varargin) -%backup Backup a session folder for a specific data location +%BACKUP Backup a session folder for the specified data location % -% backup(sessionObject) backs up data for the selected data location. +% BACKUP backs up data for the selected data location of a session. % Todo: % [ ] Add mirror mode. (I.e delete files in target that are missing ) @@ -119,10 +119,10 @@ %getDefaultParameters Define the default parameters for this function params = struct(); - params.BackupMode = 'merge (no replace)'; + params.BackupMode = 'merge (no replace)'; % Mode for backup. Options: "merge (no replace)", "only replace files if newer", "replace all files" params.BackupMode_ = {'merge (no replace)', 'only replace files if newer', 'replace all files'}; - params.BackupLocation = ''; + params.BackupLocation = ''; % Absolute path to folder where data should be backed up params.BackupLocation_ = 'uigetdir'; end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/deltaFOverF.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/deltaFOverF.m index 089b208e9..3001218f0 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/deltaFOverF.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/deltaFOverF.m @@ -1,6 +1,16 @@ function varargout = deltaFOverF(sessionObject, varargin) -%deltaFOverF Summary of this function goes here -% Detailed explanation goes here +%View delta-F-over-F ROI signals for this session. +% +%Use this when: +%- You have already run signal extraction and delta-F-over-F computation. +%- You want to inspect normalized ROI activity traces in SignalViewer. +% +%What happens: +%- NANSEN checks that `RoiSignals_Dff` exists for the selected session. +%- The `RoiSignals_Dff` column is opened in the interactive SignalViewer app. +% +%Outputs: +%- No data are written; this method only opens an interactive viewer. % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % % Create a struct of default parameters (if applicable) and specify one or @@ -28,7 +38,7 @@ params = utility.parsenvpairs(params, [], varargin); % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % -% Implementation of the method : Add your code here: +% Implementation of the session method. sessionObject.validateVariable('RoiSignals_Dff') roiSignalArray = sessionObject.loadData('RoiSignals_Dff'); @@ -41,6 +51,5 @@ function S = getDefaultParameters() S = struct(); - % Add more fields: end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/imageStack.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/imageStack.m index 874c2b374..28a3d4cde 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/imageStack.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/imageStack.m @@ -1,21 +1,19 @@ function varargout = imageStack(sessionObj, varargin) -%imageStack Open image stack data variable in imviewer +%Open any image-stack data variable from this session in imviewer. % -% imageStack(sessionObj) opens the first imagestack variable for -% the given session using default options. +%Use this when: +%- You want to inspect an ImageStack variable without running processing. +%- You need to choose between raw, motion-corrected, denoised, downsampled, +% or other ImageStack variables registered in the active variable model. % -% imageStack(sessionObj, Name, Value) opens an imagestack using -% the options given as name, value pairs. +%What happens: +%- NANSEN builds one menu alternative for each ImageStack variable. +%- With `UseVirtualStack` enabled, imviewer reads frames lazily from disk. +%- With `UseVirtualStack` disabled, only `FirstImage` through `LastImage` +% are loaded into memory before opening imviewer. % -% fcnAttributes = imageStack() returns a struct of attributes for -% the function. -% -% List of options (name, value pairs): -% -% VariableName : Name of data variable to open in imviewer -% UseVirtualStack : Boolean flag, open using virtual stack or not -% FirstImage : First image to load (if UseVirtualStack is false) -% LastImage : Last image to load (if UseVirtualStack is false) +%Outputs: +%- No data are written; this method only opens an interactive viewer. import nansen.session.SessionMethod @@ -73,9 +71,9 @@ %getDefaultParameters Define the default parameters for this function S = struct(); - S.UseVirtualStack = true; - S.FirstImage = 1; - S.LastImage = inf; + S.UseVirtualStack = true; % Open the stack lazily from disk when possible. + S.FirstImage = 1; % First frame to load when UseVirtualStack is false. + S.LastImage = inf; % Last frame to load when UseVirtualStack is false. end function alternatives = getVariableNameAlternatives() diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/meanFluorescence.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/meanFluorescence.m index 6df7f7c0c..24865a042 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/meanFluorescence.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/meanFluorescence.m @@ -1,6 +1,17 @@ function varargout = meanFluorescence(sessionObject, varargin) -%meanFluorescence Summary of this function goes here -% Detailed explanation goes here +%View extracted mean-fluorescence ROI signals for this session. +% +%Use this when: +%- You have already extracted ROI signals from the motion-corrected stack. +%- You want to inspect raw fluorescence traces before neuropil correction, +% delta-F-over-F computation, or deconvolution. +% +%What happens: +%- NANSEN loads `RoiSignals_MeanF`. +%- The signals are opened in the interactive SignalViewer app. +% +%Outputs: +%- No data are written; this method only opens an interactive viewer. % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % % Create a struct of default parameters (if applicable) and specify one or @@ -11,7 +22,7 @@ params = getDefaultParameters(); % Create a cell array with attribute keywords - ATTRIBUTES = {'batch', 'unqueueable'}; + ATTRIBUTES = {'serial', 'unqueueable', 'MethodName', 'View Mean Fluorescence'}; % % % % % % % % % % % % % DEFAULT CODE BLOCK % % % % % % % % % % % % % % % - - - - - - - - - - Please do not edit this part - - - - - - - - - - - @@ -28,7 +39,7 @@ params = utility.parsenvpairs(params, [], varargin); % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % -% Implementation of the method : Add your code here: +% Implementation of the session method. sessionObject.validateVariable('RoiSignals_MeanF') roiSignalArray = sessionObject.loadData('RoiSignals_MeanF'); @@ -39,6 +50,5 @@ function S = getDefaultParameters() S = struct(); - % Add more fields: end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/twoPhotonMotionCorrected.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/twoPhotonMotionCorrected.m index 768d3cc21..39df1ef15 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/twoPhotonMotionCorrected.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/twoPhotonMotionCorrected.m @@ -1,14 +1,19 @@ function varargout = twoPhotonMotionCorrected(sessionObj, varargin) -%twoPhotonMotionCorrected Open 2-photon corrected recording in imviewer +%View the motion-corrected two-photon recording in imviewer. % -% twoPhotonMotionCorrected(sessionObj) opens the motion-corrected -% two-photon recording for the given session using default options. +%Use this when: +%- You want to inspect the registered recording after motion correction. +%- You need a quick quality-control pass before ROI detection or signal +% extraction. % -% twoPhotonMotionCorrected(sessionObj, Name, Value) opens the recording -% using the options given as name, value pairs. +%What happens: +%- NANSEN opens `TwoPhotonSeries_Corrected` for the selected session. +%- With `UseVirtualStack` enabled, imviewer reads frames lazily from disk. +%- With `UseVirtualStack` disabled, only `FirstImage` through `LastImage` +% are loaded into memory before opening imviewer. % -% fcnAttributes = twoPhotonMotionCorrected() returns a struct of -% attributes for the function. +%Outputs: +%- No data are written; this method only opens an interactive viewer. % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % % Please create a struct of default parameters (if applicable) and specify @@ -17,7 +22,7 @@ % % % Get struct of default parameters for function. params = getDefaultParameters(); - ATTRIBUTES = {'serial', 'unqueueable'}; + ATTRIBUTES = {'serial', 'unqueueable', 'MethodName', 'View Motion-Corrected 2-Photon'}; % % % % % % % % % % % % % DEFAULT CODE BLOCK % % % % % % % % % % % % % % % - - - - - - - - - - Please do not edit this part - - - - - - - - - - - @@ -51,10 +56,9 @@ end end -function S = getDefaultParameters() - S = struct(); - S.UseVirtualStack = true; - S.FirstImage = 1; - S.LastImage = inf; - +function params = getDefaultParameters() + params = struct(); + params.UseVirtualStack = true; % Whether to open the stack without loading data into memory (virtual stack). + params.FirstImage = uint64(1); % Index of first frame to load if UseVirtualStack is false + params.LastImage = inf; % Index of last frame to load if UseVirtualStack is false end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/twoPhotonRawImages.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/twoPhotonRawImages.m index aec1f51f0..4e2072847 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/twoPhotonRawImages.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+data/+openInViewer/twoPhotonRawImages.m @@ -1,14 +1,21 @@ function varargout = twoPhotonRawImages(sessionObj, varargin) -%twoPhotonRawImages Open 2-photon raw recording in imviewer +%View the original two-photon recording in imviewer. % -% twoPhotonRawImages(sessionObj) opens the raw two-photon recording for -% the given session using default options. +%Use this when: +%- You want to inspect the raw recording before motion correction. +%- You need to check imaging quality, drift, motion, flyback lines, or +% acquisition artifacts in the source data. % -% twoPhotonRawImages(sessionObj, Name, Value) opens the recording using -% the options given as name, value pairs. +%What happens: +%- NANSEN opens `TwoPhotonSeries_Original` for the selected session. +%- With `UseVirtualStack` enabled, imviewer reads frames lazily from disk. +%- With `UseVirtualStack` disabled, only `FirstImage` through `LastImage` +% are loaded into memory before opening imviewer. +%- If a recording contains multiple FOV stacks, you are prompted to choose +% which FOVs to open. % -% fcnAttributes = twoPhotonRawImages() returns a struct of attributes for -% the function. +%Outputs: +%- No data are written; this method only opens an interactive viewer. % Todo: Implement dynamic retrieval of parameters based on file adapter % for opening files. @@ -20,7 +27,7 @@ % % % Get struct of default parameters for function. params = getDefaultParameters(); - ATTRIBUTES = {'serial', 'unqueueable'}; + ATTRIBUTES = {'serial', 'unqueueable', 'MethodName', 'View Original (Raw) 2-Photon'}; % % % % % % % % % % % % % DEFAULT CODE BLOCK % % % % % % % % % % % % % % % - - - - - - - - - - Please do not edit this part - - - - - - - - - - - @@ -70,9 +77,9 @@ end end -function S = getDefaultParameters() - S = struct(); - S.UseVirtualStack = true; - S.FirstImage = 1; - S.LastImage = inf; +function params = getDefaultParameters() + params = struct(); + params.UseVirtualStack = true; % Open the stack lazily from disk when possible. + params.FirstImage = uint64(1); % First frame to load when UseVirtualStack is false. + params.LastImage = inf; % Last frame to load when UseVirtualStack is false. end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+plot/plotImageStats.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+plot/plotImageStats.m index ed684ccd4..5ce2c3011 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+plot/plotImageStats.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+plot/plotImageStats.m @@ -1,8 +1,19 @@ function varargout = plotImageStats(sessionObj, varargin) -%plotImageStats Plot stats from raw two photon recording +%Plot frame-wise image statistics for a two-photon recording. % -% Plot mean pixel value for each frame with a shaded error bar -% corresponding to a lower and an upper percentile of the pixel values. +%Use this when: +%- You want a fast quality-control view of brightness, saturation, or +% acquisition instability across time. +%- You have an `ImageStats` data variable for the selected session. +% +%What happens: +%- NANSEN loads `ImageStats` and plots the mean pixel value per frame in +% SignalViewer. +%- Optional shaded envelopes show the minimum/maximum range and configured +% percentile ranges. +% +%Outputs: +%- No data are written; this method only opens an interactive plot. % % % Get struct of default parameters for function. params = getDefaultParameters(); @@ -40,6 +51,7 @@ axes(hViewer.Axes) if params.ShowExtremes + assertHasShadedErrorBar() lowerBound = (S.meanValue - S.minimumValue)'; upperBound = (S.maximumValue - S.meanValue)'; h = shadedErrorBar([], S.meanValue, [upperBound; lowerBound], 'lineprops',{'color', cmap(:,1)}); @@ -52,6 +64,7 @@ uistack(hLine, 'top') if params.ShowPrctile1 + assertHasShadedErrorBar() lowerBound = (S.meanValue - S.prctileL1)'; upperBound = (S.prctileU1 - S.meanValue)'; h = shadedErrorBar([], S.meanValue, [upperBound; lowerBound], 'lineprops',{'color', cmap(:,2)}); @@ -61,6 +74,7 @@ uistack(hLine, 'top') if params.ShowPrctile2 + assertHasShadedErrorBar() lowerBound = (S.meanValue - S.prctileL2)'; upperBound = (S.prctileU2 - S.meanValue)'; h = shadedErrorBar([], S.meanValue, [upperBound; lowerBound], 'lineprops',{'color', cmap(:,3)}); @@ -73,7 +87,12 @@ function params = getDefaultParameters() params = struct(); - params.ShowExtremes = true; - params.ShowPrctile1 = true; - params.ShowPrctile2 = true; + params.ShowExtremes = true; % Show the minimum-to-maximum pixel range. + params.ShowPrctile1 = true; % Show the first configured percentile envelope. + params.ShowPrctile2 = true; % Show the second configured percentile envelope. end + +function assertHasShadedErrorBar() + assert( exist('shadedErrorBar', 'file') == 2, ... + 'This method requires shadedErrorBar from MATLAB FileExchange') +end \ No newline at end of file diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/EXTRACT.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/EXTRACT.m index b23159686..f2058b73a 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/EXTRACT.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/EXTRACT.m @@ -1,6 +1,25 @@ classdef EXTRACT < nansen.session.SessionMethod & nansen.wrapper.extract.Processor -%EXTRACT Summary of this function goes here -% Detailed explanation goes here +%Detect ROIs automatically with EXTRACT. +% +%Use this when: +%- You want automated ROI detection on `TwoPhotonSeries_Corrected`. +%- You expect EXTRACT's calcium-imaging model to be a better fit than +% Quicky or suite2p for the current recording. +% +%What happens: +%- NANSEN loads the motion-corrected stack and opens it as the source stack +% for the EXTRACT wrapper. +%- EXTRACT is run through the NANSEN ROI segmentation pipeline, including +% chunking, result merging, ROI-image computation, and ROI-statistics +% computation. +% +%Outputs: +%- `roiArrayExtractAuto`: automatically detected ROIs. +%- `ExtractOptions`, `ExtractResultsTemp`, and `ExtractResultsFinal` are +% saved as method outputs for provenance and restart support. +% +%Reference: +%- https://github.com/schnitzer-lab/EXTRACT-public properties (Constant) % SessionMethod attributes BatchMode = 'serial' % Move to data method? diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/Quicky.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/Quicky.m index 545b01617..0129f5ed0 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/Quicky.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/Quicky.m @@ -1,6 +1,26 @@ classdef Quicky < nansen.session.SessionMethod & nansen.wrapper.quicky.Processor -%QUICKY Summary of this function goes here -% Detailed explanation goes here +%Detect ROIs automatically with Quicky. +% +%Use this when: +%- You want a fast automated segmentation pass on `TwoPhotonSeries_Corrected`. +%- Your data are similar to the Thy1-GCaMP6s recordings this method was +% developed around in the Vervaeke Lab. +% +%What happens: +%- NANSEN loads the motion-corrected stack and opens it as the source stack +% for the Quicky wrapper. +%- Quicky is run through the NANSEN ROI segmentation pipeline, including +% chunking, result merging, ROI-image computation, and ROI-statistics +% computation. +% +%Outputs: +%- `roiArrayQuickyAuto`: automatically detected ROIs. +%- `QuickyOptions` and intermediate Quicky result files are saved for +% provenance and restart support. +% +%Limitations: +%- Quicky is tuned for a specific data style and may not generalize as well +% to other preparations, indicators, or imaging systems. properties (Constant) % SessionMethod attributes BatchMode = 'serial' % Move to data method? diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/suite2p.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/suite2p.m index e325070a9..60824f0d5 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/suite2p.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+autoSegmentation/suite2p.m @@ -1,6 +1,23 @@ classdef suite2p < nansen.session.SessionMethod & nansen.wrapper.suite2p.Processor -%EXTRACT Summary of this function goes here -% Detailed explanation goes here +%Detect ROIs automatically with suite2p. +% +%Use this when: +%- You want to run suite2p ROI detection on `TwoPhotonSeries_Corrected` +% from inside the NANSEN session workflow. +%- You want suite2p results saved back into the session data model rather +% than managed only in an external suite2p output folder. +% +%What happens: +%- NANSEN loads the motion-corrected stack and opens it as the source stack +% for the suite2p wrapper. +%- suite2p is run through the NANSEN ROI segmentation pipeline, including +% chunking, component merging, ROI-image computation, and ROI-statistics +% computation. +% +%Outputs: +%- `roiArraySuite2pAuto`: automatically detected ROIs. +%- `Suite2pOptions`, `Suite2pResultsTemp`, and `Suite2pResultsFinal` are +% saved as method outputs for provenance and restart support. properties (Constant) % SessionMethod attributes BatchMode = 'serial' % Move to data method? diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+deconvolution/Caiman.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+deconvolution/Caiman.m index 078f1e677..f5d7c7795 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+deconvolution/Caiman.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+deconvolution/Caiman.m @@ -1,6 +1,22 @@ classdef Caiman < nansen.session.SessionMethod -%DeconvolutionCaImAn Summary of this function goes here -% Detailed explanation goes here +%Deconvolve delta-F-over-F traces with the CaImAn signal model. +% +%Use this when: +%- You have already computed `RoiSignals_Dff`. +%- You want denoised and event-like deconvolved traces for downstream +% analysis or inspection. +% +%What happens: +%- NANSEN loads `RoiSignals_Dff`. +%- The selected CaImAn deconvolution parameters are applied to each ROI +% trace. +%- Preview mode opens the deconvolution explorer so parameters can be tuned +% before committing the run. +% +%Outputs: +%- `RoiSignals_Deconvolved`: deconvolved ROI activity estimates. +%- `RoiSignals_Denoised`: denoised delta-F-over-F traces. +%- `OptionsDeconvolution`: the options used for this run. properties (Constant) % SessionMethod attributes MethodName = 'Deconvolution CaImAn' @@ -17,7 +33,7 @@ methods (Static) function options = getDefaultOptions() - %GETDEFAULTOPTIONS Summary of this function goes here + %GETDEFAULTOPTIONS Return default CaImAn deconvolution options. options = nansen.twophoton.roisignals.getDeconvolutionParameters(); end end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+imageProcessing/DenoiseStack.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+imageProcessing/DenoiseStack.m index 542d9ab2a..7cc37ab31 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+imageProcessing/DenoiseStack.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+imageProcessing/DenoiseStack.m @@ -1,31 +1,26 @@ function varargout = DenoiseStack(sessionObject, varargin) -%DenoiseStack Denoise an ImageStack using the DeepInterpolation method +%Denoise an image stack with DeepInterpolation. % -% This method is based on the DeepInterpolation method from the Allen -% Institute. It requires MATLAB 2023a or later and the external toolbox -% "Deep Learning Toolbox Converter for TensorFlow Models". +%Use this when: +%- You want to suppress frame-wise noise in an ImageStack before visual +% inspection or downstream processing. +%- You have MATLAB R2023a or newer and the Deep Learning Toolbox Converter +% for TensorFlow Models available. % -% Note: Currently it uses the tretrained model they provided for two-photon -% data. It is possible to train models (See the DeepInterpolation_Matlab) -% on GitHub for details. Create an issue on VervaekeLab/Nansen if you -% need help adapting the code to run with another model. +%What happens: +%- You choose which ImageStack variable to denoise. +%- NANSEN runs the stack denoiser with the configured DeepInterpolation +% window and stack-processing options. +%- The current implementation uses the pretrained two-photon model from the +% DeepInterpolation MATLAB workflow. % -% Input: -% Input must be an ImageStack +%Outputs: +%- A denoised stack is produced by the stack processor according to the +% selected export options. % -% Options: -% PrePostOmission : How many frames around the target frame to omit -% for the interpolation. Default = 0. -% -% PreFrame : How many frames before the target frame to use for -% interpolation. Default = 30. -% -% PostFrame : How many frames after the target frame to use for -% interpolation. Default = 30. -% -% References: -% (1) https://github.com/AllenInstitute/deepinterpolation -% (2) https://github.com/MATLAB-Community-Toolboxes-at-INCF/DeepInterpolation-MATLAB +%References: +%- https://github.com/AllenInstitute/deepinterpolation +%- https://github.com/MATLAB-Community-Toolboxes-at-INCF/DeepInterpolation-MATLAB % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % % Create a struct of default parameters (if applicable) and specify one or @@ -53,7 +48,7 @@ params = utility.parsenvpairs(params, [], varargin); % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % -% Implementation of the method : Add your code here: +% Implementation of the session method. imageStack = sessionObject.loadData(params.Input.VariableName); params = utility.struct.renamefield(params, 'StackOptions', 'Run'); diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+imageProcessing/DownsampleStack.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+imageProcessing/DownsampleStack.m index 65ee8a949..f8a9c8aa0 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+imageProcessing/DownsampleStack.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+imageProcessing/DownsampleStack.m @@ -1,6 +1,20 @@ function varargout = DownsampleStack(sessionObject, varargin) -%DOWNSAMPLESTACK Summary of this function goes here -% Detailed explanation goes here +%Create a temporally downsampled copy of an image stack. +% +%Use this when: +%- You need a smaller stack for quick inspection, algorithm testing, or +% lightweight downstream processing. +%- You want to bin frames over time while preserving the original image +% height, width, channel count, and data type. +% +%What happens: +%- NANSEN loads the selected stack variable. +%- Consecutive groups of frames are binned with the selected method. +%- The downsampled stack is saved to file by the stack processor. +% +%Outputs: +%- A temporally downsampled ImageStack file is created according to the +% selected downsampling factor and binning method. % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % % Create a struct of default parameters (if applicable) and specify one or @@ -28,7 +42,7 @@ params = utility.parsenvpairs(params, [], varargin); % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % -% Implementation of the method : Add your code here: +% Implementation of the session method. imageStack = sessionObject.loadData(params.StackName); @@ -46,10 +60,10 @@ function S = getDefaultParameters() S = struct(); - S.StackName = 'TwoPhotonSeries_Corrected'; + S.StackName = 'TwoPhotonSeries_Corrected'; % ImageStack variable to downsample. S.StackName_ = {'TwoPhotonSeries_Corrected'}; - S.DownsamplingFactor = 10; - S.BinningMethod = 'mean'; + S.DownsamplingFactor = 10; % Number of consecutive frames per output frame. + S.BinningMethod = 'mean'; % Method used to combine frames within each bin. S.BinningMethod_ = {'mean', 'max'}; end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+motionCorrection/flowreg.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+motionCorrection/flowreg.m index 44a884369..9e9f78be8 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+motionCorrection/flowreg.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+motionCorrection/flowreg.m @@ -1,6 +1,26 @@ classdef flowreg < nansen.session.SessionMethod & nansen.wrapper.flowreg.Processor -%normcorre Summary of this function goes here -% Detailed explanation goes here +%Motion-correct the raw two-photon recording with FlowRegistration. +% +%Use this when: +%- You want non-rigid motion correction using the FlowRegistration toolbox. +%- You need motion-corrected data plus diagnostic projections and movement +% statistics saved back into the session. +% +%What happens: +%- NANSEN loads `TwoPhotonSeries_Original` and enables stack preprocessing. +%- FlowRegistration estimates frame shifts and writes the corrected stack. +%- Reference images, projections, and correction statistics are generated +% by the shared motion-correction pipeline. +% +%Outputs: +%- `TwoPhotonSeries_Corrected`: motion-corrected image stack. +%- `FlowregOptions` and `FlowregShifts`: method-specific settings and +% frame shifts. +%- Motion-correction QC outputs such as `FovAverageProjection`, +% `FovMaximumProjection`, and `MotionCorrectionStats`. +% +%Reference: +%- https://github.com/phflot/flow_registration properties (Constant) % SessionMethod attributes BatchMode = 'serial' % Move to data method? diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+motionCorrection/normcorre.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+motionCorrection/normcorre.m index d966b9063..23d9f4c2c 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+motionCorrection/normcorre.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+motionCorrection/normcorre.m @@ -1,6 +1,26 @@ classdef normcorre < nansen.session.SessionMethod & nansen.wrapper.normcorre.Processor -%normcorre Summary of this function goes here -% Detailed explanation goes here +%Motion-correct the raw two-photon recording with NoRMCorre. +% +%Use this when: +%- You want non-rigid motion correction using the NoRMCorre MATLAB toolbox. +%- You need motion-corrected data plus diagnostic projections and movement +% statistics saved back into the session. +% +%What happens: +%- NANSEN loads `TwoPhotonSeries_Original` and enables stack preprocessing. +%- NoRMCorre estimates frame shifts and writes the corrected stack. +%- Reference images, projections, and correction statistics are generated +% by the shared motion-correction pipeline. +% +%Outputs: +%- `TwoPhotonSeries_Corrected`: motion-corrected image stack. +%- `NormcorreOptions` and `NormcorreShifts`: method-specific settings and +% frame shifts. +%- Motion-correction QC outputs such as `FovAverageProjection`, +% `FovMaximumProjection`, and `MotionCorrectionStats`. +% +%Reference: +%- https://github.com/flatironinstitute/NoRMCorre properties (Constant) % SessionMethod attributes BatchMode = 'serial' % Move to data method? diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiCuration/openRoiClassifier.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiCuration/openRoiClassifier.m index 515d274dc..bf62d51be 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiCuration/openRoiClassifier.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiCuration/openRoiClassifier.m @@ -1,6 +1,21 @@ function varargout = openRoiClassifier(sessionObject, varargin) -%OPENROICLASSIFIER Open roi classifier on a session -% Detailed explanation goes here +%Open the ROI classifier for a selected ROI variable. +% +%Use this when: +%- You want to manually label ROIs as cells, non-cells, or other configured +% classes. +%- You have one or more ROI variables available for the selected session. +% +%What happens: +%- NANSEN asks which ROI variable to classify. +%- If needed, the ROI data are loaded through the ROI file adapter so the +% classifier receives a roiGroup. +%- If ROI images are missing, the motion-corrected stack is passed in so +% the classifier can show image context. +% +%Outputs: +%- Classification edits are handled by the ROI classifier app for the +% selected ROI file. % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % % Create a struct of default parameters (if applicable) and specify one or @@ -28,7 +43,7 @@ params = utility.parsenvpairs(params, 1, varargin); % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % -% Implementation of the method : Add your code here: +% Implementation of the session method. sessionData = nansen.session.SessionData( sessionObject ); sessionData.updateDataVariables() @@ -74,8 +89,7 @@ function S = getDefaultParameters() S = struct(); - % Add more fields: - S.RoiSelectedCallbackFunction = ''; + S.RoiSelectedCallbackFunction = ''; % Optional callback used when an ROI is selected. end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiCuration/openRoiManager.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiCuration/openRoiManager.m index c70280c8c..d4e2c3da2 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiCuration/openRoiManager.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiCuration/openRoiManager.m @@ -1,6 +1,21 @@ function varargout = openRoiManager(sessionObj, varargin) -%openRoimanager Open roimanager for corrected two-photon images +%Open RoiManager for the motion-corrected two-photon recording. % +%Use this when: +%- You want to inspect, draw, edit, or curate ROIs directly on the +% motion-corrected image stack. +%- You want to load an existing ROI variable into RoiManager for manual +% correction. +% +%What happens: +%- NANSEN loads `TwoPhotonSeries_Corrected` and enables dynamic caching for +% interactive browsing. +%- RoiManager opens on the corrected stack. +%- If you choose an ROI variable, those ROIs are loaded into RoiManager and +% linked to the current session data. +% +%Outputs: +%- Edits are managed by RoiManager; this method itself only opens the app. % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % % Please create a struct of default parameters (if applicable) and specify diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/classifyMultiSessionRois.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/classifyMultiSessionRois.m index d66c38025..cae0156d6 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/classifyMultiSessionRois.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/classifyMultiSessionRois.m @@ -1,6 +1,23 @@ function varargout = classifyMultiSessionRois(sessionObject, varargin) -%CLASSIFYMULTISESSIONROIS Summary of this function goes here -% Detailed explanation goes here +%Classify matched ROIs across a longitudinal session set. +% +%Use this when: +%- You have already migrated ROIs across sessions and computed +% classification data for each session. +%- You want one classifier view where corresponding ROIs from different +% sessions stay aligned. +% +%What happens: +%- NANSEN loads the multi-session ROI cross-reference from the first +% selected session. +%- `RoiGroupLongitudinal` data are loaded from each selected session and +% reordered so matched ROIs appear together. +%- The ROI classifier opens with session-aware labels. +%- When classifications are saved, each session's `RoiGroupLongitudinal` +% is updated with the new labels. +% +%Outputs: +%- Updated `RoiGroupLongitudinal` files for the selected sessions. % % % % % % % % % % % % % % % INSTRUCTIONS % % % % % % % % % % % % % % % % - - - - - - - - - - You can remove this part - - - - - - - - - - - @@ -43,7 +60,7 @@ params = utility.parsenvpairs(params, [], varargin); % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % -% Implementation of the method : Add your code here: +% Implementation of the session method. varName = 'MultisessionRoiCrossReference'; filePath = sessionObject(1).loadData(varName); diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/computeClassificationData.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/computeClassificationData.m index 5c926d09d..fe78dd1b0 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/computeClassificationData.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/computeClassificationData.m @@ -1,6 +1,22 @@ function varargout = computeClassificationData(sessionObject, varargin) -%computeClassificationData Summary of this function goes here -% Detailed explanation goes here +%Compute ROI images and statistics for longitudinal ROI classification. +% +%Use this when: +%- You have `RoiArrayLongitudinal` for a session and want to classify those +% ROIs later. +%- You need ROI thumbnails and ROI statistics derived from the +% motion-corrected stack. +% +%What happens: +%- NANSEN loads `RoiArrayLongitudinal` and the selected ImageStack. +%- For each channel and plane, it computes ROI images and ROI statistics +% from an initial chunk of frames. +%- The ROI data, images, statistics, and empty classification labels are +% packaged as a longitudinal ROI group. +% +%Outputs: +%- `RoiGroupLongitudinal`: ROI data prepared for longitudinal +% classification. % % % % % % % % % % % % CONFIGURATION CODE BLOCK % % % % % % % % % % % % % Create a struct of default parameters (if applicable) and specify one or @@ -104,16 +120,9 @@ end function params = getDefaultParameters() -%getDefaultParameters Get the default parameters for this session method -% -% params = getDefaultParameters() should return a struct, params, which -% contains fields and values for parameters of this session method. - - % Add fields to this struct in order to define parameters for this - % session method: params = struct(); - params.ImageStackVariableName = 'TwoPhotonSeries_Corrected'; - params.RoiImageSize = [21, 21]; + params.ImageStackVariableName = 'TwoPhotonSeries_Corrected'; % ImageStack used to compute ROI images and statistics. + params.RoiImageSize = [21, 21]; % Pixel size of generated ROI thumbnails. end function throwError(errorID, sessionID) diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/editRois.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/editRois.m index c8236bcd7..d5ec2ff51 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/editRois.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/editRois.m @@ -1,9 +1,21 @@ function varargout = editRois(sessionObject, varargin) -%EDITROIS Edit rois for a set of longitudinal rois in roimanager. -% This method opens the roimanager for editing rois for a set of -% longitudinal sessions. It additionally opens a thumbnail selector for -% switching between each of the sessions. If changes are made to rois in -% one session, those changes will be copied to all other sessions. +%Edit longitudinal ROIs across a selected session set. +% +%Use this when: +%- You have migrated ROIs across sessions and want to manually correct the +% matched ROI set. +%- You need to switch between sessions while keeping RoiManager focused on +% the corresponding longitudinal ROIs. +% +%What happens: +%- NANSEN loads an FOV projection for each selected session. +%- It loads `RoiArrayLongitudinal` and the selected ImageStack for each +% session. +%- RoiManager opens on the first session, together with a multi-session FOV +% switcher for moving through the selected sessions. +% +%Outputs: +%- ROI edits are managed by RoiManager and the multi-session switcher. % Todo: Also support loading of roi classifications. % Specify FOV image variable name in parameters. @@ -75,15 +87,8 @@ end function params = getDefaultParameters() -%getDefaultParameters Get the default parameters for this session method -% -% params = getDefaultParameters() should return a struct, params, which -% contains fields and values for parameters of this session method. - - % Add fields to this struct in order to define parameters for this - % session method: params = struct(); - params.ImageStackVariableName = 'TwoPhotonSeries_Corrected'; + params.ImageStackVariableName = 'TwoPhotonSeries_Corrected'; % ImageStack shown in RoiManager. end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/migrateRoisToFovs.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/migrateRoisToFovs.m index 0951c01b6..053aaf900 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/migrateRoisToFovs.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+roiRegistration/migrateRoisToFovs.m @@ -1,25 +1,30 @@ function varargout = migrateRoisToFovs(sessionObject, varargin) -%migrateRoisToFovs Summary of this function goes here +%Migrate ROIs from one reference FOV to matching FOVs in other sessions. % -% This is a multisession method to use for migrating a roi array from one -% FoV to other matching FOVs. +%Use this when: +%- You have repeated imaging sessions from matching fields of view. +%- You want to start longitudinal ROI curation from one reference ROI set +% instead of drawing ROIs independently in every session. % -% Requirements: -% - - - - - - - -% The method requires a FOV image ("FovAverageProjection") to be present -% for all sessions and a roi array to be present for the reference session. +%Requirements: +%- Every selected session must have `FovAverageProjection` or +% `FovAverageProjectionCorr`. +%- The reference session must contain the ROI array you want to migrate. +%- Multi-plane recordings are not supported by this method yet. % -% What will happen? -% - - - - - - - - - -% 1) An average FOV image is loaded for each session and aligned -% using an image registration method (flowreg or normcorre). -% 2) The roi array from the reference session will be copied to all other -% sessions and the roi positions will be adjusted based on the pixel -% shifts from the image registration step. -% 3) Rois are added and saved to a MultiSessionRoiCollection. This can -% later be used to manually edit rois for individual sessions and -% synchronize those changes across all sessions. See -% Roi Registration -> Edit Rois +%What happens: +%- You choose the reference session. +%- FOV projections are loaded, aligned, and used to estimate pixel shifts +% from the reference FOV to each target FOV. +%- The reference ROI array is copied to each target session and shifted into +% the aligned FOV coordinate frame. +%- QC images are generated and opened so the migration can be inspected. +% +%Outputs: +%- `RoiArrayLongitudinal`: migrated ROI arrays saved for each selected +% session. +%- `MultisessionRoiCrossReference`: path to the shared multi-session ROI +% collection saved for each selected session. % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % % Create a struct of default parameters (if applicable) and specify one or @@ -47,7 +52,7 @@ params = utility.parsenvpairs(params, [], varargin); % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % -% Implementation of the method : Add your code here: +% Implementation of the session method. % Count number of sessions numSessions = numel(sessionObject); @@ -251,12 +256,11 @@ function saveMultiSessionRois() function S = getDefaultParameters() S = struct(); - S.WorkingChannel = 2; + S.WorkingChannel = 2; % Imaging channel used for aligning FOVs and migrating ROIs. S.WorkingChannel_ = {1,2}; % S.MultisessionRoiSynchMode ? % 'LoadFromMaster', struct('Alternatives', {{'Use Master', 'Use Single', 'Merge'}}, 'Selection', {'Merge'}), ... % 'SaveToMaster', struct('Alternatives', {{'Only Add', 'Mirror'}}, 'Selection', {'Mirror'}), ... - % Add more fields: end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/SelectRoiForSignalExtraction.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/SelectRoiForSignalExtraction.m index 03fdb6a8f..ba5ccb26d 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/SelectRoiForSignalExtraction.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/SelectRoiForSignalExtraction.m @@ -1,18 +1,20 @@ function varargout = SelectRoiForSignalExtraction(sessionObject, varargin) -%SELECTROIFORSIGNALEXTRACTION Select which rois to use for signal extraction +%Choose which ROI set should be used for signal extraction. % -% This method updates the default ROI data variable for a Nansen session. It -% prompts the user to select which ROI variable to work with. If a valid -% variable name is chosen, the corresponding ROI group is retrieved and -% saved to the default ROI variable. +%Use this when: +%- A session has multiple ROI variables, for example automatic detections +% and manually curated ROIs. +%- You want `Extract Signals` to use a specific ROI set. % -% Note: The default ROI variable is used for signal extraction +%What happens: +%- NANSEN asks which ROI variable to use. +%- The selected ROI group can optionally be cleaned by removing ROIs with +% pixels outside the FOV boundary. +%- The selected ROI group is saved as the default `RoiArray`. % -% Parameters: -% - deleteRoisOutsideTheBorders (logical) - -% Boolean flag true, determining if ROIs whose pixels lie partially or -% completely outside the field of view (FOV) boundaries are removed. The -% updated ROI group is then saved back to the session. +%Outputs: +%- `RoiArray`: the ROI set that signal-extraction methods will use by +% default. % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % % Create a struct of default parameters (if applicable) and specify one or @@ -40,7 +42,7 @@ params = utility.parsenvpairs(params, [], varargin); % % % % % % % % % % % % % % CUSTOM CODE BLOCK % % % % % % % % % % % % % % -% Implementation of the method : Add your code here: +% Implementation of the session method. sessionData = nansen.session.SessionData( sessionObject ); sessionData.updateDataVariables() @@ -86,6 +88,5 @@ function S = getDefaultParameters() S = struct(); - % Add more fields: - S.deleteRoisOutsideTheBorders = true; + S.deleteRoisOutsideTheBorders = true; % Remove ROIs with pixels outside the FOV before saving RoiArray. end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/computeDff.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/computeDff.m index 152bfb259..07a5d812b 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/computeDff.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/computeDff.m @@ -1,6 +1,21 @@ classdef computeDff < nansen.session.SessionMethod -%COMPUTEDFF Summary of this function goes here -% Detailed explanation goes here +%Compute delta-F-over-F traces from extracted ROI signals. +% +%Use this when: +%- You have already run signal extraction for the session. +%- You want normalized ROI activity traces for visualization, deconvolution, +% or downstream analysis. +% +%What happens: +%- NANSEN loads `RoiSignals_MeanF`. +%- If the selected dF/F method requires neuropil data, NANSEN also loads +% `RoiSignals_NeuropilF`. +%- The selected dF/F algorithm is applied to each ROI trace. +%- Preview mode opens the dF/F explorer so parameters can be tuned before +% committing the run. +% +%Outputs: +%- `RoiSignals_Dff`: delta-F-over-F ROI signals. properties (Constant) % SessionMethod attributes MethodName = 'Compute Delta F over F' @@ -30,7 +45,7 @@ methods (Static) function options = getDefaultOptions() - %GETDEFAULTOPTIONS Summary of this function goes here + %GETDEFAULTOPTIONS Return default delta-F-over-F options. options = nansen.twophoton.roisignals.getDffParameters(); end end diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/extractSignals.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/extractSignals.m index ad5c55b43..621c738c0 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/extractSignals.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/extractSignals.m @@ -1,6 +1,22 @@ classdef extractSignals < nansen.session.SessionMethod -%EXTRACTSIGNALS Summary of this function goes here -% Detailed explanation goes here +%Extract fluorescence and neuropil signals from the corrected stack. +% +%Use this when: +%- `TwoPhotonSeries_Corrected` and the default `RoiArray` are ready. +%- You want per-ROI fluorescence traces before dF/F computation or +% deconvolution. +% +%What happens: +%- NANSEN loads the motion-corrected stack and the default ROI array. +%- For each ROI, fluorescence is extracted from the ROI region and configured +% neuropil regions. +%- Signal metadata such as sample rate are inherited from the image stack +% where available. +% +%Outputs: +%- `RoiSignals_MeanF`: mean ROI fluorescence traces. +%- `RoiSignals_NeuropilF`: neuropil fluorescence traces. +%- `OptionsSignalExtraction`: options used for the extraction run. properties (Constant) % SessionMethod attributes MethodName = 'Extract Signals' diff --git a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/extractSignalsMultiChannel.m b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/extractSignalsMultiChannel.m index 9431110d1..8bc53868a 100644 --- a/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/extractSignalsMultiChannel.m +++ b/code/modules/+nansen/+module/+ophys/+twophoton/+sessionmethod/+process/+signalExtraction/extractSignalsMultiChannel.m @@ -1,8 +1,21 @@ classdef extractSignalsMultiChannel < nansen.session.SessionMethod -%EXTRACTSIGNALS Summary of this function goes here -% Detailed explanation goes here +%Extract ROI signals from all channels in a multi-channel recording. % -% Session method wrapper for SignalExtractor +%Use this when: +%- `TwoPhotonSeries_Corrected` has multiple channels. +%- The default `RoiArray` should be matched to the stack's channel and plane +% layout before signal extraction. +% +%What happens: +%- NANSEN loads the corrected stack and temporarily selects all channels. +%- The ROI group is checked and adapted to match the stack dimensions. +%- The `SignalExtractor` processor extracts ROI and neuropil signals using +% the configured extraction options. +% +%Outputs: +%- `RoiSignals_MeanF`: mean ROI fluorescence traces. +%- `RoiSignals_NeuropilF`: neuropil fluorescence traces. +%- `OptionsSignalExtraction`: options used by the signal extractor. properties (Constant) % SessionMethod attributes MethodName = 'Extract Signals (MultiChannel)' diff --git a/code/resources/templates/helpwin.css b/code/resources/templates/helpwin.css new file mode 100644 index 000000000..db4d6145a --- /dev/null +++ b/code/resources/templates/helpwin.css @@ -0,0 +1,274 @@ +body { + font-family: 'Avenir Next', Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 1.55; + color: #303E4C; + background-color: #f7f8fb; + margin: 0; + padding: 0; +} + +a { + color: #1F78B4; + text-decoration: none; + font-weight: 500; +} + +a:visited { + color: #5C6F8C; +} + +a:active { + color: #184A6D; +} + +a:hover { + color: #0F3654; + text-decoration: underline; +} + +hr { + border: none; + border-top: 2px solid #1F78B4; + margin: 30px 0; +} + +p { + margin: 10px 0; + color: #404A59; + font-size: 16px; +} + +code { + background-color: #EEF2F6; + border: 1px solid #DDE4EC; + border-radius: 3px; + color: #243746; + font-family: Menlo, Consolas, monospace; + font-size: 0.92em; + padding: 1px 4px; +} + +table { + width: 100%; + border-collapse: collapse; + margin: 12px 0 22px; + font-size: 14px; + background-color: #fff; +} + +table th, +table td { + padding: 12px 15px; + text-align: left; + color: #303E4C; + border: none; +} + +table th { + background-color: #E8EFF7; + font-weight: bold; + border-bottom: 2px solid #1F78B4; +} + +table tr td { + border-bottom: 1px solid #E0E4EB; +} + +table tr:nth-child(even) { + background-color: #F9FAFC; +} + +table tbody tr:hover { + background-color: #E0E4EB !important; /* Ensure the hover color is applied */ +} + +.title { + color: #182633; + font-size: 30px; + font-weight: 700; + margin: 22px 0 8px; + border-bottom: 2px solid #D6DEE8; + padding-bottom: 10px; +} + +.sectiontitle { + color: #184A6D; + font-size: 20px; + font-weight: 700; + margin: 28px 0 10px; +} + +.headertitle { + font-weight: 700; + font-size: 13px; + letter-spacing: 0; + text-transform: uppercase; + color: #5F6F80; +} + +.helptopic { + font-weight: bold; + color: #1F78B4; + font-size: 18px; + margin-top: 20px; +} + +.helptext { + margin: 20px 0; + font-family: 'Avenir Next', Helvetica, Arial, sans-serif; + font-size: 17px; + line-height: 1.7; + color: #505F72; + background-color: #F2F5FA; + padding: 15px 20px; + border-left: 4px solid #1F78B4; + border-radius: 3px; +} + +.method-page { + max-width: 980px; + margin: 0 auto; + padding: 22px 28px 36px; +} + +.lead { + color: #344657; + font-size: 18px; + line-height: 1.55; + margin: 12px 0 20px; +} + +.guide-text { + background-color: #FFFFFF; + border: 1px solid #DDE4EC; + border-radius: 6px; + padding: 18px 22px; +} + +.guide-text h2 { + color: #184A6D; + font-size: 17px; + margin: 18px 0 8px; +} + +.guide-text h2:first-child { + margin-top: 0; +} + +.guide-text ul { + margin: 8px 0 14px 20px; + padding: 0; +} + +.guide-text li { + margin: 5px 0; +} + +.subheader { + border-bottom: 1px solid #DDE4EC; + padding-bottom: 10px; + color: #1F78B4; +} + +.subheader-left { + float: left; + font-size: 18px; + font-weight: bold; +} + +.subheader-right { + text-align: right; + float: right; + font-size: 18px; + font-weight: bold; +} + +.footerlinktitle { + margin: 30px 0 10px; + color: #1F78B4; + font-size: 18px; + font-weight: bold; +} + +.footerlink { + padding: 5px 10px; + color: #505F72; + display: inline-block; +} + +.topiclinks { + margin: 20px 0; +} + +a.topiclink { + padding-right: 15px; + color: #1F78B4; + font-weight: 500; +} + +.class-details { + margin: 15px 20px; + padding: 15px; + background-color: #f2f5fa; + border-radius: 5px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.class-detail-label { + font-weight: bold; + padding-right: 10px; + color: #1F78B4; + font-size: 18px; +} + +.name { + color: #184A6D; + font-weight: bold; + font-size: 18px; +} + +.summary-list { + margin: 20px 0; + list-style: none; + padding: 0; +} + +.summary-list .summary-item { + padding: 10px 12px; + margin-bottom: 6px; + background-color: #F9FAFC; + border-bottom: 1px solid #E0E4EB; +} + +.summary-list .summary-item .name { + font-weight: bold; + color: #303E4C; + font-size: 14px; +} + +.summary-list .summary-item .m-help { + font-size: 14px; + color: #505F72; + margin-top: 5px; +} + +.summary-list .summary-item .attributes { + font-size: 14px; + text-align: center; + color: #1F78B4; + font-weight: 500; +} + +.columntitle { + font-size: 14px; + font-weight: bold; + color: #303E4C; + padding: 8px 8px; + margin-bottom: 10px; +} + +.empty-row .m-help { + color: #6B7887; + font-style: italic; +} diff --git a/code/resources/templates/session_method_help.html.template b/code/resources/templates/session_method_help.html.template new file mode 100644 index 000000000..fb61d32de --- /dev/null +++ b/code/resources/templates/session_method_help.html.template @@ -0,0 +1,51 @@ + + + + + + {{ title }} + + + +
+
+
NANSEN Session Method
+
+
{{ main_title }}
+
+ {{ helptext }} +
+ +
Guide
+
+ {{ description }} +
+ +
Option Presets
+ + {% for preset in option_presets %} + + + + + {% endfor %} +
{{ preset.name }}{{ preset.description }}
+ +
Parameters
+ + + + + + + + + + + + + +
NameDefault valueDescription
{{ param.name }}{{ param.default_value }}{{ param.description }}
+
+ + diff --git a/code/wrappers/+nansen/+wrapper/+quicky/+presets/Axon.m b/code/wrappers/+nansen/+wrapper/+quicky/+presets/Axon.m index 41107b7f4..69bf782d1 100644 --- a/code/wrappers/+nansen/+wrapper/+quicky/+presets/Axon.m +++ b/code/wrappers/+nansen/+wrapper/+quicky/+presets/Axon.m @@ -2,7 +2,7 @@ properties (Constant) Name = 'Axons' - Description = 'Autosegment neuronal axonal boutons' + Description = 'Tuned to detect smaller axonal bouton-like structures' end methods (Static) diff --git a/code/wrappers/+nansen/+wrapper/+quicky/+presets/Soma.m b/code/wrappers/+nansen/+wrapper/+quicky/+presets/Soma.m index 7922b8af4..986d1be59 100644 --- a/code/wrappers/+nansen/+wrapper/+quicky/+presets/Soma.m +++ b/code/wrappers/+nansen/+wrapper/+quicky/+presets/Soma.m @@ -2,7 +2,7 @@ properties (Constant) Name = 'Soma' - Description = 'Autosegment neuronal somas' + Description = 'Optimized for detecting soma-like structures' end methods (Static) diff --git a/code/wrappers/+nansen/+wrapper/+quicky/+presets/Soma_Virus.m b/code/wrappers/+nansen/+wrapper/+quicky/+presets/Soma_Virus.m index 7d947957f..ff5c2345b 100644 --- a/code/wrappers/+nansen/+wrapper/+quicky/+presets/Soma_Virus.m +++ b/code/wrappers/+nansen/+wrapper/+quicky/+presets/Soma_Virus.m @@ -2,7 +2,7 @@ properties (Constant) Name = 'Soma (Virus)' - Description = 'Autosegment neuronal somas' + Description = 'Optimized for viral GCaMP expression' end methods (Static)