MonitoringPanel
使用opencv-python或ffmpeg方式实时读取10路网络摄像头rtsp视频流,请求后端行为识别模型,返回行为识别结果包括骨架、包围盒进行展示,该项目为前端展示部分
依赖
1 | pyqt5 |
文件结构
1 | ├── App.py 主入口 |
视频流获取
提供opencv-python和ffmpeg-python两种方式获取视频流,其中ffmpeg-python以tcp方式读取rtsp流更稳定
使用多进程,一个进程负责读取视频流放入队列,一个进程负责从队列取帧进行处理并请求后端服务1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18def procVideo(self, camera):
self.__cameras.append(camera)
videoCapture = VideoCapture(camera, self.__cameras.index(camera))
self.waitingQueueDict[camera] = Queue(maxsize=Controller.__WAITING_QUEUE_MAXSIZE)
p = multiprocessing.Process(target=videoCapture.captureFrameByFfmpeg, args=(self.waitingQueueDict[camera],))
self.__processes.append(p)
p.start()
def procRecognizeQueue(self, waitingQueueDict, responseQueue):
while True:
imagesData, needRecognize = self.__gainFramePerVideo(waitingQueueDict)
if not imagesData:
continue
if needRecognize:
# pass
responseQueue.put(self.__requestRecognizeAction(imagesData))
else:
responseQueue.put(list(map(self.__procResponseData, filter(None, imagesData))))
请求后端
轮询向各路视频流队列取帧,对图像base64编码,组合成请求列表,使用grequest请求多服务器进行识别1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33def recognizeAction(self, params):
requestList = self.__buildRequestList(params)
responseList = grequests.map(requestList)
return self.__processMultiResponse(requestList, responseList)
def __buildRecognizeParam(self, images, pose: bool = False, box: bool = False):
if not images: return None
images = list(map(
lambda image: {'camera': str(image[0]), 'image': base64.b64encode(cv2.imencode('.jpg', image[2])[1]).decode()},
images))
option = dict(pose=pose, box=box)
return dict(images=images, option=option)
def __processMultiResponse(self, requestList, responseList):
mergedData = []
for request, response in zip(requestList, responseList):
if response is None:
imagesNum = len(json.loads(request.kwargs['data'])['images'])
mergedData.extend([dict() for _ in range(imagesNum)])
continue
if response.status_code == 200:
res = response.json()
if res.get('status') == 0 or res.get('status') == 3:
mergedData.extend(res.get('data'))
else:
raise Exception('Recognize Error: ' + res.get('message'))
else:
raise response.raise_for_status()
return mergedData
def __buildRequestList(self, params):
return [grequests.post(url, data=json.dumps(param), timeout=(1, 3)) for url, param in
zip(Controller.__RECOGNIZE_ACTION_URLS, params) if param]
显示界面
使用QGridLayout布局每个Grid放置QWidget,对每个QWidget中的QLabel通过设置一个定时器,定时刷新达到视频的效果1
2
3
4
5
6
7
8
9
10
11
12
13def refresh(self, imageInfos):
for imageInfo in imageInfos:
self.__refreshScreen(imageInfo)
def __refreshScreen(self, info: dict):
screen = self.myWindow.screenByCamera[info["camera"]]
screen.setActionLabel(info.get('label'))
screen.setImage(info['image'])
def __startRefresh(self):
self.refreshTimer = QTimer()
self.refreshTimer.timeout.connect(self.process)
self.refreshTimer.start(App.__REFRESH_INTERVAL)
骨架和包围盒绘制
骨架采用OpenPose 18个关节点进行绘制
1 | def renderPose(image, poses, inplace: bool = True, inverseNormalization='auto'): |