@@ -93,7 +93,7 @@ def __init__(self, data, axes=None, aspect_ratio=(1, 1, 1), cmap='gray',
93
93
self ._axes = dict (x = axes [0 , 1 ], y = axes [0 , 0 ], z = axes [1 , 0 ],
94
94
v = axes [1 , 1 ])
95
95
plt .tight_layout (pad = 0.1 )
96
- if not self .multi_volume :
96
+ if self .n_volumes <= 1 :
97
97
fig .delaxes (self ._axes ['v' ])
98
98
del self ._axes ['v' ]
99
99
else :
@@ -109,7 +109,7 @@ def __init__(self, data, axes=None, aspect_ratio=(1, 1, 1), cmap='gray',
109
109
colors = dict ()
110
110
for k , size in zip ('xyz' , self ._data .shape [:3 ]):
111
111
self ._idx [k ] = size // 2
112
- self ._ims [k ] = self ._axes [k ].imshow (self ._get_slice (k ), ** kw )
112
+ self ._ims [k ] = self ._axes [k ].imshow (self ._get_slice_data (k ), ** kw )
113
113
self ._sizes [k ] = size
114
114
colors [k ] = (0 , 1 , 0 )
115
115
self ._idx ['v' ] = 0
@@ -148,23 +148,24 @@ def __init__(self, data, axes=None, aspect_ratio=(1, 1, 1), cmap='gray',
148
148
ax .axes .get_xaxis ().set_visible (False )
149
149
150
150
# Set up volumes axis
151
- if self .multi_volume and 'v' in self ._axes :
151
+ if self .n_volumes > 1 and 'v' in self ._axes :
152
152
ax = self ._axes ['v' ]
153
153
ax .set_axis_bgcolor ('k' )
154
154
ax .set_title ('Volumes' )
155
- n_vols = np .prod (self ._volume_dims )
156
- y = np .mean (np .mean (np .mean (self ._data , 0 ), 0 ), 0 ).ravel ()
157
- y = np .concatenate ((y , [y [- 1 ]]))
158
- x = np .arange (n_vols + 1 ) - 0.5
155
+ y = self ._get_voxel_levels ()
156
+ x = np .arange (self .n_volumes + 1 ) - 0.5
159
157
step = ax .step (x , y , where = 'post' , color = 'y' )[0 ]
160
- ax .set_xticks (np .unique (np .linspace (0 , n_vols - 1 , 5 ).astype (int )))
158
+ ax .set_xticks (np .unique (np .linspace (0 , self .n_volumes - 1 ,
159
+ 5 ).astype (int )))
161
160
ax .set_xlim (x [0 ], x [- 1 ])
162
- lims = ax .get_ylim ()
163
- patch = mpl_patch .Rectangle ([- 0.5 , lims [0 ]], 1. , np .diff (lims )[0 ],
164
- fill = True , facecolor = (0 , 1 , 0 ),
165
- edgecolor = (0 , 1 , 0 ), alpha = 0.25 )
161
+ yl = [self ._data .min (), self ._data .max ()]
162
+ yl = [l + s * np .diff (lims )[0 ] for l , s in zip (yl , [- 1.01 , 1.01 ])]
163
+ patch = mpl_patch .Rectangle ([- 0.5 , yl [0 ]], 1. , np .diff (yl )[0 ],
164
+ fill = True , facecolor = (0 , 1 , 0 ),
165
+ edgecolor = (0 , 1 , 0 ), alpha = 0.25 )
166
166
ax .add_patch (patch )
167
- self ._time_lines = [patch , step ]
167
+ ax .set_ylim (yl )
168
+ self ._volume_ax_objs = dict (step = step , patch = patch )
168
169
169
170
# setup pairwise connections between the slice dimensions
170
171
self ._click_update_keys = dict (x = 'yz' , y = 'xz' , z = 'xy' )
@@ -197,9 +198,9 @@ def close(self):
197
198
plt .close (f )
198
199
199
200
@property
200
- def multi_volume (self ):
201
- """Whether or not the displayed data is multi-volume """
202
- return len ( self ._volume_dims ) > 0
201
+ def n_volumes (self ):
202
+ """Number of volumes in the data"""
203
+ return int ( np . prod ( self ._volume_dims ))
203
204
204
205
def set_indices (self , x = None , y = None , z = None , v = None ):
205
206
"""Set current displayed slice indices
@@ -221,39 +222,51 @@ def set_indices(self, x=None, y=None, z=None, v=None):
221
222
v = int (v ) if v is not None else None
222
223
draw = False
223
224
if v is not None :
224
- if not self .multi_volume :
225
+ if self .n_volumes <= 1 :
225
226
raise ValueError ('cannot change volume index of single-volume '
226
227
'image' )
227
- self ._set_vol_idx (v , draw = False ) # delay draw
228
+ self ._set_vol_idx (v )
228
229
draw = True
229
230
for key , val in zip ('zyx' , (z , y , x )):
230
231
if val is not None :
231
232
self ._set_viewer_slice (key , val )
232
233
draw = True
233
234
if draw :
235
+ self ._update_voxel_levels ()
234
236
self ._draw ()
235
237
236
- def _set_vol_idx (self , idx , draw = True ):
237
- """Helper to change which volume is shown"""
238
+ def _get_voxel_levels (self ):
239
+ """Get levels of the current voxel as a function of volume"""
240
+ y = self ._data [self ._idx ['x' ],
241
+ self ._idx ['y' ],
242
+ self ._idx ['z' ], :].ravel ()
243
+ y = np .concatenate ((y , [y [- 1 ]]))
244
+ return y
245
+
246
+ def _update_voxel_levels (self ):
247
+ """Update voxel levels in time plot"""
248
+ if self .n_volumes > 1 :
249
+ self ._volume_ax_objs ['step' ].set_ydata (self ._get_voxel_levels ())
250
+
251
+ def _set_vol_idx (self , idx ):
252
+ """Change which volume is shown"""
238
253
max_ = np .prod (self ._volume_dims )
239
254
self ._idx ['v' ] = max (min (int (round (idx )), max_ - 1 ), 0 )
240
255
# Must reset what is shown
241
256
self ._current_vol_data = self ._data [:, :, :, self ._idx ['v' ]]
242
257
for key in 'xyz' :
243
- self ._ims [key ].set_data (self ._get_slice (key ))
244
- self ._time_lines [0 ].set_x (self ._idx ['v' ] - 0.5 )
245
- if draw :
246
- self ._draw ()
258
+ self ._ims [key ].set_data (self ._get_slice_data (key ))
259
+ self ._volume_ax_objs ['patch' ].set_x (self ._idx ['v' ] - 0.5 )
247
260
248
- def _get_slice (self , key ):
261
+ def _get_slice_data (self , key ):
249
262
"""Helper to get the current slice image"""
250
263
ii = dict (x = 0 , y = 1 , z = 2 )[key ]
251
264
return np .take (self ._current_vol_data , self ._idx [key ], axis = ii ).T
252
265
253
266
def _set_viewer_slice (self , key , idx ):
254
267
"""Helper to set a viewer slice number"""
255
268
self ._idx [key ] = max (min (int (round (idx )), self ._sizes [key ] - 1 ), 0 )
256
- self ._ims [key ].set_data (self ._get_slice (key ))
269
+ self ._ims [key ].set_data (self ._get_slice_data (key ))
257
270
for fun in self ._cross_setters [key ]:
258
271
fun ([self ._idx [key ]] * 2 )
259
272
@@ -272,14 +285,15 @@ def _on_scroll(self, event):
272
285
return
273
286
delta = 10 if event .key is not None and 'control' in event .key else 1
274
287
if event .key is not None and 'shift' in event .key :
275
- if not self .multi_volume :
288
+ if self .n_volumes <= 1 :
276
289
return
277
290
key = 'v' # shift: change volume in any axis
278
291
idx = self ._idx [key ] + (delta if event .button == 'up' else - delta )
279
292
if key == 'v' :
280
293
self ._set_vol_idx (idx )
281
294
else :
282
295
self ._set_viewer_slice (key , idx )
296
+ self ._update_voxel_levels ()
283
297
self ._draw ()
284
298
285
299
def _on_mousemove (self , event ):
@@ -294,6 +308,7 @@ def _on_mousemove(self, event):
294
308
for sub_key , idx in zip (self ._click_update_keys [key ],
295
309
(event .xdata , event .ydata )):
296
310
self ._set_viewer_slice (sub_key , idx )
311
+ self ._update_voxel_levels ()
297
312
self ._draw ()
298
313
299
314
def _on_keypress (self , event ):
@@ -303,16 +318,15 @@ def _on_keypress(self, event):
303
318
def _draw (self ):
304
319
for im in self ._ims .values ():
305
320
ax = im .axes
306
- ax .draw_artist (ax .patch )
307
321
ax .draw_artist (im )
308
322
ax .draw_artist (im .vert_line )
309
323
ax .draw_artist (im .horiz_line )
310
324
ax .figure .canvas .blit (ax .bbox )
311
325
for t in im .texts :
312
326
ax .draw_artist (t )
313
- if self .multi_volume and 'v' in self ._axes : # user might only pass 3
327
+ if self .n_volumes > 1 and 'v' in self ._axes : # user might only pass 3
314
328
ax = self ._axes ['v' ]
315
- ax .draw_artist (ax .patch )
316
- for artist in self . _time_lines :
317
- ax .draw_artist (artist )
329
+ ax .draw_artist (ax .patch ) # axis bgcolor to erase old lines
330
+ for key in ( 'step' , 'patch' ) :
331
+ ax .draw_artist (self . _volume_ax_objs [ key ] )
318
332
ax .figure .canvas .blit (ax .bbox )
0 commit comments