Update label based on facial recognition - python

I am building a program in python tkinter that will put a label with the name of the person in frame on it. How do I get the name to update?
I tried while True but it didn't work.
import cv2
from time import sleep
import face_recognition as fr
from tkinter import *
def main():
tk = Tk()
cap = cv2.VideoCapture(0)
sleep(1)
while True:
ret, frame = cap.read()
# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
cv2.imwrite("temp.jpg", frame)
image = fr.load_image_file("temp.jpg")
#img = __draw_label(frame, "Jack", face_locations[0][:2], (255,0,0))
#cv2.imshow("Hello", img)
v = StringVar()
w = Label(tk, textvariable=v)
w.pack()
if len(fr.face_locations(image)) > 0:
face_encoding = fr.face_encodings(image)[0]
faceid = fr.compare_faces(faces, face_encoding)
if True in faceid:
v.set(names[faceid.index(True)])
else:
v.set("Unknown")
else:
v.set("None")
mainloop()
I expect that when I'm looking at the camera it should read Jack and when I'm not it should say none. Currently if I begin looking at it when it starts it says Jack. And if I do't it says non but it doesn't update. How can I fix this?

Simply use tk.update():
while True:
ret, frame = cap.read()
…
v = StringVar()
w = Label(tk, textvariable=v)
w.pack()
tk.update() #show potential changes on your window
Hope that helped.

Related

Problems connecting Tello Drone camera in Python for a GUI

I'm a very Python and Tkinter beginner. I've developed a program for Tello Drone in Python 3.6. The program works well but now I'm programming the user interface with Tkinter.
When i use the image from the laptop camera it works well, but when I try to connect drone's camera as I've aready do in all my projects, I've next error:
succes, img = me.get_frame_read().frame
TypeError: 'NoneType' object is not iterable
I know what's a NoneType, but I dont't understand why is getting this value. Looks like the camera have a delay or is not ready when the program starts.
This's a sample of the code:
from tkinter import *
import tkinter as tk
from PIL import ImageTk, Image
import cv2
#img = img3
# Capture from camera
from djitellopy import tello
'''
me = tello.Tello()
me.connect()
print(me.get_battery())
me.send_rc_control(0,0,0,0)
me.streamon()
#'''
w,h = 660, 360
#succes, img = me.get_frame_read().frame
cap = cv2.VideoCapture(0)
if __name__ == '__main__':
def return_pressed(event):
print('Return key pressed.')
root = Tk()
root.title("Inventory control")
root.iconbitmap(r'Ico/drone3.ico')
# Create a frame
app = Frame(root, bg="white")
app.grid(row=1, column=0)
# Create a label in the frame
lmain = Label(app)
lmain.grid()
droneCamera_label = Label(root, text="Drone camera", font=("Arial 20 bold"))
textBox_label = Label(root, text="Inventory results", font=("Arial 20 bold"))
textBox_label.grid(row=0, column=2)
droneCamera_label.grid(row=0, column=0)
text_box = Text(root, height=25, width=40)
text_box.grid(row=1, column=2)
text_box.config(font=("consolas", 12), undo=True, wrap='word')
text_box.config(borderwidth=3, relief="sunken")
text_box.insert('end', "Inventory results:", )
text_box.config(state='disabled')
scrollb = tk.Scrollbar(command=text_box.yview)
scrollb.grid(row=1, column=3, sticky='nsew')
text_box['yscrollcommand'] = scrollb.set
def manualControl(event):
global message
text_box.config(state='normal')
print(1)
text_box.insert(tk.INSERT, "l")
text_box.config(state='disabled')
# function for video streaming
def video_stream():
_, frame = cap.read()
# succes, frame = me.get_frame_read().frame
img = cv2.resize(frame, (w, h))
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img2 = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img2)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(1, video_stream)
root.bind('<Button-1>', manualControl)
video_stream()
root.mainloop()
And this's how GUI looks like with the laptop webcam:
GUI
I belive .frame is a function that turns the tello 'backgroundframeread' type into a cv2/numpy array.
Your tello might be failing to grab the first few groups of frames, if it has no frame then the objects value is NONE. If you attempt to .frame an object you will get TypeError: 'NoneType' object is not iterable.
I think you can solve this by putting a delay between your framecapture and your .frame that is as long as your tello, or camera takes to grab a frame. A time.sleep() is usually a safe bet.
try this:
img = me.get_frame_read()
time.sleep(5)
img = img.frame

