From 49302903cc7b6bdbb7f6405290d46790558679af Mon Sep 17 00:00:00 2001 From: Takfarinas Medani Date: Wed, 14 Oct 2020 23:40:03 -0700 Subject: [PATCH 1/6] Add more functionnalities to the leadfield viewer --- toolbox/gui/view_leadfields.m | 355 ++++++++++++++++++++++++++++++---- 1 file changed, 319 insertions(+), 36 deletions(-) diff --git a/toolbox/gui/view_leadfields.m b/toolbox/gui/view_leadfields.m index 07aaea2ad..0b32ca9e4 100644 --- a/toolbox/gui/view_leadfields.m +++ b/toolbox/gui/view_leadfields.m @@ -149,7 +149,13 @@ %% ===== DISPLAY LEADFIELD ===== % Current sensor +useLogScale = false; +useLogScaleLegendMsg = 'Off'; iChannel = 1; +iQuiverSize = 1; +iQuiverWidth = 1; +iThresholdLF = 1; +iBalance = 0; % sense of the threshold DrawArrows(); bst_progress('stop'); @@ -163,16 +169,54 @@ function KeyPress_Callback(hFig, keyEvent) switch (keyEvent.Key) % === LEFT, RIGHT, PAGEUP, PAGEDOWN : Processed by TimeWindow === - case {'leftarrow', 'space', 'uparrow'} - iChannel = iChannel - 1; - case 'pagedown' - iChannel = iChannel - 10; - case {'rightarrow', 'downarrow'} - iChannel = iChannel + 1; - case 'pageup' - iChannel = iChannel + 10; + case {'leftarrow',} + if ismember('shift', keyEvent.Modifier) + iQuiverSize = iQuiverSize /1.2; + elseif ismember('control', keyEvent.Modifier) + iQuiverWidth = iQuiverWidth / 1.2; + elseif ismember('alt', keyEvent.Modifier) + iThresholdLF = iThresholdLF - 0.01; + else + iChannel = iChannel - 1; + end + case {'rightarrow'} + if ismember('shift', keyEvent.Modifier) + iQuiverSize = iQuiverSize * 1.2; + elseif ismember('control', keyEvent.Modifier) + iQuiverWidth = iQuiverWidth * 1.2; + elseif ismember('alt', keyEvent.Modifier) + iThresholdLF = iThresholdLF + 0.01; + else + iChannel = iChannel + 1; + end + case 'uparrow' + if ismember('shift', keyEvent.Modifier) + iQuiverSize = iQuiverSize * 1.2; + elseif ismember('control', keyEvent.Modifier) + iQuiverWidth = iQuiverWidth * 1.2; + elseif ismember('alt', keyEvent.Modifier) + iThresholdLF = iThresholdLF + 0.01; + else + if ~isempty(iRef) + iRef = iRef + 1; + end + end + case 'downarrow' + if ismember('shift', keyEvent.Modifier) + iQuiverSize = iQuiverSize / 1.2; + elseif ismember('control', keyEvent.Modifier) + iQuiverWidth = iQuiverWidth / 1.2; + elseif ismember('alt', keyEvent.Modifier) + iThresholdLF = iThresholdLF - 0.01; + else + if ~isempty(iRef) + iRef = iRef - 1; + end + end case 'r' %% not for MEG SelectReference(); + case 't' %% not for MEG + SelectTarget(); case 's' if ~isempty(findobj(hFig, 'Tag', 'SetVertices')) delete(findobj(hFig, 'Tag', 'SetVertices')) @@ -201,16 +245,79 @@ function KeyPress_Callback(hFig, keyEvent) if ~isempty(findobj(hAxes, 'Tag', 'allChannel')) delete(findobj(hAxes, 'Tag', 'allChannel')) end + + case 'l' + if ismember('shift', keyEvent.Modifier) + useLogScale = ~useLogScale; + if (useLogScale) + useLogScaleLegendMsg = 'On'; + else + useLogScaleLegendMsg = 'Off'; + end + end + + case 'return' + if ismember('shift', keyEvent.Modifier) + useLogScale = ~useLogScale; + if (useLogScale) + useLogScaleLegendMsg = 'On'; + else + useLogScaleLegendMsg = 'Off'; + end + elseif ismember('alt', keyEvent.Modifier) + iBalance = ~iBalance; + else + return; + end case 'h' java_dialog('msgbox', ['' ... - '' .... - ''.... - ''.... - ''.... + '' .... + ''.... + ''.... + ''.... + ''... + ''... + ''... + ''... + ''... ''.... - ''.... + ''.... + ''.... ''.... - '
Left arrowPrevious channel
Right arrowNext channel
Page upPrevious 10th channel
Page downNext 10th channel
Left arrowPrevious target channel (red color)
Right arrowNext target channel (red color)
Up arrowPrevious ref channel (green color)
Down arrowNext ref channel (green color)
Shift + uparrowIncrease the vector length
Shift + downarrowDecrease the vector length
Control + uparrowIncrease the vector width
Control + downarrowDecrease the vector width
Shift + LToggle on/off logarithmic scale
MChange the Modality (MEG, EEG, SEEG, ECOG)
RChange the Reference electrode
RSelect the Reference channel
TSelect the Target channel
SShow/hide the source grid
EShow/hide the sensors
'], 'Keyboard shortcuts'); + 'EShow/hide the sensors'... + '0 to 9Change view'... + ''], 'Keyboard shortcuts'); + + + % === NUMBERS : VIEW SHORTCUTS === + case '0' + SetStandardView(hFig, {'left', 'right', 'top'}); + case '1' + SetStandardView(hFig, 'left'); + + case '2' + SetStandardView(hFig, 'bottom'); + case '3' + SetStandardView(hFig, 'right'); + + case '4' + SetStandardView(hFig, 'front'); + + case '5' + SetStandardView(hFig, 'top'); + + case '6' + SetStandardView(hFig, 'back'); + + case '7' + SetStandardView(hFig, {'left', 'right'}); + + case '8' + SetStandardView(hFig, {'bottom', 'top'}); + + case '9' + SetStandardView(hFig, {'front', 'back'}); + otherwise KeyPressFcn_bak(hQuiver, keyEvent); return; @@ -222,10 +329,16 @@ function KeyPress_Callback(hFig, keyEvent) if (iChannel > length(Channels)) iChannel = 1; end + % Redraw arrows + if (iRef <= 0) + iRef = length(Channels); + end + if (iRef > length(Channels)) + iRef = 1; + end DrawArrows(); end - %% ===== DRAW CURRENT CHANNEL ===== function DrawArrows() % Delete previous Channels and sensors @@ -255,12 +368,34 @@ function DrawArrows() end % Display arrows LeadField = reshape(LeadField,3,[])'; % each column is a vector + if(useLogScale) + LeadField = logScaledLeadField(LeadField); + end + % thresholding + normLF = sqrt((LeadField(:,1) ).^2 +(LeadField(:,2) ).^2 + (LeadField(:,3)).^2); + [col1, ind] = sort(normLF, 'ascend'); + LeadFieldRordered = LeadField(ind,:); + cdf = cumsum(col1); % Compute cdf + cdf = cdf/cdf(end); % Normalize + % Find index bellow or above the thresholding + if iBalance == 0 % 0 ==> inferior and 1 is superior + index = find(cdf <= iThresholdLF); + iSymbole = '<='; + else + index = find(cdf > iThresholdLF); + iSymbole = '>'; + end + dataValue = zeros(size(LeadFieldRordered)); + dataValue(index,:) = LeadFieldRordered(index,:); + hQuiver(iLF) = quiver3(... - HeadmodelMat{iLF}.GridLoc(:,1), HeadmodelMat{iLF}.GridLoc(:,2), HeadmodelMat{iLF}.GridLoc(:,3), ... - LeadField(:,1), LeadField(:,2), LeadField(:,3), ... - 5, ... + ...HeadmodelMat{iLF}.GridLoc(:,1), HeadmodelMat{iLF}.GridLoc(:,2), HeadmodelMat{iLF}.GridLoc(:,3), ... + ...LeadField(:,1), LeadField(:,2), LeadField(:,3), ... + HeadmodelMat{iLF}.GridLoc(ind,1), HeadmodelMat{iLF}.GridLoc(ind,2), HeadmodelMat{iLF}.GridLoc(ind,3), ... + dataValue(:,1), dataValue(:,2), dataValue(:,3), ... + iQuiverSize, ... 'Parent', hAxes, ... - 'LineWidth', 1, ... + 'LineWidth', iQuiverWidth, ... 'Color', ColorOrder(mod(iLF-1, length(ColorOrder)) + 1, :), ... 'Tag', 'lfArrows'); % Arrow legends @@ -299,18 +434,18 @@ function DrawArrows() end % Title bar (channel name) if isAvgRef - strTitle = sprintf('Channel #%d/%d (%s) | %s ref Channel = AvgRef', iChannel, length(Channels), Channels(iChannel).Name,selectedModality); + strTitle = sprintf('Target channel(red) #%d/%d (%s) | %s Ref Channel(green) = AvgRef | iThresholdLF %s %s %%| Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality, iSymbole, num2str(iThresholdLF*100),useLogScaleLegendMsg); else - strTitle = sprintf('Channel #%d/%d (%s) | %s ref Channel = %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality,Channels(iRef).Name); + strTitle = sprintf('Target channel(red) #%d/%d (%s) | %s Ref Channel(green) = %s| iThresholdLF %s %s %%| Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality,Channels(iRef).Name, iSymbole, num2str(iThresholdLF*100),useLogScaleLegendMsg); end else - strTitle = sprintf('Channel #%d/%d (%s)', iChannel, length(Channels), Channels(iChannel).Name); + strTitle = sprintf('Target channel (red) #%d/%d (%s) | iThresholdLF %s %s %%|Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name, iSymbole,num2str(iThresholdLF*100),useLogScaleLegendMsg); end if (iChannel == 1) && (length(Channels) > 1) strTitle = [strTitle, ' [Press arrows for next/previous channel (or H for help)]']; end - set(hLabel, 'String', strTitle, 'Position', [10 1 1200 35]); + set(hLabel, 'String', strTitle, 'Position', [10 1 1500 35],'ForegroundColor', [1 1 1]); % Arrows legend legend(hQuiver, strLegend, ... 'TextColor', 'w', ... @@ -351,32 +486,180 @@ function DrawArrows() function isOk = SelectReference() isOk = 1; if ~strcmp(selectedModality,'MEG') - [isAvgRef, isCancel] = java_dialog('confirm', ... - ['Do you want to use the average refence for the ' selectedModality ' ?
'... - 'Otherwise you will choose one reference electrode.'], [selectedModality ' average reference'], [], ... - {'Yes, use average reference'}, 1); - if isCancel - isOk = 0; - return; - end - if ~isAvgRef +% [isAvgRef, isCancel] = java_dialog('confirm', ... +% ['Do you want to use the average refence for the ' selectedModality ' ?
'... +% 'Otherwise you will choose one reference electrode.'], [selectedModality ' average reference'], [], ... +% {'Yes, use average reference'}, 1); +% if isCancel +% isOk = 0; +% return; +% end +% if ~isAvgRef +% % Ask for the reference electrode +% refChan = java_dialog('combo', 'Select the reference channel:

', [selectedModality ' reference'], [], {Channels.Name}); +% if isempty(refChan) +% isOk = 0; +% return; +% end +% iRef = find(strcmpi({Channels.Name}, refChan)); +% end % Ask for the reference electrode - refChan = java_dialog('combo', 'Select the reference channel:

', [selectedModality ' reference'], [], {Channels.Name}); + refChan = java_dialog('combo', 'Select the reference channel (green color):

', [selectedModality ' reference'], [], {'Average Ref', Channels.Name}); if isempty(refChan) isOk = 0; return; - end + end iRef = find(strcmpi({Channels.Name}, refChan)); - end + if isempty(iRef) + isAvgRef = 1; + else + isAvgRef = 0; + end end end +%% ===== SET TARGET ===== + function isOk = SelectTarget() + isOk = 1; + % Ask for the target electrode + trgChan = java_dialog('combo', 'Select the target channel (red color):

', [selectedModality ' Target'], [], {Channels.Name}); + if isempty(trgChan) + isOk = 0; + return; + end + iChannel = find(strcmpi({Channels.Name}, trgChan)); + end -%% ===== GET LEADFIELD ===== + %% ===== GET LEADFIELD ===== function GetLeadField % Update the LF according to the selected channels only for iLF = 1:length(HeadmodelFiles) LF_finale{iLF} = HeadmodelMat{iLF}.Gain(iChannels,:); end end + +%% ===== LEADFIELD TO LOG SPACE ===== + function lf_log = logScaledLeadField(lf) + lf_2 = lf.^2; + r = sqrt(sum(lf_2,2)); + rho = sqrt(lf_2(:,1) + lf_2(:,2)); + t = atan2(rho,lf(:,3)); + f = atan2(lf(:,2),lf(:,1)); + lf_log = [ log10(r) .* sin(t) .* cos(f) ... + log10(r) .* sin(t) .* sin(f) ... + log10(r) .* cos(t)]; + end + + %= = From this line, these functions are part of the figure3d + %% ===== SET STANDARD VIEW ===== + function SetStandardView(hFig, viewNames) + % Make sure that viewNames is a cell array + if ischar(viewNames) + viewNames = {viewNames}; + end + % Get Axes handle + hAxes = findobj(hFig, '-depth', 1, 'Tag', 'Axes3D'); + % Get the data types displayed in this figure + ColormapInfo = getappdata(hFig, 'Colormap'); + % Get surface information + TessInfo = getappdata(hFig, 'Surface'); + + % ===== ANATOMY ORIENTATION ===== + % If MRI displayed in the figure, use the orientation of the slices, instead of the orientation of the axes + R = eye(3); + % Get the mri surface + Ranat = []; + if ismember('anatomy', ColormapInfo.AllTypes) + iTess = find(strcmpi({TessInfo.Name}, 'Anatomy')); + if ~isempty(iTess) + % Get the subject MRI structure in memory + sMri = bst_memory('GetMri', TessInfo(iTess).SurfaceFile); + % Calculate transformation: SCS => MRI (inverse MRI => SCS) + Ranat = pinv(sMri.SCS.R); + end + end + % Displaying a surface: Load the SCS field from the MRI + if isempty(Ranat) && ~isempty(TessInfo) && ~isempty(TessInfo(1).SurfaceFile) + % Get subject + sSubject = bst_get('SurfaceFile', TessInfo(1).SurfaceFile); + % If there is an MRI associated with it + if ~isempty(sSubject) && ~isempty(sSubject.Anatomy) && ~isempty(sSubject.Anatomy(sSubject.iAnatomy).FileName) + % Load the SCS+MNI transformation from this file + sMri = load(file_fullpath(sSubject.Anatomy(sSubject.iAnatomy).FileName), 'NCS', 'SCS', 'Comment'); + if isfield(sMri, 'NCS') && isfield(sMri.NCS, 'R') && ~isempty(sMri.NCS.R) && isfield(sMri, 'SCS') && isfield(sMri.SCS, 'R') && ~isempty(sMri.SCS.R) + % Calculate the SCS => MNI rotation (inverse(MRI=>SCS) * MRI=>MNI) + Ranat = sMri.NCS.R * pinv(sMri.SCS.R); + end + end + end + % Get the rotation to change orientation + if ~isempty(Ranat) + R = [0 1 0;-1 0 0; 0 0 1] * Ranat; + end + + % ===== MOVE CAMERA ===== + % Apply the first orientation to the target figure + switch lower(viewNames{1}) + case {'left', 'right_intern'} + newView = [0,1,0]; + newCamup = [0 0 1]; + case {'right', 'left_intern'} + newView = [0,-1,0]; + newCamup = [0 0 1]; + case 'back' + newView = [-1,0,0]; + newCamup = [0 0 1]; + case 'front' + newView = [1,0,0]; + newCamup = [0 0 1]; + case 'bottom' + newView = [0,0,-1]; + newCamup = [1 0 0]; + case 'top' + newView = [0,0,1]; + newCamup = [1 0 0]; + end + % Update camera position + view(hAxes, newView * R); + camup(hAxes, double(newCamup * R)); + % Update head light position + camlight(findobj(hAxes, '-depth', 1, 'Tag', 'FrontLight'), 'headlight'); + % Select only one hemisphere + if any(ismember(viewNames, {'right_intern', 'left_intern'})) + bst_figures('SetCurrentFigure', hFig, '3D'); + drawnow; + if strcmpi(viewNames{1}, 'right_intern') + panel_surface('SelectHemispheres', 'right'); + elseif strcmpi(viewNames{1}, 'left_intern') + panel_surface('SelectHemispheres', 'left'); + else + panel_surface('SelectHemispheres', 'none'); + end + end + + % ===== OTHER FIGURES ===== + % If there are other view to represent + if (length(viewNames) > 1) + hClones = bst_figures('GetClones', hFig); + % Process the other required views + for i = 2:length(viewNames) + if ~isempty(hClones) + % Use an already cloned figure + hNewFig = hClones(1); + hClones(1) = []; + else + % Clone figure + hNewFig = bst_figures('CloneFigure', hFig); + end + % Set orientation + SetStandardView(hNewFig, viewNames(i)); + end + % If there are some cloned figures left : close them + if ~isempty(hClones) + close(hClones); + % Update figures layout + gui_layout('Update'); + end + end + end end From 028e72c3f2deb076c3e5380bf96cf7897f29e5da Mon Sep 17 00:00:00 2001 From: Takfarinas Medani Date: Thu, 15 Oct 2020 16:28:24 -0700 Subject: [PATCH 2/6] add documentation to the help panel --- toolbox/gui/view_leadfields.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/toolbox/gui/view_leadfields.m b/toolbox/gui/view_leadfields.m index 0b32ca9e4..c8874d30e 100644 --- a/toolbox/gui/view_leadfields.m +++ b/toolbox/gui/view_leadfields.m @@ -277,17 +277,19 @@ function KeyPress_Callback(hFig, keyEvent) 'Down arrowNext ref channel (green color)'.... 'Shift + uparrowIncrease the vector length'... 'Shift + downarrowDecrease the vector length'... + 'Shift + LToggle on/off logarithmic scale'... 'Control + uparrowIncrease the vector width'... 'Control + downarrowDecrease the vector width'... - 'Shift + LToggle on/off logarithmic scale'... + 'Alt + Enter Toggle to superior/inferior for LF threshold'... + 'Alt + uparrow Increase LF threshold'... + 'Alt + downarrow Decrease LF threshold'... 'MChange the Modality (MEG, EEG, SEEG, ECOG)'.... 'RSelect the Reference channel'.... 'TSelect the Target channel'.... 'SShow/hide the source grid'.... 'EShow/hide the sensors'... '0 to 9Change view'... - ''], 'Keyboard shortcuts'); - + ''], 'Keyboard shortcuts'); % === NUMBERS : VIEW SHORTCUTS === case '0' From 29eed5fb757fb88ef2bca0fa335cd365c488f91d Mon Sep 17 00:00:00 2001 From: Takfarinas Medani Date: Thu, 15 Oct 2020 16:32:41 -0700 Subject: [PATCH 3/6] increase the hLabel displat window --- toolbox/gui/view_leadfields.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolbox/gui/view_leadfields.m b/toolbox/gui/view_leadfields.m index c8874d30e..4116c1b46 100644 --- a/toolbox/gui/view_leadfields.m +++ b/toolbox/gui/view_leadfields.m @@ -447,7 +447,7 @@ function DrawArrows() if (iChannel == 1) && (length(Channels) > 1) strTitle = [strTitle, ' [Press arrows for next/previous channel (or H for help)]']; end - set(hLabel, 'String', strTitle, 'Position', [10 1 1500 35],'ForegroundColor', [1 1 1]); + set(hLabel, 'String', strTitle, 'Position', [10 1 1600 35],'ForegroundColor', [1 1 1]); % Arrows legend legend(hQuiver, strLegend, ... 'TextColor', 'w', ... From 2afe76c1da3b0b0d9954b32cf62e114c68702a45 Mon Sep 17 00:00:00 2001 From: Takfarinas Medani Date: Thu, 15 Oct 2020 17:05:58 -0700 Subject: [PATCH 4/6] fixe the bounding value of the threshold [0,1] --- toolbox/gui/view_leadfields.m | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/toolbox/gui/view_leadfields.m b/toolbox/gui/view_leadfields.m index 4116c1b46..2de385fa7 100644 --- a/toolbox/gui/view_leadfields.m +++ b/toolbox/gui/view_leadfields.m @@ -281,8 +281,8 @@ function KeyPress_Callback(hFig, keyEvent) 'Control + uparrowIncrease the vector width'... 'Control + downarrowDecrease the vector width'... 'Alt + Enter Toggle to superior/inferior for LF threshold'... - 'Alt + uparrow Increase LF threshold'... - 'Alt + downarrow Decrease LF threshold'... + 'Alt + uparrow Increase Amplitude threshold'... + 'Alt + downarrow Decrease Amplitude threshold'... 'MChange the Modality (MEG, EEG, SEEG, ECOG)'.... 'RSelect the Reference channel'.... 'TSelect the Target channel'.... @@ -338,6 +338,14 @@ function KeyPress_Callback(hFig, keyEvent) if (iRef > length(Channels)) iRef = 1; end + + if iThresholdLF <= 0 + iThresholdLF = 0; + end + if iThresholdLF >= 1 + iThresholdLF = 1; + end + DrawArrows(); end @@ -373,10 +381,11 @@ function DrawArrows() if(useLogScale) LeadField = logScaledLeadField(LeadField); end + % thresholding normLF = sqrt((LeadField(:,1) ).^2 +(LeadField(:,2) ).^2 + (LeadField(:,3)).^2); [col1, ind] = sort(normLF, 'ascend'); - LeadFieldRordered = LeadField(ind,:); + LeadFieldReordered = LeadField(ind,:); cdf = cumsum(col1); % Compute cdf cdf = cdf/cdf(end); % Normalize % Find index bellow or above the thresholding @@ -387,11 +396,11 @@ function DrawArrows() index = find(cdf > iThresholdLF); iSymbole = '>'; end - dataValue = zeros(size(LeadFieldRordered)); - dataValue(index,:) = LeadFieldRordered(index,:); + dataValue = zeros(size(LeadFieldReordered)); + dataValue(index,:) = LeadFieldReordered(index,:); hQuiver(iLF) = quiver3(... - ...HeadmodelMat{iLF}.GridLoc(:,1), HeadmodelMat{iLF}.GridLoc(:,2), HeadmodelMat{iLF}.GridLoc(:,3), ... + ...HeadmodelMat{iLF}.GridLoc(:,1), HeadmodelMat{iLF}.GridLoc(:,2), HeadmodelMat{iLF}.GridLoc(:,3), ... % These two line are remaining in order to check if the thresholiding display is correct ...LeadField(:,1), LeadField(:,2), LeadField(:,3), ... HeadmodelMat{iLF}.GridLoc(ind,1), HeadmodelMat{iLF}.GridLoc(ind,2), HeadmodelMat{iLF}.GridLoc(ind,3), ... dataValue(:,1), dataValue(:,2), dataValue(:,3), ... @@ -436,12 +445,12 @@ function DrawArrows() end % Title bar (channel name) if isAvgRef - strTitle = sprintf('Target channel(red) #%d/%d (%s) | %s Ref Channel(green) = AvgRef | iThresholdLF %s %s %%| Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality, iSymbole, num2str(iThresholdLF*100),useLogScaleLegendMsg); + strTitle = sprintf('Target channel(red) #%d/%d (%s) | %s Ref Channel(green) = AvgRef | Amp threshold %s %s %%| Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality, iSymbole, num2str(iThresholdLF*100),useLogScaleLegendMsg); else - strTitle = sprintf('Target channel(red) #%d/%d (%s) | %s Ref Channel(green) = %s| iThresholdLF %s %s %%| Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality,Channels(iRef).Name, iSymbole, num2str(iThresholdLF*100),useLogScaleLegendMsg); + strTitle = sprintf('Target channel(red) #%d/%d (%s) | %s Ref Channel(green) = %s| Amp threshold %s %s %%| Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality,Channels(iRef).Name, iSymbole, num2str(iThresholdLF*100),useLogScaleLegendMsg); end else - strTitle = sprintf('Target channel (red) #%d/%d (%s) | iThresholdLF %s %s %%|Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name, iSymbole,num2str(iThresholdLF*100),useLogScaleLegendMsg); + strTitle = sprintf('Target channel (red) #%d/%d (%s) | Amp threshold %s %s %%|Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name, iSymbole,num2str(iThresholdLF*100),useLogScaleLegendMsg); end if (iChannel == 1) && (length(Channels) > 1) From abe1158024c9f7e562ffe2422b2fdad80742a79a Mon Sep 17 00:00:00 2001 From: Takfarinas Medani Date: Mon, 26 Oct 2020 13:23:30 -0700 Subject: [PATCH 5/6] revision of the function according to the PR discussion + add option to display channels names --- toolbox/gui/view_leadfields.m | 296 +++++++++------------------------- 1 file changed, 79 insertions(+), 217 deletions(-) diff --git a/toolbox/gui/view_leadfields.m b/toolbox/gui/view_leadfields.m index 2de385fa7..598c8a5d5 100644 --- a/toolbox/gui/view_leadfields.m +++ b/toolbox/gui/view_leadfields.m @@ -26,7 +26,7 @@ % =============================================================================@ % % Authors: John Mosher, Takfarinas Medani, Francois Tadel, 2020 - +% Juan Garcia-Prieto : add logarithmic scale for LF vectors %% ===== PARSE INPUTS ===== if ischar(HeadmodelFiles) @@ -152,15 +152,14 @@ useLogScale = false; useLogScaleLegendMsg = 'Off'; iChannel = 1; -iQuiverSize = 1; -iQuiverWidth = 1; -iThresholdLF = 1; -iBalance = 0; % sense of the threshold +% initial value for the quiver display +quiverSize = 1; +quiverWidth = 1; +thresholdAmplitude = 1; % ratio of the amplitude +thresholdBalance = 0; % orientation of the threshold "<" or " >" DrawArrows(); bst_progress('stop'); - - %% ================================================================================= % === INTERNAL CALLBACKS ========================================================== % ================================================================================= @@ -169,33 +168,33 @@ function KeyPress_Callback(hFig, keyEvent) switch (keyEvent.Key) % === LEFT, RIGHT, PAGEUP, PAGEDOWN : Processed by TimeWindow === - case {'leftarrow',} + case {'leftarrow',} if ismember('shift', keyEvent.Modifier) - iQuiverSize = iQuiverSize /1.2; + quiverSize = quiverSize /1.2; elseif ismember('control', keyEvent.Modifier) - iQuiverWidth = iQuiverWidth / 1.2; + quiverWidth = quiverWidth /1.2; elseif ismember('alt', keyEvent.Modifier) - iThresholdLF = iThresholdLF - 0.01; + thresholdAmplitude = thresholdAmplitude - 0.01; else iChannel = iChannel - 1; end case {'rightarrow'} if ismember('shift', keyEvent.Modifier) - iQuiverSize = iQuiverSize * 1.2; + quiverSize = quiverSize * 1.2; elseif ismember('control', keyEvent.Modifier) - iQuiverWidth = iQuiverWidth * 1.2; + quiverWidth = quiverWidth * 1.2; elseif ismember('alt', keyEvent.Modifier) - iThresholdLF = iThresholdLF + 0.01; + thresholdAmplitude = thresholdAmplitude + 0.01; else iChannel = iChannel + 1; end case 'uparrow' if ismember('shift', keyEvent.Modifier) - iQuiverSize = iQuiverSize * 1.2; + quiverSize = quiverSize * 1.2; elseif ismember('control', keyEvent.Modifier) - iQuiverWidth = iQuiverWidth * 1.2; + quiverWidth = quiverWidth * 1.2; elseif ismember('alt', keyEvent.Modifier) - iThresholdLF = iThresholdLF + 0.01; + thresholdAmplitude = thresholdAmplitude + 0.01; else if ~isempty(iRef) iRef = iRef + 1; @@ -203,11 +202,11 @@ function KeyPress_Callback(hFig, keyEvent) end case 'downarrow' if ismember('shift', keyEvent.Modifier) - iQuiverSize = iQuiverSize / 1.2; + quiverSize = quiverSize / 1.2; elseif ismember('control', keyEvent.Modifier) - iQuiverWidth = iQuiverWidth / 1.2; + quiverWidth = quiverWidth / 1.2; elseif ismember('alt', keyEvent.Modifier) - iThresholdLF = iThresholdLF - 0.01; + thresholdAmplitude = thresholdAmplitude - 0.01; else if ~isempty(iRef) iRef = iRef - 1; @@ -227,14 +226,36 @@ function KeyPress_Callback(hFig, keyEvent) 'Tag', 'SetVertices'); end case 'e' - hold on; - % Plot sensors - if ~isempty(findobj(hAxes, 'Tag', 'allChannel')) - delete(findobj(hAxes, 'Tag', 'allChannel')) + if ~ismember('shift', keyEvent.Modifier) + hold on; + % Plot sensors + if ~isempty(findobj(hAxes, 'Tag', 'allChannel')) + delete(findobj(hAxes, 'Tag', 'allChannel')) + else + if length(Channels) > 10 + hSensors = figure_3d('PlotSensorsNet', hAxes, markersLocs, 0, 0); + set(hSensors, 'LineWidth', 1, 'MarkerSize', 5,'Tag','allChannel'); + end + end else - if length(Channels) > 10 - hSensors = figure_3d('PlotSensorsNet', hAxes, markersLocs, 0, 0); - set(hSensors, 'LineWidth', 1, 'MarkerSize', 5,'Tag','allChannel'); + hold on; + % Plot sensors name + if ~isempty(findobj(hAxes, 'Tag', 'allChannelName')) + delete(findobj(hAxes, 'Tag', 'allChannelName')) + else + if length(Channels) > 10 + %hSensors = figure_3d('PlotSensorsNet', hAxes, markersLocs, 0, 0); + %set(hSensors,'Tag','allChannelName'); + channelAllName = cell(length(Channels),1); + for iChan = 1 : length(Channels) + channelAllName{iChan} = Channels(iChan).Name; + end + hold on; + text(markersLocs(:,1), markersLocs(:,2), markersLocs(:,3),channelAllName,... + 'color','y',... + 'Parent', hAxes, ... + 'Tag', 'allChannelName'); + end end end case 'm' @@ -265,7 +286,7 @@ function KeyPress_Callback(hFig, keyEvent) useLogScaleLegendMsg = 'Off'; end elseif ismember('alt', keyEvent.Modifier) - iBalance = ~iBalance; + thresholdBalance = ~thresholdBalance; else return; end @@ -288,40 +309,11 @@ function KeyPress_Callback(hFig, keyEvent) 'TSelect the Target channel'.... 'SShow/hide the source grid'.... 'EShow/hide the sensors'... + 'Shift + EShow/hide the sensors labels'... '0 to 9Change view'... - ''], 'Keyboard shortcuts'); - - % === NUMBERS : VIEW SHORTCUTS === - case '0' - SetStandardView(hFig, {'left', 'right', 'top'}); - case '1' - SetStandardView(hFig, 'left'); - - case '2' - SetStandardView(hFig, 'bottom'); - case '3' - SetStandardView(hFig, 'right'); - - case '4' - SetStandardView(hFig, 'front'); - - case '5' - SetStandardView(hFig, 'top'); - - case '6' - SetStandardView(hFig, 'back'); - - case '7' - SetStandardView(hFig, {'left', 'right'}); - - case '8' - SetStandardView(hFig, {'bottom', 'top'}); - - case '9' - SetStandardView(hFig, {'front', 'back'}); - + ''], 'Keyboard shortcuts'); otherwise - KeyPressFcn_bak(hQuiver, keyEvent); + KeyPressFcn_bak(hFig, keyEvent); return; end % Redraw arrows @@ -339,11 +331,11 @@ function KeyPress_Callback(hFig, keyEvent) iRef = 1; end - if iThresholdLF <= 0 - iThresholdLF = 0; + if thresholdAmplitude <= 0 + thresholdAmplitude = 0; end - if iThresholdLF >= 1 - iThresholdLF = 1; + if thresholdAmplitude >= 1 + thresholdAmplitude = 1; end DrawArrows(); @@ -379,7 +371,7 @@ function DrawArrows() % Display arrows LeadField = reshape(LeadField,3,[])'; % each column is a vector if(useLogScale) - LeadField = logScaledLeadField(LeadField); + LeadField = LogScaleLeadfield(LeadField); end % thresholding @@ -389,24 +381,24 @@ function DrawArrows() cdf = cumsum(col1); % Compute cdf cdf = cdf/cdf(end); % Normalize % Find index bellow or above the thresholding - if iBalance == 0 % 0 ==> inferior and 1 is superior - index = find(cdf <= iThresholdLF); + if thresholdBalance == 0 % 0 ==> inferior and 1 is superior + index = find(cdf <= thresholdAmplitude); iSymbole = '<='; else - index = find(cdf > iThresholdLF); + index = find(cdf > thresholdAmplitude); iSymbole = '>'; end dataValue = zeros(size(LeadFieldReordered)); dataValue(index,:) = LeadFieldReordered(index,:); hQuiver(iLF) = quiver3(... - ...HeadmodelMat{iLF}.GridLoc(:,1), HeadmodelMat{iLF}.GridLoc(:,2), HeadmodelMat{iLF}.GridLoc(:,3), ... % These two line are remaining in order to check if the thresholiding display is correct + ...HeadmodelMat{iLF}.GridLoc(:,1), HeadmodelMat{iLF}.GridLoc(:,2), HeadmodelMat{iLF}.GridLoc(:,3), ... % These two line are remaining in order to check if the thresholding display is correct ...LeadField(:,1), LeadField(:,2), LeadField(:,3), ... HeadmodelMat{iLF}.GridLoc(ind,1), HeadmodelMat{iLF}.GridLoc(ind,2), HeadmodelMat{iLF}.GridLoc(ind,3), ... dataValue(:,1), dataValue(:,2), dataValue(:,3), ... - iQuiverSize, ... + quiverSize, ... 'Parent', hAxes, ... - 'LineWidth', iQuiverWidth, ... + 'LineWidth', quiverWidth, ... 'Color', ColorOrder(mod(iLF-1, length(ColorOrder)) + 1, :), ... 'Tag', 'lfArrows'); % Arrow legends @@ -445,12 +437,12 @@ function DrawArrows() end % Title bar (channel name) if isAvgRef - strTitle = sprintf('Target channel(red) #%d/%d (%s) | %s Ref Channel(green) = AvgRef | Amp threshold %s %s %%| Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality, iSymbole, num2str(iThresholdLF*100),useLogScaleLegendMsg); + strTitle = sprintf('Target channel(red) #%d/%d (%s) | %s Ref Channel(green) = AvgRef | Amp threshold %s %s %%| Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality, iSymbole, num2str(thresholdAmplitude*100),useLogScaleLegendMsg); else - strTitle = sprintf('Target channel(red) #%d/%d (%s) | %s Ref Channel(green) = %s| Amp threshold %s %s %%| Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality,Channels(iRef).Name, iSymbole, num2str(iThresholdLF*100),useLogScaleLegendMsg); + strTitle = sprintf('Target channel(red) #%d/%d (%s) | %s Ref Channel(green) = %s| Amp threshold %s %s %%| Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name,selectedModality,Channels(iRef).Name, iSymbole, num2str(thresholdAmplitude*100),useLogScaleLegendMsg); end else - strTitle = sprintf('Target channel (red) #%d/%d (%s) | Amp threshold %s %s %%|Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name, iSymbole,num2str(iThresholdLF*100),useLogScaleLegendMsg); + strTitle = sprintf('Target channel (red) #%d/%d (%s) | Amp threshold %s %s %%|Log. scale %s', iChannel, length(Channels), Channels(iChannel).Name, iSymbole,num2str(thresholdAmplitude*100),useLogScaleLegendMsg); end if (iChannel == 1) && (length(Channels) > 1) @@ -497,35 +489,18 @@ function DrawArrows() function isOk = SelectReference() isOk = 1; if ~strcmp(selectedModality,'MEG') -% [isAvgRef, isCancel] = java_dialog('confirm', ... -% ['Do you want to use the average refence for the ' selectedModality ' ?
'... -% 'Otherwise you will choose one reference electrode.'], [selectedModality ' average reference'], [], ... -% {'Yes, use average reference'}, 1); -% if isCancel -% isOk = 0; -% return; -% end -% if ~isAvgRef -% % Ask for the reference electrode -% refChan = java_dialog('combo', 'Select the reference channel:

', [selectedModality ' reference'], [], {Channels.Name}); -% if isempty(refChan) -% isOk = 0; -% return; -% end -% iRef = find(strcmpi({Channels.Name}, refChan)); -% end - % Ask for the reference electrode - refChan = java_dialog('combo', 'Select the reference channel (green color):

', [selectedModality ' reference'], [], {'Average Ref', Channels.Name}); - if isempty(refChan) - isOk = 0; - return; - end - iRef = find(strcmpi({Channels.Name}, refChan)); - if isempty(iRef) - isAvgRef = 1; - else - isAvgRef = 0; - end + % Ask for the reference electrode + refChan = java_dialog('combo', 'Select the reference channel (green color):

', [selectedModality ' reference'], [], {'Average Ref', Channels.Name}); + if isempty(refChan) + isOk = 0; + return; + end + iRef = find(strcmpi({Channels.Name}, refChan)); + if isempty(iRef) + isAvgRef = 1; + else + isAvgRef = 0; + end end end @@ -550,7 +525,7 @@ function DrawArrows() end %% ===== LEADFIELD TO LOG SPACE ===== - function lf_log = logScaledLeadField(lf) + function lf_log = LogScaleLeadfield(lf) lf_2 = lf.^2; r = sqrt(sum(lf_2,2)); rho = sqrt(lf_2(:,1) + lf_2(:,2)); @@ -560,117 +535,4 @@ function DrawArrows() log10(r) .* sin(t) .* sin(f) ... log10(r) .* cos(t)]; end - - %= = From this line, these functions are part of the figure3d - %% ===== SET STANDARD VIEW ===== - function SetStandardView(hFig, viewNames) - % Make sure that viewNames is a cell array - if ischar(viewNames) - viewNames = {viewNames}; - end - % Get Axes handle - hAxes = findobj(hFig, '-depth', 1, 'Tag', 'Axes3D'); - % Get the data types displayed in this figure - ColormapInfo = getappdata(hFig, 'Colormap'); - % Get surface information - TessInfo = getappdata(hFig, 'Surface'); - - % ===== ANATOMY ORIENTATION ===== - % If MRI displayed in the figure, use the orientation of the slices, instead of the orientation of the axes - R = eye(3); - % Get the mri surface - Ranat = []; - if ismember('anatomy', ColormapInfo.AllTypes) - iTess = find(strcmpi({TessInfo.Name}, 'Anatomy')); - if ~isempty(iTess) - % Get the subject MRI structure in memory - sMri = bst_memory('GetMri', TessInfo(iTess).SurfaceFile); - % Calculate transformation: SCS => MRI (inverse MRI => SCS) - Ranat = pinv(sMri.SCS.R); - end - end - % Displaying a surface: Load the SCS field from the MRI - if isempty(Ranat) && ~isempty(TessInfo) && ~isempty(TessInfo(1).SurfaceFile) - % Get subject - sSubject = bst_get('SurfaceFile', TessInfo(1).SurfaceFile); - % If there is an MRI associated with it - if ~isempty(sSubject) && ~isempty(sSubject.Anatomy) && ~isempty(sSubject.Anatomy(sSubject.iAnatomy).FileName) - % Load the SCS+MNI transformation from this file - sMri = load(file_fullpath(sSubject.Anatomy(sSubject.iAnatomy).FileName), 'NCS', 'SCS', 'Comment'); - if isfield(sMri, 'NCS') && isfield(sMri.NCS, 'R') && ~isempty(sMri.NCS.R) && isfield(sMri, 'SCS') && isfield(sMri.SCS, 'R') && ~isempty(sMri.SCS.R) - % Calculate the SCS => MNI rotation (inverse(MRI=>SCS) * MRI=>MNI) - Ranat = sMri.NCS.R * pinv(sMri.SCS.R); - end - end - end - % Get the rotation to change orientation - if ~isempty(Ranat) - R = [0 1 0;-1 0 0; 0 0 1] * Ranat; - end - - % ===== MOVE CAMERA ===== - % Apply the first orientation to the target figure - switch lower(viewNames{1}) - case {'left', 'right_intern'} - newView = [0,1,0]; - newCamup = [0 0 1]; - case {'right', 'left_intern'} - newView = [0,-1,0]; - newCamup = [0 0 1]; - case 'back' - newView = [-1,0,0]; - newCamup = [0 0 1]; - case 'front' - newView = [1,0,0]; - newCamup = [0 0 1]; - case 'bottom' - newView = [0,0,-1]; - newCamup = [1 0 0]; - case 'top' - newView = [0,0,1]; - newCamup = [1 0 0]; - end - % Update camera position - view(hAxes, newView * R); - camup(hAxes, double(newCamup * R)); - % Update head light position - camlight(findobj(hAxes, '-depth', 1, 'Tag', 'FrontLight'), 'headlight'); - % Select only one hemisphere - if any(ismember(viewNames, {'right_intern', 'left_intern'})) - bst_figures('SetCurrentFigure', hFig, '3D'); - drawnow; - if strcmpi(viewNames{1}, 'right_intern') - panel_surface('SelectHemispheres', 'right'); - elseif strcmpi(viewNames{1}, 'left_intern') - panel_surface('SelectHemispheres', 'left'); - else - panel_surface('SelectHemispheres', 'none'); - end - end - - % ===== OTHER FIGURES ===== - % If there are other view to represent - if (length(viewNames) > 1) - hClones = bst_figures('GetClones', hFig); - % Process the other required views - for i = 2:length(viewNames) - if ~isempty(hClones) - % Use an already cloned figure - hNewFig = hClones(1); - hClones(1) = []; - else - % Clone figure - hNewFig = bst_figures('CloneFigure', hFig); - end - % Set orientation - SetStandardView(hNewFig, viewNames(i)); - end - % If there are some cloned figures left : close them - if ~isempty(hClones) - close(hClones); - % Update figures layout - gui_layout('Update'); - end - end - end end From 0baead95bc1d52e192717099171af587f7f449b8 Mon Sep 17 00:00:00 2001 From: Francois Date: Sat, 31 Oct 2020 13:40:22 +0100 Subject: [PATCH 6/6] Removed extra "hold on" --- toolbox/gui/view_leadfields.m | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/toolbox/gui/view_leadfields.m b/toolbox/gui/view_leadfields.m index 598c8a5d5..6849d45d1 100644 --- a/toolbox/gui/view_leadfields.m +++ b/toolbox/gui/view_leadfields.m @@ -220,14 +220,12 @@ function KeyPress_Callback(hFig, keyEvent) if ~isempty(findobj(hFig, 'Tag', 'SetVertices')) delete(findobj(hFig, 'Tag', 'SetVertices')) else - hold on; plot3(HeadmodelMat{1}.GridLoc(:,1), HeadmodelMat{1}.GridLoc(:,2), HeadmodelMat{1}.GridLoc(:,3), 'r.', ... 'Parent', hAxes, ... 'Tag', 'SetVertices'); end case 'e' if ~ismember('shift', keyEvent.Modifier) - hold on; % Plot sensors if ~isempty(findobj(hAxes, 'Tag', 'allChannel')) delete(findobj(hAxes, 'Tag', 'allChannel')) @@ -238,7 +236,6 @@ function KeyPress_Callback(hFig, keyEvent) end end else - hold on; % Plot sensors name if ~isempty(findobj(hAxes, 'Tag', 'allChannelName')) delete(findobj(hAxes, 'Tag', 'allChannelName')) @@ -250,7 +247,6 @@ function KeyPress_Callback(hFig, keyEvent) for iChan = 1 : length(Channels) channelAllName{iChan} = Channels(iChan).Name; end - hold on; text(markersLocs(:,1), markersLocs(:,2), markersLocs(:,3),channelAllName,... 'color','y',... 'Parent', hAxes, ... @@ -403,7 +399,6 @@ function DrawArrows() 'Tag', 'lfArrows'); % Arrow legends strLegend{iLF} = [SubjectName{iLF} ' : ' selectedModality ' ' HeadmodelMat{iLF}.Comment]; - hold on end % Remove previous selected sensor @@ -516,7 +511,7 @@ function DrawArrows() iChannel = find(strcmpi({Channels.Name}, trgChan)); end - %% ===== GET LEADFIELD ===== +%% ===== GET LEADFIELD ===== function GetLeadField % Update the LF according to the selected channels only for iLF = 1:length(HeadmodelFiles)