Commit 44761f78 authored by André Anjos's avatar André Anjos 💬

[gui] Add filtering option

parent 4581260a
Pipeline #26455 failed with stages
in 13 minutes and 12 seconds
......@@ -92,6 +92,7 @@ existing annotations.
import os
import math
from six.moves import tkinter
import numpy
......@@ -114,7 +115,7 @@ class HelpDialog(tkinter.Toplevel):
def __init__(self, parent, message, canvas):
tkinter.Toplevel.__init__(self, parent)
super(HelpDialog, self).__init__(parent)
self.transient(parent)
self.title('Help')
self.parent = parent
......@@ -202,12 +203,15 @@ class AnnotatorApp(tkinter.Tk):
"""
def __init__(self, images, extension, annotations, readonly=False, zoom=1.0,
imfilter=False,marker_radius=1, pixel_skip=5, default_mode = 'line',
*args, **kwargs):
marker_radius=1, pixel_skip=5, default_mode = 'line', *args, **kwargs):
tkinter.Tk.__init__(self, *args, **kwargs)
super(AnnotatorApp, self).__init__(*args, **kwargs)
self.title("annotate")
# TODO: cannot resize window and reset zoom level for the time being
# this would be a nice-to-have feature.
self.resizable(False, False)
self.basedir = images
self.filelist = io.find(self.basedir, '*%s' % extension)
logger.info('Loading %d images files found on %s', len(self.filelist),
......@@ -226,7 +230,7 @@ class AnnotatorApp(tkinter.Tk):
self.curr_annotation = None #the currently annotated object
self.annotation = [] #annotations existing on the current image
self.canvas = None #the main canvas
self.filter = imfilter
self.filter = False
self._update_image()
......@@ -289,7 +293,12 @@ class AnnotatorApp(tkinter.Tk):
if self.filter == True:
self.image = numpy.array(self.image)
self.image = exposure.equalize_adapthist(self.image, clip_limit=0.01)
kernel_size = self.variables['adaheq-kernel-size'].get()
nr = math.ceil(self.image.shape[0] / kernel_size)
nc = math.ceil(self.image.shape[1] / kernel_size)
clip = self.variables['adaheq-clip'].get()
self.image = exposure.equalize_adapthist(self.image,
kernel_size=(nr, nc), clip_limit=clip)
self.image = self.image * 255
self.image = Image.fromarray(self.image)
self.curr_photo = ImageTk.PhotoImage(self.image, Image.ANTIALIAS)
......@@ -300,6 +309,7 @@ class AnnotatorApp(tkinter.Tk):
self.buttons = {}
self.labels = {}
self.variables = {}
#self.tooltips = {}
f = self.frames['left'] = tkinter.Frame(self)
f.pack(side=tkinter.LEFT, padx=5, pady=5, fill=tkinter.BOTH, expand=True)
......@@ -312,6 +322,9 @@ class AnnotatorApp(tkinter.Tk):
b = self.buttons['previous-frame'] = tkinter.Button(f, text="← (p)",
command=self.previous_frame)
b.grid(row=0, column=0)
# Not working with Python 3.6 on macOS Mojave...
#self.tooltips['previous-frame'] = \
# widgets.Tooltip(b, text='Go to previous image (keyboard: p)')
b = self.buttons['next-frame'] = tkinter.Button(f, text="→ (n)",
command=self.next_frame)
......@@ -363,6 +376,30 @@ class AnnotatorApp(tkinter.Tk):
l = self.labels['annotation-mode'] = tkinter.Label(f, textvariable=v)
l.grid(row=5, columnspan=2)
f = self.frames['filters'] = tkinter.LabelFrame(self.frames['left'],
text="Filters", padx=5, pady=5)
f.grid(row=2)
b = self.buttons['adaheq'] = tkinter.Checkbutton(f,
text="CLAHE", command=self.toggle_filter)
b.grid(row=0, columnspan=3, sticky=tkinter.W)
v = self.variables['adaheq-kernel-size'] = tkinter.IntVar()
v.set(18)
l = self.labels['adaheq-kernel-size'] = tkinter.Label(f, text='Kernel:')
l.grid(row=1, column=1, sticky=tkinter.W)
b = self.buttons['adaheq-kernel-size'] = tkinter.Entry(f, textvariable=v,
width=4)
b.grid(row=1, column=2, sticky=tkinter.W)
v = self.variables['adaheq-clip'] = tkinter.DoubleVar()
v.set(0.02)
l = self.labels['adaheq-clip'] = tkinter.Label(f, text='Clip:')
l.grid(row=2, column=1, sticky=tkinter.W)
b = self.buttons['adaheq-clip'] = tkinter.Entry(f, textvariable=v,
width=4)
b.grid(row=2, column=2, sticky=tkinter.W)
# creates the image canvas on the root window
self.canvas = tkinter.Canvas(self, width=self.image.width,
height=self.image.height)
......@@ -441,7 +478,7 @@ class AnnotatorApp(tkinter.Tk):
self.variables['annotation-progress'].set('#%d out of %d' % (
self.annotation.index(self.curr_annotation)+1, len(self.annotation)))
self.variables['annotation-mode'].set('%s: %s' % (
'View' if self.readonly else 'Edit', self.default_mode.upper()))
'View' if self.readonly else 'Edit', self.default_mode.upper()[:4]))
stem = os.path.relpath(self.filelist[self.curr_pos], self.basedir)
self.title("annotate: %s (%d/%d)" % (stem, self.curr_pos+1,
......@@ -674,6 +711,17 @@ class AnnotatorApp(tkinter.Tk):
self._update_status_bar()
def toggle_filter(self, event=None):
"""Togglers filter/no-filter on the displayed image"""
if self.filter:
self.filter = False
self._update_image()
else:
self.filter = True
self._update_image()
def add_keyboard_bindings(self):
"""Adds mouse bindings to the given widget"""
......
......@@ -387,3 +387,154 @@ class Annotation(object):
self.point[closest_point] = (y+dy, x+dx)
self.canvas.move(self.widget[closest_point], dx, dy)
self._update_decoration()
class Tooltip:
'''Creates a tooltip for a given widget as the mouse goes on it.
see:
http://stackoverflow.com/questions/3221956/
what-is-the-simplest-way-to-make-tooltips-
in-tkinter/36221216#36221216
http://www.daniweb.com/programming/software-development/
code/484591/a-tooltip-class-for-tkinter
- Originally written by vegaseat on 2014.09.09.
- Modified to include a delay time by Victor Zaccardo on 2016.03.25.
- Modified
- to correct extreme right and extreme bottom behavior,
- to stay inside the screen whenever the tooltip might go out on
the top but still the screen is higher than the tooltip,
- to use the more flexible mouse positioning,
- to add customizable background color, padding, waittime and
wraplength on creation
by Alberto Vassena on 2016.11.05.
Tested on Ubuntu 16.04/16.10, running Python 3.5.2
TODO: themes styles support
'''
def __init__(self, widget,
*,
bg='#FFFFEA',
pad=(5, 3, 5, 3),
text='widget info',
waittime=400,
wraplength=250):
self.waittime = waittime # in miliseconds, originally 500
self.wraplength = wraplength # in pixels, originally 180
self.widget = widget
self.text = text
self.widget.bind("<Enter>", self.onEnter)
self.widget.bind("<Leave>", self.onLeave)
self.widget.bind("<ButtonPress>", self.onLeave)
self.bg = bg
self.pad = pad
self.id = None
self.tw = None
def onEnter(self, event=None):
self.schedule()
def onLeave(self, event=None):
self.unschedule()
self.hide()
def schedule(self):
self.unschedule()
self.id = self.widget.after(self.waittime, self.show)
def unschedule(self):
id_ = self.id
self.id = None
if id_:
self.widget.after_cancel(id_)
def show(self):
def tip_pos_calculator(widget, label,
*,
tip_delta=(10, 5), pad=(5, 3, 5, 3)):
w = widget
s_width, s_height = w.winfo_screenwidth(), w.winfo_screenheight()
width, height = (pad[0] + label.winfo_reqwidth() + pad[2],
pad[1] + label.winfo_reqheight() + pad[3])
mouse_x, mouse_y = w.winfo_pointerxy()
x1, y1 = mouse_x + tip_delta[0], mouse_y + tip_delta[1]
x2, y2 = x1 + width, y1 + height
x_delta = x2 - s_width
if x_delta < 0:
x_delta = 0
y_delta = y2 - s_height
if y_delta < 0:
y_delta = 0
offscreen = (x_delta, y_delta) != (0, 0)
if offscreen:
if x_delta:
x1 = mouse_x - tip_delta[0] - width
if y_delta:
y1 = mouse_y - tip_delta[1] - height
offscreen_again = y1 < 0 # out on the top
if offscreen_again:
# No further checks will be done.
# TIP:
# A further mod might automagically augment the
# wraplength when the tooltip is too high to be
# kept inside the screen.
y1 = 0
return x1, y1
bg = self.bg
pad = self.pad
widget = self.widget
# creates a toplevel window
self.tw = tkinter.Toplevel(widget)
# Leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
win = tkinter.Frame(self.tw,
background=bg,
borderwidth=0)
label = tkinter.Label(win,
text=self.text,
justify=tkinter.LEFT,
background=bg,
relief=tkinter.SOLID,
borderwidth=0,
wraplength=self.wraplength)
label.grid(padx=(pad[0], pad[2]),
pady=(pad[1], pad[3]),
sticky=tkinter.NSEW)
win.grid()
x, y = tip_pos_calculator(widget, label)
self.tw.wm_geometry("+%d+%d" % (x, y))
def hide(self):
tw = self.tw
if tw:
tw.destroy()
self.tw = None
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment