Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thresholding of isosurface without creating a new one #31

Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 38 additions & 20 deletions toolbox/anatomy/tess_isosurface.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
MeshFile = [];
iSurface = [];
isSave = true;
global GlobalData;

% Parse inputs
if (nargin < 3) || isempty(Comment)
Expand Down Expand Up @@ -128,6 +129,7 @@

%% ===== CREATE SURFACE =====
% Compute isosurface
sMesh = db_template('LoadedSurface');
bst_progress('start', 'Generate thresholded isosurface from CT', 'Creating isosurface...');
[sMesh.Faces, sMesh.Vertices] = mri_isosurface(sMri.Cube, isoValue);
bst_progress('inc', 10);
Expand All @@ -145,6 +147,8 @@
sMesh.Vertices = bst_bsxfun(@times, sMesh.Vertices, sMri.Voxsize);
% Convert to SCS
sMesh.Vertices = cs_convert(sMri, 'mri', 'scs', sMesh.Vertices ./ 1000);
sMesh.Comment = sprintf('isoSurface (ISO_%d)', isoValue);
sMesh.VertConn = tess_vertconn(sMesh.Vertices, sMesh.Faces);

%% ===== SAVE FILES =====
if isSave
Expand All @@ -154,28 +158,42 @@
SurfaceDir = bst_fullfile(ProtocolInfo.SUBJECTS, bst_fileparts(CtFile));
% Get the mesh file
MeshFile = bst_fullfile(SurfaceDir, 'tess_isosurface.mat');

% Replace existing isoSurface surface (tess_isosurface.mat)
[sSubjectTmp, iSubjectTmp, iSurfaceTmp] = bst_get('SurfaceFile', MeshFile);
if ~isempty(iSurfaceTmp)
file_delete(file_fullpath(MeshFile), 1);
sSubjectTmp.Surface(iSurfaceTmp) = [];
bst_set('Subject', iSubjectTmp, sSubjectTmp);
end
% Get isoSurface (tess_isosurface.mat)
[~, ~, iSurface] = bst_get('SurfaceFile', MeshFile);

