Skip to content

Commit

Permalink
Donders-Institute#25 Skull indices are used instead of new layer labels
Browse files Browse the repository at this point in the history
The segmentation in SimNibs is now done using Charm instead of headreco. Therefore, the labels of every tissue are different. This is fixed and the code is also made more robust, so less hardcoded label values.
  • Loading branch information
MaCuinea committed Jul 27, 2023
1 parent 5a8a2e9 commit f509512
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 56 deletions.
127 changes: 82 additions & 45 deletions functions/smooth_and_crop_layered.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,72 +19,107 @@
labels = fieldnames(parameters.layer_labels);
% Adds a smoothing threshold to bone and other non-water tissue.

layer_labels = parameters.layer_labels;

% labels to be determined
default_value = -1; % default label value so in mask non found values can be distinguished
water_label = default_value;
brain_label = default_value;
skin_label = default_value;
cortical_label = default_value;
trabecular_label = default_value;
all_skull_labels = default_value; % combine all skull masks for smoothing
for label_i = 1:length(labels)
sim_nibs_layers = layer_labels.(labels{label_i});
smooth_threshold = parameters.other_smooth_threshold;

if strcmp(labels{label_i}, 'water')
continue
end
sim_nibs_layers = parameters.layer_labels.(labels{label_i});
layer_mask = ismember(segmented_img, sim_nibs_layers);
if contains(labels{label_i}, 'skull')
smooth_threshold = parameters.skull_smooth_threshold;
if any(contains(labels, 'skull_cortical')) % two bone types are smoothed together later
continue
water_label = sim_nibs_layers;
continue
elseif strcmp(labels{label_i}, 'brain')
brain_label = sim_nibs_layers;
elseif strcmp(labels{label_i}, 'skin')
skin_label = sim_nibs_layers;
elseif contains(labels{label_i}, 'skull')
if strcmp(labels{label_i}, 'skull_cortical')
cortical_label = sim_nibs_layers;
% if all_skull_labels is equal to default, remove default
% value
if all_skull_labels == default_value
all_skull_labels = cortical_label;
else
all_skull_labels = [all_skull_labels cortical_label];
end
continue % two bone types are smoothed together later
elseif strcmp(labels{label_i}, 'skull_trabecular')
trabecular_label = sim_nibs_layers;
% if all_skull_labels is equal to default, remove default
% value
if all_skull_labels == default_value
all_skull_labels = trabecular_label;
else
all_skull_labels = [all_skull_labels trabecular_label];
end
continue % two bone types are smoothed together later
else
smooth_threshold = parameters.skull_smooth_threshold; % override smooth threshold
% if all_skull_labels is equal to default, remove default
% value
if all_skull_labels == default_value
all_skull_labels = sim_nibs_layers;
else
all_skull_labels = [all_skull_labels sim_nibs_layers];
end
end
else
smooth_threshold = parameters.other_smooth_threshold;
end

layer_mask_smoothed = smooth_img(layer_mask, windowSize, smooth_threshold);
smoothed_segmented_img(layer_mask_smoothed~=0) = label_i;

% smooth mask per layer
for layer = sim_nibs_layers
layer_mask = ismember(segmented_img, layer); % determine mask
layer_mask_smoothed = smooth_img(layer_mask, windowSize, smooth_threshold);
smoothed_segmented_img(layer_mask_smoothed~=0) = layer;
end
end

% fill gaps in the skull by using the boundary of a bone image

if any(contains(labels, 'skull_cortical'))
skull_i = find(strcmp(labels, 'skull_cortical')); % gives to skull_i the index of skull_cortical in labels array

% combine all skull masks for smoothing
% 1) find all skull_layer ids
layer_labels = parameters.layer_labels;
all_skull_ids = [];
for label_i = find(contains(labels, 'skull'))'
all_skull_ids = [all_skull_ids layer_labels.(labels{label_i})];
end
layer_mask = ismember(segmented_img, all_skull_ids);
layer_mask = ismember(segmented_img, all_skull_labels);
smooth_threshold = parameters.skull_smooth_threshold;
layer_mask_smoothed = smooth_img(layer_mask, windowSize, smooth_threshold);
smoothed_segmented_img(layer_mask_smoothed~=0) = skull_i; % add it to image as cortical bone
trabecular_i = find(strcmp(labels, 'skull_trabecular'));
trabecular_mask = ismember(segmented_img, parameters.layer_labels.(labels{trabecular_i}));
smoothed_segmented_img(layer_mask_smoothed~=0) = cortical_label; % add it to image as cortical bone

trabecular_mask = ismember(segmented_img, trabecular_label);
trabecular_mask_smoothed = smooth_img(trabecular_mask, windowSize, smooth_threshold);
smoothed_segmented_img(trabecular_mask_smoothed~=0) = trabecular_i;

else
skull_i = find(strcmp(labels, 'skull')); % gives to skull_i the index of skull in labels array, because find finds the index of each non zero element --> where labels == 'skull'
smoothed_segmented_img(trabecular_mask_smoothed~=0) = trabecular_label; % add it to image as trabecular bone
end

