def test_constructor(self): # Parameters color_engine_interface = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': { 'ship': Color(255, 255, 255, ctype='sRGB255'), 'car': Color(255, 128, 128, ctype='sRGB255') } } scale = 1 axis = 0 mosaic_size = (500, 500) background_color = (0, 0, 0) item_margins = (10, 10) main_axis_align = 'start' minor_axis_align = 'start' LegendPainter(color_engine_interface=color_engine_interface, scale=scale, axis=axis, mosaic_size=mosaic_size, background_color=background_color, item_margins=item_margins, main_axis_align=main_axis_align, minor_axis_align=minor_axis_align)
def test_linear(self, ctype): color = Color(50, 100, 50, ctype='JCh') cm = LightnessColorMap(color, lightness_range=(0, 0), ctype=ctype) array = np.linspace(0, 1, 10) assert all(color == Color(50, 100, 50, ctype='JCh').astype(ctype) for color in cm(array, keep_colors=True)) cm = LightnessColorMap(color, ctype=ctype) self._base_color_map_test(cm, color=color, ctype=ctype) array = np.linspace(0, 1, 10) local_deriv = np.diff(np.linspace(0, 1, 10 - 1))[0] * np.diff( cm(array, keep_colors=True)) local_deriv = np.diff(np.linspace(0, 1, 10 - 2))[0] * np.diff(local_deriv) assert np.allclose(local_deriv.astype(np.float64), 0) cm = LightnessColorMap(color, chroma_range=(0.1, 0.1), ctype=ctype) self._base_color_map_test(cm, color=color, chroma_range=(0.1, 0.1), ctype=ctype) array = np.linspace(0, 1, 10) local_deriv = np.diff(np.linspace(0, 1, 10 - 1))[0] * np.diff( cm(array, keep_colors=True)) local_deriv = np.diff(np.linspace(0, 1, 10 - 2))[0] * np.diff(local_deriv) assert np.allclose(local_deriv.astype(np.float64), 0)
def test_add_title(self): import PIL.Image import numpy as np # Create data points records = [ Record( [[[100, 100], [100, 150], [150, 150], [150, 100], [100, 100]]], ['car'], confidence=0.9), ] records_collection = RecordCollection(*records) annotation = Annotation(records_collection) tile = TileWrapper(np.zeros((100, 100, 3)), filename='test.png') data_point = DataPoint(tile, annotation) data_points = [data_point] # Color engine interface simple_categorical_interface = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': { 'Ship': Color(26, 188, 156, ctype='sRGB255'), 'Car': Color(241, 196, 15, ctype='sRGB255'), 'Truck': Color(41, 128, 185, ctype='sRGB255'), 'Wind-turbines': Color(236, 240, 241, ctype='sRGB255') } } # Parameters width, height = (300, 300) background_color = (0, 0, 0) title_size = 25 # Init compositor _compositor = Compositor( data_points=data_points, color_engine_interface=simple_categorical_interface) image = PIL.Image.fromarray(np.zeros((width, height))) # Add title final_image = _compositor._add_title(mosaic=image, title='', background_color=background_color, title_size=title_size) assert isinstance(final_image, PIL.Image.Image) assert final_image.width == width assert final_image.height == height + 2 * title_size
def test_draw_simple_category(self): # Parameters name = 'Test' fill_color = Color(255, 255, 255, ctype='sRGB255') margins = (15, 15) drawer = LegendItemDrawer((0, 0, 0), scale=1, margins=margins) item_1 = drawer.draw_simple_category(name=name, fill_color=fill_color) assert isinstance(item_1, PIL.Image.Image) assert item_1.width > 0 assert item_1.height > 0 # Change scale (> 1) drawer = LegendItemDrawer((0, 0, 0), scale=2, margins=margins) item_2 = drawer.draw_simple_category(name=name, fill_color=fill_color) assert isinstance(item_2, PIL.Image.Image) assert item_2.width > item_1.width assert item_2.height > item_1.height # Change scale (< 1) drawer = LegendItemDrawer((0, 0, 0), scale=0.5, margins=margins) item_3 = drawer.draw_simple_category(name=name, fill_color=fill_color) assert isinstance(item_3, PIL.Image.Image) assert item_1.width > item_3.width assert item_1.height > item_3.height
def test_draw_composite_category(self): # Parameters category_name = 'Test' color_engine_interface = { 'ship': Color(255, 255, 255, ctype='sRGB255'), 'car': Color(255, 128, 128, ctype='sRGB255') } max_category_height = 500 margins = (10, 10) drawer = LegendItemDrawer((0, 0, 0), scale=1, margins=margins) item_1 = drawer.draw_composite_category( descriptor_name='Name', category_name=category_name, color_engine_interface=color_engine_interface, max_category_height=max_category_height) assert isinstance(item_1, PIL.Image.Image) assert item_1.width > 0 assert item_1.height > 0 # Change scale (> 1) drawer = LegendItemDrawer((0, 0, 0), scale=2, margins=margins) item_2 = drawer.draw_composite_category( descriptor_name='Name', category_name=category_name, color_engine_interface=color_engine_interface, max_category_height=max_category_height) assert isinstance(item_2, PIL.Image.Image) assert item_2.width > item_1.width assert item_2.height > item_1.height # Change scale (< 1) drawer = LegendItemDrawer((0, 0, 0), scale=0.5, margins=margins) item_3 = drawer.draw_composite_category( descriptor_name='Name', category_name=category_name, color_engine_interface=color_engine_interface, max_category_height=max_category_height) assert isinstance(item_3, PIL.Image.Image) assert item_1.width > item_3.width assert item_1.height > item_3.height
def test_key_point_cm(self, ctype): cm = KeyPointsColorMap( { 1: 2 * Color(0, 255, 0), 0: 2 * Color(0, 0, 255), 2: 2 * Color(255, 0, 0) }, ctype=ctype) self._base_color_map_test(cm, mapping={ 1: 2 * Color(0, 255, 0), 0: 2 * Color(0, 0, 255), 2: 2 * Color(255, 0, 0) }, ctype=ctype) assert cm.get_color(0.5) == Color(0, 255, 255).astype(ctype) assert cm.get_color(1.5) == Color(255, 255, 0).astype(ctype)
def test_semi_circular(self, ctype): cm = SemiCircularColorMap(0, 3, 0.1, ctype=ctype) array = np.linspace(0, 1, 10) assert all( np.allclose(color._components, Color(40, 0, 0, ctype='JCh')._components) for color in cm(array, keep_colors=True)) cm = SemiCircularColorMap(0.3, 3, 0.1, ctype=ctype) self._base_color_map_test(cm, ray=0.3, period=3, intensity=0.1, ctype=ctype) # # Check periodicity for value in array: assert cm.get_color(value) == cm.get_color(value + 3.0)
def _make_color_maps(self): if self._main_interface['type'] == 'categorical': self._primary_color_map = CategoricalColorMap(len(self._main_interface['schema'].keys()) or 1, ctype=self.ctype) _schema = self._main_interface['schema'] if self._secondary_descriptor is not None: self._secondary_color_map = { value: self._make_secondary_color_map(self._primary_color_map.get_color(_schema[key]), str(value)) for key, value in _schema.items() } if self._main_interface['type'] == 'continuous': self._primary_color_map = LightnessColorMap(Color(50, 78, 50, ctype='JCh'), lightness_range=(0.8, 0.9), chroma_range=(-0.3, -0.1), data_range=self._main_interface['schema'], ctype=self.ctype).discretize()
def test_circular(self, ctype): cm = CircularColorMap(0, ctype=ctype) array = np.linspace(0, 1, 10) assert all( np.allclose(color._components, Color(50, 0, 0, ctype='JCh')._components) for color in cm(array, keep_colors=True)) cm = CircularColorMap(0.3, ctype=ctype) self._base_color_map_test(cm, ray=0.3, ctype=ctype) # Check periodicity for value in array: assert cm.get_color(value) == cm.get_color(value + 1.0) array = np.linspace(0, 1, 10) local_deriv = np.diff(np.linspace(0, 1, 10 - 1))[0] * np.diff( cm(array, keep_colors=True)) local_deriv = np.diff(np.linspace(0, 1, 10 - 2))[0] * np.diff(local_deriv) assert np.allclose(local_deriv.astype(np.float64), 0)
def test_draw_colormap(self): # Parameters descriptor_name = 'Test' color_map = LightnessColorMap(Color(50, 78, 50, ctype='JCh'), lightness_range=(0.8, 0.8), chroma_range=(-0.3, -0.1), ctype="sRGB1").discretize(256) margins = (15, 15) drawer = LegendItemDrawer((0, 0, 0), scale=1, margins=margins) item_1 = drawer.draw_colormap(descriptor_name='Name', color_map=color_map, category_name=descriptor_name) assert isinstance(item_1, PIL.Image.Image) assert item_1.width > 0 assert item_1.height > 0 # Change scale (> 1) drawer = LegendItemDrawer((0, 0, 0), scale=2, margins=margins) item_2 = drawer.draw_colormap(descriptor_name='Name', color_map=color_map, category_name=descriptor_name) assert isinstance(item_2, PIL.Image.Image) assert item_2.width > item_1.width assert item_2.height > item_1.height # Change scale (< 1) drawer = LegendItemDrawer((0, 0, 0), scale=0.5, margins=margins) item_3 = drawer.draw_colormap(descriptor_name='Name', color_map=color_map, category_name=descriptor_name) assert isinstance(item_3, PIL.Image.Image) assert item_1.width > item_3.width assert item_1.height > item_3.height
def test_constructor(self): # Create data points records = [ Record( [[[100, 100], [100, 150], [150, 150], [150, 100], [100, 100]]], ['car'], confidence=0.9), ] records_collection = RecordCollection(*records) annotation = Annotation(records_collection) tile = TileWrapper(np.zeros((100, 100, 3)), filename='test.png') tile_2 = TileWrapper(np.zeros((200, 200, 3)), filename='test_2.png') data_point = DataPoint(tile, annotation) data_point_2 = DataPoint(tile_2, annotation) # Color engine interface simple_categorical_interface = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': { 'Ship': Color(26, 188, 156, ctype='sRGB255'), 'Car': Color(241, 196, 15, ctype='sRGB255'), 'Truck': Color(41, 128, 185, ctype='sRGB255'), 'Wind-turbines': Color(236, 240, 241, ctype='sRGB255') } } # Valid datapoints data_points_1 = [data_point] data_points_2 = [data_point, data_point_2] data_points_3 = (data_point_2, ) data_points_4 = (data_point, data_point_2) data_points_5 = [data_points_1] data_points_6 = [data_points_3] data_points_7 = (data_points_2, ) data_points_8 = (data_points_4, ) # Invalid data points inv_data_points_1 = None inv_data_points_2 = ['test'] inv_data_points_3 = ('test', data_point) # Checks Compositor(data_points=data_points_1, color_engine_interface=simple_categorical_interface) Compositor(data_points=data_points_2, color_engine_interface=simple_categorical_interface) Compositor(data_points=data_points_3, color_engine_interface=simple_categorical_interface) Compositor(data_points=data_points_4, color_engine_interface=simple_categorical_interface) Compositor(data_points=data_points_5, color_engine_interface=simple_categorical_interface) Compositor(data_points=data_points_6, color_engine_interface=simple_categorical_interface) Compositor(data_points=data_points_7, color_engine_interface=simple_categorical_interface) Compositor(data_points=data_points_8, color_engine_interface=simple_categorical_interface) with pytest.raises(AttributeError): Compositor(data_points=inv_data_points_1, color_engine_interface=simple_categorical_interface) with pytest.raises(AttributeError): Compositor(data_points=inv_data_points_2, color_engine_interface=simple_categorical_interface) with pytest.raises(AttributeError): Compositor(data_points=inv_data_points_3, color_engine_interface=simple_categorical_interface)
class TestSchema: _valid_names = [ 'valid_name', '__valid_name__', 'valid__name', 'Valid_name' ] _invalid_names = ['invalid-name', 'invalid_name0'] _valid_properties = ['valid_property', 'valid_property', 'valid__property'] _invalid_properties = [ 'Invalid_property', 'invalid-property', 'invalid_property0', '_invalid_property', 'invalid_property_' ] _valid_type = ['categorical', 'continuous'] _invalid_type = ['other'] _valid_category = [ { 'category': 0 }, { 'category': 0.0 }, { 'category': Color(0, 0, 0) }, { 'category': CategoricalColorMap(10) }, { 'category': { 'category': 0 }, }, { 'category': { 'category': 0.0 }, }, { 'category': { 'category': Color(0, 0, 0) }, }, { 'category': { 'category': CategoricalColorMap(10) }, }, ] _valid_continuous = [CategoricalColorMap(10), (0, 1)] _invalid_category = \ [ { 'category': '0' }, { 'category': (0, ) }, { 'category': Color(0, 0, 0).components }, { 'category': CategoricalColorMap(10).map_fn }, { 'category': { 'category': '0' }, }, { 'category': { 'category': (0, ) }, }, { 'category': { 'category': Color(0, 0, 0).components }, }, { 'category': { 'category': CategoricalColorMap(10).map_fn }, }, ] + _valid_continuous _invalid_continuous = [ CategoricalColorMap(10).map_fn, [0, 1], '0, 1', 0, 0.0 ] + _valid_category @staticmethod def _make_test(names, types, properties, schemas): for name in names: for type_ in types: for property_ in properties: for schema in schemas: with pytest.raises(SchemaError): dictionary = { 'name': name, 'type': type_, 'property': property_, 'schema': schema } print(dictionary) Descriptor.__interface_schema__.validate( dictionary) class DummyDescriptor(Descriptor): def update(self, *record_collections): pass def compute(self, *record_collections): pass def reset(self): pass def _make_interface(self): return dictionary @property def property_name(self): return property_ with pytest.raises(ValueError): descriptor = DummyDescriptor(name) print(descriptor.__descriptor__) def test_schema(self): # Name self._make_test(self._invalid_names, ['categorical'], self._valid_properties, self._valid_category) self._make_test(self._invalid_names, ['continuous'], self._valid_properties, self._valid_continuous) # Type self._make_test(self._valid_names, self._invalid_type, self._valid_properties, self._valid_category) self._make_test(self._valid_names, self._invalid_type, self._valid_properties, self._valid_continuous) # Property self._make_test(self._valid_names, ['categorical'], self._invalid_properties, self._valid_category) self._make_test(self._valid_names, ['continuous'], self._invalid_properties, self._valid_continuous) # Schema self._make_test(self._valid_names, ['categorical'], self._valid_properties, self._invalid_category) self._make_test(self._valid_names, ['continuous'], self._valid_properties, self._invalid_continuous) # Dependency self._make_test(self._valid_names, ['continuous'], self._valid_properties, self._valid_category) self._make_test(self._valid_names, ['categorical'], self._valid_properties, self._valid_continuous)
def test_plot(self): import PIL.Image import numpy as np # Create data points records = [ Record( [[[100, 100], [100, 150], [150, 150], [150, 100], [100, 100]]], ['car'], confidence=0.9, confidence_confidence=0.9, color=Color(26, 188, 156)), ] records_collection = RecordCollection(*records) annotation = Annotation(records_collection) tile = TileWrapper(np.zeros((100, 100, 3), dtype=np.uint8), filename='test.png') data_point = DataPoint(tile, annotation) data_points = [] # Legend simple_categorical_interface = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': { 'Ship': Color(26, 188, 156, ctype='sRGB255'), 'Car': Color(241, 196, 15, ctype='sRGB255'), 'Truck': Color(41, 128, 185, ctype='sRGB255'), 'Wind-turbines': Color(236, 240, 241, ctype='sRGB255') } } kwargs = { 'plot_centers': False, 'plot_confidences': True, 'zoom': 1, 'alpha': 128, 'scale': 1, 'axis': 0, 'background_color': (48, 56, 68, 255), 'item_margins': (10, 10), 'main_axis_align': 'start', 'minor_axis_align': 'start' } # Accumulates datapoints (flattened) nb_datapoints = 15 for _ in range(nb_datapoints): data_points.append(data_point) # Parameters _compositor = Compositor( data_points=data_points, color_engine_interface=simple_categorical_interface) n_cols = 10 margins = (5, 5) # Test with neither title nor legend (use AdaptiveImagePositionGenerator) final_image = _compositor.plot(n_cols=n_cols, margins=margins, title=None, center=True, **kwargs) assert isinstance(final_image, PIL.Image.Image) assert final_image.width > n_cols * (100 + 2 * margins[0]) assert final_image.height > 2 * (100 + 2 * margins[1]) # 2 rows assert final_image.height < 3 * (100 + 2 * margins[1] ) # but less than 3 rows # Test with neither title nor legend (use SimpleImagePositionGenerator) final_image = _compositor.plot(n_cols=n_cols, margins=margins, title=None, center=False, **kwargs) assert isinstance(final_image, PIL.Image.Image) assert final_image.width > n_cols * (100 + 2 * margins[0]) assert final_image.height > 2 * (100 + 2 * margins[1]) # 2 rows assert final_image.height < 3 * (100 + 2 * margins[1] ) # but less than 3 rows # Nested datapoints nested_data_points = [[data_point, data_point, data_point, data_point], [data_point, data_point, data_point], [data_point, data_point, data_point, data_point], [ data_point, data_point, data_point, data_point, data_point ], [data_point, data_point]] _compositor = Compositor( data_points=nested_data_points, color_engine_interface=simple_categorical_interface) final_image = _compositor.plot(n_cols=n_cols, margins=margins, title=None, center=True, **kwargs) # Check 5 cols and 5 rows (plus each tile title) painter_title_height = 70 assert isinstance(final_image, PIL.Image.Image) assert final_image.width > 5 * (100 + 2 * margins[0]) assert final_image.height > 5 * (100 + 2 * margins[1]) assert final_image.height < 5 * (100 + painter_title_height + 2 * margins[1]) # Add title final_image_with_title = _compositor.plot(n_cols=n_cols, margins=margins, title='Test', center=True, **kwargs) assert isinstance(final_image, PIL.Image.Image) assert final_image_with_title.width == final_image.width assert final_image_with_title.height > final_image.height # Add legend and title final_image_with_legend = _compositor.plot(n_cols=n_cols, margins=margins, title='Test', center=True, **kwargs) assert isinstance(final_image, PIL.Image.Image) assert final_image_with_legend.width == final_image_with_title.width assert final_image_with_legend.height == final_image_with_title.height
def test_draw_items(self): # Parameters scale = 1 axis = 0 mosaic_size = (500, 500) background_color = (0, 0, 0) item_margins = (10, 10) main_axis_align = 'start' minor_axis_align = 'start' # Test invalid mapping schema invalid_color_engine_interface = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': { 'ship': 'Hello' } } painter = LegendPainter( color_engine_interface=invalid_color_engine_interface, scale=scale, axis=axis, mosaic_size=mosaic_size, background_color=background_color, item_margins=item_margins, main_axis_align=main_axis_align, minor_axis_align=minor_axis_align) drawer = LegendItemDrawer(background_color=background_color, scale=scale, margins=item_margins) # Draw items items = painter._draw_items(drawer) # Checks assert isinstance(items, list) assert len(items) == 0 # Single descriptor - categorical simple_categorical_interface = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': { 'Ship': Color(26, 188, 156, ctype='sRGB255'), 'Car': Color(241, 196, 15, ctype='sRGB255'), 'Truck': Color(41, 128, 185, ctype='sRGB255'), 'Wind-turbines': Color(236, 240, 241, ctype='sRGB255') } } painter = LegendPainter( color_engine_interface=simple_categorical_interface, scale=scale, axis=axis, mosaic_size=mosaic_size, background_color=background_color, item_margins=item_margins, main_axis_align=main_axis_align, minor_axis_align=minor_axis_align) drawer = LegendItemDrawer(background_color=background_color, scale=scale, margins=item_margins) # Draw items items = painter._draw_items(drawer) # Checks assert isinstance(items, list) assert len(items) == 4 for item in items: assert isinstance(item, PIL.Image.Image) # Single descriptor - continuous - color map simple_continuous_interface_cm = { 'name': 'Name(Main, Secondary)', 'type': 'continuous', 'schema': LightnessColorMap(Color(50, 78, 50, ctype='JCh'), lightness_range=(0.8, 0.8), chroma_range=(-0.3, -0.1), ctype="sRGB1").discretize(256) } painter = LegendPainter( color_engine_interface=simple_continuous_interface_cm, scale=scale, axis=axis, mosaic_size=mosaic_size, background_color=background_color, item_margins=item_margins, main_axis_align=main_axis_align, minor_axis_align=minor_axis_align) drawer = LegendItemDrawer(background_color=background_color, scale=scale, margins=item_margins) # Draw items items = painter._draw_items(drawer) # Checks assert isinstance(items, list) assert len(items) == 1 for item in items: assert isinstance(item, PIL.Image.Image) # Two descriptors - both categorical double_categorical_interface = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': { 'Ship size': { 'Small (< 26m)': Color(26, 188, 156, ctype='sRGB255'), 'Medium': Color(241, 196, 15, ctype='sRGB255'), 'Large (< 100m)': Color(41, 128, 185, ctype='sRGB255') }, 'Confidence': { 'Little (< 0.5)': Color(26, 188, 156, ctype='sRGB255'), 'Strong (>= 0.5)': Color(241, 196, 15, ctype='sRGB255') } } } painter = LegendPainter( color_engine_interface=double_categorical_interface, scale=scale, axis=axis, mosaic_size=mosaic_size, background_color=background_color, item_margins=item_margins, main_axis_align=main_axis_align, minor_axis_align=minor_axis_align) drawer = LegendItemDrawer(background_color=background_color, scale=scale, margins=item_margins) # Draw items items = painter._draw_items(drawer) # Checks assert isinstance(items, list) assert len(items) == 2 for item in items: assert isinstance(item, PIL.Image.Image) # One descriptor - categorical / color map double_categorical_interface_cm = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': { 'Confidence': LightnessColorMap(Color(50, 78, 50, ctype='JCh'), lightness_range=(0.8, 0.8), chroma_range=(-0.3, -0.1), ctype="sRGB1").discretize(256), 'Size': LightnessColorMap(Color(50, 78, 270, ctype='JCh'), lightness_range=(0.8, 0.8), chroma_range=(-0.3, -0.1), ctype="sRGB1").discretize(256) } } painter = LegendPainter( color_engine_interface=double_categorical_interface_cm, scale=scale, axis=axis, mosaic_size=mosaic_size, background_color=background_color, item_margins=item_margins, main_axis_align=main_axis_align, minor_axis_align=minor_axis_align) drawer = LegendItemDrawer(background_color=background_color, scale=scale, margins=item_margins) # Draw items items = painter._draw_items(drawer) # Checks assert isinstance(items, list) assert len(items) == 2 for item in items: assert isinstance(item, PIL.Image.Image)
def test_add_legend(self): import PIL.Image import numpy as np # Create data points records = [ Record( [[[100, 100], [100, 150], [150, 150], [150, 100], [100, 100]]], ['car'], confidence=0.9), ] records_collection = RecordCollection(*records) annotation = Annotation(records_collection) tile = TileWrapper(np.zeros((100, 100, 3)), filename='test.png') data_point = DataPoint(tile, annotation) data_points = [data_point] # Legend simple_categorical_interface = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': { 'Ship': Color(26, 188, 156, ctype='sRGB255'), 'Car': Color(241, 196, 15, ctype='sRGB255'), 'Truck': Color(41, 128, 185, ctype='sRGB255'), 'Wind-turbines': Color(236, 240, 241, ctype='sRGB255') } } legend_config = { 'scale': 1, 'axis': 0, 'item_margins': (10, 10), 'main_axis_align': 'start', 'minor_axis_align': 'start' } # Parameters width, height = (300, 300) background_color = (0, 0, 0) # Init compositor _compositor = Compositor( data_points=data_points, color_engine_interface=simple_categorical_interface) image = PIL.Image.fromarray(np.zeros((width, height))) # Add legend final_image = _compositor._add_legend( mosaic=image, background_color=background_color, **legend_config) # Checks (vertical mode) assert isinstance(final_image, PIL.Image.Image) assert final_image.height == height assert final_image.width > width # Checks horizontal mode legend_config['axis'] = 1 final_image = _compositor._add_legend( mosaic=image, background_color=background_color, **legend_config) assert isinstance(final_image, PIL.Image.Image) assert final_image.width == width assert final_image.height > height
def __init__(self, type, coordinates): self.id = '1' self.type = type self.coordinates = coordinates self.labels = Label('test'), self.color = Color(255, 128, 128, ctype='sRGB255')
def test_draw(self): # Fake records invalid_records = [ Record( [[[100, 100], [100, 150], [150, 150], [150, 100], [100, 100]]], ('car', ), confidence=0.9), Record([[[100, 100], [110, 110], [100, 100]]], ('car', ), confidence=0.9), ] records = [ Record( [[[100, 100], [100, 150], [150, 150], [150, 100], [100, 100]]], ('car', ), confidence=0.9, color=Color(255, 128, 128)), Record([[[100, 100], [100, 150], [100, 100]]], ('car', ), confidence=0.9, color=Color(128, 255, 255)), Record([100, 100], ('ship', ), confidence=0.3, color=Color(128, 255, 255)), ] # Create collection invalid_records_collection = RecordCollection(*invalid_records) records_collection = RecordCollection(*records) # Finally, the annotation invalid_annotation = Annotation(invalid_records_collection) annotation = Annotation(records_collection) # Instantiate a tile tile = TileWrapper(np.zeros((300, 300, 4), dtype=np.uint8), filename='test.png') # Create a datapoint invalid_datapoint = DataPoint(tile, invalid_annotation) datapoint = DataPoint(tile, annotation) # Parameters _painter = Painter(fill=True) # Sanity check with pytest.raises(AttributeError): _painter.draw(invalid_datapoint) # Draw datapoint final_image = _painter.draw(datapoint) assert isinstance(final_image, PIL.Image.Image) assert final_image.width == 300 assert final_image.height == 300 + _painter.TITLE_HEIGHT # With centers _painter = Painter(plot_centers=True) # Sanity check with pytest.raises(AttributeError): _painter.draw(invalid_datapoint) # Draw datapoint final_image = _painter.draw(datapoint) assert isinstance(final_image, PIL.Image.Image) assert final_image.width == 300 assert final_image.height == 300 + _painter.TITLE_HEIGHT # Test zoom zoom = 2.5 _painter = Painter(zoom=zoom) final_image = _painter.draw(datapoint) assert isinstance(final_image, PIL.Image.Image) assert final_image.width == 300 * zoom assert final_image.height == (300 + _painter.TITLE_HEIGHT) * zoom # Check invalid geometry class InvalidRecord(object): def __init__(self, type, coordinates): self.id = '1' self.type = type self.coordinates = coordinates self.labels = Label('test'), self.color = Color(255, 128, 128, ctype='sRGB255') records = [ Record( [[[100, 100], [100, 150], [150, 150], [150, 100], [100, 100]]], ('car', ), confidence=0.9, color=Color(255, 128, 128)), Record([[[100, 100], [100, 150], [100, 100]]], ('car', ), confidence=0.9, color=Color(128, 255, 255)), InvalidRecord(type='Test', coordinates=[]) ] # Create data point records_collection = RecordCollection(*records) annotation = Annotation(records_collection) tile = TileWrapper(np.zeros((300, 300, 4), dtype=np.uint8), filename='test.png') data_point = DataPoint(tile, annotation) _painter = Painter(fill=True) with pytest.raises(ValueError): _painter.draw(data_point)
def draw(self): """Draw the legend, along the given axis. The generated legend is a grid with each cell having the dimensions of the biggest item to draw. The grid is then filled along the main axis first. The size of the legend along the main axis is fixed while the size along the minor one is variable. Returns: :class:`~PIL.Image.Image`: The output legend. """ # Init legend item drawer drawer = LegendItemDrawer(background_color=self._background_color, scale=self._scale, margins=self._item_margins) margin = int(drawer.TEXT_SIZE * self._scale) # Draw all items items = self._draw_items(drawer) # No items to draw means empty legend if not items: return PIL.Image.new(mode='RGBA', size=(0, 0)) # Compute position for each item position_generator = LegendItemPositionGenerator( items_sizes=[item.size for item in items], axis=self._axis, max_size_along_axis=self._max_size_along_axis - 2 * margin, main_axis_align=self._main_axis_align, minor_axis_align=self._minor_axis_align) items_with_position = [ (items[i], position) for i, position in enumerate(list(position_generator)) ] # Prepare tag if needed upper_margin = 0 draw = None if self._plot_tag: upper_margin = int( (2 * 2 + 0.75 * drawer.TEXT_SIZE + 2 * 4) * self._scale) # Prepare record and descriptor tag_descriptor = CategoricalDescriptor(name='name') tag_record = Record( coordinates=[[[0.5 * margin, int(upper_margin / 2)], [0.5 * margin, int(upper_margin / 2) + 1], [0.5 * margin + 1, int(upper_margin / 2) + 1], [0.5 * margin + 1, int(upper_margin / 2)], [0.5 * margin, int(upper_margin / 2)]]], labels=['Dummy'], name=self._tag_descriptor.__descriptor__['name'], categorical_descriptor_name=0.5, color=Color(*get_outline_color(self._background_color), ctype='sRGB255')) tag_descriptor.update(RecordCollection(tag_record)) # Prepare TagPainter and Draw tag_painter = TagPainter(tag_descriptor, text_margin=2 * self._scale, text_size=0.75 * drawer.TEXT_SIZE * self._scale) draw = Draw(size=(int(position_generator.legend_size[0] + 2 * margin), upper_margin), zoom=1, mode='RGBA', background_color=self._background_color) # Draw legend tag tag_painter.draw(tag_record, draw=draw) # Create legend legend = PIL.Image.new( mode='RGBA', size=(position_generator.legend_size[0] + 2 * margin, position_generator.legend_size[1] + 2 * margin + upper_margin), color=self._background_color) for item, position in items_with_position: legend.alpha_composite(im=item, dest=(position[0] + margin, position[1] + margin + upper_margin)) if draw is not None: legend.alpha_composite(im=draw.overlay, dest=(0, 4)) if self._color_engine_interface['type'] == 'categorical': bottom_margin = position_generator.legend_size[ 1] - position_generator.true_legend_size[1] drawer.draw_outline_with_title(self._primary_descriptor_name, legend, margins=(0, upper_margin, 0, bottom_margin)) return legend
def test_draw(self): # Parameters color_engine_interface = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': { 'size': { 'small': Color(255, 0, 0, ctype='sRGB255'), 'medium': Color(255, 0, 0, ctype='sRGB255'), 'large': Color(255, 0, 0, ctype='sRGB255') } } } scale = 1 axis = 0 mosaic_size = (500, 500) background_color = (0, 0, 0) item_margins = (10, 10) main_axis_align = 'start' minor_axis_align = 'start' painter = LegendPainter(color_engine_interface=color_engine_interface, scale=scale, axis=axis, mosaic_size=mosaic_size, background_color=background_color, item_margins=item_margins, main_axis_align=main_axis_align, minor_axis_align=minor_axis_align) # Draw legend legend = painter.draw() # Checks assert isinstance(legend, PIL.Image.Image) assert legend.height == 500 assert legend.width > 0 # Empty Color Engine schema -> means empty RecordCollection # Parameters color_engine_interface = { 'name': 'Name(Main, Secondary)', 'type': 'categorical', 'schema': {} } painter = LegendPainter(color_engine_interface=color_engine_interface, scale=scale, axis=axis, mosaic_size=mosaic_size, background_color=background_color, item_margins=item_margins, main_axis_align=main_axis_align, minor_axis_align=minor_axis_align) # Draw legend legend = painter.draw() # Checks assert isinstance(legend, PIL.Image.Image) assert legend.height == 0 assert legend.width == 0