in utils_cv/classification/widget.py [0:0]
def _create_ui(self):
"""Create and initialize widgets"""
# ------------
# Callbacks + logic
# ------------
def skip_image():
"""Return true if image should be skipped, and false otherwise."""
# See if UI-checkbox to skip images is checked
if not self.w_skip_annotated.value:
return False
# Stop skipping if image index is out of bounds
if (
self.vis_image_index <= 0
or self.vis_image_index >= len(self.im_filenames) - 1
):
return False
# Skip if image has annotation
im_filename = self.im_filenames[self.vis_image_index]
labels = self.annos[im_filename].labels
exclude = self.annos[im_filename].exclude
if exclude or len(labels) > 0:
return True
return False
def button_pressed(obj):
"""Next / previous image button callback."""
# Find next/previous image. Variable step is -1 or +1 depending on which button was pressed.
step = int(obj.value)
self.vis_image_index += step
while skip_image():
self.vis_image_index += step
self.vis_image_index = min(
max(self.vis_image_index, 0), len(self.im_filenames) - 1
)
self.w_image_slider.value = self.vis_image_index
self.update_ui()
def slider_changed(obj):
"""Image slider callback.
Need to wrap in try statement to avoid errors when slider value is not a number.
"""
try:
self.vis_image_index = int(obj["new"]["value"])
self.update_ui()
except Exception:
pass
def anno_changed(obj):
"""Label checkbox callback.
Update annotation file and write to disk
"""
# Test if call is coming from the user having clicked on a checkbox to change its state,
# rather than a change of state when e.g. the checkbox value was updated programatically. This is a bit
# of hack, but necessary since widgets.Checkbox() does not support a on_click() callback or similar.
if (
"new" in obj
and isinstance(obj["new"], dict)
and len(obj["new"]) == 0
):
# If single-label annotation then unset all checkboxes except the one which the user just clicked
if not self.w_multi_class.value:
for w in self.label_widgets:
if w.description != obj["owner"].description:
w.value = False
# Update annotation object
im_filename = self.im_filenames[self.vis_image_index]
self.annos[im_filename].labels = [
w.description for w in self.label_widgets if w.value
]
self.annos[im_filename].exclude = self.exclude_widget.value
# Write to disk as tab-separated file.
with open(self.anno_path, "w") as f:
f.write(
"{}\t{}\t{}\n".format(
"IM_FILENAME", "EXCLUDE", "LABELS"
)
)
for k, v in self.annos.items():
if v.labels != [] or v.exclude:
f.write(
"{}\t{}\t{}\n".format(
k, v.exclude, ",".join(v.labels)
)
)
# ------------
# UI - image + controls (left side)
# ------------
w_next_image_button = widgets.Button(description="Next")
w_next_image_button.value = "1"
w_next_image_button.layout = Layout(width="80px")
w_next_image_button.on_click(button_pressed)
w_previous_image_button = widgets.Button(description="Previous")
w_previous_image_button.value = "-1"
w_previous_image_button.layout = Layout(width="80px")
w_previous_image_button.on_click(button_pressed)
self.w_filename = widgets.Text(
value="", description="Name:", layout=Layout(width="200px")
)
self.w_path = widgets.Text(
value="", description="Path:", layout=Layout(width="200px")
)
self.w_image_slider = IntSlider(
min=0,
max=len(self.im_filenames) - 1,
step=1,
value=self.vis_image_index,
continuous_update=False,
)
self.w_image_slider.observe(slider_changed)
self.w_img = widgets.Image()
self.w_img.layout.width = f"{self.IM_WIDTH}px"
w_header = widgets.HBox(
children=[
w_previous_image_button,
w_next_image_button,
self.w_image_slider,
self.w_filename,
self.w_path,
]
)
# ------------
# UI - info (right side)
# ------------
# Options widgets
self.w_skip_annotated = widgets.Checkbox(
value=False, description="Skip annotated images."
)
self.w_multi_class = widgets.Checkbox(
value=False, description="Allow multi-class labeling"
)
# Label checkboxes widgets
self.exclude_widget = widgets.Checkbox(
value=False, description="EXCLUDE IMAGE"
)
self.exclude_widget.observe(anno_changed)
self.label_widgets = [
widgets.Checkbox(value=False, description=label)
for label in self.labels
]
for label_widget in self.label_widgets:
label_widget.observe(anno_changed)
# Combine UIs into tab widget
w_info = widgets.VBox(
children=[
widgets.HTML(value="Options:"),
self.w_skip_annotated,
self.w_multi_class,
widgets.HTML(value="Annotations:"),
self.exclude_widget,
*self.label_widgets,
]
)
w_info.layout.padding = "20px"
self.ui = widgets.Tab(
children=[
widgets.VBox(
children=[
w_header,
widgets.HBox(children=[self.w_img, w_info]),
]
)
]
)
self.ui.set_title(0, "Annotator")
# Fill UI with content
self.update_ui()