imshowpair(label2rgb(squeeze(segmented_img(:,trans_pos_upsampled_grid(2),:))), label2rgb(squeeze(smoothed_segmented_img(:,trans_pos_upsampled_grid(2),:))), 'montage')
output_plot = fullfile(parameters.output_dir,sprintf('sub-%03d_%s_segmented_img_before_after_smoothing%s.png', parameters.subject_id, parameters.simulation_medium, parameters.results_filename_affix));
title('Original (left) and smoothed (right) segmented image')
export_fig(output_plot, '-native')

if any(contains(labels, 'skull'))
skull = smoothed_segmented_img==skull_i;
% skull is 1 when smoothed segm img == skull_i (=4 by default in headreco) and it is 0 otherwise
if any(contains(labels, 'skull'))
% get skull mask (trabecular and cortical)
skull = ismember(smoothed_segmented_img, all_skull_labels);

% skull is 1 when smoothed segm img == all_skull_labels (=4 by default in headreco, =7,8 by default in charm) and it is 0 otherwise
smoothed_bone_img = smooth_img(bone_img, windowSize, parameters.skull_smooth_threshold);
bone_perimeter = smoothed_bone_img - imerode(smoothed_bone_img, strel('cube',3));
new_skull = skull | bone_perimeter;
figure;
montage({squeeze(skull(:,focus_pos_upsampled_grid(2),:))*255, ...
squeeze(bone_perimeter(:,focus_pos_upsampled_grid(2),:))*255, squeeze(new_skull(:,focus_pos_upsampled_grid(2),:))*255},gray, 'Size',[1 3])
smoothed_segmented_img(new_skull) = skull_i;

smoothed_segmented_img(new_skull) = cortical_label;
end
%% remove gaps between skull & skin
if any(contains(labels, 'skull')) && any(strcmp(labels, 'skin'))
skin_i = find(strcmp(labels, 'skin')); % skin_i = 3 because skin is the 3rd element in labels [by default]

skin = smoothed_segmented_img==skin_i;
skull = smoothed_segmented_img==skull_i;

% get skin mask
skin = ismember(smoothed_segmented_img, skin_label);

% get skull mask (trabecular and cortical)
skull = ismember(smoothed_segmented_img, all_skull_labels);

skin_skull = skin+skull;

Expand All @@ -97,9 +132,12 @@
[~, sortIndexes] = sort(allAreas, 'descend');
biggestBlob = ismember(labeledImage, sortIndexes(2));

smoothed_segmented_img((skin_skull_filled-skin_skull - biggestBlob)>0) = skull_i;
if any(contains(labels, 'skull_cortical'))
smoothed_segmented_img(trabecular_mask_smoothed~=0) = trabecular_i; % makes sure that the trabecular mask is not affected
smoothed_segmented_img((skin_skull_filled-skin_skull - biggestBlob)>0) = cortical_label;

if any(contains(labels, 'skull_cortical'))
layer_mask = ismember(segmented_img, trabecular_label);
trabecular_mask_smoothed = smooth_img(layer_mask, windowSize, smooth_threshold);
smoothed_segmented_img(trabecular_mask_smoothed~=0) = trabecular_label; % makes sure that the trabecular mask is not affected
end
end

