Hello. The jpeg below shows the circles that are added to an image when I click on a location in the image (say a blemish I want to remove):
Below is the code I used to do this. I need help knowing how to access these shapes so I can edit or delete them? I found a way of using plotly callbacks to create these shapes, but if you look at the image - all shapes have trace 0
and 0
as a trace_name and trace_index. The only thing that changes are the x and y cordinates.
I can select and edit any shape I want using the mouse, but I can’t delete the shape, nor can I access any information on these shapes.
I’m sure plotly must be storing this somewhere. Can someone please help me understand how to access these shapes, and shape properties so I can edit or delete them?
Thank you!
# LIbraries used to run this code:
# Package that will be used to make graphs and capture user interactions
import plotly.graph_objects as go
# Package to create interactive controls
import ipywidgets as widgets
# Library used to handle array and array-like objects
import numpy as np
# OpenCV Library
import cv2
# Load an image using ipywidgets and opencv to create an image
# Object to store loaded files
uploader = widgets.FileUpload(accept = '', # Accepted file extension e.g. '.txt', '.pdf', 'image/*', 'image/*,.pdf'
multiple = False # True to accept multiple files upload else False
)
# Display the button to load an image
# This code is orginally run in a notebook, so there is a pause in the remaining code below
# This code will cause an error if attempting to "run-all" or outside a jupyter environment
display(uploader)
# Grab the file content and store it in a new object
uploaded_file = list(uploader.value.values())[0]
# Load image into OpenCV
image = cv2.imdecode(np.asarray(bytearray(uploaded_file['content'])), cv2.IMREAD_UNCHANGED)
# If image is not read properly, return error
if image is None:
print('Failed to load image file: {}'.format(uploaded_file['metadata']['name']))
else:
print('Succesfully loaded image file: {}'.format(uploaded_file['metadata']['name']))
# Convert from bgr to rgb
image = image[:, :, :: -1]
# Create a figurewidget object that will be used to
# handle the image
fig = go.FigureWidget()
# Add the image to the figure object
fig.add_image(z = image);
# Update the layout
fig.update_layout(height = 800,
width = 1200,
title = f"Image filename: {uploaded_file['metadata']['name']}",
);
# Image figure object
image_fig = fig.data[0]
# Create an output object
out = widgets.Output()
# Callback function
@out.capture(clear_output = True)
def get_mouse_clicks(trace, points, selector):
size = 10
ind = points.point_inds[0]
x = ind[1]
y = ind[0]
trace_name = points.trace_name
trace_index = points.trace_index
message = f"The mouse points are:x = {x}, y = {y}.\nThe trace name is: {trace_name}. \nThe trace index is: {trace_index}."
print(points)
print()
print(selector)
print()
print(message)
# When the left button is clicked
if selector.button == 0:
# Draw a circle
fig.add_shape(editable = True,
type = "circle",
x0 = x - size / 2,
y0 = y - size / 2,
x1 = x + size / 2,
y1 = y + size / 2,
xref = 'x',
yref = 'y',
fillcolor = "grey",
line_color = "black",
)
# Register the callback
image_fig.on_click(get_mouse_clicks)
# Display output
widgets.VBox([fig, out])
I’m not interested in finding a solution using Dash. At this point I’m trying to put something conceptual together first, which is why I’m interested in using ipywidgets and plotly to do this.
Thank you!
–Igor