Python OpenCV streaming from camera - multithreading, timestamps
I ran simple python script on Raspberry Pi 3. This script is responsible to open video device and stream data (800x600) to HTTP endpoint using MJPEG. When I receive this stream one of my Raspberry Pi cores works on 100%. It possible to run OpenCV with multi threading?
This is my code
import cv2
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import time
import argparse
import socket as Socket
camera = None
def setUpCameraCV():
global camera
camera = cv2.VideoCapture(0)
class mjpgServer(BaseHTTPRequestHandler):
ip = None
hostname = None
def do_GET(self):
print('connection from:', self.address_string())
if self.ip is None or self.hostname is None:
self.ip, _ = 0.0.0.0
self.hostname = Socket.gethostname()
if self.path == '/mjpg':
self.send_response(200)
self.send_header('Cache-Control', 'no-cache')
self.send_header('Pragma', 'no-cache')
self.send_header('Connection', 'close')
self.send_header(
'Content-type',
'multipart/x-mixed-replace; boundary=mjpegstream'
)
self.end_headers()
while True:
if camera:
ret, img = camera.read()
else:
raise Exception('Error, camera not setup')
if not ret:
print('no image from camera')
time.sleep(1)
continue
ret, jpg = cv2.imencode('.jpg', img)
self.end_headers()
self.wfile.write('--mjpegstream')
self.end_headers()
self.send_header('Content-type', 'image/jpeg')
self.send_header('Content-length', str(jpg.size))
self.end_headers()
self.wfile.write(jpg.tostring())
def main():
try:
setUpCameraCV()
mjpgServer.ip = 0.0.0.0
mjpgServer.hostname = Socket.gethostname()
server = HTTPServer((ipv4, args['port']), mjpgServer)
print("server started on {}:{}".format(Socket.gethostname(), args['port']))
server.serve_forever()
except KeyboardInterrupt:
print('KeyboardInterrupt')
server.socket.close()
if __name__ == '__main__':
main()
Another question, how to get timestamp of each frame on the client side (receiver) it possible?
Solution 1:
Using threading to handle I/O heavy operations (such as reading frames from a webcam) is a classic programming model. Since accessing the webcam/camera using cv2.VideoCapture().read()
is a blocking operation, our main program is stalled until the frame is read from the camera device and returned to our script. Essentially the idea is to spawn another thread to handle grabbing the frames in parallel instead of relying on a single thread (our 'main' thread) to grab the frames in sequential order. This will allow frames to be continuously read from the I/O thread, while our root thread processes the current frame. Once the root thread finishes processing its frame, it simply needs to grab the current frame from the I/O thread without having to wait for blocking I/O operations.
Thus we can improve performance by creating a new thread that does nothing but poll for new frames while our main thread handles processing the current frame. For an implementation to handle multiple camera streams, take a look at capture multiple camera streams with OpenCV
from threading import Thread
import cv2, time
class VideoStreamWidget(object):
def __init__(self, src=0):
self.capture = cv2.VideoCapture(src)
# Start the thread to read frames from the video stream
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
def update(self):
# Read the next frame from the stream in a different thread
while True:
if self.capture.isOpened():
(self.status, self.frame) = self.capture.read()
time.sleep(.01)
def show_frame(self):
# Display frames in main program
cv2.imshow('frame', self.frame)
key = cv2.waitKey(1)
if key == ord('q'):
self.capture.release()
cv2.destroyAllWindows()
exit(1)
if __name__ == '__main__':
video_stream_widget = VideoStreamWidget()
while True:
try:
video_stream_widget.show_frame()
except AttributeError:
pass