def layer(request): """Parameterized fixture that supplies a layer for testing. Parameters ---------- request : _pytest.fixtures.SubRequest The pytest request object Returns ------- napari.layers.Layer The desired napari Layer. """ np.random.seed(0) if request.param == 'image': data = np.random.rand(20, 20) return Image(data) elif request.param == 'labels': data = np.random.randint(10, size=(20, 20)) return Labels(data) elif request.param == 'points': data = np.random.rand(20, 2) return Points(data) elif request.param == 'shapes': data = [ np.random.rand(2, 2), np.random.rand(2, 2), np.random.rand(6, 2), np.random.rand(6, 2), np.random.rand(2, 2), ] shape_type = ['ellipse', 'line', 'path', 'polygon', 'rectangle'] return Shapes(data, shape_type=shape_type) elif request.param == 'shapes-rectangles': data = np.random.rand(7, 4, 2) return Shapes(data) elif request.param == 'vectors': data = np.random.rand(20, 2, 2) return Vectors(data) else: return None
def test_3D_rectangles(): """Test instantiating Shapes layer with 3D planar rectangles.""" # Test a single four corner rectangle np.random.seed(0) planes = np.tile(np.arange(10).reshape((10, 1, 1)), (1, 4, 1)) corners = np.random.uniform(0, 10, size=(10, 4, 2)) data = np.concatenate((planes, corners), axis=2) layer = Shapes(data) assert layer.nshapes == len(data) assert np.all([np.all(ld == d) for ld, d in zip(layer.data, data)]) assert layer.ndim == 3 assert np.all([s == 'rectangle' for s in layer.shape_types])
def test_lines(): """Test instantiating Shapes layer with a random 2D lines.""" # Test a single two end point line shape = (1, 2, 2) np.random.seed(0) data = 20 * np.random.random(shape) layer = Shapes(data, shape_type='line') assert layer.nshapes == shape[0] assert np.all(layer.data[0] == data[0]) assert layer.ndim == shape[2] assert np.all([s == 'line' for s in layer.shape_types]) # Test multiple lines shape = (10, 2, 2) np.random.seed(0) data = 20 * np.random.random(shape) layer = Shapes(data, shape_type='line') assert layer.nshapes == shape[0] assert np.all([np.all(ld == d) for ld, d in zip(layer.data, data)]) assert layer.ndim == shape[2] assert np.all([s == 'line' for s in layer.shape_types])
def test_polygons(): """Test instantiating Shapes layer with a random 2D polygons.""" # Test a single polygon with 6 points shape = (1, 6, 2) np.random.seed(0) data = 20 * np.random.random(shape) layer = Shapes(data, shape_type='polygon') assert layer.nshapes == shape[0] assert np.all(layer.data[0] == data[0]) assert layer.ndim == shape[2] assert np.all([s == 'polygon' for s in layer.shape_types]) # Test multiple polygons with different numbers of points data = [ 20 * np.random.random((np.random.randint(2, 12), 2)) for i in range(10) ] layer = Shapes(data, shape_type='polygon') assert layer.nshapes == len(data) assert np.all([np.all(ld == d) for ld, d in zip(layer.data, data)]) assert layer.ndim == 2 assert np.all([s == 'polygon' for s in layer.shape_types])
def test_change_text_color_updates_node_color(): shapes = np.random.rand(3, 4, 2) properties = {'class': np.array(['A', 'B', 'C'])} text = {'string': 'class', 'color': [1, 0, 0]} layer = Shapes(shapes, properties=properties, text=text) vispy_layer = VispyShapesLayer(layer) text_node = vispy_layer._get_text_node() np.testing.assert_array_equal(text_node.color.rgb, [[1, 0, 0]]) layer.text.color = [0, 0, 1] np.testing.assert_array_equal(text_node.color.rgb, [[0, 0, 1]])
def layer_data_and_types(): np.random.seed(0) layers = [ Image(np.random.rand(20, 20)), Labels(np.random.randint(10, size=(20, 2))), Points(np.random.rand(20, 2)), Shapes(np.random.rand(10, 2, 2)), Vectors(np.random.rand(10, 2, 2)), ] layer_data = [l.as_layer_data_tuple() for l in layers] layer_types = [ld[2] for ld in layer_data] return layer_data, layer_types
class Shapes2DSuite: """Benchmarks for the Shapes layer with 2D data""" params = [2**i for i in range(4, 9)] def setup(self, n): np.random.seed(0) self.data = [50 * np.random.random((6, 2)) for i in range(n)] self.layer = Shapes(self.data, shape_type='polygon') def time_create_layer(self, n): """Time to create an image layer.""" layer = Shapes(self.data, shape_type='polygon') def time_set_view_slice(self, n): """Time to set view slice.""" self.layer._set_view_slice() def time_update_thumbnail(self, n): """Time to update thumbnail.""" self.layer._update_thumbnail() def time_get_value(self, n): """Time to get current value.""" self.layer.get_value() def mem_layer(self, n): """Memory used by layer.""" return self.layer def mem_data(self, n): """Memory used by raw data.""" return self.data
def test_adding_properties(attribute): """Test adding properties to an existing layer""" shape = (10, 4, 2) np.random.seed(0) data = 20 * np.random.random(shape) layer = Shapes(data) # add properties properties = {'shape_type': _make_cycled_properties(['A', 'B'], shape[0])} layer.properties = properties np.testing.assert_equal(layer.properties, properties) # add properties as a dataframe properties_df = pd.DataFrame(properties) layer.properties = properties_df np.testing.assert_equal(layer.properties, properties) # add properties as a dictionary with list values properties_list = { 'shape_type': list(_make_cycled_properties(['A', 'B'], shape[0])) } layer.properties = properties_list assert isinstance(layer.properties['shape_type'], np.ndarray) # removing a property that was the _*_color_property should give a warning setattr(layer, f'_{attribute}_color_property', 'shape_type') properties_2 = { 'not_shape_type': _make_cycled_properties(['A', 'B'], shape[0]) } with pytest.warns(RuntimeWarning): layer.properties = properties_2
def test_value(): """Test getting the value of the data at the current coordinates.""" shape = (10, 4, 2) np.random.seed(0) data = 20 * np.random.random(shape) data[-1, :] = [[0, 0], [0, 10], [10, 0], [10, 10]] layer = Shapes(data) value = layer.get_value((0, 0)) assert value == (9, None) layer.mode = 'select' layer.selected_data = [9] value = layer.get_value((0, 0)) assert value == (9, 7) layer = Shapes(data + 5) value = layer.get_value((0, 0)) assert value == (None, None)
def test_rectangles(): """Test instantiating Shapes layer with a random 2D rectangles.""" # Test a single four corner rectangle shape = (1, 4, 2) np.random.seed(0) data = 20 * np.random.random(shape) layer = Shapes(data) assert layer.nshapes == shape[0] assert np.all(layer.data[0] == data[0]) assert layer.ndim == shape[2] assert np.all([s == 'rectangle' for s in layer.shape_types]) # Test multiple four corner rectangles shape = (10, 4, 2) data = 20 * np.random.random(shape) layer = Shapes(data) assert layer.nshapes == shape[0] assert np.all([np.all(ld == d) for ld, d in zip(layer.data, data)]) assert layer.ndim == shape[2] assert np.all([s == 'rectangle' for s in layer.shape_types]) # Test a single two corner rectangle, which gets converted into four # corner rectangle shape = (1, 2, 2) data = 20 * np.random.random(shape) layer = Shapes(data) assert layer.nshapes == 1 assert len(layer.data[0]) == 4 assert layer.ndim == shape[2] assert np.all([s == 'rectangle' for s in layer.shape_types]) # Test multiple two corner rectangles shape = (10, 2, 2) data = 20 * np.random.random(shape) layer = Shapes(data) assert layer.nshapes == shape[0] assert np.all([len(ld) == 4 for ld in layer.data]) assert layer.ndim == shape[2] assert np.all([s == 'rectangle' for s in layer.shape_types])
def test_mixed_shapes(): """Test instantiating Shapes layer with a mix of random 2D shapes.""" # Test multiple polygons with different numbers of points np.random.seed(0) data = [ 20 * np.random.random((np.random.randint(2, 12), 2)) for i in range(5) ] + list(np.random.random((5, 4, 2))) shape_type = ['polygon'] * 5 + ['rectangle'] * 3 + ['ellipse'] * 2 layer = Shapes(data, shape_type=shape_type) assert layer.nshapes == len(data) assert np.all([np.all(ld == d) for ld, d in zip(layer.data, data)]) assert layer.ndim == 2 assert np.all([s == so for s, so in zip(layer.shape_type, shape_type)]) # Test roundtrip with mixed data new_layer = Shapes(layer.data, shape_type=layer.shape_type) assert np.all( [np.all(nd == d) for nd, d in zip(new_layer.data, layer.data)] ) assert np.all( [ns == s for ns, s in zip(new_layer.shape_type, layer.shape_type)] )
def test_add_colormap(attribute): """Test directly adding a vispy Colormap object""" shape = (10, 4, 2) np.random.seed(0) data = 20 * np.random.random(shape) annotations = {'shape_type': _make_cycled_properties([0, 1.5], shape[0])} color_kwarg = f'{attribute}_color' colormap_kwarg = f'{attribute}_colormap' args = {color_kwarg: 'shape_type', colormap_kwarg: 'viridis'} layer = Shapes(data, properties=annotations, **args) setattr(layer, f'{attribute}_colormap', get_colormap('gray')) layer_colormap = getattr(layer, f'{attribute}_colormap') assert 'unnamed colormap' in layer_colormap[0]
def test_lock_aspect_ratio_selected_box_zeros(): # Test a single four corner rectangle that has zero size layer = Shapes(20 * np.zeros((1, 4, 2))) # select a shape layer._selected_box = layer.interaction_box(0) layer._moving_coordinates = (0, 0, 0) layer._is_moving = True # need to go through the generator _ = list(key_bindings.hold_to_lock_aspect_ratio(layer))
def test_4D_ellispse(): """Test instantiating Shapes layer with 4D planar ellipse.""" # Test a single 4D ellipse np.random.seed(0) data = [[ [3, 5, 108, 108], [3, 5, 108, 148], [3, 5, 148, 148], [3, 5, 148, 108], ]] layer = Shapes(data, shape_type='ellipse') assert layer.nshapes == len(data) assert np.all([np.all(ld == d) for ld, d in zip(layer.data, data)]) assert layer.ndim == 4 assert np.all([s == 'ellipse' for s in layer.shape_type])
def layers(): """Fixture that supplies a layers list for testing. Returns ------- napari.components.LayerList The desired napari LayerList. """ np.random.seed(0) list_of_layers = [ Image(np.random.rand(20, 20)), Labels(np.random.randint(10, size=(20, 2))), Points(np.random.rand(20, 2)), Shapes(np.random.rand(10, 2, 2)), Vectors(np.random.rand(10, 2, 2)), ] return LayerList(list_of_layers)
def test_add_color_cycle_to_empty_layer(attribute): """ Test adding a shape to an empty layer when edge/face color is a color cycle See: https://github.com/napari/napari/pull/1069 """ default_properties = {'shape_type': np.array(['A'])} color_cycle = ['red', 'blue'] shapes_kwargs = { 'properties': default_properties, f'{attribute}_color': 'shape_type', f'{attribute}_color_cycle': color_cycle, } layer = Shapes(**shapes_kwargs) # verify the current_edge_color is correct expected_color = transform_color(color_cycle[0]) current_color = getattr(layer, f'_current_{attribute}_color') np.testing.assert_allclose(current_color, expected_color) # add a shape np.random.seed(0) new_shape = 20 * np.random.random((1, 4, 2)) layer.add(new_shape) props = {'shape_type': np.array(['A'])} expected_color = np.array([[1, 0, 0, 1]]) np.testing.assert_equal(layer.properties, props) attribute_color = getattr(layer, f'{attribute}_color') np.testing.assert_allclose(attribute_color, expected_color) # add a shape with a new property layer.selected_data = [] layer.current_properties = {'shape_type': np.array(['B'])} new_shape_2 = 20 * np.random.random((1, 4, 2)) layer.add(new_shape_2) new_color = np.array([0, 0, 1, 1]) expected_color = np.vstack((expected_color, new_color)) new_properties = {'shape_type': np.array(['A', 'B'])} attribute_color = getattr(layer, f'{attribute}_color') np.testing.assert_allclose(attribute_color, expected_color) np.testing.assert_equal(layer.properties, new_properties)
def test_blending(): """Test setting layer blending.""" np.random.seed(0) data = 20 * np.random.random((10, 4, 2)) layer = Shapes(data) assert layer.blending == 'translucent' layer.blending = 'additive' assert layer.blending == 'additive' layer = Shapes(data, blending='additive') assert layer.blending == 'additive' layer.blending = 'opaque' assert layer.blending == 'opaque'
def test_opacity(): """Test setting layer opacity.""" np.random.seed(0) data = 20 * np.random.random((10, 4, 2)) layer = Shapes(data) assert layer.opacity == 0.7 layer.opacity = 0.5 assert layer.opacity == 0.5 layer = Shapes(data, opacity=0.6) assert layer.opacity == 0.6 layer.opacity = 0.3 assert layer.opacity == 0.3
def test_visiblity(): """Test setting layer visiblity.""" np.random.seed(0) data = 20 * np.random.random((10, 4, 2)) layer = Shapes(data) assert layer.visible == True layer.visible = False assert layer.visible == False layer = Shapes(data, visible=False) assert layer.visible == False layer.visible = True assert layer.visible == True
def setup(self, n): np.random.seed(0) self.data = [50 * np.random.random((6, 2)) for i in range(n)] self.layer = Shapes(self.data, shape_type='polygon') self.layer.mode = 'select' # create events self.click_event = ReadOnlyWrapper( Event(type='mouse_press', is_dragging=False, modifiers=[])) self.release_event = ReadOnlyWrapper( Event(type='mouse_release', is_dragging=False, modifiers=[])) self.drag_event = ReadOnlyWrapper( Event(type='mouse_move', is_dragging=True, modifiers=[])) # initialize the position and select a shape self.layer.position = tuple(np.mean(self.layer.data[0], axis=0)) # Simulate click mouse_press_callbacks(self.layer, self.click_event) # Simulate release mouse_release_callbacks(self.layer, self.release_event)
def test_set_text_with_kwarg_dict(properties): text_kwargs = { 'text': 'type: {shape_type}', 'color': [0, 0, 0, 1], 'rotation': 10, 'translation': [5, 5], 'anchor': 'upper_left', 'size': 10, 'visible': True, } shape = (10, 4, 2) np.random.seed(0) data = 20 * np.random.random(shape) layer = Shapes(data, properties=copy(properties), text=text_kwargs) expected_text = ['type: ' + v for v in properties['shape_type']] np.testing.assert_equal(layer.text.values, expected_text) for property, value in text_kwargs.items(): if property == 'text': continue layer_value = getattr(layer._text, property) np.testing.assert_equal(layer_value, value)
def create_known_shapes_layer(): """Create shapes layer with known coordinates Returns ------- layer : napari.layers.Shapes Shapes layer. n_shapes : int Number of shapes in the shapes layer known_non_shape : list Data coordinates that are known to contain no shapes. Useful during testing when needing to guarantee no shape is clicked on. """ data = [[[1, 3], [8, 4]], [[10, 10], [15, 4]]] known_non_shape = [20, 30] n_shapes = len(data) layer = Shapes(data) assert layer.ndim == 2 assert len(layer.data) == n_shapes assert len(layer.selected_data) == 0 return layer, n_shapes, known_non_shape
def test_activate_modes(): # Test a single four corner rectangle layer = Shapes(20 * np.random.random((1, 4, 2))) # need to go through the generator key_bindings.activate_add_rectangle_mode(layer) assert layer.mode == 'add_rectangle' key_bindings.activate_add_ellipse_mode(layer) assert layer.mode == 'add_ellipse' key_bindings.activate_add_line_mode(layer) assert layer.mode == 'add_line' key_bindings.activate_add_path_mode(layer) assert layer.mode == 'add_path' key_bindings.activate_add_polygon_mode(layer) assert layer.mode == 'add_polygon' key_bindings.activate_direct_mode(layer) assert layer.mode == 'direct' key_bindings.activate_select_mode(layer) assert layer.mode == 'select' key_bindings.activate_pan_zoom_mode(layer) assert layer.mode == 'pan_zoom' key_bindings.activate_vertex_insert_mode(layer) assert layer.mode == 'vertex_insert' key_bindings.activate_vertex_remove_mode(layer) assert layer.mode == 'vertex_remove'
def _make_shapes_layer(self, shape, shape_type, name, **kwargs): layer = Shapes(shape, shape_type=shape_type, name=name, **kwargs) self._init_layer(layer)
def layer_writer_and_data(request): """Fixture that supplies layer io utilities for tests. Parameters ---------- request : _pytest.fixtures.SubRequest The pytest request object Returns ------- tuple ``(writer, layer_data, extension, reader, Layer)`` - writer: a function that can write layerdata to a path - layer_data: the layerdata tuple for this layer - extension: an appropriate extension for this layer type - reader: a function that can read this layer type from a path and returns a ``(data, meta)`` tuple. - Layer: the Layer class """ if request.param == 'image': data = np.random.rand(20, 20) Layer = Image layer = Image(data) writer = napari_write_image extension = '.tif' def reader(path): return (io.imread(path), {}, 'image') # metadata elif request.param == 'labels': data = np.random.randint(0, 16000, (32, 32), 'uint64') Layer = Labels layer = Labels(data) writer = napari_write_labels extension = '.tif' def reader(path): return (io.imread(path), {}, 'labels') # metadata elif request.param == 'points': data = np.random.rand(20, 2) Layer = Points layer = Points(data) writer = napari_write_points extension = '.csv' reader = partial(io.csv_to_layer_data, require_type='points') elif request.param == 'points-with-properties': data = np.random.rand(20, 2) Layer = Points layer = Points(data, properties={'values': np.random.rand(20)}) writer = napari_write_points extension = '.csv' reader = partial(io.csv_to_layer_data, require_type='points') elif request.param == 'shapes': np.random.seed(0) data = [ np.random.rand(2, 2), np.random.rand(2, 2), np.random.rand(6, 2), np.random.rand(6, 2), np.random.rand(2, 2), ] shape_type = ['ellipse', 'line', 'path', 'polygon', 'rectangle'] Layer = Shapes layer = Shapes(data, shape_type=shape_type) writer = napari_write_shapes extension = '.csv' reader = partial(io.csv_to_layer_data, require_type='shapes') else: return None, None, None, None, None layer_data = layer.as_layer_data_tuple() return writer, layer_data, extension, reader, Layer
data[5, 5] = 1000 assert data[5, 5] == 1000 if mode == 'int8' or mode == 'uint8': # label value 1000 is outside of the target data type range. with pytest.raises(AssertionError): _convert_dtype(ll, mode=mode) assert ll[-1].data.dtype == np.int16 else: _convert_dtype(ll, mode=mode) assert ll[-1].data.dtype == np.dtype(mode) assert ll[-1].data[5, 5] == 1000 assert ll[-1].data.flatten().sum() == 1000 @pytest.mark.parametrize( 'input, type_', [ (Image(np.random.rand(10, 10)), 'labels'), (Labels(np.ones((10, 10), dtype=int)), 'image'), (Shapes([np.array([[0, 0], [0, 10], [10, 0], [10, 10]])]), 'labels'), ], ) def test_convert_layer(input, type_): ll = LayerList() ll.append(input) assert ll[0]._type_string != type_ _convert(ll, type_) assert ll[0]._type_string == type_
def time_create_layer(self, n): """Time to create a layer.""" Shapes(self.data, shape_type='polygon')
def setup(self, n): np.random.seed(0) self.data = [50 * np.random.random((6, 3)) for i in range(n)] self.layer = Shapes(self.data, shape_type='polygon')
def test_copy_and_paste(): """Test copying and pasting selected shapes.""" shape = (10, 4, 2) np.random.seed(0) data = 20 * np.random.random(shape) layer = Shapes(data) # Clipboard starts empty assert layer._clipboard == [] # Pasting empty clipboard doesn't change data layer._paste_data() assert len(layer.data) == 10 # Copying with nothing selected leave clipboard empty layer._copy_data() assert layer._clipboard == [] # Copying and pasting with two shapes selected adds to clipboard and data layer.selected_data = [0, 1] layer._copy_data() layer._paste_data() assert len(layer._clipboard) == 2 assert len(layer.data) == shape[0] + 2 assert np.all( [np.all(a == b) for a, b in zip(layer.data[:2], layer.data[-2:])]) # Pasting again adds two more points to data layer._paste_data() assert len(layer.data) == shape[0] + 4 assert np.all( [np.all(a == b) for a, b in zip(layer.data[:2], layer.data[-2:])]) # Unselecting everything and copying and pasting will empty the clipboard # and add no new data layer.selected_data = [] layer._copy_data() layer._paste_data() assert layer._clipboard == [] assert len(layer.data) == shape[0] + 4
def test_z_index(): """Test setting z-index during instantiation.""" shape = (10, 4, 2) np.random.seed(0) data = 20 * np.random.random(shape) layer = Shapes(data) assert layer.z_indices == [0] * shape[0] # Instantiate with custom z-index layer = Shapes(data, z_index=4) assert layer.z_indices == [4] * shape[0] # Instantiate with custom z-index list z_index_list = [2, 3] * 5 layer = Shapes(data, z_index=z_index_list) assert layer.z_indices == z_index_list # Add new shape and its z-index new_shape = np.random.random((1, 4, 2)) layer.add(new_shape) assert len(layer.z_indices) == shape[0] + 1 assert layer.z_indices == z_index_list + [4] # Check removing data adjusts colors correctly layer.selected_data = [0, 2] layer.remove_selected() assert len(layer.data) == shape[0] - 1 assert len(layer.z_indices) == shape[0] - 1 assert layer.z_indices == [z_index_list[1]] + z_index_list[3:] + [4]