Videostreaming is lagging when implementing facedetection - python-3.x

I am using a Rapberry Pi to videostream in a web server.
The video streaming works fine, but once I implement the face recognition, it will be so laggy.
This is my code :
from flask import Flask, render_template, Response
import cv2
import argparse
from imutils.video import VideoStream
import imutils
import subprocess
app = Flask(__name__)
# Load the cascade
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
vs = VideoStream(usePiCamera=1).start()
def gen_frames(): # generate frame by frame from camera
while True:
# Capture frame-by-frame
global vs
frame = vs.read() # read the camera frame
frame = imutils.resize(frame, width=800)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Detect the faces
faces = face_cascade.detectMultiScale(gray, 1.1, 4)
#Draw the rectangle around each face
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') # concat frame one by one and show result
#app.route('/video_feed')
def video_feed():
#Video streaming route. Put this in the src attribute of an img tag
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/')
def index():
"""Video streaming home page."""
return render_template('index.html')
def videostreaming():
# start the flask app
app.run(host="192.168.0.33",port=8000, debug=True,use_reloader=False)
videostreaming()
Is there a way to make the videostreaming smoother with the facedetection feature ?

It seems that the width of the video was the one causing problems in my videostreaming. Just had to reduce it to 500 to make it smoother.
From :
frame = imutils.resize(frame, width=800)
To :
frame = imutils.resize(frame, width=500)

Related

How to do OpenCV Laser shot detection with Python on light backgrounds?

I'm trying to build a laser shot detection game in Python3 using OpenCV. I have a proof of concept working which will highlight detected momentary "shots" of the laser on the background.
The problem I'm having is that the laser signature doesn't get detected on lighter backgrounds, or if there is a very white / bright color item near the shot. I'm sure this is because the way I'm using binary thresholds to detect the laser as the brightest thing in the frame, and with light elements the laser gets washed out.
My question is how can I alter my approach to handle this situation, or perhaps "calibrate" the background / other items so that the laser can be detected? Ultimately I'm trying to detect laser shots on a computer screen, where the background where shots are landing is a video with its own high lights and the screen puts out its own light.
Any guidance is appreciated.
main.py
import cv2
from camera import VideoCamera
from detection import LaserDetector
debug=False
radius_min = float(1)
radius_max = float(10)
shot_size = 5
color_blue = (255, 0, 0)
cam = VideoCamera(640, 480)
shots = []
try:
while(True):
frame = cam.get_frame()
if frame is not False:
shot = LaserDetector(frame, debug).detect()
if shot:
x, y, radius = shot
if radius >= radius_min and radius <= radius_max:
shots.append(shot)
if debug:
print(f"(x:{int(x)}, y:{int(y)}, r:{int(radius)})")
for s in shots:
cv2.circle(frame, (int(s[0]), int(s[1])), shot_size, color_blue, 1)
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
except KeyboardInterrupt:
pass
finally:
print("\nClosing video capture...")
cam.release()
cv2.destroyAllWindows()
camera.py
import cv2
class VideoCamera(object):
def __init__(self, cam_width, cam_height):
self.capture = cv2.VideoCapture(0)
self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, cam_width)
self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, cam_height)
def release(self):
self.capture.release()
def __del__(self):
self.capture.release()
def get_frame(self):
success, frame = self.capture.read()
if success:
return frame
else:
return False
def frame_to_jpeg(self, frame):
ret, jpeg = cv2.imencode('.jpg', frame)
return jpeg.tobytes()
detection.py
import cv2
import numpy as np
import decimal
class LaserDetector(object):
def __init__(self, frame, debug=False):
self.debug = debug
self.frame = frame
def get_contour_points(self, mask):
countours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
if len(countours) > 0:
# find the largest contour in the mask, then use
# it to compute the minimum enclosing circle and
c = max(countours, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
moments = cv2.moments(c)
if moments["m00"] > 0:
# set the center
(x,y) = int(moments["m10"] / moments["m00"]), \
int(moments["m01"] / moments["m00"])
radius = round(decimal.Decimal(radius), 3)
return (int(x), int(y), radius)
return False
def get_hsv_threshold_mask(self, frame):
hsv_image = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv_image)
ret, h_frame = cv2.threshold(h, 125, 160, cv2.THRESH_BINARY)
ret, v_frame = cv2.threshold(v, 250, 256, cv2.THRESH_BINARY)
output = cv2.bitwise_and(h_frame, v_frame, frame)
if self.debug:
indiv_output = np.concatenate((h_frame, v_frame, output), axis=1)
cv2.imshow("threshold", indiv_output)
return output
def detect(self):
mask = self.get_hsv_threshold_mask(self.frame)
return self.get_contour_points(mask)

