之前写了一个大华SDK取图程序,中间遇见很多问题,查找解决办法的时候发现网上关于大华SDK的调用,用Python写的少得可怜,基本没有。记录一下开发中遇见的问题和解决办法。
业务场景是识别人脸到人脸的时候保存背景图和人脸图。根据开发文档,流程为初始化SDK,登录设备,订阅智能事件,在智能事件回调函数中可以取到需要的图片信息。
声明函数调用
python调用C语言函数,需要先声明函数及函数返回值类型参数类型,下面以SDK初始化及设备登录函数举例。
SDK初始化第一个参数为回调函数。需要根据文档先注册回调函数。
Init_Callback = CFUNCTYPE(c_void_p,c_longlong,c_char_p,c_long,c_ulonglong)
CFUNCTYPE()第一个参数为返回值类型,后面依次为参数类型
然后声明函数调用及返回值类型和参数类型
CLIENT_Init = NetSDK_dll.CLIENT_InitCLIENT_Init.restype = c_boolCLIENT_Init.argtypes = (Init_Callback,c_ulonglong)
设备登录函数前几个参数传递方式是input,后两个是output,实际调用传参需要通过byref方式,声明的时候需要声明为POINTER指针
CLIENT_LoginEx2 = NetSDK_dll.CLIENT_LoginEx2CLIENT_LoginEx2.restype = c_longlongCLIENT_LoginEx2.argtypes = (c_char_p,c_ushort,c_char_p,c_char_p,c_int,c_void_p,POINTER(NetDeviceInfoEx),POINTER(c_int))
初始化SDK,登录设备
初始化SDK,登录设备没有特别的地方,直接调用CLIENT_Init和CLIENT_LoginEx2函数。
订阅智能事件
订阅智能事件调用CLIENT_RealLoadPictureEx
def smart_event_subscribe(lLoginID, nChannelID , dwAlarmType, bNeedPicFile, analy_call_back, dwUser, Reserved): lAnalyer_handle = CLIENT_RealLoadPictureEx(c_longlong(lLoginID), c_int(nChannelID), c_ulong(dwAlarmType), c_bool(bNeedPicFile), analy_call_back, c_ulonglong(dwUser), c_void_p(Reserved) ) print(lAnalyer_handle) if lAnalyer_handle == 0: print('CLIENT_RealLoadPictureEx: failed! Error code %x.', CLIENT_GetLastError()) return -1 else: print('lAnalyer_handle address', id(lAnalyer_handle)) return 1
第一个参数为设备登录成功返回的登录句柄,参数二为通道号,这个参数是个坑,如果是1号通道,这个参数必须是0,参数值是实际通道号减1,因为这个参数传错,无法检测到智能事件,回调函数一直不执行。参数三为订阅的智能事件,这里传值订阅全部,智能事件类型和其代表的值开发文档里面没有,要在demo里找。这里定义成枚举类。传枚举类的值Alarm.EVENT_IVS_ALL.value。因为我们只需要关注人脸识别,所以类里只需要定义和人脸识别相关的智能事件类型,可以根据自己的业务需要定义自己的枚举类属性。
class Alarm(Enum): EVENT_IVS_ALL = 0x00000001 EVENT_IVS_FACEDETECT = 0x0000001A EVENT_IVS_FACERECOGNITION = 0x00000117
智能事件回调函数
在订阅智能事件函数里需要将智能事件回调函数作为参数传递,智能事件回调函数需要自己根据业务逻辑去定义。
智能事件回调函数第一个个参数是订阅智能事件成功的订阅句柄。第二个参数为智能事件类型,第三个参数为智能事件类型对应的结构体。第四个参数为图片字节,第五个参数为图片大小。
AnalyzerData = CFUNCTYPE(c_int, POINTER(c_longlong), c_ulong, POINTER(DEV_EVENT_FACERECOGNITION_INFO), c_void_p, c_ulong, c_ulonglong, c_int, c_void_p)AnalyzerDataCallBack(lAnalyzerHandle, dwAlarmType, pAlarmInfo, pBuffer, dwBufSize, dwUser, nSequence, reserved)
第五个参数类型声明为c_void_p,得到的值为变量地址,由于变量本身是byte类型,使用
pic_byte = string_at(pBuffer, dwBufSize)
取得参数实际的值。即为背景图的byte内容。
参数三为智能事件对应的结构体信息。根据结构体中的pAlarmInfo.stuObject.stPicInfo.dwOffSet得到小图在背景图中的偏移量来从背景图中取到人脸小图的byte内容。
结构体定义这里绕了很多。最开始结构体定义是按照开发文档里的示意的结构体。取得到stPicInfo的结构体内容全部为0。后来按照C语言demo里的结构体定义重新定义了结构体,得到的结果是错乱的,pAlarmInfo.stuObject.szObjectType正确的取值应该是HumanFace,pAlarmInfo.stuObject.szObjectSubType正确的取值为Noraml,我的结构体类型是按照C语言demo里定义的,第一次取结果仍然不对,第一次pAlarmInfo.stuObject.szObjectType取到的值为nFace。pAlarmInfo.stuObject.szObjectSubType取到的值为al,试着注释掉szObjectType前定义的nObjectID,才能正确对齐取到正确的值,得到offset及dwFileLenth的值。
由于pAlarmInfo的参数传递方式是POINTER(DEV_EVENT_FACERECOGNITION_INFO),在回调函数里得到的直接的值是一个LP_DEV_EVENT_FACERECOGNITION_INFO类型的对象。如果想要取得对象的值有两种方式
1.pAlarmInfo.contents得到指针内容,一个结构体对象
2.pAlarmInfo[0]取消指针引用,得到结构体对象
拿到结构体的值,图片byte值及图片大小就可以根据自己的需要做对应的处理了。
来源:https://www.cnblogs.com/masami-/p/12447343.html