本篇文章介绍基于python的海康SDK二次开发,基于海康SDK可以完成摄像头的云台控制,包括抓图、PTZ参数获取、基于PTZ摄像头控制、预置点切换等功能,而这些功能整体流程都是一样的(都需要先登录),因此以类的形式对这些常用可能进行了整合,在类初始化的时候进行摄像头登录,使整个控制流程变得更加清晰。由于公司要求,部分核心代码进行了加密处理,交流讨论联系qq:1048782143。
目录结构
目录结构如图所示
主要包括一个摄像头控制模块类agent,工具模块util,海康SDK配置模块HCNetSDK
pipeline
通过初始化一个类,返回摄像头的登录handle,部分代码如下:
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class Agent(object): def __init__(self, ip, port, username, password): self.ip = ip self.port = port self.username = username self.password = password
if WINDOWS_FLAG: os.chdir(r'../auto/lib/win') self.Objdll = ctypes.CDLL(r'./HCNetSDK.dll') self.Playctrldll = ctypes.CDLL(r'./PlayCtrl.dll') else: os.chdir(r'./lib/linux') self.Objdll = cdll.LoadLibrary(r'./libhcnetsdk.so') self.Playctrldll = cdll.LoadLibrary(r'./libPlayCtrl.so')
SetSDKInitCfg(self.Objdll) self.Objdll.NET_DVR_Init() self.Objdll.NET_DVR_SetLogToFile(3, bytes('./SdkLog_Python/', encoding="utf-8"), False)
(self.lUserId, self.device_info) = self.loginDev()
if self.lUserId < 0: err = self.Objdll.NET_DVR_GetLastError() print('Login device fail, error code is: %d' % self.Objdll.NET_DVR_GetLastError()) self.Objdll.NET_DVR_Cleanup() exit() os.chdir(retval)
def loginDev(self): DEV_IP = create_string_buffer(self.ip.encode("utf-8")) DEV_PORT = self.port DEV_USER_NAME = create_string_buffer(self.username.encode("utf-8")) DEV_PASSWORD = create_string_buffer(self.password.encode("utf-8"))
device_info = NET_DVR_DEVICEINFO_V30() lUserId = self.Objdll.NET_DVR_Login_V30(DEV_IP, DEV_PORT, DEV_USER_NAME, DEV_PASSWORD, byref(device_info)) return (lUserId, device_info)
|
设备登录后就可以调用一些云台控制功能,比如PTZ控制,抓图等,值得注意的是其结构体的定义:
1 2 3 4 5 6
| class NET_DVR_PTZPOS(Structure): _fields_ = [ ("wAction", ctypes.c_uint16), ("wPanPos", ctypes.c_uint16), ("wTiltPos", ctypes.c_uint16), ("wZoomPos", ctypes.c_uint16)]
|
另外关于海康SDK的知识是其角度存储的值有一个10进制-16进制转换关系,具体而言:存的值是10进制,而具体的读数就是这个10进制的值先转换为16进制然后除以10。实际显示的PTZ值是获取到的十六进制值的十分之一,如获取的水平参数P的值是0x1750,实际显示的P值为175度;获取到的垂直参数T的值是0x0789,实际显示的T值为78.9度;获取到的变倍参数Z的值是0x1100,实际显示的Z值为110倍。所以有如下转换关系:
1 2 3 4 5 6 7 8 9 10
| def HexToDecMa(x): x = int(hex(x)[2:].upper()) / 10 return x
def DEC2HEX_doc(x): x = int(str(int(x * 10)), 16) return x
|
另外抓图功能也比较重要,在此记录一下:
1 2 3 4 5 6 7 8 9 10 11 12
| def getPic(self): lChannel = 1 lpJpegPara = NET_DVR_JPEGPARA() lpJpegPara.wPicSize = 0 lpJpegPara.wPicQuality = 0 sJpegPicFileName = bytes("cur.jpg", "ascii") res = self.Objdll.NET_DVR_CaptureJPEGPicture(self.lUserId, lChannel, ctypes.byref(lpJpegPara), sJpegPicFileName) if res: print("抓图成果") else: error_info = callCpp("NET_DVR_GetLastError") print("抓图失败:" + str(error_info))
|
其中NET_DVR_JPEGPARA定义如下
1 2 3 4 5 6
| class NET_DVR_JPEGPARA(Structure): pass NET_DVR_JPEGPARA._fields_ = [ ('wPicSize', c_ushort), ('wPicQuality', c_ushort), ]
|
另外有时候可能是抓图生成视频,不需要写先到图像中,可以直接保存在内存里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| def Get_JPEGpicture_New2(): print("摄像机开始抓图...") jpegpara = NET_DVR_JPEGPARA() jpegpara.wPicSize = 5 jpegpara.wPicQuality = 0 addr_jpg = byref(jpegpara)
pbuff = create_string_buffer(1440 * 2560) if callCpp("NET_DVR_CaptureJPEGPicture_NEW", lUserID, c_long(1), addr_jpg, pbuff, 1440 * 2560, 0): print("cap success") nparr = np.fromstring(pbuff.raw, np.uint8) img_decode = cv2.imdecode(nparr, 1) print("img_decode: ", img_decode) return img_decode else: print("cap error {}".format(callCpp("NET_DVR_GetLastError")))
|
参考资料:https://blog.csdn.net/byna11sina11/article/details/110823256