% Save isosurface
sMesh.Comment = sprintf('isoSurface (ISO_%d)', isoValue);
sMesh = bst_history('add', sMesh, 'threshold_ct', 'CT thresholded isosurface generated with Brainstorm');
bst_save(MeshFile, sMesh, 'v7');
iSurface = db_add_surface(iSubject, MeshFile, sMesh.Comment);
% Display mesh with 3D orthogonal slices of the default MRI
MriFile = sSubject.Anatomy(1).FileName;
hFig = bst_figures('GetFiguresByType', '3DViz');
if isempty(hFig)
hFig = view_mri_3d(MriFile, [], 0.3, []);
sMesh.FileName = file_short(MeshFile);
sMesh.Name = 'Other';
% if isosurface not created create it
if isempty(iSurface)
sMesh = bst_history('add', sMesh, 'threshold_ct', 'CT thresholded isosurface generated with Brainstorm');
bst_save(MeshFile, sMesh, 'v7');
iSurface = db_add_surface(iSubject, MeshFile, sMesh.Comment);
MriFile = sSubject.Anatomy(1).FileName;
hFig = bst_figures('GetFiguresByType', '3DViz');
if isempty(hFig)
hFig = view_mri_3d(MriFile, [], 0.3, []);
end
view_surface(MeshFile, [], [], hFig, []);
else % else just update the isosurface surface patch with new computed values
% update the surface displayed in figure
hFig = bst_figures('GetFiguresByType', '3DViz');
TessInfo = getappdata(hFig, 'Surface');
iSurfPatch = find(cellfun(@(c)(~isempty(strfind(char(c), 'tess_isosurface'))), {TessInfo.SurfaceFile}));
set(TessInfo(iSurfPatch).hPatch, 'Vertices', sMesh.Vertices);
set(TessInfo(iSurfPatch).hPatch, 'Faces', sMesh.Faces);
set(TessInfo(iSurfPatch).hPatch, 'FaceVertexCData', ones(size(sMesh.Vertices)));
TessInfo(iSurfPatch).nVertices = size(sMesh.Vertices, 1);
TessInfo(iSurfPatch).nFaces = size(sMesh.Faces, 1);
setappdata(hFig, 'Surface', TessInfo);
% update the GlobalData with new surface
iSurfGlobal = find(file_compare({GlobalData.Surface.FileName}, sMesh.FileName));
GlobalData.Surface(iSurfGlobal) = sMesh;
% update the surface in tess_isosurface.mat
sMesh = bst_history('add', sMesh, 'threshold_ct', 'CT thresholded isosurface updated');
bst_save(MeshFile, sMesh, 'v7');
% reload the subject to reflect updated values
db_reload_subjects(iSubject);
end
view_surface(MeshFile, 0.6, [], hFig, []);
panel_surface('SetIsoValue', isoValue);
else
% Return surface
MeshFile = sMesh.Vertices;
Expand Down
55 changes: 54 additions & 1 deletion toolbox/gui/figure_3d.m
Original file line number Diff line number Diff line change
Expand Up @@ -1071,8 +1071,28 @@ function FigureKeyPressedCallback(hFig, keyEvent)
case {'uparrow', 'downarrow'}
% If there are tensors displayed: update display
Handles = bst_figures('GetFigureHandles', hFig);
TessInfo = getappdata(hFig, 'Surface');
iSurf = find(cellfun(@(c)(~isempty(strfind(char(c), 'tess_isosurface'))), {TessInfo.SurfaceFile}));
if isfield(Handles, 'TensorDisplay') && ~isempty(Handles.TensorDisplay)
PlotTensorCut(hFig, [], [], [], keyEvent.Key, []);
% isovalue
elseif ismember('shift', keyEvent.Modifier) && ~isempty(iSurf)
SubjectFile = getappdata(hFig, 'SubjectFile');
sSubject = bst_get('Subject', SubjectFile);
iCt = find(cellfun(@(c)(~isempty(strfind(char(c), '_volct'))), {sSubject.Anatomy.FileName}));
CtFile = sSubject.Anatomy(iCt(1)).FileName;
sCt = bst_memory('LoadMri', CtFile);
sSurface = bst_memory('LoadSurface', TessInfo(iSurf(1)).SurfaceFile);
val = regexp(sSurface.Comment, '\d+', 'match');
if strcmpi(keyEvent.Key, 'uparrow')
if (str2double(val(1))+100 >= double(sCt.Histogram.whiteLevel)) && (str2double(val(1))+100 <= double(sCt.Histogram.intensityMax))
tess_isosurface(CtFile, str2double(val(1))+100);
end
else
if (str2double(val(1))-100 >= double(sCt.Histogram.whiteLevel)) && (str2double(val(1))-100 <= double(sCt.Histogram.intensityMax))
tess_isosurface(CtFile, str2double(val(1))-100);
end
end
% Up/Down: Process by Freq panel
else
panel_freq('FreqKeyCallback', keyEvent);
Expand Down Expand Up @@ -1556,6 +1576,8 @@ function ApplyViewToAllFigures(hSrcFig, isView, isSurfProp)
%% ===== POPUP MENU =====
% Show a popup dialog about the target 3DViz figure
function DisplayFigurePopup(hFig)
import java.awt.*;
import javax.swing.*;
import java.awt.event.KeyEvent;
import javax.swing.KeyStroke;
import org.brainstorm.icon.*;
Expand Down Expand Up @@ -1662,6 +1684,37 @@ function DisplayFigurePopup(hFig)
end
jPopup.addSeparator();
end

% ==== MENU: ISOSURFACE ====
% Create isosurface
iSurf = find(cellfun(@(c)(~isempty(strfind(char(c), 'tess_isosurface'))), {TessInfo.SurfaceFile}));
if ~isempty(iSurf)
% get the CT file details
SubjectFile = getappdata(hFig, 'SubjectFile');
sSubject = bst_get('Subject', SubjectFile);
iCt = find(cellfun(@(c)(~isempty(strfind(char(c), '_volct'))), {sSubject.Anatomy.FileName}));
CtFile = sSubject.Anatomy(iCt(1)).FileName;
sCt = bst_memory('LoadMri', CtFile);
% panel operations
jPanel = gui_component('Panel');
jPanel.setOpaque(0);
jPopup.add(jPanel);
% Title
jLabel = gui_component('label', [], '', '<HTML><B>isoValue threshold </B>', IconLoader.ICON_SURFACE);
jPanel.add(jLabel, BorderLayout.WEST);
% Spin button
sSurface = bst_memory('LoadSurface', TessInfo(iSurf).SurfaceFile);
val = regexp(sSurface.Comment, '\d+', 'match');
spinmodel = SpinnerNumberModel(str2double(val(1)), double(sCt.Histogram.whiteLevel), double(sCt.Histogram.intensityMax), 50);
jSpinner = JSpinner(spinmodel);
jSpinner.setPreferredSize(Dimension(55,25));
% jSpinner.setToolTipText(strTooltip);
java_setcb(spinmodel, 'StateChangedCallback', @(h,ev)tess_isosurface(CtFile, jSpinner.getValue()));
jPanel.add(jSpinner, BorderLayout.EAST);
% jButtonSet = gui_component('button', [], '', 'Set', [], [], @(h,ev)tess_isosurface(CtFile, jSpinner.getValue()));
Copy link
Collaborator Author

