I am trying to continuously display and replace an image in a Tkinter interface taken from OpenCV's VideoCapture. However, I am getting the following error that I think is a result of improper formatting of the image numpy array:
TypeError: unhashable type: 'numpy.ndarray'
How can I reformat it to all it to display properly? Below is my code:
import tkinter as tk
import cv2
import numpy as np
from PIL import ImageTk, Image
main = tk.Tk()
main.title("Hole Pattern Recognition")
main.geometry("300x300")
frame = tk.Frame(main)
frame.pack()
def startScan():
global main, frame
#begins utilizing webcam for scan
cap = cv2.VideoCapture(0)
while(True):
ret,img = cap.read()
img = ImageTk.PhotoImage(img)
panel = tk.Label(main, image = img)
panel.pack(side = "bottom", fill = "both", expand = "yes")
ch = cv2.waitKey(1)
if ch == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
startButton = tk.Button(frame,
text="Start Scan",
fg="blue",
command=startScan)
startButton.pack(side=tk.TOP)
main.mainloop()
First you need to use PIL.Image.fromarray() to convert the captured image to format supported by tkinter.
Second better not use while loop in main thread as it will block the tkinter mainloop. Use after() instead.
import tkinter as tk
from PIL import Image, ImageTk
import cv2
cap = None
main = tk.Tk()
main.title('Hole Pattern Recognition')
#main.geometry('300x300')
frame = tk.Frame(main)
frame.pack()
def startScan():
global cap
def scan():
ret, img = cap.read()
if ret:
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = Image.fromarray(img)
tkimg = ImageTk.PhotoImage(img)
panel.config(image=tkimg)
panel.tkimg = tkimg # save a reference to the image to avoid garbage collection
panel.after(25, scan) # change 25 to other value to adjust FPS
if cap is None:
cap = cv2.VideoCapture(0)
scan() # start the capture loop
else:
print('capture already started')
startButton = tk.Button(frame, text='Start Scan', fg='blue', command=startScan)
startButton.pack()
panel = tk.Label(main)
panel.pack()
main.mainloop()
if cap:
cap.release()
Related
i need to to add webcam window inside tkinter UI , what i actually need when the user pressed on start the webcam must appear in white box as it shown in the picture and the code down below also ive made a class that opens the webacam window and recognize faces
canvans=tkinter.Canvas(pro, width =900, height = 700, borderwidth=10, relief="solid",bg="#ffffff",
highlightbackground="#0E6655")
canvans.place(x=300,y=100)
pro.mainloop()
x=Gui()
In comments you get link to example but I made some changes
check if read() gives any frame
use cap.release() after using webcam
(PEP8) import * is not preferred
(PEP8) organize code - put all functions after import
import tkinter as tk # PEP8: `import *` is not preferred
from PIL import Image, ImageTk
import cv2
# --- functions --- # PEP8: all functions after imports
def show_frame():
# get frame
ret, frame = cap.read()
if ret:
# cv2 uses `BGR` but `GUI` needs `RGB`
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# convert to PIL image
img = Image.fromarray(frame)
# convert to Tkinter image
photo = ImageTk.PhotoImage(image=img)
# solution for bug in `PhotoImage`
label.photo = photo
# replace image in label
label.configure(image=photo)
# run again after 20ms (0.02s)
root.after(20, show_frame)
# --- main ---
cap = cv2.VideoCapture(0)
root = tk.Tk()
# create a Label to display frames
label = tk.Label(root)
label.pack(fill='both', expand=True) # to resize label when resize window
# start function which shows frame
show_frame()
root.mainloop()
cap.release()
BTW:
PEP 8 -- Style Guide for Python Code
Tkinter: Why Label doesn't display image? Bug with Garbage Collector in PhotoImage.
EDIT:
The same for Canvas
It uses image's ID to replace PhotoImage on Canvas
import tkinter as tk # PEP8: `import *` is not preferred
from PIL import Image, ImageTk
import cv2
# --- functions ---
def show_frame():
global image_id # inform function to assign new value to global variable instead of local variable
# get frame
ret, frame = cap.read()
if ret:
# cv2 uses `BGR` but `GUI` needs `RGB
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# convert to PIL image
img = Image.fromarray(frame)
# convert to Tkinter image
photo = ImageTk.PhotoImage(image=img)
# solution for bug in `PhotoImage`
canvas.photo = photo
# check if image already exists
if image_id:
# replace image in PhotoImage on canvas
canvas.itemconfig(image_id, image=photo)
else:
# create first image on canvas and keep its ID
image_id = canvas.create_image((0,0), image=photo, anchor='nw')
# resize canvas
canvas.configure(width=photo.width(), height=photo.height())
# run again after 20ms (0.02s)
root.after(20, show_frame)
# --- main ---
image_id = None # default value at start (to create global variable)
cap = cv2.VideoCapture(0)
root = tk.Tk()
# create a Label to display frames
canvas = tk.Canvas(root)
canvas.pack(fill='both', expand=True)
# start function which shows frame
show_frame()
root.mainloop()
cap.release()
I'm using ueye cameras connected to a Raspberry pi 4 running raspbian and I'm trying to get and display images from the cameras using OpenCV from python. The problem comes when I use:
cap=cv2.VideoCapture(0,cv2.CAP_DSHOW)
or
cv2.CAP_V4L2
or
cv2.CAP_ANY.
It doesn't detect the camera.
Maybe the problem was the device index '0' so I ran this code to try other indexes:
import cv2
cams_test=100
for i in range (-1,cams_test):
cap=cv2.VideoCapture(i,cv2.CAP_DSHOW)
test, frame=cap.read()
print("i : "+str(i)+" // result: " +str(test))
if test:
print("SUCCESSFULL!")
All indexes failed.
I read the following qüestion How can I use OpenCV to capture video stream of ueye cameras?
but i'm not able to find this /dev/ueye directory they are talking about.
Can I substitute the index number in videocapture to a path where my ueye cameras are installed? (I don't know this path)
Is there a way to retrieve the video stream from ueye cameras? Preferably keeping VideoCapture function.
Here's my code:
from tkinter import *
from PIL import Image
from PIL import ImageTk
import cv2
import imutils
def iniciar():
global cap
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
visualizar()
def visualizar():
global cap
if cap is not None:
ret, frame = cap.read()
if ret == True:
frame = imutils.resize(frame, width=640)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
im = Image.fromarray(frame)
img = ImageTk.PhotoImage(image=im)
lblVideo.configure(image=img)
lblVideo.image = img
lblVideo.after(10, visualizar)
else:
lblVideo.image = ""
cap.release()
def finalizar():
global cap
cap.release()
cap = None
root = Tk()
btnIniciar = Button(root, text="Iniciar", width=45, command=iniciar)
btnIniciar.grid(column=0, row=0, padx=5, pady=5)
btnFinalizar = Button(root, text="Finalizar", width=45, command=finalizar)
btnFinalizar.grid(column=1, row=0, padx=5, pady=5)
lblVideo = Label(root)
lblVideo.grid(column=0, row=1, columnspan=2)
root.mainloop()
Thanks in to whoever is reading my qüestion I hope the answer helps other people
I am making a gui with a button to make webcam on and off. and it is working but when i open it again after closing it then its shows only black screen.
how to resolve this issue.
Here is my code. my whole code is huge so i posted on the webcam part here.
i updated my code .......................
import tkinter as tk
from tkinter import *
import cv2
import numpy as np
from PIL import Image, ImageTk, ImageGrab
cap = cv2.VideoCapture(0)
webcam = None
WEBCAM_SIZE = (280, 200)
def read_frame(imgbox):
if cap.isOpened():
ret, frame = cap.read()
if ret:
frame = cv2.flip(frame, 1)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = cv2.resize(frame, WEBCAM_SIZE)
image = Image.fromarray(frame)
imgbox.image.paste(image)
webcam.after(20, read_frame, imgbox)
def stop_webcam(event):
global webcam
if webcam:
webcam.destroy()
webcam = None
cap.release()
def start_webcam():
global webcam
cap.open()
if webcam is None:
cap.isOpened()
webcam = tk.Toplevel()
#webcam = tk.lift()
webcam.attributes("-topmost", True) #it keep the window on top of others
webcam.geometry('{}x{}+5+520'.format(WEBCAM_SIZE[0], WEBCAM_SIZE[1]))
webcam.overrideredirect(1)
imgbox = tk.Label(webcam)
imgbox.pack()
imgbox.image = ImageTk.PhotoImage(image=Image.new('RGB',WEBCAM_SIZE,(0,0,0)))
imgbox.config(image=imgbox.image)
#webcam.bind('', stop_webcam)
read_frame(imgbox)
cap_btn = ttk.Button(frame, image=web, width=20, command=change_w)
cap_btn.grid(row=0, column=2)
cap_btn.image = web
def change_w():
if cap_btn.image == web:
start_webcam()
cap_btn.config(image=web2)
cap_btn.image = web2
else:
stop_webcam(None)
cap_btn.config(image=web)
cap_btn.image = web
web = PhotoImage(file='webcam.png')
web2 = PhotoImage(file='bl.png')
root.mainloop()
How to add a cv2 trackbar to a Tkinter window?
I have 2 snippets of code that my teammates have made, but it is difficult to integrate them in the same window.
I am able to use them in separate windows but I want to use them in the same window.
CODE FOR THE VIDEO PLAYING IN TKINTER:
import cv2
import tkinter as tk
from tkinter import *
# from tkinter import ttk
# from ttk import Frame
# import ImageTk
white = "#ffffff"
lightBlue2 = "#adc5ed"
font = "Constantia"
fontButtons = (font, 12)
maxWidth = 800
maxHeight = 480
#Graphics window
mainWindow = tk.Tk()
mainWindow.configure(bg=lightBlue2)
mainWindow.geometry('%dx%d+%d+%d' % (maxWidth,maxHeight,0,0))
mainWindow.resizable(0,0)
# mainWindow.overrideredirect(1)
mainFrame = Frame(mainWindow)
mainFrame.place(x=20, y=20)
#Capture video frames
lmain = tk.Label(mainFrame)
lmain.grid(row=0, column=0)
cap = cv2.VideoCapture('test.mp4')
def show_frame():
ret, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image).resize((760, 400))
imgtk = ImageTk.PhotoImage(image = img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(10, show_frame)
closeButton = Button(mainWindow, text = "CLOSE", font = fontButtons, bg = white, width = 20, height= 1)
closeButton.configure(command= lambda: mainWindow.destroy())
closeButton.place(x=270,y=430)
show_frame() #Display
mainWindow.mainloop() #Starts GUI
And I have a second snippet of code that uses cv2 to display the track bar and changes the position of the video.
import cv2
CURRENT_FRAME_FLAG = cv2.CAP_PROP_POS_FRAMES
TOTAL_FRAMES_FLAG = cv2.CAP_PROP_FRAME_COUNT
WIN_NAME = "Frame Grabber"
POS_TRACKBAR = "pos_trackbar"
cap = cv2.VideoCapture('C:/Users/ayush/Desktop/test.mp4')
ret, frame = cap.read()
def dummy():
pass
def save_image():
filename = "image_%0.5f.png" % t.time()
cv2.imwrite(filename, frame)
def seek_callback(x):
global frame
i = cv2.getTrackbarPos(POS_TRACKBAR, WIN_NAME)
cap.set(CURRENT_FRAME_FLAG, i-1)
_, frame = cap.read()
cv2.imshow(WIN_NAME, frame)
def mouse_callback(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDBLCLK:
save_image()
def skip_frame_generator(df):
def skip_frame():
global frame
cf = cap.get(CURRENT_FRAME_FLAG) - 1
cap.set(CURRENT_FRAME_FLAG, cf+df)
cv2.setTrackbarPos(POS_TRACKBAR, WIN_NAME, int(cap.get(CURRENT_FRAME_FLAG)))
_, frame = cap.read()
return skip_frame
cv2.namedWindow(WIN_NAME)
cv2.createTrackbar(POS_TRACKBAR, WIN_NAME, 0, int(cap.get(TOTAL_FRAMES_FLAG)), seek_callback)
cv2.setMouseCallback(WIN_NAME, mouse_callback)
actions = dict()
actions[ord("D")] = skip_frame_generator(10)
actions[ord("d")] = skip_frame_generator(1)
actions[ord("a")] = skip_frame_generator(-1)
actions[ord("A")] = skip_frame_generator(-10)
actions[ord("q")] = lambda: exit(0)
actions[ord("s")] = save_image
while True:
cv2.imshow(WIN_NAME, frame)
key = cv2.waitKey(0) & 0xFF
actions.get(key, dummy)()
I have to integrate the cv2 trackbar into the Tkinter window.
I am able to integrate but as I don't know how to use implement CV2 trackbar in tkinter, I am not able to do anything.
Please Help.
Try using tkinter Scale widgets instead of cv2 trackbars. They are slightly different but they should be fairly easy to swap out and work about the same. Then you can easily add them in your tkinter window/frame.
I am creating just a simple application for a live face detection using python opencv tkinter. I could do the live face detection but when I try to create a button to start the live feed, it is not even executing the program. Could anybody help me?
from ttk import *
import Tkinter as tk
from Tkinter import *
import cv2
from PIL import Image, ImageTk
import os
import numpy as np
face_classifier = cv2.CascadeClassifier('Haarcascades/haarcascade_frontalface_default.xml')
global last_frame #creating global variable
last_frame = np.zeros((480, 640, 3), dtype=np.uint8)
global cap
cap = cv2.VideoCapture(0)
def show_vid(): #creating a function
if not cap.isOpened():
print("cant open the camera")
flag, frame = cap.read()
frame = cv2.flip(frame, 1)
if flag is None:
print "Major error!"
elif flag:
global last_frame
last_frame = frame.copy()
pic = cv2.cvtColor(last_frame, cv2.COLOR_BGR2GRAY)
faces = face_classifier.detectMultiScale(pic, 1.3, 5)
for (x,y,w,h) in faces:
x = x - 50
w = w + 50
y = y - 50
h = h + 50
cv2.rectangle(last_frame,(x,y),(x+w,y+h),(255,0,0),2)
last_frame=cv2.cvtColor(last_frame, cv2.COLOR_BGR2GRAY)
img = Image.fromarray(last_frame)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(10, show_vid)
if __name__ == '__main__':
root=tk.Tk()
lmain = tk.Label(master=root)
lmain.grid(column=0, rowspan=4, padx=5, pady=5)
root.title("Live face detection")
button = Button(root, text = "Start", command=show_vid).pack()
#show_vid()
root.mainloop()
cap.release()
When I commented button line and uncommented show_vid() line live face detection works, but when I do the opposite it is not working.
You should replace the .pack() part of the line with button(row=yourrow, column=yourcolumn sticky=E) where you specify the column and row to place the button