基于python的海康摄像头二次开发

本篇文章介绍基于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:
# print(sys.path)
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) # 设置组件库和SSL库加载路径
# 初始化DLL
self.Objdll.NET_DVR_Init()
# 启用SDK写日志
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)
# point_sJpegPicBuffer = POINTER(sJpegPicBuffer)
if callCpp("NET_DVR_CaptureJPEGPicture_NEW", lUserID, c_long(1), addr_jpg, pbuff, 1440 * 2560, 0):
print("cap success")
# print("pbuff: ",pbuff, pbuff.value)
nparr = np.fromstring(pbuff.raw, np.uint8)
img_decode = cv2.imdecode(nparr, 1)
print("img_decode: ", img_decode)
return img_decode
# return 0, pbuff
else:
print("cap error {}".format(callCpp("NET_DVR_GetLastError")))
# return HKAdapter.hksdk.NET_DVR_GetLastError(), None

参考资料:https://blog.csdn.net/byna11sina11/article/details/110823256