@chinmaychinara91 chinmaychinara91 Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for commit 1b99a59 also removed the need to another button click

% jButtonSet.setPreferredSize(Dimension(55,25));
% jPanel.add(jButtonSet, BorderLayout.EAST);
end

% ==== MENU: 2DLAYOUT ====
if strcmpi(FigureType, 'Topography') && strcmpi(GlobalData.DataSet(iDS).Figure(iFig).Id.SubType, '2DLayout')
Expand Down Expand Up @@ -1853,7 +1906,7 @@ function DisplayFigurePopup(hFig)
jMenuMontage = gui_component('Menu', jPopup, [], 'Montage', IconLoader.ICON_TS_DISPLAY_MODE);
panel_montage('CreateFigurePopupMenu', jMenuMontage, hFig);
end

% ==== MENU: COLORMAPS ====
% Create the colormaps menus
bst_colormaps('CreateAllMenus', jPopup, hFig, 0);
Expand Down
105 changes: 0 additions & 105 deletions toolbox/gui/panel_surface.m
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,6 @@
% Quick preview
java_setcb(jSliderSurfSmoothValue, 'StateChangedCallback', @(h,ev)SliderQuickPreview(jSliderSurfSmoothValue, jLabelSurfSmoothValue, 1));

% Threshold title
jLabelSurfIsoValueTitle = gui_component('label', jPanelSurfaceOptions, 'br', 'Thresh.:');
% Min size slider
jSliderSurfIsoValue = JSlider(1, GetIsoValueMaxRange(), 1);
jSliderSurfIsoValue.setPreferredSize(Dimension(SLIDER_WIDTH, DEFAULT_HEIGHT));
jSliderSurfIsoValue.setToolTipText('isoSurface Threshold');
java_setcb(jSliderSurfIsoValue, 'MouseReleasedCallback', @(h,ev)SliderCallback(h, ev, 'SurfIsoValue'), ...
'KeyPressedCallback', @(h,ev)SliderCallback(h, ev, 'SurfIsoValue'));
jPanelSurfaceOptions.add('tab hfill', jSliderSurfIsoValue);
% IsoValue label
jLabelSurfIsoValue = gui_component('label', jPanelSurfaceOptions, [], ' 1', {JLabel.RIGHT, Dimension(LABEL_WIDTH, DEFAULT_HEIGHT)});
% Quick preview
java_setcb(jSliderSurfIsoValue, 'StateChangedCallback', @(h,ev)SliderQuickPreview(jSliderSurfIsoValue, jLabelSurfIsoValue, 0));