Record Video Button in tkinter GUI python

I'm pretty new to python and espcially tkinter and opencv.
I've got someway (a little way) to creating a gui that will eventually control a microscope and ai. But I've hit a stumbling block, trying to record the video that is displayed within the gui, I think its to do with the video feed already been captured in the display, but I can't find a way around it. It all works fine until I hit record then it crashes and i get the error: open VIDEOIO(V4L2:/dev/video0): can't open camera by index.
Apologies for the long code but I've cut it down to as much as I think possible.
The problem is in the root.recbtn and def rec sections.
import cv2
import tkinter as tk
import multiprocessing
from tkinter import *
from PIL import Image,ImageTk
from datetime import datetime
from tkinter import messagebox, filedialog
e = multiprocessing.Event()
p = None
# Defining CreateWidgets() function to create necessary tkinter widgets
def createwidgets():
root.cameraLabel = Label(root, bg="gray25", borderwidth=3, relief="ridge")
root.cameraLabel.grid(row=2, column=1, padx=10, pady=10, columnspan=3)
root.browseButton = Button(root, bg="gray25", width=10, text="BROWSE", command=destBrowse)
root.browseButton.grid(row=1, column=1, padx=10, pady=10)
root.recbtn = Button(root, bg="gray25", width=10, text="Record", command=rec)
root.recbtn.grid(row=1, column=5, padx=10, pady=10)
root.saveLocationEntry = Entry(root, width=55, textvariable=destPath)
root.saveLocationEntry.grid(row=1, column=2, padx=10, pady=10)
# Calling ShowFeed() function
ShowFeed()
# Defining ShowFeed() function to display webcam feed in the cameraLabel;
def ShowFeed():
# t5 # Capturing frame by frame
ret, frame = root.cap.read()
if ret:
# Flipping the frame vertically
frame = cv2.flip(frame, 1)
# Changing the frame color from BGR to RGB
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
# Creating an image memory from the above frame exporting array interface
videoImg = Image.fromarray(cv2image)
# Creating object of PhotoImage() class to display the frame
imgtk = ImageTk.PhotoImage(image = videoImg)
# Configuring the label to display the frame
root.cameraLabel.configure(image=imgtk)
# Keeping a reference
root.cameraLabel.imgtk = imgtk
# Calling the function after 10 milliseconds
root.cameraLabel.after(10, ShowFeed)
else:
# Configuring the label to display the frame
root.cameraLabel.configure(image='')
def destBrowse():
# Presenting user with a pop-up for directory selection. initialdir argument is optional
# Retrieving the user-input destination directory and storing it in destinationDirectory
# Setting the initialdir argument is optional. SET IT TO YOUR DIRECTORY PATH
destDirectory = filedialog.askdirectory(initialdir="YOUR DIRECTORY PATH")
# Displaying the directory in the directory textbox
destPath.set(destDirectory)
def rec():
vid_name = datetime.now().strftime('%d-%m-%Y %H-%M-%S')
# If the user has selected the destination directory, then get the directory and save it in image_path
if destPath.get() != '':
vid_path = destPath.get()
# If the user has not selected any destination directory, then set the image_path to default directory
else:
messagebox.showerror("ERROR", "No Directory Selected!")
# Concatenating the image_path with image_name and with .jpg extension and saving it in imgName variable
vidName = vid_path + '/' + vid_name + ".avi"
capture = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
videoWriter = cv2.VideoWriter(vidName, fourcc, 30.0, (640, 480))
while (True):
ret, frame = capture.read()
if ret:
cv2.imshow('video', frame)
videoWriter.write(frame)
if cv2.waitKey(1) == 27:
break
capture.release()
videoWriter.release()
# Creating object of tk class
root = tk.Tk()
# Creating object of class VideoCapture with webcam index
root.cap = cv2.VideoCapture(0)
# Setting width and height
width, height = 1200, 1200
root.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
root.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
# Setting the title, window size, background color and disabling the resizing property
root.title("Test-AI-tes")
root.geometry("1600x1024")
root.resizable(True, True)
root.configure(background = "gray18")
# Creating tkinter variables
destPath = StringVar()
imagePath = StringVar()
createwidgets()
root.mainloop()
Thanks!
This answer is similar to #Art's answer but I removed the after_id and queue.
import cv2
import threading
import tkinter as tk
from PIL import Image, ImageTk
def stop_rec():
global running
running = False
start_button.config(state="normal")
stop_button.config(state="disabled")
def start_capture():
global capture, last_frame
capture = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc("X", "V", "I", "D")
video_writer = cv2.VideoWriter(r"sample.avi", fourcc, 30.0, (640, 480))
while running:
rect, frame = capture.read()
if rect:
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
last_frame = Image.fromarray(cv2image)
video_writer.write(frame)
capture.release()
video_writer.release()
def update_frame():
if last_frame is not None:
tk_img = ImageTk.PhotoImage(master=video_label, image=last_frame)
video_label.config(image=tk_img)
video_label.tk_img = tk_img
if running:
root.after(10, update_frame)
def start_rec():
global running
running = True
thread = threading.Thread(target=start_capture, daemon=True)
thread.start()
update_frame()
start_button.config(state="disabled")
stop_button.config(state="normal")
def closeWindow():
stop_rec()
root.destroy()
running = False
after_id = None
last_frame = None
root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", closeWindow)
video_label = tk.Label()
video_label.pack(expand=True, fill="both")
start_button = tk.Button(text="Start", command=start_rec)
start_button.pack()
stop_button = tk.Button(text="Stop", command=stop_rec, state="disabled")
stop_button.pack()
root.mainloop()
It uses the boolean flag running instead of using after_id. Also instead of storing the images in a queue then showing it, I only keep the last image. That way it can run in real time on my computer. Don't worry all of the frames are still being stored in the video file.
You cannot have infinite while loop along with the GUI's loop. You should instead make use of threading, whenever you have an IO operation to complete.
Example code:
import cv2
import threading
import tkinter as tk
from PIL import Image, ImageTk
from queue import Queue
def stop_rec():
global running, after_id
running = False
if after_id:
root.after_cancel(after_id)
after_id = None
with frame_queue.mutex:
frame_queue.queue.clear()
def start_capture():
global capture
capture = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
video_writer = cv2.VideoWriter(r"sample.avi", fourcc, 30.0, (640, 480))
while running:
rect, frame = capture.read()
if rect:
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
videoImg = Image.fromarray(cv2image)
# current_frame = ImageTk.PhotoImage(image = videoImg)
frame_queue.put(videoImg)
video_writer.write(frame)
capture.release()
video_writer.release()
def update_frame():
global after_id
if not frame_queue.empty():
video_label.image_frame = ImageTk.PhotoImage(frame_queue.get_nowait())
video_label.config(image=video_label.image_frame)
after_id = root.after(10, update_frame)
def start_rec():
global running
stop_rec()
running = True
thread = threading.Thread(target=start_capture, daemon=True)
thread.start()
update_frame()
def closeWindow():
stop_rec()
root.destroy()
running = False
after_id = None
frame_queue = Queue()
root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", closeWindow)
video_label = tk.Label()
video_label.pack(expand=True, fill="both")
tk.Button(text="Start", command=start_rec).pack()
tk.Button(text="stop", command=stop_rec).pack()
root.mainloop()
Quick explanation:
start the record function in a new thread.
Use Queue to store the frames.
Then make use of [widget].after to update the label at regular intervals.
To stop the recording make use of [widget].after_cancel(after_id)(after_id is returned when you use .after method) and set the running variable to False to stop the loop.

