Thread inside a 'while True' loop - python-3.x

I have this project where I detect driver's face (Real-Time), I'd like to combine it with an Age Estimation NN (Pre-trained model, developed by Tal Hassner).
The thing is I only want to send ONE single frame to the NN while keep running the rest of my code.. I tried using threading.Thread as well as multiprocessing.pool.ThreadPool.
It seems that the NN executed successfuly and predicted the driver's Age, however, as soon as it's done the 'while' loop breaks.
attached the relevant piece of my code:
cap = cv2.VideoCapture(0)
num_faces = {str(n):0 for n in range(0,10)}
CAP_AGE = True
try:
while True:
ret, frame = cap.read()
if CAP_AGE:
pool = ThreadPool(processes=1)
async_result = pool.apply_async(predict_age,(frame,))
age_label = async_result.get()
CAP_AGE = False
frame = np.flip(frame,axis=1)
(detected_img, rects, num_faces) = adj_detect_face(frame, age_label)
print("Found {} faces!".format(len(rects)))
cv2.imshow('Normal', detected_img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
except:
print('An Error Occured, Cannot Run The Program\nConsider Restarting the
Kernel')
finally:
cap.release()
cv2.destroyAllWindows()

Related

How to run time.sleep() without stopping while loop

Is there a way to call a function which has wait(time.sleep()) from infinite while loop without disturbing the loop?
I am trying to run a few task that require to wait for a few seconds but the issue is that the while loop also stops when the wait process is happening.
This is what I have tried out-
Here is my code:
import cv2
import time
def waiting():
print("Inside function")
# Running Some Tasks
time.sleep(5)
print("Done sleeping")
def main():
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
cv2.imshow("Webcam", frame)
k = cv2.waitKey(10)
if k == 32: # Press SPACEBAR for wait function
waiting()
elif k == 27: # Press ESC to stop code
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
Thanks for the quick responses from #JLT and #TerePiim. Here is the updated code for anyone who might benefit from this:
import cv2
import time
import threading
def waiting():
print("Inside parallel function")
# Running some Tasks
time.sleep(5)
print("Done sleeping")
def main():
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
cv2.imshow("Webcam", frame)
k = cv2.waitKey(10)
if k == 32: # Press SPACEBAR for wait function
t = threading.Thread(target=waiting)
t.start()
elif k == 27: # Press ESC to stop code
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
You are working in a single thread script at the moment. You should use threading or multiprocessing. This makes (it look like) multiple processes (are) active. Dependent on if you use threading or multiprocessing.
You should use threads. It will look like the computer is doing them both at the same time.
import threading
t = threading.Thread(target=function)
t.start()

Playing a movie in OpenCV

I get the following error while trying to show a movie:
cv2.imshow("Video Output", frames)
TypeError: Expected Ptr<cv::UMat> for argument 'mat'
The commented-out lines are my attempts to fix the problem, but I still get the error.
What am i doing wrong?
import cv2
import numpy as np
vid = cv2.VideoCapture("resources/Plaza.mp4")
while True:
frames = vid.read()
# frames = cv2.cvtColor(frames, cv2.COLOR_RGB2BGR)
# frames_arr = np.array(frames)
cv2.imshow("Video Output", frames)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
Better implementation would be:
Check if the video is opened using vid.isOpened()
vid = cv2.VideoCapture("resources/Plaza.mp4")
while vid.isOpened():
If the frame is returned successfully then display it.
ret, frames = vid.read()
if ret:
# frames = cv2.cvtColor(frames, cv2.COLOR_RGB2BGR)
# frames_arr = np.array(frames)
cv2.imshow("Video Output", frames)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
Make sure always close all the windows and release the VideoCapture object
cv2.destoyAllWindows()
vid.release()
Code:
import cv2
import numpy as np
vid = cv2.VideoCapture("resources/Plaza.mp4")
while vid.isOpened():
ret, frames = vid.read()
if ret:
# frames = cv2.cvtColor(frames, cv2.COLOR_RGB2BGR)
# frames_arr = np.array(frames)
cv2.imshow("Video Output", frames)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else
# continue with the next frame
continue
cv2.destoyAllWindows()
vid.release()
In your code, vid.read() returns two values. The first contains a boolean value, which, according to the documentation:
returns a bool (True/False). If frame is read correctly, it will be True. So you can check end of the video by checking this return value.
So your frames variable essentially is a tuple containing the boolean and the frames themselves. You need to index in to the second element (frames[1]) to play the video with imshow.
Always read the docs well!

Sharing camera frames between threads

I'm trying to build a script to manage multiple threads that are supposed to be running in parallel, and exchange data between them.
As a starting point, I have two threads...the first one should be reading frames from a USB camera and send them to queue, while the second should read them and display them.
I tried:
import json
import queue, threading
from queue import Queue
import cv2
class system_manager():
def __init__(self, source):
## camera reader
self.camera_queue = queue.Queue()
self.camera_reader = threading.Thread(target=camera_reader, args=(source, self.camera_queue))
self.camera_reader.daemon = True
self.camera_reader.run()
self.camera_display = threading.Thread(target=camera_display, args=(self.camera_queue))
self.camera_display.daemon = True
self.camera_display.run()
def camera_reader(source, camera_queue):
print("Cam Loading...")
cap = cv2.VideoCapture(source)
print("Cam Loaded...")
while(True):
ret, frame = cap.read()
camera_queue.put(frame)
def camera_display(camera_queue):
print("doing something")
while(True):
frame = camera_queue.get()
key = cv2.waitKey(1)
if (key == ord('q')):
break
cv2.imshow("frame", frame)
if __name__ == "__main__":
SM = system_manager(source=0)
but it's not really working. The first thread, the one supposed to read frames, is actually doing that, but the second one is not displaying anything (there's a print statement at the beginning, and it's not shown). Also, after running for a few minutes, it got my computer completely stuck, so I assume I'm accidentally continuously occupying memory.
I'm fairly new to multiprocessing/multithreading, so I'm probably doing some very basic mistake somewhere.
EDIT
Ok, fixed the memory problem by using:
self.camera_queue = queue.Queue(maxsize=5)
but the second thread is not working yet
Use thread.start() instead of thread.run() , fix the thread target method and add comma after the argument. This works.
import json
import queue, threading
from queue import Queue
import cv2
class system_manager():
def __init__(self, source):
## camera reader
self.camera_queue = queue.Queue(maxsize=5)
self.camera_reader = threading.Thread(target=camera_reader, args=(source, self.camera_queue))
self.camera_reader.daemon = True
self.camera_reader.start()
self.camera_display = threading.Thread(target=camera_display, args=(self.camera_queue,))
self.camera_display.daemon = True
self.camera_display.start()
def camera_reader(source, camera_queue):
print("Cam Loading...")
cap = cv2.VideoCapture(source)
print("Cam Loaded...")
while(True):
ret, frame = cap.read()
camera_queue.put(frame)
def camera_display(camera_queue):
print("doing something")
while(True):
frame = camera_queue.get()
key = cv2.waitKey(1)
if (key == ord('q')):
break
cv2.imshow("frame", frame)
if __name__ == "__main__":
SM = system_manager(source=0)

Running opencv in a thread, unable to start a second time

When I try to start openvc in a thread, its working once, but after I call it a second time, it won’t start again. The code stuck’s when it reaches a cv2 function in this casecv2.imshow() (but after cv2.VideoCapture()).
If I call the start_videofeed() function right after it receives the signal of the socket, its working properly, but I would like to have the sock.recvfrom() to continue receiving for other commands.
This code is only a small example of my script, but I tried a lot and also searched on web but I couldn’t find any answer, only others with similar problems.
I’m running the latest opencv-python package, with python3.8 on manjaro.
I hope someone knows an answer how to solve this issue.
Thanks in advance.
import socket
from threading import Thread
import cv2
class VideoFeed():
def __init__(self):
self.url = 0
self.window_name = "Video"
def run(self):
sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
sock.bind( ('',54666) )
while True:
d, addr = sock.recvfrom( 1024 )
payload = d.decode()
if payload == "show_video":
self.start_video()
if payload == "exit":
break
def start_video(self):
vs = Thread(target=self.start_videofeed)
vs.start()
def start_videofeed(self):
cap = cv2.VideoCapture(self.url)
if (cap.isOpened()== False):
print("Error opening video file")
print("Starting videofeed")
while True:
ret, self.frame = cap.read()
cv2.imshow(self.window_name, self.frame)
key = cv2.waitKey(10)
if key == 27 or key == ord('q'):
break
if cv2.getWindowProperty(self.window_name, cv2.WND_PROP_VISIBLE) < 1:
break
cv2.destroyAllWindows()
cap.release()
print("Video release and Destroy")
return
vf = VideoFeed()
vf.run()
Edit:
I finally found an answer. It seems it's not possible to start the video from or in a thread a second time, so I set a boolean in the socket thread to start the videofeed from the main program.
class VideoFeed():
def __init__(self):
self.url = "http://192.168.178.103:8081"
self.window_name = "Video"
self.start_video = False
def run(self):
Thread(target=self.start_socket).start()
while True:
if self.start_video:
self.start_videofeed()
self.start_video = False
def start_socket(self):
sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
sock.bind( ('',54666) )
while True:
d, addr = sock.recvfrom( 1024 )
payload = d.decode()
if payload == "show_video":
self.start_video = True
if payload == "exit":
break
def start_videofeed(self):
cap = cv2.VideoCapture(self.url)
if (cap.isOpened()== False):
print("Error opening video file")
print("Starting videofeed")
while True:
ret, self.frame = cap.read()
cv2.imshow(self.window_name, self.frame)
key = cv2.waitKey(10)
if key == 27 or key == ord('q'):
break
if cv2.getWindowProperty(self.window_name, cv2.WND_PROP_VISIBLE) < 1:
break
cv2.destroyAllWindows()
cap.release()
print("Video release and Destroy")
return

Output 2 Functions on 1 Video

I'm a beginner on Python and want to ask whether can I draw images of different function onto one video? Below is my practice code.
import numpy as np
import cv2
from multiprocessing import Process
cap = cv2.VideoCapture('C:/Users/littl/Desktop/Presentation/Crop_DownResolution.mp4')
def line_drawing():
while cap.isOpened():
ret, img = cap.read()
if ret is True:
cv2.line (img,(50,180),(380,180),(0,255,0),5)
cv2.imshow('img',img)
k = cv2.waitKey(1) & 0xff
if k == 27:
break
else:
break
cap.release()
cv2.destroyAllWindows()
def rectangle_drawing():
while cap.isOpened():
ret, img = cap.read()
if ret is True:
cv2.rectangle(img,(180,0),(380,128),(0,255,0),3)
cv2.imshow('img',img)
k = cv2.waitKey(1) & 0xff
if k == 27:
break
else:
break
cap.release()
cv2.destroyAllWindows()
if __name__=='__main__':
p1 = Process(target = rectangle_drawing)
p1.start()
p2 = Process(target = line_drawing)
p2.start()
When I run the code, it gives me 2 tabs running the same video, one with the line drawn, the other with the rectangle drawn. How to I make both the rectangle and line to be on the video and have the functions separated instead of putting both in the same function?
I won't be able to give you an answer with Python code but...
What you have is two different threads, both capturing data from the video feed independently, and drawing the elements on separate pieces of data.
What you need to do is Have one process that is just in charge of data capture from your video feed which then provides that data for the other two threads. You would likely need to look into Mutexes so that the two threads don't clash with each other.
Resources
There are quite a few questions on SO and the internet that will help you achieve this:
opencv python Multi Threading Video Capture
https://www.pyimagesearch.com/2015/12/21/increasing-webcam-fps-with-python-and-opencv/
http://algomuse.com/c-c/developing-a-multithreaded-real-time-video-processing-application-in-opencv
http://forum.piborg.org/node/2363

Resources