Tkinter resizing images in labels arranged in a grid

To start, I'm totally new to Tkinter and am trying to make a Raspberry Pi Media Player of sorts...
I grab the directories of all .mp4 files on a USB drive, and use PIL to put the thumbnails of the videos into a 3x3 grid of labels, with the grid inside of a frame (frame2 in the code).
Right now, with the thumbnails of varying sizes, the labels are also inconsistently sized. Also, only the top-right portion of larger thumbnails are displayed, rather than the entire image.
How can I to scale and fit the thumbnails into consistently sized labels, in grid form?
Here is part of my code (It's quite large so I try to include only the relevant parts):
import tkinter as tk
from subprocess import Popen
from time import sleep
import os
from random import randint
import imageio
from PIL import ImageTk, Image
from pathlib import Path
#putting 100th frame of video with 'path' into the label
def pack_thumbnail(path, label):
#this is probably not a good way to do this
video = imageio.get_reader(path)
for i in range(100):
try:
image = video.get_next_data()
except:
video.close()
break
frame_image = ImageTk.PhotoImage(Image.fromarray(image))
label.config(image=frame_image)
label.image = frame_image
window = tk.Tk()
window.attributes("-fullscreen", True)
frame1 = tk.Frame(master=window, width=200, height=100, bg="white")
frame1.pack(fill=tk.Y, side=tk.LEFT)
#frame2 contains the grid of labels
frame2 = tk.Frame()
for i in range(3):
frame2.columnconfigure(i, weight=1, minsize=75)
frame2.rowconfigure(i, weight=1, minsize=50)
for j in range(0, 3):
frame = tk.Frame(master=frame2, relief=tk.RAISED, borderwidth=1)
frame.grid(row=i, column=j, padx=5, pady=5)
#path to video to get thumbnail (i only have 3 videos so i randomize it)
vid_path=f"/media/pi/{os.listdir('/media/pi/')[0]}/{folder_name}/{videos[randint(0, 2)]}"
label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
pack_thumbnail(vid_path, label)
label.pack(padx=5, pady=5)
frame2.pack()
window.bind("<Escape>", lambda x: window.destroy())
window.mainloop()
You can use Image.thumbnail() to resize the image:
# putting 100th frame of video with 'path' into the label
def pack_thumbnail(path, label):
with imageio.get_reader(path) as video:
image = video.get_data(100) # use get_date() instead of get_next_data()
w, h = 200, 200 # thumbnail size
image = Image.fromarray(image)
image.thumbnail((w, h)) # resize the image
frame_image = ImageTk.PhotoImage(image)
label.config(image=frame_image, width=w, height=h)
label.image = frame_image

PyQt5 VideoPlayer: Converting to Object Orientated Code Prevents Playback

I've been looking into how to incorporate a video or livestream into an app. I have found some functional code that works fine:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import cv2 # OpenCV
import qimage2ndarray # for a memory leak,see gist
import sys # for exiting
# Minimal implementation...
def displayFrame():
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = qimage2ndarray.array2qimage(frame)
label.setPixmap(QPixmap.fromImage(image))
app = QApplication([])
window = QWidget()
# OPENCV
cap = cv2.VideoCapture("Vid.mp4")
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
# timer for getting frames
timer = QTimer()
timer.timeout.connect(displayFrame)
timer.start(60)
label = QLabel('No Camera Feed')
button = QPushButton("Quiter")
button.clicked.connect(sys.exit) # quiter button
layout = QVBoxLayout()
layout.addWidget(button)
layout.addWidget(label)
window.setLayout(layout)
window.show()
app.exec_()
And I am trying to be able to use this in some object orientated code, with the aim of creating a video playback widget to incorporate into other apps:
import cv2
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import qimage2ndarray # for a memory leak,see gist
import sys # for exiting
# Minimal implementation...
class basicWindow(QMainWindow):
def __init__(self):
super(basicWindow, self).__init__()
# OPENCV
cap = cv2.VideoCapture("Vid.mp4")
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
# timer for getting frames
timer = QTimer()
timer.timeout.connect(displayFrame)
timer.start(60)
label = QLabel('No Camera Feed')
button = QPushButton("Quiter")
button.clicked.connect(sys.exit) # quiter button
layout = QVBoxLayout()
layout.addWidget(button)
layout.addWidget(label)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
def displayFrame():
ret, frame = cap.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = qimage2ndarray.array2qimage(frame)
try:
label.setPixmap(QPixmap.fromImage(image))
except Exception as e:
print(e)
if __name__ == '__main__':
app = QApplication(sys.argv)
windowExample = basicWindow()
windowExample.show()
sys.exit(app.exec_())
I'm new to both OO coding and PyQt5, so any advice on how I'm either misinterpreting how the code works or what Im missing would be great. I have tried already setting label to a global variable, as I wasnt sure the function displayFrame() was aware of what label to change with label.setPixmap, but otherwise Im a little lost.
In your first example it works because label is a global variable, so displayFrame can access it as such.
In the other case, label is only declared in the scope of the __init__ of the basicWindow instance, so displayFrame knows nothing about it.
Make label a member of the instance (self.label = QLabel()), displayFrame a method of the basicWindow class (def displayFrame(self):) and then access the label correctly; note that you also need to make both cap and timer member of the instance (self), otherwise their objects will be immediately "garbage collected" after __init__ returns.
class basicWindow(QMainWindow):
def __init__(self):
super(basicWindow, self).__init__()
# ...
self.cap = cv2.VideoCapture("Vid.mp4")
# ...
self.timer = QTimer()
self.timer.timeout.connect(self.displayFrame)
self.timer.start(60)
self.label = QLabel('No Camera Feed')
# ...
def displayFrame(self):
ret, frame = self.cap.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = qimage2ndarray.array2qimage(frame)
try:
self.label.setPixmap(QPixmap.fromImage(image))
except Exception as e:
print(e)
Since you're new to OO programming, I suggest you to start by studying how classes and instances and name resolution work in python.

Flickering video for tkinter video

I am trying to make a simple play/pause application in tkinter. Basically I want to show a video and have a play/pause button underneath.
So, after some research I found this suitable post to show a video using tkinter and opencv:
to show video streaming inside frame in tkinter
When using the code, given in the accepted answer to show a video, there is no problem and I don't see any flickering. Here is the code:
# import the necessary packages
from __future__ import print_function
import tkinter as tk
from PIL import ImageTk, Image
import cv2
root = tk.Tk()
# Create a frame
app = tk.Frame(root, bg="white")
app.grid()
# Create a label in the frame
lmain = tk.Label(app)
lmain.grid()
# Capture from camera
cap = cv2.VideoCapture(r'PATH_TO_VIDEO_FILE')
# function for video streaming
frame_number = 0
def video_stream():
global frame_number
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
success, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(1, video_stream)
frame_number += 1
video_stream()
root.mainloop()
Now, I slightly altered the code to be able to use the grid manager and add a play button:
# import the necessary packages
from __future__ import print_function
import tkinter as tk
from PIL import ImageTk, Image
import cv2
class PhotoBoothApp:
def __init__(self, path_to_video):
# initialize the root window
self.window = tk.Tk()
self.window.title("Video_Player")
self.videocap = cv2.VideoCapture(path_to_video)
self.frame_number = 0
# Initalize
self.videocap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_number)
success, self.frame = self.videocap.read()
cv2image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGBA)
self.img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=self.img)
# Show frames
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE).grid(row=0, column=0)
self.btn_next_image=tk.Button(self.window, text="Play", width=50, bg ="green",command=self.video_stream).grid(row=1,column=0)
self.window.mainloop()
def video_stream(self):
self.videocap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_number)
sucess, frame = self.videocap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=img)
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE).grid(row=0, column=0)
# Update Frame Number to display
self.frame_number = self.frame_number + 1
self.window.after(1, self.video_stream)
ph = PhotoBoothApp(r'PATH_TO_FILE')
The problem is that when I execute the above code, the video flickers as if tkinter need to reload something in-between frames. I have no clue why this happens.
P.S. This post here Flickering video in opencv-tkinter integration did not help me.
You need to make two changes: split your self.picture_label line to create a proper reference to your Label object, and then use self.picure_label.config(...) to change the image.
class PhotoBoothApp:
def __init__(self, path_to_video):
# initialize the root window
...
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE)
self.picture_label.grid(row=0, column=0)
...
def video_stream(self):
...
img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=img)
self.picture_label.config(image=self.imgtk)
# Update Frame Number to display
self.frame_number = self.frame_number + 1
self.window.after(1, self.video_stream)
ph = PhotoBoothApp(r'PATH_TO_FILE')