Flickering video in opencv-tkinter integration

I am trying to build a GUI using tkinter in python 3.6.4 64-bit on Windows 8 by integrating opencv components into the program. I can get the video to play, but there is significant flicker going on. Namely, a screen the same color as the native tkinter background shows up briefly several times a second. I've tested several cameras with similar results, and double-checked that the cameras work properly via native video playback software. Here is my code:
from tkinter import *
from PIL import Image, ImageTk
import cv2
import threading
cap = cv2.VideoCapture(0)
root = Tk()
def videoLoop():
global root
global cap
vidLabel = None
while True:
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = Image.fromarray(frame)
frame = ImageTk.PhotoImage(frame)
if vidLabel: vidLabel.configure(image=frame)
else:
vidLabel = Label(root, image=frame, anchor=NW)
vidLabel.pack(expand=YES, fill=BOTH)
videoThread = threading.Thread(target=videoLoop, args=())
videoThread.start()
root.mainloop()
Could anyone suggest what I might be doing wrong? I did hear that tkinter doesn't always play well with threads, but that's about all I can think of. In response to a comment suggesting the flicker is caused by label updating, I added some code that still reads from video and updates the label within the loop, but uses an image loaded outside of the loop to update the label. The flicker then goes away, although (to my understanding) the efficiency of the loop and updating of the label hasn't changed. Here is the changed videoLoop function (with flicker gone):
def videoLoop():
global root
vidLabel = None
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = Image.fromarray(frame)
frame = ImageTk.PhotoImage(frame)
while True:
ret, lame = cap.read()
lame = cv2.cvtColor(lame, cv2.COLOR_BGR2RGB)
lame = Image.fromarray(lame)
lame = ImageTk.PhotoImage(lame)
if vidLabel:
vidLabel.configure(image=None)
vidLabel.configure(image=frame)
else:
vidLabel = Label(root, image=frame, anchor=NW)
vidLabel.pack(expand=YES, fill=BOTH)
Solution: Make sure the image configure call precedes storing the image in the label image attribute.
I've managed to solve this issue, however I don't fully understand WHY it works as I am a python/tkinter novice. I will post the solution for now and will update the answer when I manage to find a proper explanation to this behavior. My best guess is that the storing of the image in the label attribute is what actually causes it to update on the screen, while the configure method just declares that there will be an image attached, which causes the loop to have to go through another iteration before getting to the update image update statement. The below code works fine without flicker:
from tkinter import *
from PIL import Image, ImageTk
import cv2
import threading
cap = cv2.VideoCapture(0)
root = Tk()
def videoLoop():
global root
global cap
vidLabel = Label(root, anchor=NW)
vidLabel.pack(expand=YES, fill=BOTH)
while True:
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = Image.fromarray(frame)
frame = ImageTk.PhotoImage(frame)
vidLabel.configure(image=frame)
vidLabel.image = frame
videoThread = threading.Thread(target=videoLoop, args=())
videoThread.start()
root.mainloop()
In tkinter in order to display images, the image needs to have a global reference reference that doesn't go out of scope and then gets garbage-collected, perhaps flickering is caused by lack of such reference. See below code that has such reference to the image, and also ditched the if/else with better structuring:
from tkinter import *
from PIL import Image, ImageTk
import cv2
import threading
cap = cv2.VideoCapture(0)
root = Tk()
def videoLoop():
global root
global cap
vidLabel = Label(root, anchor=NW)
vidLabel.pack(expand=YES, fill=BOTH)
while True:
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = Image.fromarray(frame)
vidLabel.image = ImageTk.PhotoImage(frame)
vidLabel.configure(image=vidLabel.image)
videoThread = threading.Thread(target=videoLoop, args=())
videoThread.start()
root.mainloop()

