Java调用海康SDK读取图像
前言
视频识别的一个项目,前期采用Java调用OpenCV读取的方案,然后发送到Redis服务器,由于OpenCV采图像不稳定,需要研究使用海康SDK读取图像。由于本人海康SDK的demo已成功运行,这里直接删除了demo里不需要的东西,demo正常运行也会附到本文。
海康windows64SDK下载
opencv-4-4-0下载
代码示例下载
1、加载动态链接库dll
下载海康SDK,并放到指定文件夹
HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("D:\\Java\\HCNetSDK\\库文件\\HCNetSDK.dll", HCNetSDK.class); PlayCtrl INSTANCE = (PlayCtrl) Native.loadLibrary("D:\\Java\\HCNetSDK\\库文件\\PlayCtrl.dll", PlayCtrl.class);
这里采用了直接加载dll的方式,没有用eclipse加载lib文件的方式,HCNetSDK用于登录、预览,PlayCtrl用于播放,OpenCV 4.4.0
2、调用逻辑
先调用NET_DVR_Init
方法进行初始化,调用NET_DVR_Login_V30
进行登录,调用NET_DVR_RealPlay_V30
进行预览
重点来了
lPreviewHandle = hCNetSDK.NET_DVR_RealPlay_V30(lUserID, m_strClientInfo, fRealDataCallBack, null, true);
这里需要fRealDataCallBack
预览回调,是一个参数,需要自定义一个类去实现一个接口,
case HCNetSDK.NET_DVR_SYSHEAD
是预览时第一次会进入系统头这个逻辑,相当于配置一下,这里是配置了PlayM4_SetDecCallBack
SDK解码回调,这里需要一个fDecCallBack
SDK解码回调函数,在下面附上
case HCNetSDK.NET_DVR_STREAMDATA:
是上面配置完,下一次就会进入这个case语句,这个逻辑是放入码流数据
class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 { public void invoke(NativeLong lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize, Pointer pUser) { switch (dwDataType) { case HCNetSDK.NET_DVR_SYSHEAD: //系统头 System.out.println(logHead + "FRealDataCallBack 系统头"); if (!playControl.PlayM4_GetPort(m_lPort)) //获取播放库未使用的通道号 { break; } if (dwBufSize > 0) { if (!playControl.PlayM4_SetDecCallBack(m_lPort.getValue(), fDecCallBack)) //设置解码回调 { break; } // 设置解码回调函数 解码且显示 // if (!playControl.PlayM4_SetDecCallBackEx(m_lPort.getValue(), fDecCallBack, null, null)) { // break; // } if (!playControl.PlayM4_SetStreamOpenMode(m_lPort.getValue(), PlayCtrl.STREAME_REALTIME)) //设置实时流播放模式 { break; } if (!playControl.PlayM4_OpenStream(m_lPort.getValue(), pBuffer, dwBufSize, 1024 * 1024)) //打开流接口 { break; } if (!playControl.PlayM4_Play(m_lPort.getValue(), null)) //播放开始 { break; } } case HCNetSDK.NET_DVR_STREAMDATA: //码流数据 if (!playControl.PlayM4_InputData(m_lPort.getValue(), pBuffer, dwBufSize)) { break; } } } }
fDecCallBack
SDK解码回调函数如下,解码回调函数里面的数据,是sdk软解码以后的数据,可以直接拿来处理,不过里面的代码需要在40ms里处理完毕,不然会产生很大的延迟
class FDecCallBack implements PlayCtrl.DecCallBack { @Override public void invoke(NativeLong nPort, ByteByReference pBuffer, NativeLong nSize, PlayCtrl.FRAME_INFO frameInfo, NativeLong nReserved1, NativeLong nReserved2) { if (++count % 6 == 0) { try { handle(pBuffer, nSize.intValue(), frameInfo); } catch (Exception e) { e.printStackTrace(); } } } //这样在回调函数DecCallBack 中可以得到视音频数据,其中视频数据是YV12格式的,音频数据是PCM格式的。 public void handle(ByteByReference pBuffer, int dwBufSize, PlayCtrl.FRAME_INFO frameInfo) { src = null; dst = null; bufferedImage = null; boolean isLog = new Date().getSeconds() % 10 == 0; String printStr = null; if (isLog) { stopWatch.reset(); stopWatch.start(); } int width = frameInfo.nWidth.intValue(); int height = frameInfo.nHeight.intValue(); byte[] byteArray = pBuffer.getPointer().getByteArray(0, dwBufSize); src = new Mat(height + height / 2, width, CvType.CV_8UC1); src.put(0, 0, byteArray); if (isLog) { stopWatch.split(); printStr = ""; printStr += logHead + "put " + stopWatch.getSplitTime() + "\r\n"; } dst = new Mat(height, width, CvType.CV_8UC3); Imgproc.cvtColor(src, dst, Imgproc.COLOR_YUV2BGR_YV12); if (isLog) { stopWatch.split(); printStr += logHead + "cvtColor " + stopWatch.getSplitTime() + "\r\n"; } bufferedImage = Mat2BufImg.Mat2BufImg(dst); if (isLog) { stopWatch.split(); printStr += logHead + "Mat2BufImg " + stopWatch.getSplitTime() + "\r\n"; } String imgHex = Mat2BufImg.bufImgToBase64(bufferedImage, "jpg"); if (isLog) { stopWatch.split(); printStr += logHead + "bufImgToBase64 " + stopWatch.getSplitTime() + "\r\n"; } Jedis jedis = JedisUtil.getJedis(); jedis.set(ip, imgHex); JedisUtil.close(jedis); if (isLog) { stopWatch.split(); printStr += logHead + "set " + stopWatch.getSplitTime() + "\r\n"; System.out.println(printStr); } } }
3、结尾
大体流程就是这样子,要不我画一张流程图哇,哈哈
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了