Skip to content

Commit

Permalink
palaimon#9: Showing grid of 4 sample images instead of one
Browse files Browse the repository at this point in the history
  • Loading branch information
daviddavo committed Oct 26, 2021
1 parent 5517f8d commit 67b8554
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 9 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,7 @@ tools/fastai
checklink/cookies.txt

#ipyannotator
**/**/autogenerated*/
**/**/autogenerated*/

#nbs
nbs/data
2 changes: 1 addition & 1 deletion ipyannotator/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.3.5"
__version__ = "0.4.0"
1 change: 1 addition & 0 deletions ipyannotator/_nbdev.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"ImCanvas": "07_im2im_annotator.ipynb",
"Im2ImAnnotatorGUI": "07_im2im_annotator.ipynb",
"text_on_img": "07_im2im_annotator.ipynb",
"grid_imgs": "07_im2im_annotator.ipynb",
"flatten": "07_im2im_annotator.ipynb",
"reconstruct_class_images": "07_im2im_annotator.ipynb",
"Im2ImAnnotatorLogic": "07_im2im_annotator.ipynb",
Expand Down
5 changes: 5 additions & 0 deletions ipyannotator/capture_annotator.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class CaptureGrid(GridBox, HasTraits):
"""
debug_output = Output(layout={'border': '1px solid black'})
current_state = Dict()
autogenerate_idx = Int()

def __init__(self, grid_item=ImageButton, image_width=150, image_height=150,
n_rows=3, n_cols=3, display_label=False):
Expand Down Expand Up @@ -64,6 +65,10 @@ def __init__(self, grid_item=ImageButton, image_width=150, image_height=150,

super().__init__(children=self._labels, layout=Layout(**centered_settings))

@observe('autogenerate_idx')
def _autogenerate_idx_changed(self, change):
for i in self._labels:
i._read_image()

@debug_output.capture(clear_output=True)
def on_state_change(self, change=None):
Expand Down
60 changes: 58 additions & 2 deletions ipyannotator/im2im_annotator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
import re

from functools import partial
import itertools as it
from math import ceil
from pathlib import Path

from ipycanvas import Canvas, hold_canvas
from ipyevents import Event
from ipywidgets import (AppLayout, VBox, HBox, Button, GridBox, Layout, Checkbox, HTML, IntText, Valid, Output, Image)
from ipywidgets import (AppLayout, VBox, HBox, Button, GridBox, Layout, Checkbox, HTML, IntText, Valid, Output)
from traitlets import Dict, Int, Float, HasTraits, observe, dlink, link, List, Unicode

from .navi_widget import Navi
Expand Down Expand Up @@ -173,6 +174,41 @@ def text_on_img(text="Hello", lbl_w=None, lbl_h=None, font_size=14, filepath=Non

# Internal Cell

#exporti

def grid_imgs(images, lbl_w=None, lbl_h=None, filepath=None,maxcols=2,maxrows=2):
assert(images)

if lbl_w is None:
lbl_w = 150

if lbl_h is None:
lbl_h = 150

grid = Image.new(mode="RGB", size=(lbl_w, lbl_h))
cols, rows = (maxcols, maxrows) if len(images) >= maxcols*maxrows else (1, 1)

w, h = lbl_w // cols, lbl_h // rows
subset = images[:cols*rows]

# We only use the first images of the subset
for i, imgpath in enumerate(subset):
# Using NEAREST as its the fastest and the images will be small
original = Image.open(imgpath)
ratio = original.width/original.height
neww, newh = int(min(w,h)*ratio), int(min(w,h))
img = original.resize((neww, newh), Image.NEAREST)
x = i%cols*w + (w-neww)//2
y = i//rows*h + (h-newh)//2
grid.paste(img, (x, y))

if filepath:
grid.save(filepath)

return grid

# Internal Cell

try:
from collections.abc import Iterable
except ImportError:
Expand All @@ -199,7 +235,17 @@ def reconstruct_class_images(label_dir, annotation_file, lbl_w=None, lbl_h=None)

cl_im_name = f'{cl_name}.jpg' if not re.findall("([-\w]+\.(?:jpg|png|jpeg))", cl_name, re.IGNORECASE) else cl_name

text_on_img(text = os.path.splitext(cl_name)[0], filepath = label_dir/cl_im_name, lbl_w=lbl_w, lbl_h=lbl_h)
# Searching for a bunch of sample images
# We get a list with up to four images to avoid iterating through all the data
sample_images = list(it.islice((im_path for im_path,classes in data.items()
if classes and
cl_name in flatten(classes) and
Path(im_path).is_file()), 4))

if sample_images:
grid_imgs(sample_images, filepath = label_dir/cl_im_name,lbl_w=lbl_w, lbl_h=lbl_h,maxcols=2,maxrows=2)
else:
text_on_img(text = os.path.splitext(cl_name)[0], filepath = label_dir/cl_im_name, lbl_w=lbl_w, lbl_h=lbl_h)

# Internal Cell

Expand All @@ -211,6 +257,7 @@ class Im2ImAnnotatorLogic(HasTraits):
disp_number = Int() # number of labels on screen
label_state = Dict()
question_value = Unicode()
autogenerate_idx = Int()


def __init__(self, project_path, file_name=None, question=None,
Expand All @@ -219,6 +266,8 @@ def __init__(self, project_path, file_name=None, question=None,

self.project_path = Path(project_path)
self.step_down = step_down
self.lbl_w = lbl_w
self.lbl_h = lbl_h
self.image_dir, self.annotation_file_path = setup_project_paths(self.project_path,
file_name=file_name,
image_dir=image_dir,
Expand All @@ -227,6 +276,7 @@ def __init__(self, project_path, file_name=None, question=None,
# artificialy generate labels if no class images given
if label_dir is None:
self.label_dir = Path(self.project_path, 'class_autogenerated_' + ''.join(random.sample(str(uuid.uuid4()), 5)))
self.autogenerate_idx = 1
self.label_dir.mkdir(parents=True, exist_ok=True)

question = 'Autogenerated classes'
Expand All @@ -237,6 +287,7 @@ def __init__(self, project_path, file_name=None, question=None,
text_on_img(text = 'None', filepath = self.label_dir /'None.jpg', lbl_w=lbl_w, lbl_h=lbl_h)
else:
self.label_dir = Path(self.project_path, label_dir)
self.autogenerate_idx = 0


# select images and labels only given annotatin file
Expand Down Expand Up @@ -313,6 +364,10 @@ def _save_annotations(self, *args, **kwargs): # to disk
self._update_annotations(index)
self.annotations.save(self.annotation_file_path)

if self.autogenerate_idx:
reconstruct_class_images(self.label_dir, self.annotation_file_path, lbl_w=self.lbl_w, lbl_h=self.lbl_h)
self.autogenerate_idx += 1


@observe('index')
def _idx_changed(self, change):
Expand Down Expand Up @@ -388,6 +443,7 @@ def __init__(self, project_path, file_name=None, image_dir=None, step_down=False

# link state of model and grid box visualizer
link((self._model, 'label_state'), (self._grid_box, 'current_state'))
link((self._model, 'autogenerate_idx'), (self._grid_box, 'autogenerate_idx'))


def to_dict(self, only_annotated=True):
Expand Down
6 changes: 5 additions & 1 deletion ipyannotator/image_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ def __init__(self, im_path=None, label=None,

@observe('image_path')
def _read_image(self, change=None):
new_path = change['new']
if change:
new_path = change["new"]
else:
new_path = self.image_path

if new_path:
self.image.value = open(new_path, "rb").read()
if not self.children:
Expand Down
6 changes: 5 additions & 1 deletion nbs/05_image_button.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,11 @@
" \n",
" @observe('image_path')\n",
" def _read_image(self, change=None):\n",
" new_path = change['new']\n",
" if change:\n",
" new_path = change[\"new\"]\n",
" else:\n",
" new_path = self.image_path\n",
"\n",
" if new_path:\n",
" self.image.value = open(new_path, \"rb\").read()\n",
" if not self.children:\n",
Expand Down
5 changes: 5 additions & 0 deletions nbs/06_capture_annotator.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
" \"\"\"\n",
" debug_output = Output(layout={'border': '1px solid black'})\n",
" current_state = Dict()\n",
" autogenerate_idx = Int()\n",
" \n",
" def __init__(self, grid_item=ImageButton, image_width=150, image_height=150, \n",
" n_rows=3, n_cols=3, display_label=False):\n",
Expand Down Expand Up @@ -103,6 +104,10 @@
" \n",
" super().__init__(children=self._labels, layout=Layout(**centered_settings))\n",
" \n",
" @observe('autogenerate_idx')\n",
" def _autogenerate_idx_changed(self, change):\n",
" for i in self._labels:\n",
" i._read_image()\n",
" \n",
" @debug_output.capture(clear_output=True)\n",
" def on_state_change(self, change=None):\n",
Expand Down
68 changes: 65 additions & 3 deletions nbs/07_im2im_annotator.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@
"import re\n",
"\n",
"from functools import partial\n",
"import itertools as it\n",
"from math import ceil\n",
"from pathlib import Path\n",
"\n",
"from ipycanvas import Canvas, hold_canvas\n",
"from ipyevents import Event\n",
"from ipywidgets import (AppLayout, VBox, HBox, Button, GridBox, Layout, Checkbox, HTML, IntText, Valid, Output, Image)\n",
"from ipywidgets import (AppLayout, VBox, HBox, Button, GridBox, Layout, Checkbox, HTML, IntText, Valid, Output)\n",
"from traitlets import Dict, Int, Float, HasTraits, observe, dlink, link, List, Unicode\n",
"\n",
"from ipyannotator.navi_widget import Navi\n",
Expand Down Expand Up @@ -402,6 +403,47 @@
"text_on_img(text=\"new labe\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"#exporti\n",
"\n",
"def grid_imgs(images, lbl_w=None, lbl_h=None, filepath=None,maxcols=2,maxrows=2):\n",
" assert(images)\n",
"\n",
" if lbl_w is None:\n",
" lbl_w = 150\n",
" \n",
" if lbl_h is None:\n",
" lbl_h = 150\n",
"\n",
" grid = Image.new(mode=\"RGB\", size=(lbl_w, lbl_h))\n",
" cols, rows = (maxcols, maxrows) if len(images) >= maxcols*maxrows else (1, 1)\n",
"\n",
" w, h = lbl_w // cols, lbl_h // rows\n",
" subset = images[:cols*rows]\n",
"\n",
" # We only use the first images of the subset\n",
" for i, imgpath in enumerate(subset):\n",
" # Using NEAREST as its the fastest and the images will be small\n",
" original = Image.open(imgpath)\n",
" ratio = original.width/original.height\n",
" neww, newh = int(min(w,h)*ratio), int(min(w,h))\n",
" img = original.resize((neww, newh), Image.NEAREST)\n",
" x = i%cols*w + (w-neww)//2\n",
" y = i//rows*h + (h-newh)//2\n",
" grid.paste(img, (x, y))\n",
"\n",
" if filepath:\n",
" grid.save(filepath)\n",
"\n",
" return grid"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -460,8 +502,18 @@
" cl_name = \"None\"\n",
" \n",
" cl_im_name = f'{cl_name}.jpg' if not re.findall(\"([-\\w]+\\.(?:jpg|png|jpeg))\", cl_name, re.IGNORECASE) else cl_name\n",
" \n",
" text_on_img(text = os.path.splitext(cl_name)[0], filepath = label_dir/cl_im_name, lbl_w=lbl_w, lbl_h=lbl_h)"
"\n",
" # Searching for a bunch of sample images\n",
" # We get a list with up to four images to avoid iterating through all the data\n",
" sample_images = list(it.islice((im_path for im_path,classes in data.items()\n",
" if classes and \n",
" cl_name in flatten(classes) and\n",
" Path(im_path).is_file()), 4))\n",
"\n",
" if sample_images:\n",
" grid_imgs(sample_images, filepath = label_dir/cl_im_name,lbl_w=lbl_w, lbl_h=lbl_h,maxcols=2,maxrows=2)\n",
" else:\n",
" text_on_img(text = os.path.splitext(cl_name)[0], filepath = label_dir/cl_im_name, lbl_w=lbl_w, lbl_h=lbl_h)"
]
},
{
Expand Down Expand Up @@ -502,6 +554,7 @@
" disp_number = Int() # number of labels on screen\n",
" label_state = Dict()\n",
" question_value = Unicode()\n",
" autogenerate_idx = Int()\n",
"\n",
" \n",
" def __init__(self, project_path, file_name=None, question=None, \n",
Expand All @@ -510,6 +563,8 @@
" \n",
" self.project_path = Path(project_path)\n",
" self.step_down = step_down\n",
" self.lbl_w = lbl_w\n",
" self.lbl_h = lbl_h\n",
" self.image_dir, self.annotation_file_path = setup_project_paths(self.project_path,\n",
" file_name=file_name,\n",
" image_dir=image_dir,\n",
Expand All @@ -518,6 +573,7 @@
" # artificialy generate labels if no class images given\n",
" if label_dir is None:\n",
" self.label_dir = Path(self.project_path, 'class_autogenerated_' + ''.join(random.sample(str(uuid.uuid4()), 5)))\n",
" self.autogenerate_idx = 1\n",
" self.label_dir.mkdir(parents=True, exist_ok=True)\n",
" \n",
" question = 'Autogenerated classes'\n",
Expand All @@ -528,6 +584,7 @@
" text_on_img(text = 'None', filepath = self.label_dir /'None.jpg', lbl_w=lbl_w, lbl_h=lbl_h) \n",
" else:\n",
" self.label_dir = Path(self.project_path, label_dir)\n",
" self.autogenerate_idx = 0\n",
" \n",
" \n",
" # select images and labels only given annotatin file\n",
Expand Down Expand Up @@ -603,6 +660,10 @@
" index = kwargs.pop('old_index', self.index)\n",
" self._update_annotations(index) \n",
" self.annotations.save(self.annotation_file_path)\n",
"\n",
" if self.autogenerate_idx:\n",
" reconstruct_class_images(self.label_dir, self.annotation_file_path, lbl_w=self.lbl_w, lbl_h=self.lbl_h)\n",
" self.autogenerate_idx += 1\n",
" \n",
" \n",
" @observe('index')\n",
Expand Down Expand Up @@ -715,6 +776,7 @@
" \n",
" # link state of model and grid box visualizer\n",
" link((self._model, 'label_state'), (self._grid_box, 'current_state'))\n",
" link((self._model, 'autogenerate_idx'), (self._grid_box, 'autogenerate_idx'))\n",
" \n",
" \n",
" def to_dict(self, only_annotated=True):\n",
Expand Down

0 comments on commit 67b8554

Please sign in to comment.