How to show a frame of a video file (image) with tkinter in python

I am trying to show video frames (not from a stream) with tkinter. The next step are buttons which allow the user to get a frame backward or forward in the video. I have to say that I am quite new in programming with python.
So first I read the following articles:
Python snippets: Converting video to images http://srand.fr/blog/python%20import%20video.html
The Tkinter PhotoImage Class: http://effbot.org/tkinterbook/photoimage.htm
The problem is that I can’t use the image converted with imageio or VideoFileClip to show it with tkinter photoimage. I get the following error:
_tkinter.TclError: image "[[ …(some numbers)… ]]" doesn't exist
Here is my simple code. I hope you can help me :)
from moviepy.editor import VideoFileClip
from tkinter import *
import pylab
vid =VideoFileClip("example.mp4")
window = Tk()
window.title("Choose Frame")
window.geometry ("900x600")
count =20
photo = vid.get_frame(count)
label =Label(window, image = photo)
label.pack()
Other Code, same problem:
import imageio
from tkinter import *
import pylab
filename = './example.mp4'
vid = imageio.get_reader(filename, 'ffmpeg')
window = Tk()
window.title("Choose Frame")
window.geometry ("900x600")
count =20
photo = vid.get_data(count)
label =Label(window, image = photo)
label.pack()
This is a bit late but better late than never.
Here is a working example I found and modify a little, this works with '.mp4', videos but not with '.flv', don't know why.
Note:
python 2.7 import Tkinter
python 3 import tkinter
import Tkinter as tk
import threading
import imageio
from PIL import Image, ImageTk
video_name = "test_video.mp4" #This is your video file path
video = imageio.get_reader(video_name)
def stream(label):
frame = 0
for image in video.iter_data():
frame += 1 #counter to save new frame number
image_frame = Image.fromarray(image)
image_frame.save('FRAMES/frame_%d.png' % frame) #if you need the frame you can save each frame to hd
frame_image = ImageTk.PhotoImage(image_frame)
label.config(image=frame_image)
label.image = frame_image
if frame == 40: break #after 40 frames stop, or remove this line for the entire video
if __name__ == "__main__":
root = tk.Tk()
my_label = tk.Label(root)
my_label.pack()
thread = threading.Thread(target=stream, args=(my_label,))
thread.daemon = 1
thread.start()
root.mainloop()
And here is another nice working example of a player that I was trying to make with Tkinter and some sample code with Opencv module.
This is a just an example idea, not finish code by any means.
import cv2
from Tkinter import *
from PIL import Image, ImageTk
import io
import threading
import os, sys
def resize(image):
im = image
new_siz = siz
im.thumbnail(new_siz, Image.ANTIALIAS)
return im
def size(event):
global siz
if siz == screenWH:
siz = (200, 200)
else:
siz = screenWH
win.state('zoomed')
print 'size is: ', siz
def view_frame_video():
vc = cv2.VideoCapture('test_video.flv')
if vc.isOpened():
rval , frame = vc.read()
else:
rval = False
while rval:
rval, frame = vc.read()
img =Image.fromarray(frame)
img = resize(img)
imgtk = ImageTk.PhotoImage(img)
lbl.config(image=imgtk)
lbl.img = imgtk
if stop == True:
vc.release()
break #stop the loop thus stops updating the label and reading imagge frames
cv2.waitKey(1)
vc.release()
def stop_():
global stop
stop = True
def play():
stop = False
t = threading.Thread(target=view_frame_video)
t.start()
win = Tk()
stop = None
screenWH = (win.winfo_screenwidth(), win.winfo_screenheight())
siz = (200, 200)
Label(text='Press Play Button').pack()
frm_ = Frame(bg='black')
frm_.pack()
lbl = Label(frm_, bg='black')
lbl.pack(expand=True)
lbl.bind('<Double-Button-1>', size)
frm = Frame()
frm.pack()
Button(text='Play', command = play).pack(side=LEFT)
Button(text='Stop', command = stop_).pack(side=LEFT)
win.mainloop()