Expand Down Expand Up @@ -169,7 +207,7 @@
% Crops the processed segmented image
smoothed_segmented_img = smoothed_segmented_img(min_dims(1):max_dims(1), min_dims(2):max_dims(2), min_dims(3):max_dims(3)); % Crop image
% Creates a mask around the edge of the figure to define the edge of the skull
skull_edge = edge3(smoothed_segmented_img==skull_i, 'approxcanny',0.1);
skull_edge = edge3(ismember(smoothed_segmented_img, all_skull_labels), 'approxcanny',0.1);
%{
% Applies an expanded binary mask of neural tissue to remove abberant
% neural tissue in skin (to correct for a bug in Charm)
Expand Down Expand Up @@ -252,5 +290,4 @@
% niftiwrite(uint8(smoothed_segmented_img),trilayer_final_file,'Compressed',1);


end

end
33 changes: 22 additions & 11 deletions single_subject_pipeline.m
Original file line number Diff line number Diff line change
Expand Up @@ -281,24 +281,35 @@
layer_labels = parameters.layer_labels;
labels = fieldnames(parameters.layer_labels);
all_skull_ids = [];
for label_i = find(contains(labels, 'skull'))'
all_skull_ids = [all_skull_ids layer_labels.(labels{label_i})];
all_brain_ids = [];
all_skin_ids = [];
for i = range(1:size(labels))
if contains(labels(i), 'skull')
all_skull_ids = [all_skull_ids layer_labels.(labels{i})];
elseif contains(labels(i), 'brain')
all_brain_ids = [all_brain_ids layer_labels.(labels{i})];
elseif contains(labels(i), 'skin')
all_skin_ids = [all_skin_ids layer_labels.(labels{i})];
end
end

% Create masks of different regions
skull_mask = ismember(medium_masks,all_skull_ids);
brain_mask = ismember(medium_masks,all_brain_ids);
skin_mask = ismember(medium_masks,all_skin_ids);

% Overwrites the max Isppa by dividing it up into the max Isppa for
% each layer in case a layered simulation_medium was selected
if contains(parameters.simulation_medium, 'skull') || strcmp(parameters.simulation_medium, 'layered')
[max_Isppa_brain, Ix_brain, Iy_brain, Iz_brain] = masked_max_3d(Isppa_map, medium_masks>0 & medium_masks<3);
half_max = Isppa_map >= max_Isppa_brain/2 & medium_masks>0 & medium_masks<3;
[max_Isppa_brain, Ix_brain, Iy_brain, Iz_brain] = masked_max_3d(Isppa_map, brain_mask);
half_max = Isppa_map >= max_Isppa_brain/2 & brain_mask;
half_max_ISPPA_volume_brain = sum(half_max(:))*(parameters.grid_step_mm^3);


[max_pressure_brain, Px_brain, Py_brain, Pz_brain] = masked_max_3d(data_max, medium_masks>0 & medium_masks<3);
[max_pressure_brain, Px_brain, Py_brain, Pz_brain] = masked_max_3d(data_max, brain_mask);
[max_Isppa_skull, Ix_skull, Iy_skull, Iz_skull] = masked_max_3d(Isppa_map, skull_mask);
[max_pressure_skull, Px_skull, Py_skull, Pz_skull] = masked_max_3d(data_max, skull_mask);
[max_Isppa_skin, Ix_skin, Iy_skin, Iz_skin] = masked_max_3d(Isppa_map, medium_masks==5);
[max_pressure_skin, Px_skin, Py_skin, Pz_skin] = masked_max_3d(data_max, medium_masks==5);
[max_Isppa_skin, Ix_skin, Iy_skin, Iz_skin] = masked_max_3d(Isppa_map, skin_mask);
[max_pressure_skin, Px_skin, Py_skin, Pz_skin] = masked_max_3d(data_max, skin_mask);
highlighted_pos = [Ix_brain, Iy_brain, Iz_brain];
real_focal_distance = norm(highlighted_pos-trans_pos_final)*parameters.grid_step_mm;

Expand All @@ -317,7 +328,7 @@
else
plot_isppa_over_image_2d(Isppa_map, segmented_image_cropped, source_labels, parameters, trans_pos_final, focus_pos_final, highlighted_pos);
end
export_fig(output_plot, '-native')
export_fig(output_plot, '-native');
close;

%% RUN HEATING SIMULATIONS
Expand Down Expand Up @@ -365,9 +376,9 @@
% Overwrites the max temperature by dividing it up for each layer
% in case a layered simulation_medium was selected
if contains(parameters.simulation_medium, 'skull') || strcmp(parameters.simulation_medium, 'layered')
output_table.maxT_brain = gather(masked_max_3d(maxT, medium_masks>0 & medium_masks<3));
output_table.maxT_brain = gather(masked_max_3d(maxT, brain_mask));
output_table.maxT_skull = gather(masked_max_3d(maxT, skull_mask));
output_table.maxT_skin = gather(masked_max_3d(maxT, medium_masks==5));
output_table.maxT_skin = gather(masked_max_3d(maxT, skin_mask));
end
writetable(output_table, output_pressure_file);

Expand Down
38 changes: 38 additions & 0 deletions starting_function.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MAIN FUNCTION %
% This script is used to perform and to debug the simulation. %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Set medium, qsub and path parameter
clear;
medium = 'layered'; % water or layered
qsub = 1; %run scripts via matlab (0) or via a job (1)
cd /home/neuromod/marcorn/Documents/PRESTUS/ % change path to demo data here

% add paths
addpath('functions')
addpath(genpath('toolboxes'))
addpath('/home/common/matlab/fieldtrip/qsub') % uncomment if you are using Donders HPC

if gpuDeviceCount==0 && ~exist('/home/common/matlab/fieldtrip/qsub','dir')
error('Many of the examples in this tutorial assume that you have a GPU available for computations or that you''re using the Donders HPC cluster. It looks like this is not the case. You can still run the tutorial but you''ll need to switch to using CPU (see matlab_code parameter in the config) and it would be slow.')
end

parameters = load_parameters('tutorial_config.yaml'); % load the configuration file

parameters.simulation_medium = medium;
subject_id = 1;
if strcmp(medium, 'layered')
parameters.transducer.source_amp = [116947 116947 116947 116947];
parameters.transducer.source_phase_rad = [0 6.28309051155650 0.748390467392312 0.000141417558751905];
parameters.transducer.source_phase_deg = [0 6.28309051155650 0.748390467392312 0.000141417558751905]/pi*180;
parameters.results_filename_affix = '_optimized';
end

if qsub == 0
single_subject_pipeline(subject_id, parameters);
else
parameters.interactive = 0;
parameters.overwrite_files = 'always';
single_subject_pipeline_with_qsub(subject_id, parameters);
end

0 comments on commit f509512

Please sign in to comment.