diff --git a/ipyannotator/bbox_canvas.py b/ipyannotator/bbox_canvas.py
index 62d7cbf..568c46a 100644
--- a/ipyannotator/bbox_canvas.py
+++ b/ipyannotator/bbox_canvas.py
@@ -51,15 +51,20 @@ def get_image_size(path):
# Internal Cell
-def draw_img(canvas, file, clear=False):
- # draws resized image on canvas and returns scale used
+def draw_img(canvas, file, clear=False, canvas_size=None, rescale=1.0):
+ # draws an image on canvas, a specific size can be passed to scale image
with hold_canvas(canvas):
if clear:
canvas.clear()
sprite1 = Image.from_file(file)
- width_canvas, height_canvas = canvas.width, canvas.height
+ if canvas_size:
+ width_canvas, height_canvas = canvas_size
+ # if no specific size is passed, use current canvas size
+ else:
+ width_canvas, height_canvas = canvas.width, canvas.height
+
width_img, height_img = get_image_size(file)
ratio_canvas = float(width_canvas) / height_canvas
@@ -73,8 +78,8 @@ def draw_img(canvas, file, clear=False):
scale = height_canvas / height_img
canvas.draw_image(sprite1, 0, 0,
- width=width_img * min(1, scale),
- height=height_img * min(1, scale))
+ width=(width_img * min(1, scale)) * rescale,
+ height=(height_img * min(1, scale)) * rescale)
return scale
# Internal Cell
@@ -202,7 +207,6 @@ def _draw_bbox(self, change):
def _clear_bbox(self):
self._multi_canvas[self._box_layer].clear()
-
@traitlets.observe('image_path')
def _draw_image(self, image):
self._image_scale = draw_img(self._multi_canvas[self._image_layer], self.image_path, clear=True)
diff --git a/ipyannotator/capture_annotator.py b/ipyannotator/capture_annotator.py
index 6483392..2205eaa 100644
--- a/ipyannotator/capture_annotator.py
+++ b/ipyannotator/capture_annotator.py
@@ -120,6 +120,9 @@ def __init__(self, image_width=150, image_height=150,
indent=False,
layout=Layout(width='100px'))
+ self._none_checkbox.add_class("none-checkbox-class")
+ display(HTML(""))
+
self._controls_box = HBox([self._navi, self._save_btn, self._none_checkbox],
layout=Layout(display='flex', justify_content='center', flex_flow='wrap', align_items='center'))
diff --git a/ipyannotator/im2im_annotator.py b/ipyannotator/im2im_annotator.py
index f9dc067..c2fdfb0 100644
--- a/ipyannotator/im2im_annotator.py
+++ b/ipyannotator/im2im_annotator.py
@@ -32,22 +32,39 @@
class ImCanvas(HBox, HasTraits):
image_path = Unicode()
_image_scale = Float()
+ _image_rescale = Float(1.0)
def __init__(self, width=150, height=150):
self._canvas = Canvas(width=width, height=height)
+ self._initial_canvas_size = self._canvas.size
super().__init__([self._canvas])
+ def _draw_image(self, canvas_size=None):
+ self._image_scale = draw_img(self._canvas, self.image_path, clear=True,
+ canvas_size=canvas_size, rescale=self._image_rescale)
+
@observe('image_path')
- def _draw_image(self, change):
- self._image_scale = draw_img(self._canvas, self.image_path, clear=True)
+ def _call_draw_image(self, change):
+ self._draw_image(self._initial_canvas_size)
# Add value as a read-only property
@property
def image_scale(self):
return self._image_scale
+ @observe('_image_rescale')
+ def _redraw_image(self, change):
+ # Resize canvas
+ new_width = self._initial_canvas_size[0] * self._image_rescale
+ new_height = self._initial_canvas_size[1] * self._image_rescale
+ self._canvas.size = (new_width, new_height)
+
+ # As draw_image method uses canvas current size, we pass
+ # as parameter the initial size of canvas (before rescaling it too)
+ self._draw_image(self._initial_canvas_size)
+
def _clear_image(self):
self._canvas.clear()
@@ -81,7 +98,7 @@ def __init__(self, im_width=300, im_height=300,
self._image = ImCanvas(width=im_width, height=im_height)
- self._navi = Navi()
+ self._navi = Navi(disable_resize=False)
self._save_btn = Button(description="Save",
layout=Layout(width='auto'))
@@ -90,6 +107,8 @@ def __init__(self, im_width=300, im_height=300,
self._controls_box = HBox([self._navi, self._save_btn],
layout=Layout(display='flex', justify_content='center', flex_flow='wrap', align_items='center'))
+ self._controls_box.add_class("im2im-annotator-class")
+ display(HTML(""))
self._grid_box = CaptureGrid(grid_item=ImageButton, image_width=label_width, image_height=label_height, n_rows=n_rows, n_cols=n_cols)
@@ -98,6 +117,7 @@ def __init__(self, im_width=300, im_height=300,
self._labels_box = VBox(children = [self._grid_label, self._grid_box],
layout=Layout(display='flex', justify_content='center', flex_wrap='wrap', align_items='center'))
+ self._navi._size_dropdown.observe(self.change_scale, names='value')
super().__init__(header=None,
left_sidebar=VBox([self._image, self._controls_box], layout=Layout(display='flex', justify_content='center', flex_wrap='wrap', align_items='center')),
@@ -107,6 +127,12 @@ def __init__(self, im_width=300, im_height=300,
pane_widths=(6, 4, 0),
pane_heights=(1, 1, 1))
+
+ def change_scale(self, change):
+ new_scale = int(change['new'])
+ if new_scale:
+ self._image._image_rescale = new_scale / 100
+
def on_client_ready(self, callback):
self._image.observe_client_ready(callback)
diff --git a/ipyannotator/navi_widget.py b/ipyannotator/navi_widget.py
index 168bdb5..4ae61cf 100644
--- a/ipyannotator/navi_widget.py
+++ b/ipyannotator/navi_widget.py
@@ -5,17 +5,20 @@
# Internal Cell
from ipywidgets import (AppLayout, Button, IntSlider,
HBox, Output,
- Layout, Label)
-from traitlets import Int, observe, link, HasTraits
+ Layout, Label, Dropdown)
+from IPython.display import display, HTML
+from traitlets import Int, Bool, observe, link, HasTraits
# Internal Cell
class NaviGUI(HBox):
max_im_number = Int(0)
+ disable_resize = Bool(True)
def __init__(self):
self._im_number_slider = IntSlider(min=0, max=self.max_im_number-1,
- value=0, description='Image Nr.')
+ value=0, description='Image Nr.', style={'description_width': 'initial'},
+ layout=Layout(width='250px'))
self._prev_btn = Button(description='< Previous',
layout=Layout(width='auto'))
@@ -23,8 +26,23 @@ def __init__(self):
self._next_btn = Button(description='Next >',
layout=Layout(width='auto'))
- super().__init__(children=[self._prev_btn, self._im_number_slider, self._next_btn],
- layout=Layout(display='flex', flex_flow='row wrap', align_items='center'))
+ self._size_dropdown = Dropdown(options=['50', '75', '100', '150', '200'],
+ value='100', description="Size", disabled=self.disable_resize,
+ layout=Layout(width='auto'), style={'description_width': 'initial'})
+
+ self._im_number_slider.add_class("navi-class")
+ self._prev_btn.add_class("navi-class")
+ self._next_btn.add_class("navi-class")
+ self._size_dropdown.add_class("navi-class")
+
+ display(HTML(""))
+ display(HTML(""))
+
+ super().__init__(children=[self._prev_btn, self._im_number_slider, self._size_dropdown,
+ self._next_btn], layout=Layout(display='inline-flex',
+ flex_flow='row wrap',
+ align_items='center',
+ justify_content='space-around'))
@observe('max_im_number')
def check_im_num(self, change):
@@ -37,6 +55,7 @@ def check_im_num(self, change):
class NaviLogic(HasTraits):
index = Int(0)
max_im_number = Int(0)
+ disable_resize = Bool(True)
def __init__(self):
super().__init__()
@@ -52,8 +71,9 @@ class Navi(NaviGUI):
Represents simple navigation module with slider.
"""
- def __init__(self, max_im_number=1):
+ def __init__(self, max_im_number=1, disable_resize=True):
self.max_im_number = max_im_number
+ self.disable_resize = disable_resize
super().__init__()
@@ -64,4 +84,5 @@ def __init__(self, max_im_number=1):
# link slider value to button increment logic
link((self._im_number_slider, 'value'), (self.model, 'index'))
- link((self, 'max_im_number'), (self.model, 'max_im_number'))
\ No newline at end of file
+ link((self, 'max_im_number'), (self.model, 'max_im_number'))
+ link((self, 'disable_resize'), (self.model, 'disable_resize'))
\ No newline at end of file
diff --git a/nbs/01_bbox_canvas.ipynb b/nbs/01_bbox_canvas.ipynb
index 96d2a45..0ebfe22 100644
--- a/nbs/01_bbox_canvas.ipynb
+++ b/nbs/01_bbox_canvas.ipynb
@@ -285,15 +285,20 @@
"source": [
"#exporti\n",
"\n",
- "def draw_img(canvas, file, clear=False):\n",
- " # draws resized image on canvas and returns scale used\n",
+ "def draw_img(canvas, file, clear=False, canvas_size=None, rescale=1.0):\n",
+ " # draws an image on canvas, a specific size can be passed to scale image\n",
" with hold_canvas(canvas):\n",
" if clear:\n",
" canvas.clear()\n",
"\n",
" sprite1 = Image.from_file(file)\n",
"\n",
- " width_canvas, height_canvas = canvas.width, canvas.height\n",
+ " if canvas_size:\n",
+ " width_canvas, height_canvas = canvas_size\n",
+ " # if no specific size is passed, use current canvas size\n",
+ " else:\n",
+ " width_canvas, height_canvas = canvas.width, canvas.height\n",
+ "\n",
" width_img, height_img = get_image_size(file)\n",
"\n",
" ratio_canvas = float(width_canvas) / height_canvas\n",
@@ -307,8 +312,8 @@
" scale = height_canvas / height_img\n",
"\n",
" canvas.draw_image(sprite1, 0, 0, \n",
- " width=width_img * min(1, scale),\n",
- " height=height_img * min(1, scale))\n",
+ " width=(width_img * min(1, scale)) * rescale,\n",
+ " height=(height_img * min(1, scale)) * rescale)\n",
" return scale"
]
},
@@ -319,7 +324,7 @@
"outputs": [],
"source": [
"file = \"../data/projects/bbox/pics/red400x640.png\"\n",
- "canvas = Canvas(width=300, height=300)\n",
+ "canvas = Canvas(width=300, height=320)\n",
"draw_bg(canvas)\n",
"scale = draw_img(canvas, file)\n",
"print(scale)\n",
@@ -559,7 +564,7 @@
" else: # otherwise, save bbox values to backend\n",
" self.bbox_coords = dict({ k: v / self._image_scale for k, v in self._canvas_bbox_coords.items() })\n",
"# print(\"<- STOP DRAWING\")\n",
- " \n",
+ "\n",
" \n",
" @traitlets.observe('bbox_coords')\n",
" def _update_canvas_bbox_coords(self, change):\n",
@@ -592,13 +597,12 @@
" \n",
" def _clear_bbox(self):\n",
" self._multi_canvas[self._box_layer].clear()\n",
- " \n",
- " \n",
+ "\n",
" @traitlets.observe('image_path')\n",
" def _draw_image(self, image):\n",
" self._image_scale = draw_img(self._multi_canvas[self._image_layer], self.image_path, clear=True)\n",
" self._im_name_box.value = Path(self.image_path).name\n",
- "\n",
+ " \n",
" @property\n",
" def image_scale(self):\n",
" return self._image_scale\n",
@@ -612,13 +616,20 @@
" self._multi_canvas.on_client_ready(cb)"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
- "gui = BBoxCanvas(width=100, height=100)\n",
+ "gui = BBoxCanvas(width=200, height=200)\n",
"gui"
]
},
diff --git a/nbs/01b_tutorial_image_classification.ipynb b/nbs/01b_tutorial_image_classification.ipynb
index 1fd3467..bb01fa1 100644
--- a/nbs/01b_tutorial_image_classification.ipynb
+++ b/nbs/01b_tutorial_image_classification.ipynb
@@ -254,7 +254,7 @@
" n_cols=n_cols,\n",
" label_autosize=False\n",
" )\n",
- "display(im2im)"
+ "im2im"
]
},
{
diff --git a/nbs/02_navi_widget.ipynb b/nbs/02_navi_widget.ipynb
index 829aea6..54a8e08 100644
--- a/nbs/02_navi_widget.ipynb
+++ b/nbs/02_navi_widget.ipynb
@@ -36,8 +36,9 @@
"#exporti\n",
"from ipywidgets import (AppLayout, Button, IntSlider,\n",
" HBox, Output,\n",
- " Layout, Label)\n",
- "from traitlets import Int, observe, link, HasTraits "
+ " Layout, Label, Dropdown)\n",
+ "from IPython.display import display, HTML\n",
+ "from traitlets import Int, Bool, observe, link, HasTraits "
]
},
{
@@ -50,10 +51,12 @@
"\n",
"class NaviGUI(HBox):\n",
" max_im_number = Int(0)\n",
+ " disable_resize = Bool(True)\n",
" \n",
" def __init__(self):\n",
" self._im_number_slider = IntSlider(min=0, max=self.max_im_number-1,\n",
- " value=0, description='Image Nr.')\n",
+ " value=0, description='Image Nr.', style={'description_width': 'initial'},\n",
+ " layout=Layout(width='250px'))\n",
" \n",
" self._prev_btn = Button(description='< Previous',\n",
" layout=Layout(width='auto'))\n",
@@ -61,9 +64,24 @@
" self._next_btn = Button(description='Next >',\n",
" layout=Layout(width='auto'))\n",
" \n",
- " super().__init__(children=[self._prev_btn, self._im_number_slider, self._next_btn],\n",
- " layout=Layout(display='flex', flex_flow='row wrap', align_items='center'))\n",
+ " self._size_dropdown = Dropdown(options=['50', '75', '100', '150', '200'],\n",
+ " value='100', description=\"Size\", disabled=self.disable_resize,\n",
+ " layout=Layout(width='auto'), style={'description_width': 'initial'})\n",
+ "\n",
+ " self._im_number_slider.add_class(\"navi-class\")\n",
+ " self._prev_btn.add_class(\"navi-class\")\n",
+ " self._next_btn.add_class(\"navi-class\")\n",
+ " self._size_dropdown.add_class(\"navi-class\")\n",
+ " \n",
+ " display(HTML(\"\"))\n",
+ " display(HTML(\"\"))\n",
" \n",
+ " super().__init__(children=[self._prev_btn, self._im_number_slider, self._size_dropdown,\n",
+ " self._next_btn], layout=Layout(display='inline-flex',\n",
+ " flex_flow='row wrap',\n",
+ " align_items='center',\n",
+ " justify_content='space-around'))\n",
+ "\n",
" @observe('max_im_number')\n",
" def check_im_num(self, change):\n",
" if not hasattr(self, '_im_number_slider'):\n",
@@ -82,6 +100,7 @@
"class NaviLogic(HasTraits):\n",
" index = Int(0)\n",
" max_im_number = Int(0)\n",
+ " disable_resize = Bool(True)\n",
" \n",
" def __init__(self):\n",
" super().__init__()\n",
@@ -104,8 +123,9 @@
" Represents simple navigation module with slider.\n",
" \n",
" \"\"\"\n",
- " def __init__(self, max_im_number=1):\n",
+ " def __init__(self, max_im_number=1, disable_resize=True):\n",
" self.max_im_number = max_im_number\n",
+ " self.disable_resize = disable_resize\n",
" \n",
" super().__init__()\n",
" \n",
@@ -116,7 +136,8 @@
" \n",
" # link slider value to button increment logic\n",
" link((self._im_number_slider, 'value'), (self.model, 'index'))\n",
- " link((self, 'max_im_number'), (self.model, 'max_im_number'))"
+ " link((self, 'max_im_number'), (self.model, 'max_im_number'))\n",
+ " link((self, 'disable_resize'), (self.model, 'disable_resize'))"
]
},
{
@@ -149,9 +170,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "ipyannotator_env_local",
+ "display_name": "Python 3",
"language": "python",
- "name": "ipyannotator_env_local"
+ "name": "python3"
}
},
"nbformat": 4,
diff --git a/nbs/06_capture_annotator.ipynb b/nbs/06_capture_annotator.ipynb
index 1e8baed..10abf0a 100644
--- a/nbs/06_capture_annotator.ipynb
+++ b/nbs/06_capture_annotator.ipynb
@@ -251,6 +251,9 @@
" indent=False,\n",
" layout=Layout(width='100px'))\n",
"\n",
+ " self._none_checkbox.add_class(\"none-checkbox-class\")\n",
+ " display(HTML(\"\"))\n",
+ "\n",
" self._controls_box = HBox([self._navi, self._save_btn, self._none_checkbox],\n",
" layout=Layout(display='flex', justify_content='center', flex_flow='wrap', align_items='center'))\n",
" \n",
diff --git a/nbs/07_im2im_annotator.ipynb b/nbs/07_im2im_annotator.ipynb
index a6d882f..68b1ca5 100644
--- a/nbs/07_im2im_annotator.ipynb
+++ b/nbs/07_im2im_annotator.ipynb
@@ -70,25 +70,42 @@
"class ImCanvas(HBox, HasTraits):\n",
" image_path = Unicode()\n",
" _image_scale = Float()\n",
- " \n",
+ " _image_rescale = Float(1.0)\n",
+ "\n",
" def __init__(self, width=150, height=150):\n",
- " \n",
+ " \n",
" self._canvas = Canvas(width=width, height=height)\n",
+ " self._initial_canvas_size = self._canvas.size\n",
+ "\n",
+ " super().__init__([self._canvas])\n",
"\n",
- " super().__init__([self._canvas]) \n",
+ " def _draw_image(self, canvas_size=None):\n",
+ " self._image_scale = draw_img(self._canvas, self.image_path, clear=True,\n",
+ " canvas_size=canvas_size, rescale=self._image_rescale)\n",
"\n",
" @observe('image_path')\n",
- " def _draw_image(self, change):\n",
- " self._image_scale = draw_img(self._canvas, self.image_path, clear=True)\n",
+ " def _call_draw_image(self, change):\n",
+ " self._draw_image(self._initial_canvas_size)\n",
"\n",
" # Add value as a read-only property\n",
" @property\n",
" def image_scale(self):\n",
" return self._image_scale\n",
- " \n",
+ "\n",
+ " @observe('_image_rescale')\n",
+ " def _redraw_image(self, change):\n",
+ " # Resize canvas\n",
+ " new_width = self._initial_canvas_size[0] * self._image_rescale\n",
+ " new_height = self._initial_canvas_size[1] * self._image_rescale\n",
+ " self._canvas.size = (new_width, new_height)\n",
+ " \n",
+ " # As draw_image method uses canvas current size, we pass\n",
+ " # as parameter the initial size of canvas (before rescaling it too)\n",
+ " self._draw_image(self._initial_canvas_size)\n",
+ "\n",
" def _clear_image(self):\n",
" self._canvas.clear()\n",
- " \n",
+ "\n",
" # needed to support voila\n",
" # https://ipycanvas.readthedocs.io/en/latest/advanced.html#ipycanvas-in-voila\n",
" def observe_client_ready(self, cb=None):\n",
@@ -107,6 +124,15 @@
"im.image_scale"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "im._image_rescale = 2"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -148,7 +174,7 @@
" \n",
" self._image = ImCanvas(width=im_width, height=im_height)\n",
" \n",
- " self._navi = Navi()\n",
+ " self._navi = Navi(disable_resize=False)\n",
" \n",
" self._save_btn = Button(description=\"Save\",\n",
" layout=Layout(width='auto'))\n",
@@ -157,14 +183,17 @@
" self._controls_box = HBox([self._navi, self._save_btn],\n",
" layout=Layout(display='flex', justify_content='center', flex_flow='wrap', align_items='center'))\n",
" \n",
+ " self._controls_box.add_class(\"im2im-annotator-class\")\n",
+ " display(HTML(\"\"))\n",
" \n",
" self._grid_box = CaptureGrid(grid_item=ImageButton, image_width=label_width, image_height=label_height, n_rows=n_rows, n_cols=n_cols)\n",
"\n",
"\n",
" self._grid_label = HTML(value=\"LABEL\",)\n",
" self._labels_box = VBox(children = [self._grid_label, self._grid_box],\n",
- " layout=Layout(display='flex', justify_content='center', flex_wrap='wrap', align_items='center'))\n",
- "\n",
+ " layout=Layout(display='flex', justify_content='center', flex_wrap='wrap', align_items='center')) \n",
+ " \n",
+ " self._navi._size_dropdown.observe(self.change_scale, names='value')\n",
" \n",
" super().__init__(header=None,\n",
" left_sidebar=VBox([self._image, self._controls_box], layout=Layout(display='flex', justify_content='center', flex_wrap='wrap', align_items='center')),\n",
@@ -173,6 +202,12 @@
" footer=None,\n",
" pane_widths=(6, 4, 0),\n",
" pane_heights=(1, 1, 1))\n",
+ " \n",
+ " \n",
+ " def change_scale(self, change):\n",
+ " new_scale = int(change['new'])\n",
+ " if new_scale:\n",
+ " self._image._image_rescale = new_scale / 100\n",
" \n",
" def on_client_ready(self, callback):\n",
" self._image.observe_client_ready(callback)\n",
@@ -201,11 +236,20 @@
"metadata": {},
"outputs": [],
"source": [
- "im2im_ = Im2ImAnnotatorGUI(im_height = 500, im_width = 500, label_width=50, label_height=50, n_rows=2, n_cols=3)\n",
+ "im2im_ = Im2ImAnnotatorGUI(im_height = 100, im_width = 100, label_width=50, label_height=50, n_rows=2, n_cols=3)\n",
"im2im_._image.image_path='../data/projects/im2im1/pics/Grass1.png'\n",
"im2im_"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "im2im_._image._image_rescale = 2"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,