I am trying to make a GUI-based face recognition program with tkinter and opencv. I have used the function cv2.VideoCapture() before, but usually NOT inside a function and it worked successfully.
This time, though, I wanted to use it inside a function, but the program just does not run. I got no errors in the terminal, and the window just froze.
Here is my code (I haven't yet added the face recognition functionality)
import tkinter as tk
from PIL import Image, ImageTk
root = tk.Tk()
root.configure(bg='#3d3d3d')
f1 = tk.LabelFrame(root, bg='#3d3d3d')
f1.place(relx=0.5, rely=0.53, anchor=tk.CENTER)
feed = tk.Label(f1)
feed.pack()
cap = cv2.VideoCapture(0)
def capture():
while cap.isOpened():
img = cap.read()[1]
img = cv2.flip(img, 1)
img1 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # changing color to RGB
img = ImageTk.PhotoImage(Image.fromarray(img1))
feed['image'] = img # putting the webcam feed in the 'feed' LabelFrame
capture()
root.mainloop()
I tried to input values 0, 1, -1 for the VideoCapture() function, but the problem persisted
Note that this is not my full code and I have just included the important parts. I am asking this question because I want to implement this functionality on a Toplevel() window which is opened by clicking a button. The code for the Toplevel() window is indside a function.
Thanks in advance!
There are mainly two issues in your code:
using while loop which will block root.mainloop() from executing. Use after() instead.
image created inside a function will be garbage collected if its reference is not saved
Below is a modified code to fix the above issues:
import tkinter as tk
from PIL import Image, ImageTk
import cv2
root = tk.Tk()
root.configure(bg='#3d3d3d')
root.geometry('800x600')
f1 = tk.LabelFrame(root, bg='#3d3d3d')
f1.place(relx=0.5, rely=0.53, anchor=tk.CENTER)
feed = tk.Label(f1)
feed.pack()
cap = cv2.VideoCapture(0)
# used after() instead of while loop
def capture():
if cap.isOpened():
ret, img = cap.read()
if ret:
img = cv2.flip(img, 1)
img1 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # changing color to RGB
img = ImageTk.PhotoImage(Image.fromarray(img1))
feed['image'] = img # putting the webcam feed in the 'feed' LabelFrame
feed.image = img # save reference of the image
root.after(10, capture)
capture()
root.mainloop()
Related
i tried to make a webcam video recording to a file using openCV python, i could not open the file with any of my video players. here is the code,
it works fine but I stop the recording and looking the file and it doesn't open. I guess there are some codec issues. I tried also (*'XVID') .avi format. but changed nothing.
here is the code
please help
from tkinter import *
from PIL import ImageTk, Image
import cv2
import threading
root = Tk()
root.geometry("750x500")
root.configure(bg="#0059aa")
#camera
camera_frame = LabelFrame(root, text=u"KAMERA STREAMING",
border=2,
width=398,
height=265)
camera_frame.place(x=183,y=33)
camera_label = Label(camera_frame,width=55,height=14)
camera_label.grid(row=0,column=0)
global capture
capture = cv2.VideoCapture(0)
# edit: close following two lines
# capture.set(3,250)
# capture.set(4,225)
global out
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('blabla.mp4', fourcc, 20.0, (640, 480))
global stopCam
stopCam = False
def show_frames():
global capture
# read the capture
ret, frame = capture.read()
# turned into image and display
cv2image = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
height, width, channels = cv2image.shape
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image = img)
camera_label.imgtk = imgtk
camera_label.configure(image=imgtk,width=width,height=height)
# record
global out
out.write(frame)
# quit
if (stopCam):
out.release()
capture.release()
cv2.destroyAllWindows()
return
camera_label.after(20,show_frames)
p1 = threading.Thread(target=show_frames)
buttonLabel = Label(camera_frame)
buttonLabel.grid(row=1,column=0)
connectButton = Button (buttonLabel, text=u"connect", command=p1.start, width=14)
connectButton.grid(row=0,column=0)
stopButton = Button(buttonLabel, text=u"stop", command= lambda: globals().update(stopCam=True) , width=14)
stopButton.grid(row=0,column=1)
root.mainloop()
edit (also solved way):
I looked at some code that worked properly. and I saw capture.set() as the difference. When I close the capture.set() lines, I had no problems with either streaming or recording. Now the main problem is that I have to show the video in a label with a certain size. Without set() the video size gets too big. how can i solve it now?
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 am a beginner in python and learning about PIL and tkinter.
Now what I am trying is to edit a image and I am struggling from blur and rotate.
When I blur an image, I cannot get rid of it without rotating it. (I want to make a button to on/off blur)
And if I blur/rotate an image,
Another one doesn't work at the same time.
How can I solve this problem? here is my code
import tkinter as tk
from PIL import Image, ImageTk, ImageFilter
from tkinter import filedialog as fd
img=None
tk_img=None
angle=0
def open():
global img, tk_img
filename=fd.askopenfilename()
img=Image.open(filename)
tk_img=ImageTk.PhotoImage(img)
canvas.create_image(250,250,image=tk_img)
window.update()
def quit():
window.destroy()
def image_rotate():
global img,tk_img,angle
angle=angle+45
tk_img=ImageTk.PhotoImage(img.rotate(angle))
canvas.create_image(250,250,image=tk_img)
window.update()
def image_blur():
global img,tk_img
tk_img=ImageTk.PhotoImage(img.filter(ImageFilter.BLUR))
canvas.create_image(250,250,image=tk_img)
window.update()
window = tk.Tk() #윈도우 생성
canvas=tk.Canvas(window,width=500,height=500)
canvas.pack()
menubar = tk.Menu(window) #메뉴바 생성
filemenu = tk.Menu(menubar) #파일메뉴를 메뉴바에 달아줌
filemenu.add_command(label="picture", command=open)
filemenu.add_command(label="exit", command=quit)
menubar.add_cascade(label="파일", menu=filemenu)
imagemenu=tk.Menu(menubar)
imagemenu.add_command(label="rotate",command=image_rotate)
imagemenu.add_command(label="blur",command=image_blur)
menubar.add_cascade(label="movement", menu=imagemenu)
window.config(menu=menubar)
window.mainloop()
The reason is quite obvious, your img object as seen is only defined/updated in open(), so the image will always refer to the original image selected and not the new edited image, so to show the change store it in a new variable and then globalize it.
Also note that you are creating a new canvas image every time you call the function, which is not efficient, so make a single canvas image and then update it each time, inside the function using itemconfig() method.
def open():
global img, tk_img
filename = fd.askopenfilename()
img = Image.open(filename)
tk_img = ImageTk.PhotoImage(img)
canvas.itemconfig('img',image=tk_img) # Update image
def image_rotate():
global tk_img, angle, img
angle += 45 # More pythonic to always use += rather than a = a + 10
img = img.rotate(angle)
tk_img = ImageTk.PhotoImage(img)
canvas.itemconfig('img',image=tk_img) # Update image
angle -= 45
def image_blur():
global tk_img, img
img = img.filter(ImageFilter.BLUR)
tk_img = ImageTk.PhotoImage(img)
canvas.itemconfig('img',image=tk_img) # Update image
canvas = tk.Canvas(window,width=500,height=500)
canvas.create_image(250,250,tag='img') # Create initial canvas object
canvas.pack()
Also take a look at how I formatted your code to make it look more near, follow PEP 8 -- Style Guide for Python Code for more.
You can blur the image using opencv package
import cv2
img = cv2.imread('imagepath')
blurImg = cv2.blur(img,(10,10))
cv2.imshow('blurred image',blurImg)
cv2.waitKey(0)
cv2.destroyAllWindows()
Or u can check the documentation of opencv to blurr the image.
U can also rotate image using opencv
# importing cv2
import cv2
src = cv2.imread(path)
window_name = 'Image'
image = cv2.rotate(src, cv2.ROTATE_90_COUNTERCLOCKWISE)
# Displaying the image
cv2.imshow(window_name, image)
cv2.waitKey(0)
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()
i want to stream from a thermal camera, usually it export its frames as gray scale frames
the thermal camera is an IP camera , i tried different codes and package but with no output.
when i change the code a little bit to view from a USB camera it works normally, so any help please.
this is the code i have tried :
import sys
sys.path.append('C:\Python27\Lib\site-packages')
import Tkinter as tk
import cv2
from PIL import Image, ImageTk
i=0
window = tk.Tk()
window.title('thermal image')
var = tk.IntVar()
width, height = 800, 600
cap = cv2.VideoCapture(0)
cap.open("http://169.254.110.119/")
left_label = tk.Label(window)
left_label.pack(side="left")
right_label = tk.Label(window)
right_label.pack(side="right")
def show_frame():
_, frame = cap.read()
print frame
if frame != None:
frame = cv2.flip(frame, 1)
img = Image.fromarray(frame)
imgtk = ImageTk.PhotoImage(image=img)
left_label.imgtk = imgtk
left_label.configure(image=imgtk)
left_label.after(10, show_frame)
show_frame()
window.mainloop()
I think that the image from the websitre is not being grabbed in the code, what worked for me was
img_requested = requests.get(url)
img_arr = np.array(bytearray(img_requested.content), dtype=np.uint8)
frame = cv2.imdecode(img_arr, -1)
And there you would get the frame (color picures/video).Keep in mind that you need to import requests and numpy as np.
It is important if you are using IP Webcam that you do not forget to write the '/shot.jpg'
at the end of the url, like this: 'http://190.160.0.0:8080/shot.jpg', so that it effectivelly grabs the image.