% Buttons
jButtonSurfColor = gui_component('button', jPanelSurfaceOptions, 'br center', 'Color', {Dimension(BUTTON_WIDTH, DEFAULT_HEIGHT), Insets(0,0,0,0)}, 'Set surface color', @ButtonSurfColorCallback);
jButtonSurfSulci = gui_component('toggle', jPanelSurfaceOptions, '', 'Sulci', {Dimension(BUTTON_WIDTH, DEFAULT_HEIGHT), Insets(0,0,0,0)}, 'Show/hide sulci map', @ButtonShowSulciCallback);
Expand Down Expand Up @@ -228,9 +214,6 @@
'jButtonSurfColor', jButtonSurfColor, ...
'jLabelSurfSmoothValue', jLabelSurfSmoothValue, ...
'jSliderSurfSmoothValue', jSliderSurfSmoothValue, ...
'jLabelSurfIsoValueTitle',jLabelSurfIsoValueTitle, ...
'jLabelSurfIsoValue', jLabelSurfIsoValue, ...
'jSliderSurfIsoValue', jSliderSurfIsoValue, ...
'jButtonSurfSulci', jButtonSurfSulci, ...
'jButtonSurfEdge', jButtonSurfEdge, ...
'jSliderResectX', jSliderResectX, ...
Expand Down Expand Up @@ -258,8 +241,6 @@ function SliderQuickPreview(jSlider, jText, isPercent)
nVertices = str2num(char(jLabelNbVertices.getText()));
sliderSizeVector = GetSliderSizeVector(nVertices);
jText.setText(sprintf('%d', sliderSizeVector(double(jSlider.getValue()))));
elseif (jSlider == jSliderSurfIsoValue)
jText.setText(sprintf('%d', double(jSlider.getValue())));
elseif isPercent
jText.setText(sprintf('%d%%', double(jSlider.getValue())));
else
Expand Down Expand Up @@ -410,42 +391,6 @@ function SliderCallback(hObject, event, target)
case 'SurfSmoothValue'
SurfSmoothValue = jSlider.getValue() / 100;
SetSurfaceSmooth(hFig, iSurface, SurfSmoothValue, 1);

case 'SurfIsoValue'
% get the handles
hFig = bst_figures('GetFiguresByType', '3DViz');
SubjectFile = getappdata(hFig, 'SubjectFile');
if ~isempty(SubjectFile)
sSubject = bst_get('Subject', SubjectFile);
CtFile = [];
MeshFile = [];
for i=1:length(sSubject.Anatomy)
if ~isempty(regexp(sSubject.Anatomy(i).FileName, 'CT', 'match'))
CtFile = sSubject.Anatomy(i).FileName;
end
end
for i=1:length(sSubject.Surface)
if ~isempty(regexp(sSubject.Surface(i).FileName, 'tess_isosurface', 'match'))
MeshFile = sSubject.Surface(i).FileName;
end
end
end

% ask user if they want to proceed
isProceed = java_dialog('confirm', 'Do you want to proceed generating mesh with new isoValue ?', 'Changing threshold');
if ~isProceed
[sSubjectTmp, iSubjectTmp, iSurfaceTmp] = bst_get('SurfaceFile', MeshFile);
isoValue = regexp(sSubjectTmp.Surface(iSurfaceTmp).Comment, '\d*', 'match');
SetIsoValue(str2double(isoValue{1}));
return;
end

% get the iso value from slider
isoValue = jSlider.getValue();

% remove the old isosurface and generate and load the new one
ButtonRemoveSurfaceCallback();
tess_isosurface(CtFile, isoValue);

case 'DataAlpha'
% Update value in Surface array
Expand Down Expand Up @@ -528,42 +473,6 @@ function SliderCallback(hObject, event, target)
end
end

%% ===== GET SLIDER ISOVALUE =====
function isoValue = GetIsoValueMaxRange()
% get the handles
hFig = bst_figures('GetFiguresByType', '3DViz');
if ~isempty(hFig)
SubjectFile = getappdata(hFig, 'SubjectFile');
if ~isempty(SubjectFile)
sSubject = bst_get('Subject', SubjectFile);
CtFile = [];
for i=1:length(sSubject.Anatomy)
if ~isempty(regexp(sSubject.Anatomy(i).FileName, 'CT', 'match'))
CtFile = sSubject.Anatomy(i).FileName;
end
end
end

if ~isempty(CtFile)
sMri = bst_memory('LoadMri', CtFile);
isoValue = double(sMri.Histogram.intensityMax);
end
else
isoValue = 4500.0;
end
end

%% ===== SET SLIDER ISOVALUE =====
function SetIsoValue(isoValue)
% get panel controls
ctrl = bst_get('PanelControls', 'Surface');
if isempty(ctrl)
return;
end
ctrl.jLabelSurfIsoValue.setText(sprintf('%d', isoValue));
ctrl.jSliderSurfIsoValue.setValue(isoValue);
end

%% ===== SCROLL MRI CUTS =====
function ScrollMriCuts(hFig, direction, value) %#ok<DEFNU>
% Get Mri and figure Handles
Expand Down Expand Up @@ -1132,11 +1041,6 @@ function UpdateSurfaceProperties()
end
% If surface is sliced MRI
isAnatomy = strcmpi(TessInfo(iSurface).Name, 'Anatomy');
if ~isempty(regexp(TessInfo(iSurface).SurfaceFile, 'isosurface', 'match'))
isIsoSurface = 1;
else
isIsoSurface = 0;
end

