Running opencv in a thread, unable to start a second time - python-3.x

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

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')

Python Multithread For Multicamera

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):

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

How do I shutdown a socket properly that is streaming webcam footage?

I am trying to stream my laptops webcam stream to my pc for cv2 manipulation. The stream works but I am not really sure how to go about closing the socket.
I don't have a webcam on my desktop so I wanted to try and stream my laptops webcam over to learn cv2 on the footage. I am new to sockets, I went through the documentation and did a bunch of googling to get this far, even though most of what I now have was copy pasted. I am not really sure where to look to learn how to shut down sockets propery when streaming video data with cv2.
I've tried using with socket.socket( ... ) as s: to close the connection
breaks after the while and try loops, that go into shutdown(socket.SHUT_WR and close() and I've tried terminating the program on both the client and server.
#server.py
import socket
import cv2
import pickle
import struct
def main():
HOST=''
PORT=12397
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as s:
print('Socket created')
s.bind((HOST,PORT))
print('Socket bind complete')
s.listen(10)
print('Socket now listening')
conn,addr=s.accept()
data = b""
payload_size = struct.calcsize(">L")
while True:
try:
key = cv2.waitKey(20) & 0xFF
while len(data) < payload_size:
data += conn.recv(4096)
if key == ord("q"):
print("Socket closed.")
break
packed_msg_size = data[:payload_size]
data = data[payload_size:]
msg_size = struct.unpack(">L", packed_msg_size)[0]
while len(data) < msg_size:
data += conn.recv(4096)
if key == ord("q"):
print("Socket closed.")
break
frame_data = data[:msg_size]
data = data[msg_size:]
frame=pickle.loads(frame_data, fix_imports=True, encoding="bytes")
frame = cv2.imdecode(frame, cv2.IMREAD_COLOR)
cv2.imshow('ImageWindow',frame)
cv2.waitKey(1)
if key == ord("q"):
print("Socket closed.")
break
except:
cv2.destroyAllWindows()
break
s.shutdown(socket.SHUT_WR)
s.close()
if __name__ == "__main__":
main()
#client.py
import socket
import cv2
import pickle
import struct
def main():
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90]
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as client_socket:
print('Connecting to socket.')
client_socket.connect((ENTER IP HERE, 12397))
connection = client_socket.makefile('wb')
print('Starting stream.')
cam = cv2.VideoCapture(0)
cam.set(3, 320*2);
cam.set(4, 240*2);
frame_time = int((1.0 / 30.0) * 1000.0)
img_counter = 0
while True:
try:
ret, frame = cam.read()
if ret:
result, frame = cv2.imencode('.jpg', frame, encode_param)
data = pickle.dumps(frame, 0)
size = len(data)
client_socket.sendall(struct.pack(">L", size) + data)
img_counter += 1
if cv2.waitKey(frame_time) & 0xFF == ord('q'):
break
else:
break
except KeyboardInterrupt:
break
cam.release()
client_socket.shutdown(socket.SHUT_WR)
client_socket.close()
if __name__ == "__main__":
main()
I want to close the connection by pressing q.
I currently get WinErrors on the server
OSError: [WinError 10038] An operation was attempted on something that is not a socket
and on the client
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
when I (spam) "q" on the server side, it doesn't seem to catch if I just press it once.
That error typically happen if you use the socket after you have closed it.
Now if we take your server, you do something like
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as s:
... code here ...
s.shutdown(socket.SHUT_WR)
s.close()
That means the shutdown and close calls are done after the socket have already been closed.
You have the same problem in the client program.

2 threads each serving 2 different HTTP Server, will not stop after a given duration

