原有的程序可以采集视频流,但不能多路并行,问题应该就是这个yield,独占式运行
app = Flask(__name__)
#相机推流
def gen(camera):
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
#相机喂流
@app.route('/video_feed')
def video_feed():
return Response(gen(VideoCamera()),
mimetype='multipart/x-mixed-replace; boundary=frame')
前端可以直接引用
<img id="bg" src="{{ url_for('video_feed') }}">
查阅文档,仔细看看yield的影响
python中return和yield的区别_一只静静的独角兽~的博客-CSDN博客_python yield和return的区别
大致意思是,yield是迭代器,会独占式不断运行,所以当一个程序运行起来后,把资源都独占了,其他线程没机会运行。
我们重新改成前后端分离的桩体,后端多线程多设备的不断更新产生当前图片,前端不断请求读取
前端代码:
<script src="static/jquery-3.5.1/jquery-3.5.1.min.js"></script>
<script type="text/javascript">
window.setInterval(update1, 10);
function update1(){
var element1 = document.getElementById('video1')
var element2 = document.getElementById('video2')
var message = {
"cmd": "update"
}
$.ajax({
url: "http://127.0.0.1:7001/video",
type: "GET",
data: message,
success: function(data) {
var result = JSON.stringify(data)
var obj = JSON.parse(result)
// console.log("result1", obj.video1)
// console.log("result2", obj.video2)
element1.src = "data:image/jpg;base64,"+obj.video1;
element2.src = "data:image/jpg;base64,"+obj.video2;
},
error: function() {
alert("命令发送失败")
}
})
console.log("video1 update.");
}
</script>
首先是定时任务,然后定义任务向后台请求图片数据,收到的base64图片数据进行解析,并更新到img的源。
后端响应代码:
app = Flask(__name__,template_folder='/home/hy/kxwell/faceid2.0/templates',static_folder="/home/hy/kxwell/faceid2.0/templates/static")
#相机推流
def gen(camera):
frame = camera.get_frame()
return base64.b64encode(frame)
@app.route("/video",methods=['GET','POST'])
def img_update():
cmd=request.args.get('cmd')
print("get update channel:",cmd)
if cmd=='update':
frame1 = gen(V1)
frame2 = gen(V2)
return jsonify({"video1":frame1,"video2":frame2})
#当前实时相机画面
@app.route('/')
def cur_camera():
return render_template('index.html')
前端摄像头处理的代码要把图片转换成bytes数据格式
class VideoCamera(object):
def __init__(self,ch):
# Using OpenCV to capture from device 0. If you have trouble capturing
# from a webcam, comment the line below out and use a video file
# instead.
self.ch=ch
# If you decide to use video.mp4, you must have this file in the folder
# as the main.py.
# self.video = cv2.VideoCapture('video.mp4')
def __del__(self):
self.video.release()
def live(self):
cascPath ='/home/hy/kxwell/py3/lib/python3.6/site-packages/cv2/data/haarcascade_frontalface_alt2.xml'
faceCascade = cv2.CascadeClassifier(cascPath)
self.video = cv2.VideoCapture(self.ch)
while True:
ret, image = self.video.read()
if ret != True:
print("read frame null ")
break
# We are using Motion JPEG, but OpenCV defaults to capture raw images,
# so we must encode it into JPEG in order to correctly display the
# video stream.
# ret, jpeg = cv2.imencode('.jpg', image)
# cv2.namedWindow('image', cv2.WINDOW_NORMAL)
# cv2.imshow("image",image)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(gray,scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30)
# flags=cv2.cv.CV_HAAR_SCALE_IMAGE
)
for (x, y, w, h) in faces:
cv2.rectangle(image, (x-50, y-50), (x+w+50, y+h+50), (0, 255, 0), 2)
cv2.putText(image,'customer',(x,y),cv2.FONT_HERSHEY_COMPLEX,2.0,(100, 200, 200),5)
# cv2.imshow("monitor"+str(self.ch),image)
self.currentIMG=image
# cv2.waitKey(20)
def get_frame(self):
ret, jpeg = cv2.imencode('.jpg', self.currentIMG)
return jpeg.tobytes()
主程序还需要考虑多线程启动
if __name__ == '__main__':
host='0.0.0.0'
port=7001
debug=False
V1=VideoCamera(0)
V2=VideoCamera(3)
TH2=threading.Thread(target=V1.live)
TH3=threading.Thread(target=V2.live)
TH1=threading.Thread(target=app.run,args=(host,port,debug))
TH2.start()
TH3.start()
# time.sleep(1)
TH1.start()
还有个遗留问题,就是如果在对象定义中,把图像监看打开imshow就会爆线程错误,暂时还没解决。
由于环境问题,可能会出现json包发送和解析错误。
此时,应将返回做个解码到string的操作
return jsonify({"video1":frame1,"video2":frame2})
改为
return jsonify({"video1":frame1.decode(),"video2":frame2.decode()})