如何在ctypes中正确解析嵌套结构体?车牌识别结果乱码问题
解决海康C SDK嵌套结构体解析与车牌乱码问题
从你的代码和乱码现象来看,问题主要出在结构体内存对齐不匹配、字符串编码处理错误,以及部分结构体初始化细节上。下面是一步步的修复方案:
1. 修复结构体内存对齐
海威视SDK的C结构体通常使用4字节对齐(优先参考官方文档确认,部分场景为1字节),而ctypes默认的对齐规则可能和SDK不一致,导致字段偏移错误,直接内存拷贝后数据完全错位。
给每个Structure类添加_pack_属性指定对齐方式:
class NET_DVR_ALARMER(Structure): _pack_ = 4 # 匹配SDK的对齐规则 _fields_ = [ ("byUserIDValid", c_byte) ] class NET_DVR_PLATE_INFO(Structure): _pack_ = 4 _fields_ = [ ("byPlateType", c_byte), ("byColor", c_byte), ("byBright", c_byte), ("byLicenseLen", c_byte), ("byEntireBelieve", c_byte), ("byRegion", c_byte), ("byCountry", c_byte), ("byRes", c_byte*24), ("dwXmlLen", c_ulong), ("sLicense", c_char*16), ("byBelieve", c_char*16) ] class NET_DVR_PLATE_RESULT(Structure): _pack_ = 4 _fields_ = [ ("dwSize", c_ulong), ("byResultType", c_byte), ("byChanIndex", c_byte), ("wAlarmRecordID", c_ushort), ("dwRelativeTime", c_ulong), ("byAbsTime", c_byte*32), ("byTrafficLight", c_byte), ("byPicNum", c_byte), ("byDriveChan", c_byte), ("byVehicleType", c_byte), ("byRes3", c_byte*8), ("struPlateInfo", NET_DVR_PLATE_INFO) ]
2. 修正结构体拷贝与字符串解码逻辑
SDK回调的pAlarmInfo已经是指向NET_DVR_PLATE_RESULT的指针,不需要用memmove手动拷贝,直接转换指针类型更安全;同时海康SDK的车牌字符串默认用GBK编码,需要指定解码方式,还要按byLicenseLen截断无效字节:
def MsgCallback(lCommand, pAlarmer, pAlarmInfo, dwBufLen, pUser): # 直接比较数值,避免字符串hex转换的错误 if lCommand == 0x2800: # 将void指针转换为结构体指针,直接读取内容 struPlateResult = cast(pAlarmInfo, POINTER(NET_DVR_PLATE_RESULT)).contents # 可选:验证结构体大小是否匹配,排查定义错误 if struPlateResult.dwSize != sizeof(NET_DVR_PLATE_RESULT): print(f"结构体大小不匹配:预期{sizeof(NET_DVR_PLATE_RESULT)},实际{struPlateResult.dwSize}") return True struPlateInfo = struPlateResult.struPlateInfo print("Accuracy: {}".format(struPlateInfo.byEntireBelieve)) print("License Plate Length: {}".format(struPlateInfo.byLicenseLen)) # 处理车牌字符串:按实际长度截断+GBK解码 license_raw = struPlateInfo.sLicense[:struPlateInfo.byLicenseLen] try: license_plate = license_raw.decode("gbk") print("License Plate: {}".format(license_plate)) except UnicodeDecodeError: # 解码失败时打印原始字节用于排查 print(f"解码失败,原始字节:{license_raw}") return True
3. 修复回调函数的GC回收问题
Python的垃圾回收会自动回收临时创建的回调对象,导致SDK回调时出现崩溃或异常,需要保留回调的全局引用:
def setDVRMsgCallback(sdk): callback_t = CFUNCTYPE(c_bool, c_long, POINTER(NET_DVR_ALARMER), c_void_p, c_ulong, c_void_p) # 保留回调引用,防止被GC回收 global msg_callback msg_callback = callback_t(MsgCallback) sdk.NET_DVR_SetDVRMessageCallBack_V31.restype = c_bool if sdk.NET_DVR_SetDVRMessageCallBack_V31(msg_callback, None): print("Init callback event") else: print("Unable to init NET_DVR_SetDVRMessageCallBack_V31") print(sdk.NET_DVR_GetLastError())
额外排查点
如果问题依旧,可以做以下验证:
- 打印
dwBufLen的值,确认是否等于sizeof(NET_DVR_PLATE_RESULT),不一致说明结构体定义有误; - 确认
NET_DVR_SetupAlarmChan_V41的lUserID是有效的登录句柄; - 尝试用
utf-8解码测试,若仍乱码则可能是SDK返回编码特殊,需对照官方文档确认。
内容的提问来源于stack exchange,提问作者Moore Tech
相关产品推荐
相关产品推荐