OpenCV : Memory leak while using QPixmap

I am trying to video stream from a 1920x1080 HD camera with 60fps,using opencv. The issue is not with the streaming but the memory leak, I am losing almost 6GB of my memory within the first minute of streaming. Please help me how to stop this.
from PySide.QtCore import *
from PySide.QtGui import *
import cv2
import sys
class MainApp(QWidget):
def __init__(self):
QWidget.__init__(self)
self.video_size = QSize(1920, 1080)
self.setup_ui()
self.setup_camera()
def setup_ui(self):
"""Initialize widgets.
"""
self.image_label = QLabel()
self.image_label.setFixedSize(self.video_size)
self.quit_button = QPushButton("Quit")
self.quit_button.clicked.connect(self.close)
self.main_layout = QVBoxLayout()
self.main_layout.addWidget(self.image_label)
self.main_layout.addWidget(self.quit_button)
self.setLayout(self.main_layout)
def setup_camera(self):
"""Initialize camera.
"""
self.capture = cv2.VideoCapture(0)
self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, self.video_size.width())
self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, self.video_size.height())
self.timer = QTimer()
self.timer.timeout.connect(self.display_video_stream)
self.timer.start(30)
def display_video_stream(self):
"""Read frame from camera and repaint QLabel widget.
"""
_, frame = self.capture.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = QImage(frame, frame.shape[1], frame.shape[0],
frame.strides[0], QImage.Format_RGB888)
self.image_label.setPixmap(QPixmap.fromImage(image))
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainApp()
win.show()
sys.exit(app.exec_())
EDIT
I found where the memory leak is self.timer.start(30) If I increase the time call , the memory leak is slower. Any suggestions how to stop the memory leak?
If anyone having the same issue, please import qimage2ndarray this library and add
the following changes to the function display_video_stream
def display_video_stream(self):
"""Read frame from camera and repaint QLabel widget.
"""
_, frame = self.capture.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# frame = cv2.flip(frame, 1)
image = qimage2ndarray.array2qimage(frame) #Solution for memory leak
self.image_label.setPixmap(QPixmap.fromImage(image))

Resources