Problems displaying video in tkinter

I'm trying to create a GUI that opens a video and an image below it:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
import Image, ImageTk
import Tkinter, tkMessageBox
import ttk
import cv2
import sys
width, height = 800, 600
banner = cv2.imread('../data/banner.png')
b,g,r = cv2.split(banner)
banner = cv2.merge((r,g,b))
im = Image.fromarray(banner)
cap = cv2.VideoCapture('../data/sample.mov')
root = Tkinter.Tk()
root.bind('<Escape>', lambda e: root.quit())
root.title("Contador")
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
lmain = Tkinter.Label(root)
lmain.grid(row=0,column=0,sticky='nsew')
bmain = Tkinter.Label(root)
bmain.grid(row=1,column=0,sticky='nsew')
baner = ImageTk.PhotoImage(image=im)
bmain.configure(image=baner)
def show_frame():
_, frame = cap.read()
if frame is None:
return
# labelWidth = root.winfo_screenwidth()
# labelHeight = root.winfo_screenheight()
# maxsize = (labelWidth, labelHeight)
# frame = frame.resize(maxsize)
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(frame)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(10, show_frame)
show_frame()
root.mainloop()
The problems I'm having are the following:
I need to resize the image to fit the label.
the commented part i got from here (how to fit image to label in Python) but it gives a channel number error(line 40) and further down the code gives a NoneType error(line 41) and a invalid type of image(numpy array) in line 42
the image and video don't change size when resizing the window
So I need solution for this tkinter code(or even a better framework for python)
I found out what the problem was.
Turns out the line I got from another question:
frame = frame.resize(maxsize)
should be:
frame = cv2.resize(frame, maxsize)
Also to get the label's size the command is:
labelWidth = lmain.winfo_width()
labelHeight = lmain.winfo_height()
since
labelWidth = root.winfo_width()
labelHeight = root.winfo_height()
gets the root's size not the label's.

Categories