% ==== Surface properties ====
% Number of vertices
Expand All @@ -1152,15 +1056,6 @@ function UpdateSurfaceProperties()
% Surface smoothing ALPHA
ctrl.jSliderSurfSmoothValue.setValue(100 * TessInfo(iSurface).SurfSmoothValue);
ctrl.jLabelSurfSmoothValue.setText(sprintf('%d%%', round(100 * TessInfo(iSurface).SurfSmoothValue)));
% Show/hide isoSurface thresholding
ctrl.jSliderSurfIsoValue.setVisible(isIsoSurface);
ctrl.jLabelSurfIsoValueTitle.setVisible(isIsoSurface);
ctrl.jLabelSurfIsoValue.setVisible(isIsoSurface);
if isIsoSurface
[sSubjectTmp, iSubjectTmp, iSurfaceTmp] = bst_get('SurfaceFile', TessInfo(iSurface).SurfaceFile);
isoValue = regexp(sSubjectTmp.Surface(iSurfaceTmp).Comment, '\d*', 'match');
SetIsoValue(str2double(isoValue{1}));
end
% Show sulci button
ctrl.jButtonSurfSulci.setSelected(TessInfo(iSurface).SurfShowSulci);
% Show surface edges button
Expand Down
6 changes: 4 additions & 2 deletions toolbox/tree/tree_callbacks.m
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,13 @@
% Get displayable modalities for this file
[tmp, DisplayMod] = bst_get('ChannelModalities', filenameRelative);
DisplayMod = intersect(DisplayMod, {'EEG','MEG','MEG GRAD','MEG MAG','ECOG','SEEG','ECOG+SEEG','NIRS'});
hFig = bst_figures('GetFiguresByType','3DViz');
% If only one modality
if ~isempty(DisplayMod)
if strcmpi(DisplayMod{1}, 'ECOG+SEEG') || (length(DisplayMod) >= 2) && all(ismember({'SEEG','ECOG'}, DisplayMod))
DisplayChannels(bstNodes, 'ECOG+SEEG', 'cortex', 1);
elseif strcmpi(DisplayMod{1}, 'SEEG')
DisplayChannels(bstNodes, DisplayMod{1}, 'anatomy', 1, 0);
DisplayChannels(bstNodes, DisplayMod{1}, 'anatomy', 1, 0, hFig);
elseif strcmpi(DisplayMod{1}, 'ECOG')
DisplayChannels(bstNodes, DisplayMod{1}, 'cortex', 1);
elseif ismember(DisplayMod{1}, {'MEG','MEG GRAD','MEG MAG'})
Expand Down Expand Up @@ -835,6 +836,7 @@
% Get avaible modalities for this data file
[AllMod, DisplayMod] = bst_get('ChannelModalities', filenameRelative);
Device = bst_get('ChannelDevice', filenameRelative);
hFig = bst_figures('GetFiguresByType','3DViz');
% Replace SEEG+ECOG with iEEG
if ~isempty(AllMod) && all(ismember({'SEEG','ECOG'}, AllMod))
AllMod = cat(2, {'ECOG+SEEG'}, setdiff(AllMod, {'SEEG','ECOG'}));
Expand Down Expand Up @@ -923,7 +925,7 @@
end
% === SEEG IMPLANTATION ===
if (length(bstNodes) == 1) && ((isempty(AllMod) && strcmpi(sStudy.Name, 'implantation')) || any(ismember({'SEEG','ECOG','ECOG+SEEG'}, AllMod)))
gui_component('MenuItem', jPopup, [], 'SEEG/ECOG implantation', IconLoader.ICON_SEEG_DEPTH, [], @(h,ev)DisplayChannels(bstNodes, 'SEEG', 'anatomy', 1, 0));
gui_component('MenuItem', jPopup, [], 'SEEG/ECOG implantation', IconLoader.ICON_SEEG_DEPTH, [], @(h,ev)DisplayChannels(bstNodes, 'SEEG', 'anatomy', 1, 0, hFig));
end
% === SEEG CONTACT LABELLING ===
if (length(bstNodes) == 1) && ~isempty(AllMod) && any(ismember({'SEEG','ECOG','ECOG+SEEG'}, AllMod))
Expand Down