def test_volume(): vol = np.zeros((20, 20, 20), 'float32') vol[8:16, 8:16, :] = 1.0 # Create V = Volume(vol) assert V.clim == (0, 1) assert V.method == 'mip' # Set wrong data assert_raises(ValueError, V.set_data, np.zeros((20, 20), 'float32')) # Clim V.set_data(vol, (0.5, 0.8)) assert V.clim == (0.5, 0.8) assert_raises(ValueError, V.set_data, vol, (0.5, 0.8, 1.0)) # Method V.method = 'iso' assert V.method == 'iso' # Step size V.relative_step_size = 1.1 assert V.relative_step_size == 1.1 # Disallow 0 step size to avoid GPU stalling assert_raises(ValueError, V.__class__.relative_step_size.fset, V, 0)
class VispyImageLayer(VispyBaseLayer): def __init__(self, layer): node = ImageNode(None, method='auto') super().__init__(layer, node) self.layer.events.rendering.connect( lambda e: self._on_rendering_change()) self.layer.events.interpolation.connect( lambda e: self._on_interpolation_change()) self.layer.events.colormap.connect( lambda e: self._on_colormap_change()) self.layer.events.contrast_limits.connect( lambda e: self._on_contrast_limits_change()) self.layer.events.gamma.connect(lambda e: self._on_gamma_change()) self._on_display_change() self._on_data_change() def _on_display_change(self): parent = self.node.parent self.node.parent = None if self.layer.dims.ndisplay == 2: self.node = ImageNode(None, method='auto') else: self.node = VolumeNode(np.zeros((1, 1, 1))) self.node.parent = parent self.reset() def _on_data_change(self): # Check if ndisplay has changed current node type needs updating if (self.layer.dims.ndisplay == 3 and not isinstance(self.node, VolumeNode)) or ( self.layer.dims.ndisplay == 2 and not isinstance(self.node, ImageNode)): self._on_display_change() data = self.layer._data_view dtype = np.dtype(data.dtype) if dtype not in texture_dtypes: try: dtype = dict(i=np.int16, f=np.float32, u=np.uint16, b=np.uint8)[dtype.kind] except KeyError: # not an int or float raise TypeError( f'type {dtype} not allowed for texture; must be one of {set(texture_dtypes)}' # noqa: E501 ) data = data.astype(dtype) if self.layer.dims.ndisplay == 3 and self.layer.dims.ndim == 2: data = np.expand_dims(data, axis=0) if self.layer.dims.ndisplay == 2: self.node._need_colortransform_update = True self.node.set_data(data) else: self.node.set_data(data, clim=self.layer.contrast_limits) self.node.update() def _on_interpolation_change(self): if self.layer.dims.ndisplay == 2: self.node.interpolation = self.layer.interpolation def _on_rendering_change(self): if not self.layer.dims.ndisplay == 2: self.node.method = self.layer.rendering def _on_colormap_change(self): cmap = self.layer.colormap[1] if self.layer.gamma != 1: # when gamma!=1, we instantiate a new colormap # with 256 control points from 0-1 cmap = Colormap(cmap[np.linspace(0, 1, 256)**self.layer.gamma]) # Below is fixed in #1712 if not self.layer.dims.ndisplay == 2: self.node.view_program['texture2D_LUT'] = (cmap.texture_lut() if ( hasattr(cmap, 'texture_lut')) else None) self.node.cmap = cmap def _on_contrast_limits_change(self): if self.layer.dims.ndisplay == 2: self.node.clim = self.layer.contrast_limits else: self._on_data_change() def _on_gamma_change(self): self._on_colormap_change() def _on_scale_change(self): self.scale = [ self.layer.scale[d] * self.layer._scale_view[d] for d in self.layer.dims.displayed[::-1] ] if self.layer.is_pyramid: self.layer.top_left = self.find_top_left() self.layer.position = self._transform_position(self._position) def _on_translate_change(self): self.translate = [ self.layer.translate[d] + self.layer._translate_view[d] + self.layer.translate_grid[d] for d in self.layer.dims.displayed[::-1] ] self.layer.position = self._transform_position(self._position) def compute_data_level(self, size): """Computed what level of the pyramid should be viewed given the current size of the requested field of view. Parameters ---------- size : 2-tuple Requested size of field of view in image coordinates Returns ---------- level : int Level of the pyramid to be viewing. """ # Convert requested field of view from the camera into log units size = np.log2(np.max(size)) # Max allowed tile in log units max_size = np.log2(self.layer._max_tile_shape) # Allow for more than 2x coverage of field of view with max tile diff = size - max_size + 1.25 # Find closed downsample level to diff ds = self.layer.level_downsamples[:, self.layer.dims.displayed].max( axis=1) level = np.argmin(abs(np.log2(ds) - diff)) return level def find_top_left(self): """Finds the top left pixel of the canvas. Depends on the current pan and zoom position Returns ---------- top_left : tuple of int Coordinates of top left pixel. """ nd = self.layer.dims.ndisplay # Find image coordinate of top left canvas pixel if self.node.canvas is not None: transform = self.node.canvas.scene.node_transform(self.node) pos = (transform.map([0, 0])[:nd] + self.translate[:nd] / self.scale[:nd]) else: pos = [0] * nd top_left = np.zeros(self.layer.ndim, dtype=int) for i, d in enumerate(self.layer.dims.displayed[::-1]): top_left[d] = pos[i] # Clip according to the max image shape top_left = np.clip(top_left, 0, np.subtract(self.layer.level_shapes[0], 1)) # Convert to offset for image array rounding_factor = self.layer._max_tile_shape / 4 top_left = rounding_factor * np.floor(top_left / rounding_factor) return top_left.astype(int) def on_draw(self, event): """Called whenever the canvas is drawn, which happens whenever new data is sent to the canvas or the camera is moved. """ self.layer.scale_factor = self.scale_factor if self.layer.is_pyramid: self.layer.scale_factor = self.scale_factor size = self.camera.rect.size data_level = self.compute_data_level(size) if data_level != self.layer.data_level: self.layer.data_level = data_level else: self.layer.top_left = self.find_top_left() def reset(self): self._reset_base() self._on_interpolation_change() self._on_rendering_change() self._on_colormap_change() if self.layer.dims.ndisplay == 2: self._on_contrast_limits_change()
class VispyImageLayer(VispyBaseLayer): def __init__(self, layer): node = ImageNode(None, method='auto') super().__init__(layer, node) self.layer.events.rendering.connect( lambda e: self._on_rendering_change()) self.layer.events.interpolation.connect( lambda e: self._on_interpolation_change()) self.layer.events.colormap.connect( lambda e: self._on_colormap_change()) self.layer.events.contrast_limits.connect( lambda e: self._on_contrast_limits_change()) self.layer.dims.events.ndisplay.connect( lambda e: self._on_display_change()) self._on_display_change() def _on_display_change(self): parent = self.node.parent self.node.parent = None if self.layer.dims.ndisplay == 2: self.node = ImageNode(None, method='auto') else: self.node = VolumeNode(np.zeros((1, 1, 1))) self.node.parent = parent self.layer._update_dims() self.layer._set_view_slice() self.reset() def _on_data_change(self): data = self.layer._data_view dtype = np.dtype(data.dtype) if dtype not in texture_dtypes: try: dtype = dict(i=np.int16, f=np.float32, u=np.uint16, b=np.uint8)[dtype.kind] except KeyError: # not an int or float raise TypeError( f'type {dtype} not allowed for texture; must be one of {set(texture_dtypes)}' ) data = data.astype(dtype) if self.layer.dims.ndisplay == 3: self.node.set_data(data, clim=self.layer.contrast_limits) else: self.node._need_colortransform_update = True self.node.set_data(data) self.node.update() def _on_interpolation_change(self): if self.layer.dims.ndisplay == 2: self.node.interpolation = self.layer.interpolation def _on_rendering_change(self): if self.layer.dims.ndisplay == 3: self.node.method = self.layer.rendering def _on_colormap_change(self): cmap = self.layer.colormap[1] if self.layer.dims.ndisplay == 3: self.node.view_program['texture2D_LUT'] = (cmap.texture_lut() if ( hasattr(cmap, 'texture_lut')) else None) self.node.cmap = cmap def _on_contrast_limits_change(self): if self.layer.dims.ndisplay == 3: self._on_data_change() else: self.node.clim = self.layer.contrast_limits def reset(self): self._reset_base() self._on_interpolation_change() self._on_rendering_change() self._on_colormap_change() self._on_contrast_limits_change() self._on_data_change()
class Ball: """Ball Class. It uses vispy to visualization.""" def __init__(self, position, velocity, boundaries = None, color = (1.0, 1.0, 1.0, 1.0)): self.pos = position self.vel = velocity self.color = color self.rad = 0.1 self.bound = None self.sizexyz = [None] * 3 if boundaries is not None: self.set_bound(boundaries) self.visual = None def set_bound(self, boundaries): """Updates the boundaries.""" self.bound = boundaries self.sizexyz = np.abs(boundaries[:,1] - boundaries[:,0]) temp_mg = np.meshgrid(np.arange(41), np.arange(41), np.arange(41)) self.meshgrid = np.asarray([temp_mg[0], temp_mg[2], temp_mg[1]]) def step(self, time_step): """Calculate the new position and speed.""" despl = self.vel * time_step self.pos = self.pos + despl for i in range(3): #-INF if self.sizexyz[i] is not None and self.pos[i] < self.bound[i,0]: #-INF self.pos[i] = self.pos[i] + self.sizexyz[i] #-INF elif self.sizexyz[i] is not None and self.pos[i] > self.bound[i,1]: #-INF self.pos[i] = self.pos[i] - self.sizexyz[i] if self.sizexyz[i] is not None: if self.pos[i] < self.bound[i,0] or self.pos[i] > self.bound[i,1]: self.pos[i] = self.pos[i] - despl[i] self.vel[i] = - self.vel[i] * 0.95 self.vel[2] = self.vel[2] - 0.1 self.update_visual() def init_visual(self, view): """Initialize the class visual.""" ball_ln_dic = dict(color=self.color) dist_sq = np.zeros(self.meshgrid[0].shape) for i in range(3): axis_sq = np.multiply((self.meshgrid[i] - self.pos[i] * 4), (self.meshgrid[i] - self.pos[i] * 4)) dist_sq = dist_sq + axis_sq dist_sq = 4.0 - dist_sq dist_sq[dist_sq < 0] = 0 self.visual = Volume(dist_sq, clim=(0.0, 10.0), method='mip', emulate_texture=False, cmap='hot') self.visual.transform = scene.STTransform(scale=(.25, .25, .25)) view.add(self.visual) self.last_dist_sq = dist_sq def update_visual(self): """Updates the class visual.""" dist_sq = np.zeros(self.meshgrid[0].shape) for i in range(3): axis_sq = np.multiply((self.meshgrid[i] - self.pos[i] * 4), (self.meshgrid[i] - self.pos[i] * 4)) dist_sq = dist_sq + axis_sq dist_sq = 4.0 - dist_sq dist_sq[dist_sq < 0] = 0 dist_sq = dist_sq + 0.99 * self.last_dist_sq self.visual.set_data(dist_sq) self.visual.update() self.last_dist_sq = dist_sq def shake(self): """Inverts the z position and gives the ball a random velocity.""" if self.sizexyz[2] is not None: self.pos[2] = self.bound[2, 1] - (self.pos[2] - self.bound[2, 0]) self.vel = (np.random.rand(3) - 0.5) * 10