由于项目需要,需要访问海康威视的流媒体服务器获取视频流,对视频图象进行识别后,推送到网页进行视频实时监控,图像识别库使用的是yolov8,而yolov8是使用python语言进行开发的,所以决定使用python语言开发一个websocket 服务器,具体工作流程如下图所示:
由于本人使用python较少,因此代码写的比较乱,请大家原谅,以下是具体代码,供大家参考
import traceback
import WebSocketHandler
import Packets
from datetime import datetime
import logging
import json
import requests
from threading import Thread, Lock
from ctypes import *
import numpy as np
import cv2
from ultralytics import YOLO
import supervision as sv
from AbortableSleep import AbortableSleep
logger = logging.getLogger('WebChatSrv.Networking')
__author__ = 'wuhanzhongxintianqi'
FUNC_STREAM=WINFUNCTYPE(None,c_longlong, c_int, c_char_p, c_int, c_void_p)
FUNC_MSG=WINFUNCTYPE(None,c_longlong, c_int, c_void_p)
FUNC_DECODEDSTREAM=WINFUNCTYPE(None,c_longlong, POINTER(c_char), c_int, c_int, c_int, c_int, c_int, c_void_p)
class VIDEO_PLAY_REQ(Structure):
_fields_=[
('iHardWareDecode',c_int),
('fnStream',FUNC_STREAM),
('fnMsg',FUNC_MSG),
('fnDecodedStream',FUNC_DECODEDSTREAM),
('pUserData',c_void_p),
('szReserve',c_char*64),
]
class hikplayer:
def __init__(self, videosocket:videowebsocket):
self.dll=WinDLL('F:/hkplayer/bin/VideoSDK.dll')
self.dll.Video_Init.argtypes=[c_char_p]
self.dll.Video_Init.restype=c_int
self.dll.Video_Fini.argtypes=[]
self.dll.Video_Fini.restype=c_int
self.dll.Video_GetLastError.argtypes=[]
self.dll.Video_GetLastError.restype=c_int
self.dll.Video_StartAsynPreview.argtypes=[c_char_p,c_void_p,POINTER(VIDEO_PLAY_REQ)]
self.dll.Video_StartAsynPreview.restype=c_longlong
self.dll.Video_StartPreview.argtypes=[c_char_p,c_void_p,POINTER(VIDEO_PLAY_REQ)]
self.dll.Video_StartPreview.restype=c_longlong
self.dll.Video_StopPreview.argtypes=[c_longlong]
self.dll.Video_StopPreview.restype=c_int
self.videosocket=videosocket
self.hikHandle=-1
self.cb_msgCallback=FUNC_MSG(self.cb_msgCallback)
self.cb_decodedDataCallback=FUNC_DECODEDSTREAM(self.cb_decodedDataCallback)
self.dll.Video_Init(None)
self.box_annotator = sv.BoxAnnotator(
thickness=2,
text_thickness=2,
text_scale=1
)
self.queue_obj=[]
self.abortable_sleep=AbortableSleep()
self.isWebsocketServerRunning=False
self.isSendStreamRunning=False
self.bookLock = Lock()
async def sendstream(self, webSocket):
self.isSendStreamRunning=True
while self.isSendStreamRunning:
try:
img=None
self.bookLock.acquire()
if (len(self.queue_obj)>0):
img=self.queue_obj.pop()
self.bookLock.release();
if (not img==None):
await webSocket.send(img)
except Exception as e:
print(e)
self.isSendStreamRunning=False
def stop(self):
self.isSendStreamRunning=False
self.abortable_sleep.abort()
if (self.hikHandle>0):
self.dll.Video_StopPreview(self.hikHandle)
self.hikHandle=-1
self.bookLock.acquire()
self.queue_obj.clear()
self.bookLock.release();
def play(self,url:str):
req=VIDEO_PLAY_REQ()
req.iHardWareDecode=0
req.pUserData=c_void_p(0)
req.fnStream=FUNC_STREAM(0)
#req.fnMsg=FUNC_MSG(0)
#req.fnDecodedStream=FUNC_DECODEDSTREAM(0)
req.fnMsg=self.cb_msgCallback
req.fnDecodedStream=self.cb_decodedDataCallback
self.hikHandle=self.dll.Video_StartAsynPreview(bytes(url,'ascii'),None,byref(req))
def translate(self,name)->str:
return name
if (name=='person'):
return '人员'
elif (name=='tv'):
return '显示屏'
elif (name=='clock'):
return '仪表'
elif (name=='chair'):
return '椅子'
else:
return name
def cb_decodedDataCallback(self,i64PlayHandle:c_longlong, pDataArray:POINTER(c_char), iDataLen:c_int, iWidth:c_int, iHeight:c_int, iFrameType:c_int, iTimeStamp:c_int, pUserData:c_void_p):
YUV = np.frombuffer(pDataArray[:iDataLen],dtype=np.uint8, count=iDataLen).reshape(iHeight*3//2,iWidth)
bgr = cv2.cvtColor(YUV, cv2.COLOR_YUV420p2BGR)
#bgr=cv2.cvtColor(YUV,cv2.COLOR_YUV2BGR)
result = self.videosocket.model(bgr, agnostic_nms=True)[0]
detections = sv.Detections.from_yolov8(result)
labels = [
f"{self.translate(self.videosocket.model.model.names[class_id])} {confidence:0.2f}"
for _, _, confidence, class_id,_
in detections
]
bgr = self.box_annotator.annotate(
scene=bgr,
detections=detections,
labels=labels
)
_,enc=cv2.imencode('.png', bgr)
self.bookLock.acquire()
self.queue_obj.append(enc.tobytes())
if (len(self.queue_obj)>50):
self.queue_obj.clear()
self.bookLock.release();
#cv2.imshow('t',bgr)
#cv2.waitKey(5)
def cb_msgCallback(self,i64PlayHandle:c_longlong, iMsg:c_int, pUserData:c_void_p):
print(iMsg)
def __del__(self):
self.stop()
class Clients(object):
def __init__(self, server):
self._clients = []
self._id = 0
self.ipackets = Packets.IncomingPackets()
self.server = server
def __contains__(self, item):
if isinstance(item, Clients.Client):
return item in self._clients
elif isinstance(item, int):
return not self.get_client(item) is None
else:
return False
def get_client(self, id):
if not isinstance(id, int):
raise ValueError('id is not instance of int')
for c in self._clients:
if c.id == id:
return c
return None
def new_client(self, socket):
if not isinstance(socket, WebSocketHandler.WebSocketHandler):
raise ValueError('socket is not instance of WebSocketHandler')
if socket not in self:
cl = self.Client(self._id, socket, self)
self._id += 1
self._clients.append(cl)
return cl
else:
raise ValueError('socket already has client')
def remove_client(self, client):
client.logger.info('Disconnected')
if client in self._clients:
self._clients.remove(client)
else:
raise ValueError('There is no client like that')
class Client(object):
# TODO: resend missing packets
def __init__(self, id_, socket, clients):
if not isinstance(socket, WebSocketHandler.WebSocketHandler):
raise ValueError('socket is not instance of WebSocketHandler')
if not isinstance(id_, int):
raise ValueError('id is not instance of int')
self.id = id_
self.socket = socket
self._providers_list = []
self._outgoing_packet_id = 0
self._sync_time = datetime.now()
self.clients = clients
def process_incoming_data(self, data):
try:
json_recv=json.loads(data)
except Exception as e:
print('{}: {}'.format(type(e).__name__, e.message))
print(traceback.format_exc())
def send_outgoing_packet(self, packet):
self.socket.sync_write_message(packet)
def is_provider_id(self, provider_id):
return 0 <= provider_id < len(self._providers_list) and self._providers_list[provider_id] is not None
def add_provider(self, provider):
self._providers_list.append(provider)
return self.get_provider_id(provider)
def get_provider_id(self, provider):
if provider not in self._providers_list:
raise ValueError('There is no provider like that')
return self._providers_list.index(provider)
def get_provider(self, provider_id):
if not self.is_provider_id(provider_id):
raise ValueError('There is no provider with this id')
return self._providers_list[provider_id]
def remove_provider(self, provider):
if provider not in self._providers_list:
raise ValueError('There is no provider like that')
self._providers_list[self.get_provider_id(provider)] = None
def seconds_since_sync(self, time):
return int((time - self._sync_time).total_seconds())
下面是websocket 服务器的代码,比较乱,仅供参考,对python的多线程没有搞清楚,等退休后再慢慢研究
# This Python file uses the following encoding: utf-8
import json
import requests
from threading import Thread, Lock
from time import sleep
import QOpenCV2FrameDisplay as d
import hashlib
from datetime import datetime
import uuid
import hmac
import base64
import asyncio
import tornado
from concurrent.futures import ThreadPoolExecutor
from tornado.web import Application
from tornado.ioloop import PeriodicCallback
from tornado.websocket import WebSocketHandler, WebSocketClosedError
from ctypes import *
import numpy as np
import cv2
from ultralytics import YOLO
import supervision as sv
from AbortableSleep import AbortableSleep
from getpointer import getPointer
class MyThreadPool:
executor = ThreadPoolExecutor(max_workers=5)
class MyWsHandler(WebSocketHandler, MyThreadPool):
def postUrl(self, url, data):
headers={'Accept':'application/json','Content-Type':'application/json',
'x-ca-timestamp':str((datetime.timestamp(datetime.now())*10000)),'x-ca-nonce':str(uuid.uuid4()),'x-ca-key':self.appKey}
signString='POST\n'
signString+=f"{headers['Accept']}\n"
signString+=f"{headers['Content-Type']}\n"
signString+=f"x-ca-key:{headers['x-ca-key']}\n"
signString+=f"x-ca-nonce:{headers['x-ca-nonce']}\n"
signString+=f"x-ca-timestamp:{headers['x-ca-timestamp']}\n"
signString+=url
signHeader="x-ca-key,x-ca-nonce,x-ca-timestamp"
headers["x-ca-signature-headers"]=signHeader
headers["x-ca-signature"]=str(base64.b64encode(hmac.new(self.secret.encode('utf-8'), signString.encode('utf-8'), digestmod=hashlib.sha256).digest()),'utf-8')
http=(self.isHttps and 'https://' or 'http://')
return requests.post(f"{http}{self.ip}:{self.port}{url}",
data=json.dumps(data),
headers=headers, verify=False)
def getCamera(self,jh:str)->str:
ret=[]
datas={
'regionIndexCodes':[self.hikID],
'isSubRegion':True,
'pageNo':1,
'pageSize':1000,
'authCodes':['view'],
'orderBy':'sort',
'orderType':'desc'
}
try:
response=self.postUrl('/artemis/api/resource/v2/camera/search', datas)
jsonRet=response.json()
hikChannels=jsonRet['data']['list']
for c in hikChannels:
#if (c['regionName'].startswith(jh)):
# ret.append(c)
upTime=datetime.strptime(c['updateTime'][0:18],'%Y-%m-%dT%H:%M:%S')
if ((datetime.now()-upTime).days<10):
ret.append(c)
return ret
except:
print('无法链接到Hik流媒体服务器')
return []
def getCameraUrl(self, cameraIndexCode, streamType):
data={
'cameraIndexCode':cameraIndexCode,
'streamType':streamType,
'protocol':'hik',
'transmode':0,
'expand':'transcode=0',
'streamform':'ps',
}
try:
response=self.postUrl('/artemis/api/video/v2/cameras/previewURLs', data)
return response.json()
except:
return {}
def data_received(self, chunk: bytes):
pass
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.event_loop = None
self.request_message = None
self.hikID="111111111-1111-1111-1111-111111111111"
self.appKey="11111111"
self.secret="XXXXXXXXXXXXXXXXXXX"
self.ip="000.000.000.000"
self.port=443
self.isHttps=True
self.hikThread = None
self.hik = None
self.aitype=1
def check_origin(self, origin):
"""允许跨域请求"""
return True
def on_ping(self, data):
print("ping", data)
# self.ping(str(round(time.time() * 1000)))
def on_pong(self, data):
print("pong", data)
if not data:
self.ping(str(round(datetime.datetime.now().timestamp() * 1000)))
def initialize(self):
"""为每个请求初始化"""
self.current_user = None
def prepare(self):
"""请求前置操作"""
pass
def open(self):
"""建立连接"""
self.set_nodelay(True) # 设置无延迟
# 单位毫秒,上下浮动50%
self.event_loop = PeriodicCallback(callback=self.send_message, callback_time=1000, jitter=0.5)
self.event_loop.start()
def on_message(self, message):
"""接收前端信息"""
print(message)
if not self.hik is None:
self.hik.stop()
print(self.hik.isSendStreamRunning)
self.request_message = message
def on_close(self):
if not self.hik is None:
self.hik.stop()
"""连接断开"""
self.event_loop.stop()
self.hik=None
print('close hik')
def startRefreshHikVideos(self):
datas={
'regionIndexCodes':[self.hikID],
'isSubRegion':True,
'pageNo':1,
'pageSize':1000,
'authCodes':['view'],
'orderBy':'sort',
'orderType':'desc'
}
try:
response=self.postUrl('/artemis/api/resource/v2/camera/search', datas)
self.bookLock.acquire()
jsonRet=response.json()
self.hikChannels=jsonRet['data']['list']
self.bookLock.release()
except:
print('无法链接到Hik流媒体服务器')
@tornado.gen.coroutine
def callback(self, message):
self.write_message(message, True)
def sending(self, loop):
asyncio.set_event_loop(loop)
asyncio.run(self.hik.sendstream(self))
async def send_message(self):
"""发送信息"""
if not self.request_message:
return
await asyncio.sleep(1)
print(self.request_message)
if self.ws_connection is None or self.ws_connection.is_closing():
self.close()
return
try:
json_recv=json.loads(self.request_message)
if json_recv['cmd'] == "getchannels":
ret=json.dumps(self.getCamera(json_recv['jh']))
await self.write_message(ret)
elif json_recv['cmd'] == "getpreviewurl":
self.aitype = json_recv['aitype']
await self.write_message(json.dumps(self.getCameraUrl(json_recv['cameraIndexCode'], json_recv['streamtype']))) #0:主码 1:子码
elif json_recv['cmd'] == "getvideo":
if self.hik is None:
self.hik=hikplayer(self)
self.hik.isWebsocketServerRunning=True
self.hik.play(json_recv['url'])
#self.hik.sendstream(self)
loop = asyncio.new_event_loop()
self.hikThread = Thread(target=self.sending, args=(loop,))
self.hikThread.start()
else:
await self.write_message("cmd fail")
except Exception as e:
print("eerr:")
print(e)
self.request_message = None
class Main:
def __init__(self, debug=False):
self.debug = debug
self.urls = [
(r"/", MyWsHandler),
]
self.IP_ADDR = "0.0.0.0"
self.IP_PORT = "8888"
self.thread_web=None
def mkApp(self):
return Application(
self.urls,
debug=self.debug,
# websocket_ping_interval=5, # 间隔5秒发送一次ping帧,第一次发送为触发的5s后
# websocket_ping_timeout=20 # 每次 ping 操作重置时间超时时间,若超时则断开连接,默认3次 ping 或 30s 断开
)
async def admin(self):
global model
#model = YOLO('F:\\dotnet\\KTSC_SRC\\clock\\runs\\segment\\train5\\weights\\last.pt') # load a pretrained model
model = YOLO('last.pt') # load a pretrained model
global model_good
model_good = YOLO('yolov8n.pt')
global box_annotator
box_annotator = sv.BoxAnnotator(
thickness=2,
text_thickness=2,
text_scale=1
)
app = self.mkApp()
app.listen(address=self.IP_ADDR, port=self.IP_PORT)
await asyncio.Event().wait()
def webSocketServer(self):
asyncio.run(self.admin())
def run(self):
self.thread_web = Thread(target=self.webSocketServer,name="Websoket_server")
self.thread_web.setDaemon(True) # 随着主线程结束而结束
self.thread_web.start()
class videowebsocket:
def __init__(self):
self.thread_hik=None
self.allhiks=[]
self.hikID="012c0b88-c5ff-4b37-9748-d1cb1e964eb2"
self.appKey="29194332"
self.secret="A36vZHDibYYZMmq1j4GX"
self.ip="10.142.80.138"
self.port=443
self.isHttps=True
self.hikChannels=None
self.bookLock = Lock()
#self.model = YOLO('F:\\dotnet\\KTSC_SRC\\clock\\runs\\detect\\train6\\weights\\last.pt') # load a pretrained model
#self.model = YOLO('F:\\dotnet\\KTSC_SRC\\clock\\runs\\segment\\train\\weights\\last.pt') # load a pretrained model
self.server=None
self.loop=None
self.stop=None
self.task=None
self.abortable_sleep=AbortableSleep()
self.isRefreshHikChannelsRunning=False
def postUrl(self, url, data):
headers={'Accept':'application/json','Content-Type':'application/json',
'x-ca-timestamp':str((datetime.timestamp(datetime.now())*10000)),'x-ca-nonce':str(uuid.uuid4()),'x-ca-key':self.appKey}
signString='POST\n'
signString+=f"{headers['Accept']}\n"
signString+=f"{headers['Content-Type']}\n"
signString+=f"x-ca-key:{headers['x-ca-key']}\n"
signString+=f"x-ca-nonce:{headers['x-ca-nonce']}\n"
signString+=f"x-ca-timestamp:{headers['x-ca-timestamp']}\n"
signString+=url
signHeader="x-ca-key,x-ca-nonce,x-ca-timestamp"
headers["x-ca-signature-headers"]=signHeader
headers["x-ca-signature"]=str(base64.b64encode(hmac.new(self.secret.encode('utf-8'), signString.encode('utf-8'), digestmod=hashlib.sha256).digest()),'utf-8')
http=(self.isHttps and 'https://' or 'http://')
return requests.post(f"{http}{self.ip}:{self.port}{url}",
data=json.dumps(data),
headers=headers, verify=False)
def getCamera(self,jh:str)->str:
ret=[]
self.bookLock.acquire()
for c in self.hikChannels:
#if (c['regionName'].startswith(jh)):
# ret.append(c)
upTime=datetime.strptime(c['updateTime'][0:18],'%Y-%m-%dT%H:%M:%S')
if ((datetime.now()-upTime).days<10):
ret.append(c)
self.bookLock.release()
return ret
def getCameraUrl(self, cameraIndexCode, streamType):
data={
'cameraIndexCode':cameraIndexCode,
'streamType':streamType,
'protocol':'hik',
'transmode':0,
'expand':'transcode=0',
'streamform':'ps',
}
try:
response=self.postUrl('/artemis/api/video/v2/cameras/previewURLs', data)
return response.json()
except:
return {}
def close(self):
self.isRefreshHikChannelsRunning=False
if not self.server == None:
for s in self.server.ws_server.sockets:
s.abort()
self.abortable_sleep.abort()
if (self.loop!=None):
print("closing")
while(self.loop.is_running()):
sleep(0.5)
self.loop=None
print("closed")
def startRefreshHikVideos(self):
while True:
datas={
'regionIndexCodes':[self.hikID],
'isSubRegion':True,
'pageNo':1,
'pageSize':1000,
'authCodes':['view'],
'orderBy':'sort',
'orderType':'desc'
}
try:
response=self.postUrl('/artemis/api/resource/v2/camera/search', datas)
self.bookLock.acquire()
jsonRet=response.json()
self.hikChannels=jsonRet['data']['list']
self.bookLock.release()
except:
print('无法链接到Hik流媒体服务器')
if not self.isRefreshHikChannelsRunning:
break;
self.abortable_sleep(120)
print('is end 2')
def start(self):
self.isRefreshHikChannelsRunning=True
self.thread_hik=Thread(target=self.startRefreshHikVideos)
self.thread_hik.start()
#run_loop(new_loop,host,port)
self.thread_web = Thread(target=self.webSocketServer,name="Websoket_server")
self.thread_web.setDaemon(True) # 随着主线程结束而结束
self.thread_web.start()
# 握手并且接收数据
async def serverRun(self, websocket, path):
#await self.linkRun(websocket,path)
#print('is end 35')
t = Thread(target=self.linkThread,args=(websocket, path))
t.setDaemon(True) # 随着主线程结束而结束
t.start()
#self.thread_links.append(t)
def linkThread(self,websocket, path):
loop=asyncio.new_event_loop()
r=self.linkRun(websocket,path)
loop.run_until_complete(r)
loop.close()
async def linkRun(self,websocket, path):
hik=hikplayer(self)
hik.isWebsocketServerRunning=True
self.allhiks.append(hik)
while self.isRefreshHikChannelsRunning and hik.isWebsocketServerRunning:
try:
recv_text = await websocket.recv()
print(recv_text)
json_recv=json.loads(recv_text)
if json_recv['cmd'] == "getchannels":
ret=json.dumps(self.getCamera(json_recv['jh']))
await websocket.send(ret)
elif json_recv['cmd'] == "getpreviewurl":
await websocket.send(json.dumps(self.getCameraUrl(json_recv['cameraIndexCode'], 0))) #0:主码 1:子码
elif json_recv['cmd'] == "getvideo":
hik.play(json_recv['url'])
hik.sendstream(websocket)
break
else:
await websocket.send("cmd fail")
except Exception as e:
print("eerr:")
print(e)
hik.isWebsocketServerRunning=False
hik.isSendStreamRunning=False
break
hik.stop()
print('is end 5')
FUNC_STREAM=WINFUNCTYPE(None,c_longlong, c_int, c_char_p, c_int, c_void_p)
FUNC_MSG=WINFUNCTYPE(None,c_longlong, c_int, c_void_p)
FUNC_DECODEDSTREAM=WINFUNCTYPE(None,c_longlong, POINTER(c_char), c_int, c_int, c_int, c_int, c_int, c_void_p)
class VIDEO_PLAY_REQ(Structure):
_fields_=[
('iHardWareDecode',c_int),
('fnStream',FUNC_STREAM),
('fnMsg',FUNC_MSG),
('fnDecodedStream',FUNC_DECODEDSTREAM),
('pUserData',c_void_p),
('szReserve',c_char*64),
]
class hikplayer:
def __init__(self, videosocket:MyWsHandler):
self.dll=WinDLL('./hkplayer/bin/VideoSDK.dll')
self.dll.Video_Init.argtypes=[c_char_p]
self.dll.Video_Init.restype=c_int
self.dll.Video_Fini.argtypes=[]
self.dll.Video_Fini.restype=c_int
self.dll.Video_GetLastError.argtypes=[]
self.dll.Video_GetLastError.restype=c_int
self.dll.Video_StartAsynPreview.argtypes=[c_char_p,c_void_p,POINTER(VIDEO_PLAY_REQ)]
self.dll.Video_StartAsynPreview.restype=c_longlong
self.dll.Video_StartPreview.argtypes=[c_char_p,c_void_p,POINTER(VIDEO_PLAY_REQ)]
self.dll.Video_StartPreview.restype=c_longlong
self.dll.Video_StopPreview.argtypes=[c_longlong]
self.dll.Video_StopPreview.restype=c_int
self.videosocket=videosocket
self.hikHandle=-1
self.cb_msgCallback=FUNC_MSG(self.cb_msgCallback)
self.cb_decodedDataCallback_good=FUNC_DECODEDSTREAM(self.cb_decodedDataCallback_good)
self.cb_decodedDataCallback_pointer=FUNC_DECODEDSTREAM(self.cb_decodedDataCallback_pointer)
self.cb_decodedDataCallback_normal=FUNC_DECODEDSTREAM(self.cb_decodedDataCallback_normal)
self.dll.Video_Init(None)
self.box_annotator = sv.BoxAnnotator(
thickness=2,
text_thickness=2,
text_scale=1
)
self.queue_obj=[]
self.abortable_sleep=AbortableSleep()
self.isWebsocketServerRunning=False
self.isSendStreamRunning=False
self.bookLock = Lock()
# 定义红色范围
self.lower_red = np.array([0, 43, 46])
self.upper_red = np.array([10, 255, 255])
self.lower_red2 = np.array([156, 43, 46])
self.upper_red2 = np.array([180, 255, 255])
self.yellow_min = np.array([15, 43, 46])
self.yellow_max = np.array([34, 255, 255])
async def sendstream(self, webSocket):
self.isSendStreamRunning=True
while self.isSendStreamRunning:
try:
img=None
if (len(self.queue_obj)>0):
img=self.queue_obj.pop(0)
else:
sleep(0.01)
if (not img is None):
await webSocket.write_message(img, True)
except Exception as e:
print(e)
self.isSendStreamRunning=False
break
def stop(self):
self.isSendStreamRunning=False
if (self.hikHandle>0):
self.dll.Video_StopPreview(self.hikHandle)
self.hikHandle=-1
self.queue_obj.clear()
def play(self,url:str):
self.stop()
req=VIDEO_PLAY_REQ()
req.iHardWareDecode=0
req.pUserData=c_void_p(0)
req.fnStream=FUNC_STREAM(0)
#req.fnMsg=FUNC_MSG(0)
#req.fnDecodedStream=FUNC_DECODEDSTREAM(0)
if (self.videosocket.aitype == 1):
req.fnDecodedStream=self.cb_decodedDataCallback_good
elif (self.videosocket.aitype == 2):
req.fnDecodedStream=self.cb_decodedDataCallback_pointer
else:
req.fnDecodedStream=self.cb_decodedDataCallback_normal
req.fnMsg=self.cb_msgCallback
self.hikHandle=self.dll.Video_StartAsynPreview(bytes(url,'ascii'),None,byref(req))
def translate(self,name)->str:
return name
if (name=='person'):
return '人员'
elif (name=='tv'):
return '显示屏'
elif (name=='clock'):
return '仪表'
elif (name=='chair'):
return '椅子'
else:
return name
#这个是仪表指针的
def cb_decodedDataCallback_pointer(self,i64PlayHandle:c_longlong, pDataArray:POINTER(c_char), iDataLen:c_int, iWidth:c_int, iHeight:c_int, iFrameType:c_int, iTimeStamp:c_int, pUserData:c_void_p):
YUV = np.frombuffer(pDataArray[:iDataLen],dtype=np.uint8, count=iDataLen).reshape(iHeight*3//2,iWidth)
bgr = cv2.cvtColor(YUV, cv2.COLOR_YUV420p2BGR)
global model
#bgr=cv2.cvtColor(YUV,cv2.COLOR_YUV2BGR)
#result = self.videosocket.model.predict(source=bgr, save=True,boxes=False, save_txt=True,task='segment')[0] # 将预测保存为标签
result = model(bgr, agnostic_nms=True,task='segment')[0]
#result = self.videosocket.model.predict(source=bgr, task='segment')[0] # 将预测保存为标签
detections = sv.Detections.from_yolov8(result)
src=None
rcPointer=None
max=0
x1=0
y1=0
x2=0
y2=0
for i in range(len(detections)):
if (model.model.names[detections.class_id[i]]=='pointer'):
x1, y1, x2, y2 = detections.xyxy[i].astype(int)
if (detections.confidence[i]>max):
max=detections.confidence[i]
m = result.masks.xy[i]
mask = np.zeros(bgr.shape[:2], np.uint8)
if (len(m)>2):
cv2.fillPoly(mask, [np.array(m,dtype=np.int32)], (255,0,255)) # 填充
# 逐位与,得到裁剪后图像,此时是黑色背景
dst = cv2.bitwise_and(bgr, bgr, mask=mask)
src=dst[y1:y2,x1:x2]
if (not src is None and len(src) > 5):
hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)
# 创建掩膜
mask1 = cv2.inRange(hsv, self.lower_red, self.upper_red)
mask2 = cv2.inRange(hsv, self.lower_red2, self.upper_red2)
mask = cv2.bitwise_or(mask1, mask2)
line2,contours2=getPointer.getLine(src,self.yellow_min, self.yellow_max)
line1,contours1=getPointer.getLine1(src, mask)
if (not line1 is None and not line2 is None):
cv2.line(bgr,(int(x1+line1[0][0]),int(y1+line1[0][1])), (int(x1+line1[1][0]),int(y1+line1[1][1])), (0,0,255),2)
cv2.line(bgr,(int(x1+line2[0][0]),int(y1+line2[0][1])), (int(x1+line2[1][0]),int(y1+line2[1][1])), (0,255,255),2)
cp=getPointer.cross_point([line1[0][0],line1[0][1],line1[1][0],line1[1][1]],[line2[0][0],line2[0][1],line2[1][0],line2[1][1]])
cv2.circle(bgr,((int)(cp[0]+x1),(int)(cp[1]+y1)),10,(255,0,0),1)
cv2.drawContours(bgr, [contours2], -1, (0, 255, 255), 1)
cv2.drawContours(bgr, [contours1], -1, (0, 0, 255), 1)
_,enc=cv2.imencode('.png', bgr)
self.queue_obj.append(enc.tobytes())
if (len(self.queue_obj)>50):
self.queue_obj.clear()
#cv2.imshow('t',bgr)
#cv2.waitKey(5)
#这个是物体的
def cb_decodedDataCallback_good(self,i64PlayHandle:c_longlong, pDataArray:POINTER(c_char), iDataLen:c_int, iWidth:c_int, iHeight:c_int, iFrameType:c_int, iTimeStamp:c_int, pUserData:c_void_p):
YUV = np.frombuffer(pDataArray[:iDataLen],dtype=np.uint8, count=iDataLen).reshape(iHeight*3//2,iWidth)
bgr = cv2.cvtColor(YUV, cv2.COLOR_YUV420p2BGR)
global model_good
result = model_good(bgr, agnostic_nms=True)[0]
detections = sv.Detections.from_yolov8(result)
labels = [
f"{model_good.model.names[class_id]} {confidence:0.2f}"
for _, _, confidence, class_id,_
in detections
]
bgr = box_annotator.annotate(
scene=bgr,
detections=detections,
labels=labels
)
_,enc=cv2.imencode('.png', bgr)
self.queue_obj.append(enc.tobytes())
if (len(self.queue_obj)>50):
print("big then 50")
self.queue_obj=self.queue_obj[:10]
#cv2.imshow('t',bgr)
#cv2.waitKey(5)
#这个是物体的
def cb_decodedDataCallback_normal(self,i64PlayHandle:c_longlong, pDataArray:POINTER(c_char), iDataLen:c_int, iWidth:c_int, iHeight:c_int, iFrameType:c_int, iTimeStamp:c_int, pUserData:c_void_p):
YUV = np.frombuffer(pDataArray[:iDataLen],dtype=np.uint8, count=iDataLen).reshape(iHeight*3//2,iWidth)
bgr = cv2.cvtColor(YUV, cv2.COLOR_YUV420p2BGR)
_,enc=cv2.imencode('.png', bgr)
self.queue_obj.append(enc.tobytes())
if (len(self.queue_obj)>50):
print("big then 50")
self.queue_obj=self.queue_obj[:10]
#cv2.imshow('t',bgr)
#cv2.waitKey(5)
def cb_msgCallback(self,i64PlayHandle:c_longlong, iMsg:c_int, pUserData:c_void_p):
pass
def __del__(self):
self.stop()
本服务器的原理是从海康威视服务器中获取图像,然后对图像进行处理,直接保存为png图片格式发送到websocket客户端,在客户端网页直接显示图像。为提高显示速度,使用了 webassembly技术。