Problems connecting Tello Drone camera in Python for a GUI - python

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

Related

How to open the cv2 window inside a tkinter window

I found on the internet this python code for counting people with the Open CV library. I would need to open the window that opens with CV2, inside a Tkinter window in order to then add the commands for the settings.
This is the code from GitHub: https://github.com/Gupu25/PeopleCounter
How can I make the two OpenCv windows open inside a Tkinter window?
Here is a minimal example of opening a video in a tkinter window with OpenCV's VideoCapture object:
from tkinter import NW, Tk, Canvas, PhotoImage
import cv2
def photo_image(img):
h, w = img.shape[:2]
data = f'P6 {w} {h} 255 '.encode() + img[..., ::-1].tobytes()
return PhotoImage(width=w, height=h, data=data, format='PPM')
def update():
ret, img = cap.read()
if ret:
photo = photo_image(img)
canvas.create_image(0, 0, image=photo, anchor=NW)
canvas.image = photo
root.after(15, update)
root = Tk()
root.title("Video")
cap = cv2.VideoCapture("video.mp4")
canvas = Canvas(root, width=1200, height=700)
canvas.pack()
update()
root.mainloop()
cap.release()
To display 2 OpenCV videos, simply make a few adjustments, and use the np.hstack() method or the np.vstack() method, depending on whether you want your videos to be displayed side by side horizontally or vertically:
from tkinter import NW, Tk, Canvas, PhotoImage
import cv2
import numpy as np
def photo_image(img):
h, w = img.shape[:2]
data = f'P6 {w} {h} 255 '.encode() + img[..., ::-1].tobytes()
return PhotoImage(width=w, height=h, data=data, format='PPM')
def update():
ret1, img1 = cap1.read()
ret2, img2 = cap2.read()
if ret1:
photo = photo_image(np.hstack((img1, img2)))
canvas.create_image(0, 0, image=photo, anchor=NW)
canvas.image = photo
root.after(15, update)
root = Tk()
root.title("Video")
cap1 = cv2.VideoCapture("video1.mp4")
cap2 = cv2.VideoCapture("video2.mp4")
canvas = Canvas(root, width=1200, height=700)
canvas.pack()
update()
root.mainloop()
cap.release()

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.

Update label based on facial recognition

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.

Tkinter button doesn't work in exe file but do when the script is executed

I'm new on this forum and on Tkinter and I need your help.
I'm trying to create a GUI. My program works perfectly when I run the file with Python 2.7, but one of the button doesn't work when I create an exe file thanks to pyinstaller.
My program is pretty simple : I select a camera in the list of the available ones, click on the "Display" button and the video frame from the selected camera should be displayed in a new window, thanks to opencv. The 'quit' button is here to quit the app.
When I create an executable file thanks to pyinstaller, the window is displayed, I can select a camera, but the 'Display' button doesn't work. I really do not understand why, and no error message is displayed.
Thanks a lot for your help
import cv2
from Tkinter import *
class Gui:
def __init__(self):
self.root = Tk()
self.root.title("Rééducation posturale")
self.root.config(background="#FA8066")
self.root.rowconfigure(0, weight=1)
self.root.rowconfigure(1, weight=1)
self.root.columnconfigure(0, weight=1)
self.root.columnconfigure(1, weight=1)
self.boutonquitter=Button(self.root,text='Quit',font=("Helvetica", 10),command=self.Quitapp)
self.boutonquitter.pack()
self.boutonquitter.grid(row=1, column=1)
camIndices=self.detectNumCameras()
camIndex = []
for n in xrange(camIndices):
camIndex.append(n)
# LIST OF AVAILABLE CAMERA
Label(self.root, text="Camera",font=("Helvetica", 10), bg="#ECA7A6").grid(row=0, column=0)
self.cameraIndex = StringVar(self.root)
self.cameraIndex.set("Choose camera")
dropCamera = apply(OptionMenu, (self.root, self.cameraIndex) + tuple(camIndex))
dropCamera.grid(row=0, column=1)
#CLICK TO DISPLAY
self.bouton = Button(self.root, text="Display", font=("Helvetica", 10), command=self.checkCamera)
self.bouton.pack()
self.bouton.grid(row=1, column=0)
imageFrame = Frame(self.root, width=600, height=400)
self.display = Label(imageFrame)
self.root.mainloop()
# DETECT CAMERAS
def detectNumCameras(self):
ind = 0
while True:
vc = cv2.VideoCapture(ind)
if (vc.isOpened()):
ind += 1
vc.release()
else:
break
return(ind)
def checkCamera(self):
try:
self.cam.release()
except:
pass
self.cam=cv2.VideoCapture(int(self.cameraIndex.get()))
_,frame=self.cam.read()
self.display.after(10,self.show_frame)
#CLOSE THE APP
def Quitapp(self):
self.root.destroy()
cv2.destroyAllWindows()
#DISPLAY FRAME IN A NEW WINDOW
def show_frame(self):
ret,frame = self.cam.read()
if ret is True:
cv2.imshow('Video',frame)
cv2.waitKey(1)
self.display.after(10, self.show_frame)
Gui()
Edit: I've tried with py2exe and have uninstalled python 2.7 to install python 3.6 and I still have the same problem ... My code is also simpler:
import cv2
import tkinter as tk
from PIL import Image, ImageTk
class Gui:
def __init__(self):
self.root = tk.Tk()
width, height = 800, 600
self.cam = cv2.VideoCapture(0)
self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
self.root.title("Test")
#CLICK TO DISPLAY
self.bouton = tk.Button(self.root, text="Display", font=("Helvetica", 10), command=self.CreateNewWindow)
self.bouton.pack()
self.boutonquitter=tk.Button(self.root,text='Quit',font=("Helvetica", 10),command=self.Quitapp)
self.boutonquitter.pack()
self.root.mainloop()
def CreateNewWindow(self):
self.top = tk.Toplevel()
self.top.title("window")
self.lmain = tk.Label(self.top)
self.lmain.pack()
self.lmain.after(10, self.show_frame)
#CLOSE THE APP
def Quitapp(self):
self.root.destroy()
cv2.destroyAllWindows()
#DISPLAY FRAME IN A NEW WINDOW
def show_frame(self):
ret, frame = self.cam.read()
if ret is True:
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
self.lmain.imgtk = imgtk
self.lmain.configure(image=imgtk)
self.lmain.after(10, self.show_frame)
Gui()
I've attached the screenshots when I run the script in python and when I run the .exe
python
exe

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