在开发B/S架构的检测算法时,需要对摄像头实时视频流进行检测处理。
下文代码能实现接收实时视频流到web端指定路由下进行播放。在后续开发中可继续将该路由视频流引入到相应的前端Html文件中的img指定位置进行播放。
其中红框位置为flask创建的路由名
第1种
第一种实现方式转载于:flask实现rtsp视频流的读取并在web界面展示。简单来说就是代码简洁清晰,足够满足单视频流的需求。但是时间久了会纯在延时,画面不同步的情况。
app.py
from flask import Flask, Response,render_template
import cv2
class VideoCamera(object):
def __init__(self):
#此处为自己的视频流url 格式 "rtsp://%s:%s@%s//Streaming/Channels/%d" % (name, pwd, ip, channel)
self.video=cv2.VideoCapture('rtsp://admin:123456@192.168.1.64//Streaming/Channels/101')
def __del__(self):
self.video.release()
def get_frame(self):
success,image=self.video.read()
ret,jpeg=cv2.imencode('.jpg',image)
return jpeg.tobytes()
app = Flask(__name__)
def genWeb(camera):
"""Video streaming generator function."""
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
@app.route('/video_feed')
def video_feed():
return Response(genWeb(VideoCamera()),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
# Run locally
# app.run(debug=True, host='127.0.0.1', port=5000)
#Run on the server
app.run(debug=True, host = '0.0.0.0', port=5000)
第2种
第二种启用线程,解决视频流延时及不同步问题。效果可参看文章最后对比视频。
目录结构:
base_camera.py
import time
import threading
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
class CameraEvent(object):
"""An Event-like class that signals all active clients when a new frame is
available.
"""
def __init__(self):
self.events = {}
def wait(self):
"""Invoked from each client's thread to wait for the next frame."""
ident = get_ident()
if ident not in self.events:
# this is a new client
# add an entry for it in the self.events dict
# each entry has two elements, a threading.Event() and a timestamp
self.events[ident] = [threading.Event(), time.time()]
return self.events[ident][0].wait()
def set(self):
"""Invoked by the camera thread when a new frame is available."""
now = time.time()
remove = None
for ident, event in self.events.items():
if not event[0].isSet():
# if this client's event is not set, then set it
# also update the last set timestamp to now
event[0].set()
event[1] = now
else:
# if the client's event is already set, it means the client
# did not process a previous frame
# if the event stays set for more than 5 seconds, then assume
# the client is gone and remove it
if now - event[1] > 5:
remove = ident
if remove:
del self.events[remove]
def clear(self):
"""Invoked from each client's thread after a frame was processed."""
self.events[get_ident()][0].clear()
class BaseCamera(object):
thread = None # background thread that reads frames from camera
frame = None # current frame is stored here by background thread
last_access = 0 # time of last client access to the camera
event = CameraEvent()
def __init__(self):
"""Start the background camera thread if it isn't running yet."""
if BaseCamera.thread is None:
BaseCamera.last_access = time.time()
# start background frame thread
BaseCamera.thread = threading.Thread(target=self._thread)
BaseCamera.thread.start()
# wait until frames are available
while self.get_frame() is None:
time.sleep(0)
def get_frame(self):
"""Return the current camera frame."""
BaseCamera.last_access = time.time()
# wait for a signal from the camera thread
BaseCamera.event.wait()
BaseCamera.event.clear()
return BaseCamera.frame
@staticmethod
def frames(path):
""""Generator that returns frames from the camera."""
raise RuntimeError('Must be implemented by subclasses.')
@classmethod
def _thread(cls):
"""Camera background thread."""
print('Starting camera thread.')
frames_iterator = cls.frames()
for frame in frames_iterator:
BaseCamera.frame = frame
BaseCamera.event.set() # send signal to clients
time.sleep(0)
# if there hasn't been any clients asking for frames in
# the last 10 seconds then stop the thread
if time.time() - BaseCamera.last_access > 60:
frames_iterator.close()
print('Stopping camera thread due to inactivity.')
break
BaseCamera.thread = None
url_stream.py
import time
from threading import Thread
import cv2
class LoadStreams: # multiple IP or RTSP cameras
def __init__(self, sources):
sources = [sources]
n = len(sources)
# print("______++++____",n)
self.imgs = [None] * n
for i, s in enumerate(sources):
# Start the thread to read frames from the video stream
print(f'{i + 1}/{n}: {s}... ', end='')
cap = cv2.VideoCapture(eval(s) if s.isnumeric() else s)
assert cap.isOpened(), f'Failed to open {s}'
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS) % 100
# print(w,h,fps)
_, self.imgs[i] = cap.read() # guarantee first frame
thread = Thread(target=self.update, args=([i, cap]), daemon=True)
print(f' success ({w}x{h} at {fps:.2f} FPS).')
thread.start()
print('') # newline
def update(self, index, cap):
# Read next stream frame in a daemon thread
n = 0
while cap.isOpened():
n += 1
cap.grab()
if n == 4: # read every 4th frame
success, im = cap.retrieve()
self.imgs[index] = im if success else self.imgs[index] * 0
n = 0
time.sleep(0.01) # wait time
def __iter__(self):
self.count = -1
return self
def __next__(self):
self.count += 1
img0 = self.imgs.copy()
if cv2.waitKey(1) == ord('q'): # q to quit
cv2.destroyAllWindows()
raise StopIteration
return img0
def __len__(self):
return 0 # 1E12 frames = 32 streams at 30 FPS for 30 years
app.py
from flask import Flask, Response
from cam.base_camera import BaseCamera
from cam.url_stream import LoadStreams
import cv2
app = Flask(__name__)
class Camera(BaseCamera):
@staticmethod
def frames():
#此处为自己的视频流url 格式 "rtsp://%s:%s@%s//Streaming/Channels/%d" % (name, pwd, ip, channel)
#例如
source = 'rtsp://admin:123456@192.168.1.64//Streaming/Channels/101'
dataset = LoadStreams(source)
for im0s in dataset:
im0 = im0s[0].copy()
frame = cv2.cvtColor(im0, cv2.COLOR_BGR2RGB)
result = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
yield cv2.imencode('.jpg', result)[1].tobytes()
@app.route('/url')
def video_feed():
"""Video streaming route. Put this in the src attribute of an img tag."""
return Response(genWeb(Camera()) ,
mimetype='multipart/x-mixed-replace; boundary=frame')
def genWeb(camera):
"""Video streaming generator function."""
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
if __name__ == '__main__':
# Run locally
# app.run(debug=True, host='127.0.0.1', port=5000)
#Run on the server
app.run(debug=True, host = '0.0.0.0', port=5000)
如果是本地读取的话:
import cv2
from cam.url_stream import LoadStreams
#此处为自己的视频流url 格式 "rtsp://%s:%s@%s//Streaming/Channels/%d" % (name, pwd, ip, channel)
source= 'rtsp://admin:123456@192.168.1.64//Streaming/Channels/101'
dataset = LoadStreams(source)
for im0s in dataset:
im0 = im0s[0].copy()
cv2.imshow("mainwindows", im0)
cv2.waitKey(1)
flask接收实时视频流url效果对比
视频地址: https://www.bilibili.com/video/BV1f34y1Q7of/