Я ищу эффективный способ добавления ограничивающих рамок к живому видео, отображаемому на веб-странице.
Проект состоит в том, чтобы обнаруживать автомобили в захваченном видеопотоке (1080p 30fps)с помощью встроенной функции rtsp и отображения результатов на веб-странице.
Мое текущее решение:
Изображение кадра извлекается с использованием OpenCV.Я запускаю алгоритмы обнаружения кадр за кадром в режиме реального времени.Изображение кадра сначала сжимается в JPEG, а затем кодируется в строку Base64.Затем строка изображения Base64 вместе с ограничивающими прямоугольниками инкапсулируется в JSON.Я передаю строку JSON внешнему интерфейсу с помощью WebSocket.
На внешнем интерфейсе я использую canvas для рисования изображения кадра при каждом получении сообщения.
Теперь проблемы заключаются в следующем:
- Процесс сжатия JPEG (OpenCV imencode) + Base64 (base64.b64encode) занимает слишком много времени, пропускная способность составляет около 20FPS (без обнаружения).
- Изображенияотображение в браузере происходит медленно и не плавно, частота кадров не может даже достичь 20FPS (без обнаружения).И веб-страница занимает много памяти и может перестать отвечать .
Интересно, есть ли решение моих проблем (достигнуть 30FPS) или естьлюбые другие способы достижения моей цели (синхронизировать результаты обнаружения бэкэнда и живого видео внешнего интерфейса) .
PS Ограничительные рамки не отображаются на изображении на бэкэнде, поскольку могут быть некоторыенеобходимо выполнить операции с ограничивающими рамками на внешнем интерфейсе, например, определить, показывать ли ограничивающие рамки или нет.
РЕДАКТИРОВАТЬ: коды на python (без обнаружения) на серверной части имеют вид:
Main.py
from multiprocessing import Process, Queue
import base64
import json
import cv2
import WSServer
def capture(path, outq):
cap = cv2.VideoCapture(path)
while True:
ret, image = cap.read()
outq.put(image)
def conv2jpg(inq, outq):
while True:
image = inq.get()
imgEnc = cv2.imencode('.jpg', image, [cv2.IMWRITE_JPEG_QUALITY, 75])[1].tostring()
outq.put(imgEnc)
def conv2b64(inq, outq):
while True:
imgEnc = inq.get()
strEnc = str(base64.b64encode(imgEnc), 'utf-8')
outq.put(strEnc)
if __name__ == '__main__':
# After arg parsing
ws = WSServer.WSServer(args.ip, args.port)
capQ = Queue(2)
jpgQ = Queue(2)
b64Q = Queue(2)
capP = Process(target=capture, args=(args.input, capQ, ))
# args.input is the rtsp address of the camera
jpgP = Process(target=conv2jpg, args=(capQ, jpgQ, ))
b64P = Process(target=conv2b64, args=(jpgQ, b64Q, ))
b64P.start()
jpgP.start()
capP.start()
while True:
strEnc = b64Q.get()
msg = {'image': strEnc}
msgJson = json.dumps(msg)
# Send the json string over Websocket
USERS = ws.getUSERS()
for user in USERS:
user.put(msgJson)
WSServer.py
import threading
from queue import Queue
import asyncio
import websockets
class WSServer:
def __init__(self, ip="localhost", port="11000", interval=1.0/60):
self.ip = ip
self.port = port
self.interval = interval
self.USERS = set()
self.USERS_Lock = threading.Lock()
self.P = threading.Thread(target=self.proc)
self.P.setDaemon(True)
self.P.start()
async def get(self, q):
if q.empty():
return None
return q.get()
async def register(self, q):
with self.USERS_Lock:
self.USERS.add(q)
async def unregister(self, q):
with self.USERS_Lock:
self.USERS.remove(q)
async def serve(self, websocket, path):
q = Queue()
await self.register(q)
try:
while True:
while True:
msg = await self.get(q)
if type(msg) == type(None):
break
await websocket.send(msg)
await asyncio.sleep(self.interval)
finally:
await self.unregister(q)
def getUSERS(self):
with self.USERS_Lock:
return self.USERS.copy()
def proc(self):
print('Listening...')
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(websockets.serve(self.serve, self.ip, self.port))
loop.run_forever()
EDIT2: упрощенные коды на внешнем интерфейсе:
<!DOCTYPE html>
<html>
<head>
<title>demo</title>
</head>
<body>
<img id="myImg" style="display: None;"></img>
<canvas id="myCanvas">
<script>
var img = document.getElementById('myImg')
var cvs = document.getElementById('myCanvas')
cvs.width = 1920
cvs.height = 1080
var ctx = cvs.getContext('2d')
var ws = new WebSocket("ws://127.0.0.1:11000/");
ws.onmessage = function (event) {
var msg = JSON.parse(event.data)
img.src = 'data:image/jpg;base64,' + msg.image
};
function render () {
ctx.drawImage(img, 0, 0)
window.requestAnimationFrame(render)
}
window.requestAnimationFrame(render)
</script>
</body>
</html>