前言
作为实习生接的第一个需求,虽然很简单,但是还是要记录一下在这个过程中遇到的问题和解决办法。
开发流程
- 打开海康威视官网找到开发文档,并下载对应的设备网络sdk ,在设备网络sdk的压缩包里面也有对应的开发文档的chm版,查看比较方便,同样在其中我们可以找到java的demo,注意将dll支持库复制到demo的lib目录下之后就可以尝试运行了。
设备检测车辆时进行车牌识别、图片抓拍,并且上传识别抓拍结果。识别和抓拍是设备实现的,由设备主动上传,sdk被动接收。这也就是说,我们只需要在后端new一个sdk对象,与设备建立连接之后选择好布防方式,设备那一端就会把信息上传给sdk,由sdk来处理信息。布防通俗理解就是打开识别功能的意思,这里布防有两种方式,
1)报警布防方式,是指sdk主动连接设备,建立报警上传通道,设备发生报警之后发送给sdk。需要先注册登录设备。
2)报警监听方式,是指触发事件时设备主动连接sdk并且上传报警信息,sdk在设定的端口上监听和接收。需要先在设备端配置报警主机的ip和端口,和sdk监听的ip、端口需要一致。我这里选择报警布防方式,由于海康威视的sdk是根据c++编写的,里面的javasdk也是根据c++翻译来的,所有比较反直觉,不过没事,我会讲清楚遇到的问题。demo的代码我就不贴出来了,文档都有,我重点讲一下遇到的问题。
问题和解决方案
dll库加载不到的问题
如果直接运行demo的话,不出意外应该没什么问题,除非你没用自己的设备和ip然后登录失败,并且库也是能加载到的。
但是我们做二次开发总会需要把这些东西整合到项目中,可能是单体项目,可能是微服务项目,大部分时候我们需要把lib放到项目一个固定的文件夹,比如web-inf下的lib目录,比如我的项目就是,这个目录下本来就放了很多依赖,已经add as library了。
private static boolean createsdkinstance() { if (hcnetsdk == null) { synchronized (hcnetsdk.class) { string strdllpath = ""; try { if (osselect.iswindows()) //win系统加载库路径 strdllpath = system.getproperty("user.dir") + "\\lib\\hcnetsdk.dll"; else if (osselect.islinux()) //linux系统加载库路径 strdllpath = system.getproperty("user.dir") + "/lib/libhcnetsdk.so"; hcnetsdk = (hcnetsdk) native.loadlibrary(strdllpath, hcnetsdk.class); system.out.println(strdllpath); } catch (exception ex) { system.out.println("loadlibrary: " + strdllpath + " error: " + ex.getmessage()); return false; } } } return true; }
native.loadlibrary()这个方法,前一个参数是要加载的dll的位置,后一个是要加载的类,在demo里面第一个参数填的是绝对路径,我在整合到项目里面的时候发现这里总是报错,后来调试发现获取到的路径是tomcat部署目录的路径,而在项目根目录新建lib文件夹他没有识别到。后来尝试写成绝对路径,写成绝对路径还是不行,报错变成了很奇怪的一条信息,显示找不到xxx(dll的绝对路径)在xxx(web-inf下的lib的目录),但是实际上我已经将dll放到了web-inf下的lib目录,为什么找不到呢?
后来搜索发现native.loadlibrary()这个方法第一个参数只需要填dll支持的名称就可以了,于是改为
if (hcnetsdk == null) { synchronized (hcnetsdk.class) { string strdllpath = ""; try { // 加载库 hcnetsdk = (hcnetsdk) native.loadlibrary("hcnetsdk", hcnetsdk.class); } catch (exception ex) { logger.info("loadlibrary: " + strdllpath + " error: " + ex.getmessage()); return false; } }
现在就可以加载到了,分析原因可能是native.loadlibrary()这个方法会到项目指定的lib目录下去找对应的dll,如果项目没有指定lib目录,那就需要指定绝对路径,如果指定了,那么就只需要名称就可以了。可能我的理解有问题,大家有更好的理解欢迎指出。
老旧版本sdk不兼容的问题
如果你使用的是新下载的sdk,那么下边这个问题应该不会遇到
在demo给出的hcnetsdk.java中,可以看到所有的类都继承了一个structure,这是因为java中没有结构体,而我们前面提到他这个sdk是由c++翻译来的,所以他自定义了一个结构体,但是有一个问题,老版本的sdk中,这个structure里面有一个getfiledname()方法需要自己实现,我们得自定义一个basestructure,写好这个方法之后再把sdk中所有集成structure的地方换成我们这个自定义的basestructure,代码如下
public class basestructure extends structure { @override protected list<string> getfieldorder() { return getfiledname(this); } public static list<string> getfiledname(object o) { field[] fields = o.getclass().getdeclaredfields(); string[] fieldnames = new string[fields.length]; for (int i = 0; i < fields.length; i++) { fieldnames[i] = fields[i].getname(); } return arrays.aslist(fieldnames); } }
但是在新版的sdk中,这个方法已经不需要我们重写了。
我的项目中之前使用过老版的sdk编写过一些老模块,所以我碰到了上边这个问题。另外就是我在更换老版sdk时发现,在老版sdk中很多方法使用了nativelong这个类型作为返回值或者变量类型,而在新版sdk中,这些都变成了int,所以如果你使用的sdk有新老版本冲突的问题,可以尝试把nativelong类型都换成int。
关键实现流程
创建sdk实例;
if (hcnetsdk == null) { if (!createsdkinstance()) { system.out.println("load sdk fail"); return; } }
初始化并加载日志
/**初始化*/ hcnetsdk.net_dvr_init(); /**加载日志*/ hcnetsdk.net_dvr_setlogtofile(3, "./sdklog1", false);
编写并设置回调函数(回调函数就是处理车牌信息的函数,comm_its_plate_result这个类型的lcommand就是我们需要的车牌信息的情况,可以在里面编写自己的逻辑),车牌照片有不同的类型,可以根据需要自己保存照片,文档里面有写不同编号对应不同场景图
public class fmsgcallback implements hcnetsdk.fmsgcallback_v31 { //报警信息回调函数 public boolean invoke(int lcommand, hcnetsdk.net_dvr_alarmer palarmer, pointer palarminfo, int dwbuflen, pointer puser) { logger.info("报警事件类型: lcommand:" + integer.tohexstring(lcommand)); string monitoringsiteid; switch (lcommand) { case 0x3058: logger.info("报警事件类型: 0x3058 车辆黑白名单数据需要同步报警上传"); break; case hcnetsdk.comm_upload_plate_result: logger.info("报警事件类型: comm_upload_plate_result"); break; case hcnetsdk.comm_its_plate_result: // 交通抓拍结果(新报警信息) hcnetsdk.net_its_plate_result stritsplateresult = new hcnetsdk.net_its_plate_result(); //可以在这里处理自己的逻辑 break; default: logger.info("报警类型:" + integer.tohexstring(lcommand)); break; } return true; } }
if (fmsfcallback_v31 == null) { fmsfcallback_v31 = new fmsgcallback_v31(); pointer puser = null; if (!hcnetsdk.net_dvr_setdvrmessagecallback_v31(fmsfcallback_v31, puser)) { system.out.println("设置回调函数失败!"); return; } else { system.out.println("设置回调函数成功!"); } }
设置结果分离参数
/** 设备上传的报警信息是comm_vca_alarm(0x4993)类型, 在sdk初始化之后增加调用net_dvr_setsdklocalcfg(enumtype为net_dvr_local_cfg_type_general)设置通用参数net_dvr_local_general_cfg的byalarmjsonpictureseparate为1, 将json数据和图片数据分离上传,这样设置之后,报警布防回调函数里面接收到的报警信息类型为comm_isapi_alarm(0x6009), 报警信息结构体为net_dvr_alarm_isapi_info(与设备无关,sdk封装的数据结构),更便于解析。*/ hcnetsdk.net_dvr_local_general_cfg strunet_dvr_local_general_cfg = new hcnetsdk.net_dvr_local_general_cfg(); strunet_dvr_local_general_cfg.byalarmjsonpictureseparate = 1; //设置json透传报警数据和图片分离 strunet_dvr_local_general_cfg.write(); pointer pstrnet_dvr_local_general_cfg = strunet_dvr_local_general_cfg.getpointer(); hcnetsdk.net_dvr_setsdklocalcfg(17, pstrnet_dvr_local_general_cfg);
接着登录设备并布防即可
luserid=alarm.logindevice( "10.9.137.17", (short) 8000, "admin", "hik12345"); //登录设备 lalarmhandle=alarm.setalarmchan(luserid);//报警布防,和报警监听二选一即可
有一点需要注意,他这个车牌识别无法直接识别静止的图片,比如说我们当时采用放个平板(平板上放个车牌照片)的方式测试,摄像头就没有抓拍,移动了好几次才识别出来,大家测试的时候可以注意。虽然它此时没有抓拍车牌,但还是有一些报警信息,
报警事件类型: 0x3058 车辆黑白名单数据需要同步报警上传
这个信息隔一段时间就会出现,问了客服说这个信息不用解析,大家也可以将这个信息作为自己摄像头是否正常布防的调试信息,如果有这个类型的报警说明已经布防成功了。
总结
到此这篇关于如何通过海康威视设备网络sdk进行java二次开发摄像头车牌识别的文章就介绍到这了,更多相关海康威视设备网络sdk摄像头车牌识别内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论