@@ -635,7 +635,7 @@ def data(self):
635
635
return data
636
636
637
637
def _check_hemi (self , hemi ):
638
- """Check for safe hemi input"""
638
+ """Check for safe single- hemi input, returns str """
639
639
if hemi is None :
640
640
if self ._hemi not in ['lh' , 'rh' ]:
641
641
raise ValueError ('hemi must not be None when both '
@@ -647,6 +647,20 @@ def _check_hemi(self, hemi):
647
647
raise ValueError ('hemi must be either "lh" or "rh"' + extra )
648
648
return hemi
649
649
650
+ def _check_hemis (self , hemi ):
651
+ """Check for safe dual or single-hemi input, returns list"""
652
+ if hemi is None :
653
+ if self ._hemi not in ['lh' , 'rh' ]:
654
+ hemi = ['lh' , 'rh' ]
655
+ else :
656
+ hemi = [self ._hemi ]
657
+ elif hemi not in ['lh' , 'rh' ]:
658
+ extra = ' or None' if self ._hemi in ['lh' , 'rh' ] else ''
659
+ raise ValueError ('hemi must be either "lh" or "rh"' + extra )
660
+ else :
661
+ hemi = [hemi ]
662
+ return hemi
663
+
650
664
def _read_scalar_data (self , source , hemi , name = None , cast = True ):
651
665
"""Load in scalar data from an image stored in a file or an array
652
666
@@ -919,7 +933,8 @@ def add_data(self, array, min=None, max=None, thresh=None,
919
933
data ['orig_ctable' ] = ct
920
934
self .data_dict [hemi ] = data
921
935
922
- def add_annotation (self , annot , borders = True , alpha = 1 , hemi = None ):
936
+ def add_annotation (self , annot , borders = True , alpha = 1 , hemi = None ,
937
+ remove_existing = True ):
923
938
"""Add an annotation file.
924
939
925
940
Parameters
@@ -932,62 +947,83 @@ def add_annotation(self, annot, borders=True, alpha=1, hemi=None):
932
947
Alpha level to control opacity
933
948
hemi : str | None
934
949
If None, it is assumed to belong to the hemipshere being
935
- shown. If two hemispheres are being shown, an error will
936
- be thrown.
950
+ shown. If two hemispheres are being shown, data must exist
951
+ for both hemispheres.
952
+ remove_existing : bool
953
+ If True (default), remove old annotations.
937
954
"""
938
- hemi = self ._check_hemi (hemi )
939
-
940
- # Get rid of any old annots
941
- for a in self .annot_list :
942
- a ['surface' ].remove ()
955
+ hemis = self ._check_hemis (hemi )
943
956
944
957
# Figure out where the data is coming from
945
958
if os .path .isfile (annot ):
946
959
filepath = annot
947
- annot = os .path .basename (filepath ).split ('.' )[1 ]
960
+ path = os .path .split (filepath )[0 ]
961
+ file_hemi , annot = os .path .basename (filepath ).split ('.' )[:2 ]
962
+ if len (hemis ) > 1 :
963
+ if annot [:2 ] == 'lh.' :
964
+ filepaths = [filepath , pjoin (path , 'rh' + annot [2 :])]
965
+ elif annot [:2 ] == 'rh.' :
966
+ filepaths = [pjoin (path , 'lh' + annot [2 :], filepath )]
967
+ else :
968
+ raise RuntimeError ('To add both hemispheres '
969
+ 'simultaneously, filename must '
970
+ 'begin with "lh." or "rh."' )
971
+ else :
972
+ filepaths = [filepath ]
948
973
else :
949
- filepath = pjoin (self .subjects_dir ,
950
- self .subject_id ,
951
- 'label' ,
952
- "." .join ([hemi , annot , 'annot' ]))
953
- if not os .path .exists (filepath ):
954
- raise ValueError ('Annotation file %s does not exist'
955
- % filepath )
956
-
957
- # Read in the data
958
- labels , cmap , _ = nib .freesurfer .read_annot (filepath , orig_ids = True )
959
-
960
- # Maybe zero-out the non-border vertices
961
- if borders :
962
- n_vertices = labels .size
963
- edges = utils .mesh_edges (self .geo [hemi ].faces )
964
- border_edges = labels [edges .row ] != labels [edges .col ]
965
- show = np .zeros (n_vertices , dtype = np .int )
966
- show [np .unique (edges .row [border_edges ])] = 1
967
- labels *= show
968
-
969
- # Handle null labels properly
970
- # (tksurfer doesn't use the alpha channel, so sometimes this
971
- # is set weirdly. For our purposes, it should always be 0.
972
- # Unless this sometimes causes problems?
973
- cmap [np .where (cmap [:, 4 ] == 0 ), 3 ] = 0
974
- if np .any (labels == 0 ) and not np .any (cmap [:, - 1 ] == 0 ):
975
- cmap = np .vstack ((cmap , np .zeros (5 , int )))
976
-
977
- # Set label ids sensibly
978
- ord = np .argsort (cmap [:, - 1 ])
979
- ids = ord [np .searchsorted (cmap [ord , - 1 ], labels )]
980
- cmap = cmap [:, :4 ]
981
-
982
- # Set the alpha level
983
- alpha_vec = cmap [:, 3 ]
984
- alpha_vec [alpha_vec > 0 ] = alpha * 255
985
-
986
- al = []
974
+ filepaths = []
975
+ for hemi in hemis :
976
+ filepath = pjoin (self .subjects_dir ,
977
+ self .subject_id ,
978
+ 'label' ,
979
+ "." .join ([hemi , annot , 'annot' ]))
980
+ if not os .path .exists (filepath ):
981
+ raise ValueError ('Annotation file %s does not exist'
982
+ % filepath )
983
+ filepaths += [filepath ]
984
+
987
985
views = self ._toggle_render (False )
988
- for brain in self ._brain_list :
989
- if brain ['hemi' ] == hemi :
990
- al .append (brain ['brain' ].add_annotation (annot , ids , cmap ))
986
+ if remove_existing is True :
987
+ # Get rid of any old annots
988
+ for a in self .annot_list :
989
+ a ['surface' ].remove ()
990
+ self .annot_list = []
991
+
992
+ al = self .annot_list
993
+ for hemi , filepath in zip (hemis , filepaths ):
994
+ # Read in the data
995
+ labels , cmap , _ = nib .freesurfer .read_annot (filepath ,
996
+ orig_ids = True )
997
+
998
+ # Maybe zero-out the non-border vertices
999
+ if borders :
1000
+ n_vertices = labels .size
1001
+ edges = utils .mesh_edges (self .geo [hemi ].faces )
1002
+ border_edges = labels [edges .row ] != labels [edges .col ]
1003
+ show = np .zeros (n_vertices , dtype = np .int )
1004
+ show [np .unique (edges .row [border_edges ])] = 1
1005
+ labels *= show
1006
+
1007
+ # Handle null labels properly
1008
+ # (tksurfer doesn't use the alpha channel, so sometimes this
1009
+ # is set weirdly. For our purposes, it should always be 0.
1010
+ # Unless this sometimes causes problems?
1011
+ cmap [np .where (cmap [:, 4 ] == 0 ), 3 ] = 0
1012
+ if np .any (labels == 0 ) and not np .any (cmap [:, - 1 ] == 0 ):
1013
+ cmap = np .vstack ((cmap , np .zeros (5 , int )))
1014
+
1015
+ # Set label ids sensibly
1016
+ ord = np .argsort (cmap [:, - 1 ])
1017
+ ids = ord [np .searchsorted (cmap [ord , - 1 ], labels )]
1018
+ cmap = cmap [:, :4 ]
1019
+
1020
+ # Set the alpha level
1021
+ alpha_vec = cmap [:, 3 ]
1022
+ alpha_vec [alpha_vec > 0 ] = alpha * 255
1023
+
1024
+ for brain in self ._brain_list :
1025
+ if brain ['hemi' ] == hemi :
1026
+ al .append (brain ['brain' ].add_annotation (annot , ids , cmap ))
991
1027
self .annot_list = al
992
1028
self ._toggle_render (True , views )
993
1029
@@ -1117,7 +1153,8 @@ def remove_labels(self, labels=None, hemi=None):
1117
1153
for ll in label :
1118
1154
ll .remove ()
1119
1155
1120
- def add_morphometry (self , measure , grayscale = False , hemi = None ):
1156
+ def add_morphometry (self , measure , grayscale = False , hemi = None ,
1157
+ remove_existing = True ):
1121
1158
"""Add a morphometry overlay to the image.
1122
1159
1123
1160
Parameters
@@ -1128,57 +1165,64 @@ def add_morphometry(self, measure, grayscale=False, hemi=None):
1128
1165
whether to load the overlay with a grayscale colormap
1129
1166
hemi : str | None
1130
1167
If None, it is assumed to belong to the hemipshere being
1131
- shown. If two hemispheres are being shown, an error will
1132
- be thrown.
1168
+ shown. If two hemispheres are being shown, data must exist
1169
+ for both hemispheres.
1170
+ remove_existing : bool
1171
+ If True (default), remove old annotations.
1133
1172
"""
1134
- hemi = self ._check_hemi (hemi )
1173
+ hemis = self ._check_hemis (hemi )
1174
+ morph_files = []
1175
+ for hemi in hemis :
1176
+ # Find the source data
1177
+ surf_dir = pjoin (self .subjects_dir , self .subject_id , 'surf' )
1178
+ morph_file = pjoin (surf_dir , '.' .join ([hemi , measure ]))
1179
+ if not os .path .exists (morph_file ):
1180
+ raise ValueError (
1181
+ 'Could not find %s in subject directory' % morph_file )
1182
+ morph_files += [morph_file ]
1135
1183
1136
- # Find the source data
1137
- surf_dir = pjoin (self .subjects_dir , self .subject_id , 'surf' )
1138
- morph_file = pjoin (surf_dir , '.' .join ([hemi , measure ]))
1139
- if not os .path .exists (morph_file ):
1140
- raise ValueError (
1141
- 'Could not find %s in subject directory' % morph_file )
1142
-
1143
- # Preset colormaps
1144
- cmap_dict = dict (area = "pink" ,
1145
- curv = "RdBu" ,
1146
- jacobian_white = "pink" ,
1147
- sulc = "RdBu" ,
1148
- thickness = "pink" )
1149
-
1150
- # Get rid of any old overlays
1151
- for m in self .morphometry_list :
1152
- m ['surface' ].remove ()
1153
- m ['colorbar' ].visible = False
1154
-
1155
- # Read in the morphometric data
1156
- morph_data = nib .freesurfer .read_morph_data (morph_file )
1157
-
1158
- # Get a cortex mask for robust range
1159
- self .geo [hemi ].load_label ("cortex" )
1160
- ctx_idx = self .geo [hemi ].labels ["cortex" ]
1161
-
1162
- # Get the display range
1163
- if measure == "thickness" :
1164
- min , max = 1 , 4
1165
- else :
1166
- min , max = stats .describe (morph_data [ctx_idx ])[1 ]
1184
+ views = self ._toggle_render (False )
1185
+ if remove_existing is True :
1186
+ # Get rid of any old overlays
1187
+ for m in self .morphometry_list :
1188
+ m ['surface' ].remove ()
1189
+ m ['colorbar' ].visible = False
1190
+ self .morphometry_list = []
1191
+ ml = self .morphometry_list
1192
+ for hemi , morph_file in zip (hemis , morph_files ):
1193
+ # Preset colormaps
1194
+ cmap_dict = dict (area = "pink" ,
1195
+ curv = "RdBu" ,
1196
+ jacobian_white = "pink" ,
1197
+ sulc = "RdBu" ,
1198
+ thickness = "pink" )
1199
+
1200
+ # Read in the morphometric data
1201
+ morph_data = nib .freesurfer .read_morph_data (morph_file )
1202
+
1203
+ # Get a cortex mask for robust range
1204
+ self .geo [hemi ].load_label ("cortex" )
1205
+ ctx_idx = self .geo [hemi ].labels ["cortex" ]
1206
+
1207
+ # Get the display range
1208
+ if measure == "thickness" :
1209
+ min , max = 1 , 4
1210
+ else :
1211
+ min , max = stats .describe (morph_data [ctx_idx ])[1 ]
1167
1212
1168
- # Set up the Mayavi pipeline
1169
- morph_data = _prepare_data (morph_data )
1213
+ # Set up the Mayavi pipeline
1214
+ morph_data = _prepare_data (morph_data )
1170
1215
1171
- if grayscale :
1172
- colormap = "gray"
1173
- else :
1174
- colormap = cmap_dict [measure ]
1216
+ if grayscale :
1217
+ colormap = "gray"
1218
+ else :
1219
+ colormap = cmap_dict [measure ]
1175
1220
1176
- views = self ._toggle_render (False )
1177
- ml = []
1178
- for brain in self ._brain_list :
1179
- if brain ['hemi' ] == hemi :
1180
- ml .append (brain ['brain' ].add_morphometry (morph_data , colormap ,
1181
- measure , min , max ))
1221
+ for brain in self ._brain_list :
1222
+ if brain ['hemi' ] == hemi :
1223
+ ml .append (brain ['brain' ].add_morphometry (morph_data ,
1224
+ colormap , measure ,
1225
+ min , max ))
1182
1226
self .morphometry_list = ml
1183
1227
self ._toggle_render (True , views )
1184
1228
0 commit comments