Python Multithread For Multicamera - python-3.x

I'm trying to build an app which will has 2 or more cameras to provide frames and going to display the frames after a few opencv processing. In this case I'm using threads. Each camera has 2 threads; one for receiving frames and put them to the queue and other one for take the frames from queue and display them. When I try to do this with only 1 camera it works just fine. But with 2 cameras I can't get the result that I expected. I'm going to provide my code and the result. I would appreciate for your helps.
Here is my main file:
from MyThread import MyThread
cam_list = [0, 2]
threads = []
if __name__ == '__main__':
for i, cam in enumerate(cam_list):
th = MyThread(cam, f"Camera {cam}")
threads.append(th)
for t in threads:
t.run()
... and this is MyThread class file:
import threading
import queue
import cv2
class MyThread:
def __init__(self, camera_index, window_name):
self.q = queue.SimpleQueue()
self.cap = cv2.VideoCapture(camera_index)
self.window_name = window_name
self.t1 = threading.Thread(target=self.receive)
self.t2 = threading.Thread(target=self.display)
def receive(self):
try:
print("Start Receiving...")
while True:
ret, frame = self.cap.read()
self.q.put(frame)
except Exception as err:
print(f"An error occured: {err}")
def display(self):
try:
print("Start Displaying...")
while True:
if self.q.empty() != True:
frame = self.q.get()
cv2.imshow(self.window_name, frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
except Exception as Err:
print(f"An error occured : {Err}")
def run(self):
self.t1.start()
self.t2.start()
This is the result.(I was expected two windows):

Related

How to safely terminate a thread in Python

I have below code, where I am using OpenCV to start webcam video. Along with that I also have a thread running that pings www.google.com to check network connectivity.
import time
import cv2
import os
from threading import Thread
stopThread = False
def CheckNetwork():
global stopThread
while True:
time.sleep(60)
host = "www.google.com"
response = os.system("ping " + host)
if response == 0:
print("Internet host reachable")
else:
print("Internet host not reachable")
if stopThread:
break
def main():
global stopThread
Thread(target=CheckNetwork).start()
cam = cv2.VideoCapture(0)
while True:
ret_val, img = cam.read()
cv2.imshow('Camera', img)
key = cv2.waitKey(1)
if key == ord('q'):
stopThread = True
break
cv2.destroyAllWindows()
main()
This code is running fine. If I have to close the application by pressing q, OpenCV window closes but application keeps running for 60sec because of the thread and only after 60sec whole application terminates safely.
I wanted to know if this is a good way to close the threads. Is there any better way available which can immediately terminate threads in Python?
There's no native way of stopping a thread in Python. Instead of using a stop flag, you can also use ctypes that calls the Python API to raise an exception in the thread.
import ctypes
# Other imports...
class ThreadWithException(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
# code here...
def get_id(self):
# returns id of the respective thread
if hasattr(self, '_thread_id'):
return self._thread_id
for id, thread in threading._active.items():
if thread is self:
return id
def raise_exception(self):
thread_id = self.get_id()
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
ctypes.py_object(SystemExit))
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
print('Exception raise failure')

Producer Consumer message sharing not working in multiprocessing

i am trying to run a scenario where i have a producer which is capturing frames from webcam and putting it in a queue.
and then consumer reads image from input queue and does some processing and puts o/p image in outgoing queue.
Issue is, consumer read from queue is not blocking. Ideally it should be, also when it reads value from queue, size is always constant 128, which is wrong. I am sure size of image that I am putting in queue is far greater.
from __future__ import print_function
import multiprocessing
import time
import logging
import sys
import cv2
class Consumer(multiprocessing.Process):
def __init__(self, incoming_q, outgoing_q):
multiprocessing.Process.__init__(self)
self.outgoing_q = outgoing_q
self.incoming_q = incoming_q
def run(self):
proc_name = self.name
print(f"{proc_name} - inside process_feed..starting")
while True:
#print(f"size of incoming_q=>{self.incoming_q.qsize()}")
try:
#print(f"{proc_name} - size of B incoming_q=>{self.incoming_q.qsize()}")
image_np = self.incoming_q.get(True)
size_of_img = sys.getsizeof(image_np)
#print(f"{proc_name} - size of A incoming_q=>{self.incoming_q.qsize()}")
if size_of_img > 128:
print(f"{proc_name} - size image=>{size_of_img}")
time.sleep(1)
self.outgoing_q.put_nowait(image_np)
except:
pass
print("inside process_feed..ending")
class Producer(multiprocessing.Process):
def __init__(self, incoming_q, outgoing_q):
multiprocessing.Process.__init__(self)
self.incoming_q = incoming_q
self.outgoing_q = outgoing_q
def run(self):
proc_name = self.name
print("inside capture_feed")
stream = cv2.VideoCapture(0)
try:
counter = 0
while True:
counter += 1
if counter == 1:
if not self.incoming_q.full():
(grabbed, image_np) = stream.read()
size_of_img = sys.getsizeof(image_np)
print(f"{proc_name}........B.......=>{self.incoming_q.qsize()}")
print(f"{proc_name} - size image=>{size_of_img}")
self.incoming_q.put(image_np)
print(f"{proc_name}........A.......=>{self.incoming_q.qsize()}")
counter = 0
try:
image_np = self.outgoing_q.get_nowait()
logging.info("reading value for o/p")
cv2.imshow('object detection', image_np)
except:
pass
if cv2.waitKey(25) & 0xFF == ord('q'):
break
finally:
stream.release()
cv2.destroyAllWindows()
print("inside capture_feed..ending")
if __name__ == '__main__':
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
stream = cv2.VideoCapture(0)
incoming_q = multiprocessing.Queue(maxsize=100)
outgoing_q = multiprocessing.Queue(maxsize=100)
logging.info("before start of thread")
max_process = 1
processes = []
processes.append(Producer(incoming_q, outgoing_q))
for i in range(max_process):
p = Consumer(incoming_q, outgoing_q)
p.daemon = True
processes.append(p)
logging.info("inside main thread..middle")
for p in processes:
p.start()
logging.info("inside main thread..ending")
logging.info("waiting in main thread too....")
logging.info("waiting in main thread finished....")
for p in processes:
p.join()
logging.info("inside main thread..ended")
I was able to figure out issue with my approach. I missed whole concept of pickle (serialization).
I changed my code to serialize numpy array before writing to queue and deserialize after reading it. Code started working as expected.
also printing 128 as sizeof np array is fine, i was misinterpreting that number.
def serialize_ndarray(arr:np.ndarray):
serialized = pickle.dumps(arr)
return serialized
def deserialize_ndarray(string):
data = pickle.loads(string)
return data

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

Using multiprocessing and ProcessPoolExecutor simultaneously

I am trying to create a simple script for python3.5 that can execute heavy computer vision algorithms in parallel. I have created a process by multiprocessing.Process in main process.
Inside that process I create concurrent.futures.ProcessPoolExecutor. Spawned process submits tasks to processPoolExecutor and it works perfectly fine. But when I try to stop and join spawned process it hangs on join.
Also if replace processPoolExecuter to threadPoolExecuter everything works perfectly. What did I miss?
Here is main file:
import multiprocessing as mp
import queue as Queue
import numpy as np
import cv2
from time import sleep
import executer_debug
def worker(queue):
pExecutor = executer_debug.Worker()
pExecutor.set()
while True:
print("-->{}<--".format(pExecutor.get()))
sleep(1)
try:
income = queue.get_nowait()
break
except Queue.Empty:
pass
pExecutor.set()
print("<1>{}<1>".format(pExecutor.get()))
print("<2>{}<2>".format(pExecutor.get()))
def main():
queue = mp.Queue()
currProcess = mp.Process(target = worker, args=(queue,))
currProcess.start()
frame = np.zeros((480,640), dtype=np.uint8)
while True:
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
print("stopped")
queue.put("stop")
currProcess.join()
if __name__ == "__main__":
main()
And here is the second file. Code is very simple just enough to demonstrate the issue.
import collections
from concurrent.futures import ProcessPoolExecutor
from time import sleep
import multiprocessing as mp
def worker():
return 1
class Worker():
def __init__(self):
self.workers_count = 4
self.poolExecutor = ProcessPoolExecutor(max_workers = self.workers_count)
self.executors = collections.deque()
def set(self):
self.executors.append(self.poolExecutor.submit(worker))
def get(self):
if len(self.executors) > 0:
if self.executors[0].done():
return self.executors.popleft().result()
else:
return 0
else:
return -1
Thank you!

Resources