使用python访问海康威视流媒体服务器

由于项目需要,需要访问海康威视的流媒体服务器获取视频流,对视频图象进行识别后,推送到网页进行视频实时监控,图像识别库使用的是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技术。

  • 13
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值