I am trying to create a small program that would let me draw over a live camera feed. To do so, I display the video stream in a label and then overlay the label with a canvas the user can draw on.
My problem is that I can't get the canvas background to be invisible. I want the polygon drawn on the canvas to be fully opaque, but I do not want the camera feed to be obscured.
Here is my code:
from PIL import Image, ImageTk
import tkinter as tk
import cv2
import matplotlib.path as mpltPath
class Application:
def __init__(self):
""" Initialize application which uses OpenCV + Tkinter. It displays
a video stream in a Tkinter window"""
self.vs = cv2.VideoCapture(0) # capture video frames, 0 is the default video camera
self.root = tk.Tk() # initialize root window
self.root.title("AutoCatLaser") # set window title
# self.destructor function gets fired when the window is closed
self.root.protocol('WM_DELETE_WINDOW', self.destructor)
#tk.wm_attributes("-transparentcolor", TRANSCOLOUR)
self.panel = tk.Label(self.root) # initialize image panel
self.panel.place(x=5, y=5, relwidth=1, relheight=1, width=-10, height=-10)
self.canvas = tk.Canvas(self.root) #initialize canvas
self.canvas.place(x=20, y=20, relwidth=.5, relheight=.5)
self.canvas.bind('<Button-1>', self.click) #left mouse click event
self.canvas.bind('<B1-Motion>', self.move) #moving mouse while left click is pressed event
self.canvas.bind('<ButtonRelease-1>', self.release) #left mouse click release event
# start a self.video_loop that constantly pools the video sensor
# for the most recently read frame
self.video_loop()
self.points = []
self.polygon = None
self.mpltpoints= []
self.mpltpoly = None
self.x = []
def video_loop(self):
""" Get frame from the video stream and show it in Tkinter """
ok, frame = self.vs.read() # read frame from video stream
if ok: # frame captured without any errors
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) # convert colors from BGR to RGBA
self.current_image = Image.fromarray(cv2image) # convert image for PIL
imgtk = ImageTk.PhotoImage(image=self.current_image) # convert image for tkinter
self.panel.imgtk = imgtk # anchor imgtk so it does not be deleted by garbage-collector
self.panel.config(image=imgtk) # show the image
self.root.after(30, self.video_loop) # call the same function after 30 milliseconds
def click(self, event):
self.mpltpoints.clear()
self.points = [event.x, event.y]
self.mpltpoints.append((event.x, event.y))
print((event.x, event.y))
# at start there is no polygon on screen so there is nothing to delete
if self.polygon:
self.canvas.delete(self.polygon)
self.polygon = None # I need it in `move()`
def move(self, event):
self.points += [event.x, event.y]
self.mpltpoints.append((event.x, event.y))
if not self.polygon:
# create line if not exists - now `self.points` have two points
self.polygon = self.canvas.create_line(self.points, width=2)
else:
# update existing line
self.canvas.coords(self.polygon, self.points)
def release(self, event):
self.canvas.delete(self.polygon)
self.polygon = self.canvas.create_polygon(self.points, width=2, fill='red', outline='black', stipple = 'gray25')
self.path = mpltPath.Path(self.mpltpoints)
def destructor(self):
""" Destroy the root object and release all resources """
print("[INFO] closing...")
self.root.destroy()
self.vs.release() # release web camera
cv2.destroyAllWindows() # it is not mandatory in this application
# start the app
print("[INFO] starting...")
pba = Application()
pba.root.mainloop()
Is there a way to get a fully transparent background while still keeping the canvas items opaque? Or should I try a totally different route to achieve my goal?