So I have spent at least a couple of days on this problem.
I would like to have 2 threads HTTP server each serving two different IP:ports.
I open a FF and navigate to either say: http://196.64.131.250:8001/ or http://196.64.131.250:8002 and it should do a GET.
Also I like my threads or program itself stop after a given duration from command line say 5 sec.
I have done everything I could, even tried SIGAlarm and variable "keepRunning" which gets changed by a third thread after duration , but my program does Not stop. What am I doing wrong please.
note that I have commented the daemon: i.e (ZhttpThread[1-2].daemon = True)
if I dont comment it my threads stop right away. I want my HTTP server threads continue servicing, and if the duration DUR expires , then the program stops or threads stop.
import SimpleHTTPServer, SocketServer, logging, cgi, sys, signal, threading, time
import subprocess as sub
keepRunning = True
origTime = int(time.time())
class ServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
logging.warning("======= GET STARTED =======")
getHdr = self.headers
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
getHdr = self.headers
print(', '.join((getHdr)))
#if ("accept-encoding" in getHdr):
if ("accept-encoding" in (', '.join((getHdr)))):
print ('Test Passed ---------------')
signal.alarm(1)
class threadWithTO(threading.Thread):
def __init__(self, thID, ip, port, timeout):
threading.Thread.__init__(self)
self.ip = ip
self.port = port
self.handler = ServerHandler
self.httpd = SocketServer.TCPServer((self.ip, self.port), self.handler)
def run(self):
print (self.httpd)
#self.httpd.serve_forever()
if (keepRunning == True):
self.httpd.handle_request()
else:
self._stop.set()
def Run(self):
self.start()
def timeHandler(signum, frame):
print('Times up', signum)
sys.exit()
def timeChkr( threadName, dur, t1, t2):
print (origTime)
print ('Begin Timer thread')
while True:
nowTime = int(time.time())
print (nowTime)
delta = nowTime - origTime
if (delta >= dur):
keepRunning = False
t1.stop()
t2.stop()
else:
time.sleep(1)
def main():
#signal.signal(signal.SIGALRM, timeHandler)
#signal.alarm(DUR)
origTime = int(time.time())
ZhttpThread1 = threadWithTO("thread1", I, PORT, DUR)
ZhttpThread2 = threadWithTO("thread2", I, (int(PORT)+1), DUR)
t = threading.Thread(target=timeChkr, args = ("thread3",DUR))
t.daemon = True
t.start()
#ZhttpThread1.daemon = True
#ZhttpThread2.daemon = True
ZhttpThread1.Run()
ZhttpThread2.Run()
ok i figured out the issue is with socket. My socket is open and even though I have tried socket.settimeout I still cant get the socket to close
Thanks to Andrew.. my son whom sparked the idea in my head... here is the solution..
class ServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
logging.warning("======= GET STARTED =======")
logging.warning(self.headers)
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
def do_POST(self):
logging.warning("======= POST STARTED =======")
logging.warning(self.headers)
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={'REQUEST_METHOD':'POST',
'CONTENT_TYPE':self.headers['Content-Type'],
})
logging.warning("======= POST VALUES =======")
print form.list
'''
for item in form.list:
logging.warning(item) '''
logging.warning("\n")
SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
class StoppableHTTPServer(BaseHTTPServer.HTTPServer):
def server_bind(self):
BaseHTTPServer.HTTPServer.server_bind(self)
self.socket.settimeout(1)
self.run = True
def get_request(self):
while self.run:
try:
sock, addr = self.socket.accept()
sock.settimeout(None)
return (sock, addr)
except socket.timeout:
pass
def stop(self):
self.run = False
def serve(self):
while self.run:
#print "1"
self.handle_request()
if __name__=="__main__":
if len(sys.argv) < 1:
I = ""
PORT = 8000
DUR = 10
else:
I = sys.argv[1]
PORT = int(sys.argv[2])
DUR = int(sys.argv[3])
#httpd1 = StoppableHTTPServer((I,PORT), SimpleHTTPServer.SimpleHTTPRequestHandler)
#httpd2 = StoppableHTTPServer((I,(int(PORT)+1)), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd1 = StoppableHTTPServer((I,PORT), ServerHandler)
httpd2 = StoppableHTTPServer((I,(int(PORT)+1)), ServerHandler)
thread.start_new_thread(httpd1.serve, ())
thread.start_new_thread(httpd2.serve, ())
#raw_input("Press <RETURN> to stop server\n")`enter code here`
print '0'
time.sleep(DUR)
print 'Times up Dude'
httpd1.stop()
httpd2.stop()

Resources