-
Notifications
You must be signed in to change notification settings - Fork 8
Open
Description
It is sometime hard to label small features. An option too zoom in would be useful.
If this feature is of interest, I made a partially functioning mock-up. It is missing the croping and scaling of the image, and how to handle non-square images. Optionally the full image can also be displayed in the smaller canvas on the right.
On the left if the image to label, on the right are the option to zoom in and out, and a display of where the zoomed image is with regards to the full scale image.
Example code
from ipycanvas import MultiCanvas
from ipywidgets import Button, VBox, HBox, Layout, Text
import numpy as np
class RectangleFlow(Canvas):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.prev_x = None
self.prev_y = None
self.fill_style = 'black'
self.on_mouse_move(self.move_rect)
self.rect_size = 10
self.draw_contour()
def clear_current_rect(self):
if self.prev_x is not None:
self.clear_rect(self.prev_x - (self.rect_size + 1),
self.prev_y - (self.rect_size + 1),
2 * (self.rect_size + 1),
2 * (self.rect_size + 1))
self.draw_contour()
def draw_contour(self):
self.stroke_rect(0,0, self.width, self.height)
def set_rect_size(self, value):
self.clear_current_rect()
self.rect_size = int(value)
if self.prev_x is not None:
self.move_rect(self.prev_x, self.prev_y)
def move_rect(self, x, y):
self.clear_current_rect()
x = max(self.rect_size, min(self.width - self.rect_size, x))
y = max(self.rect_size, min(self.height - self.rect_size, y))
self.prev_x = x
self.prev_y = y
self.stroke_rect(self.prev_x - self.rect_size,
self.prev_y - self.rect_size,
2 * self.rect_size,
2 * self.rect_size)
class ZoomableCanvas(HBox):
def __init__(self, image: np.ndarray):
self.image = image
self.canvas = Canvas(width=image.shape[0], height=image.shape[1])
self.canvas.put_image_data(self.image, 0, 0)
self.zoom_scale = 1
self.zoom_info = Text(f'{self.zoom_scale * 100}%', layout=Layout(width='90px', height='30px'))
self.zoom_btn = Button(description='+', layout=Layout(width='30px', height='30px'))
self.unzoom_btn = Button(description='-', layout=Layout(width='30px', height='30px'))
self.zoom_btn.on_click(self.zoom)
self.unzoom_btn.on_click(self.unzoom)
self.zoom_canvas = RectangleFlow(width=200, height=200)
self.zoom_canvas.set_rect_size(self.zoom_scale * (self.zoom_canvas.width // 2))
self.controls = HBox([self.zoom_btn, self.unzoom_btn, self.zoom_info])
super().__init__()
self.children = [self.canvas, VBox([self.controls, self.zoom_canvas])]
self.draw_contour()
def draw_contour(self):
self.canvas.stroke_rect(0,0, self.canvas.width, self.canvas.height)
def zoom(self, *args):
self.zoom_scale -= 0.1
self._update_zoom()
def unzoom(self, *args):
self.zoom_scale += 0.1
self._update_zoom()
def _update_zoom(self):
self.zoom_scale = round(self.zoom_scale, 2)
self.zoom_info.value = f'{self.zoom_scale * 100}%'
self.zoom_canvas.set_rect_size(self.zoom_scale * (self.zoom_canvas.width // 2))
zoomed_height, zoomed_width = self.zoom_scale * np.array(self.image.shape[:2])
self.canvas.clear()
self.canvas.put_image_data(self.image[0:int(zoomed_height), 0:int(zoomed_width)])
self.draw_contour()
x = np.linspace(-1, 1, 300)
y = np.linspace(-1, 1, 300)
x_grid, y_grid = np.meshgrid(x, y)
blue_channel = np.array(np.sin(x_grid**2 + y_grid**2) * 255, dtype=np.int32)
red_channel = np.zeros_like(blue_channel) + 200
green_channel = np.zeros_like(blue_channel) + 50
image_data = np.stack((red_channel, blue_channel, green_channel), axis=2)
ZoomableCanvas(image_data)Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
