目录
背景:
1、问题
2、截图程序自动化和多通道实现
3、摄像头参数灵活化
至此已经全部结束,运行程序可以看到
背景:
目标识别需要对每一帧图像进行检测,因此我们需要将摄像头获取的视频流转换成单帧图像,海康威视官网提供的原本的sdk包实现了点击抓图按钮就截一张图,并且不支持多摄像头截图。海康威视的sdk下载链接为SDK下载
Java版本实现单通道结果如图,在红框内输入摄像头的数据--单机注册--预览就可以看到右边摄像头实时画面了。
1、问题
对于我们的目标检测需求来说,需要解决以下几个问题:
- 截图程序自动化,例如每一秒截一张图,而不需要手动点击抓图
- 截图程序能够实现多通道截图,而不是只能使用单通道
- 摄像头配置灵活化,应该利用数据库存储每个摄像头参数,然后程序读取数据库的摄像头数据。
2、截图程序自动化和多通道实现
解决自动化问题和多通道问题我的主要思路为通过多线程(Threads)来完成,利用线程的sleep(XXX)方法,根据设定的xxx来实现自动截图。下面解释一下我的代码
(1)实现runnable接口,重写run方法
public class MultiThreaded implements Runnable {
private MonitorCameraInfo cameraInfo;
//创建构造方法,在new的时候外界传递摄像头参数对象cameraInfo。
public MultiThreaded(MonitorCameraInfo cameraInfo){
this.cameraInfo=cameraInfo;
}
//默认的构造参数
public MultiThreaded() {
}
//重写接口的run方法;这部分后期摄像头多的情况下需要改成线程池的方法
@Override
public void run() {
// TODO Auto-generated method stub
MultiThreaded mt = new MultiThreaded();
//while目的是实现自动截图,配合sleep函数
while(true){
//调用摄像头注册验证功能模块
mt.getDVRConfig(cameraInfo);
try {
//主要作用是截图并存储
mt.getDVRPic(cameraInfo);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(2)注册和验证摄像头 以及 抓图功能
/**
* 注册摄像头以及验证
* @param cameraInfo
*/
public void getDVRConfig(MonitorCameraInfo cameraInfo) {
HCNetSDK sdk = HCNetSDK.INSTANCE;
//判断摄像头是否开启
if (!sdk.NET_DVR_Init()) {
System.out.println("SDK初始化失败");
return;
}
NET_DVR_DEVICEINFO_V30 devinfo = new NET_DVR_DEVICEINFO_V30();// 设备信息
//登录信息
NativeLong id = sdk.NET_DVR_Login_V30(cameraInfo.getCameraIp(), (short) cameraInfo.getCameraPort(),
cameraInfo.getUserName(), cameraInfo.getUserPwd(), devinfo);
cameraInfo.setUserId(id);// 返回一个用户编号,同时将设备信息写入devinfo
if (cameraInfo.getUserId().intValue() < 0) {
System.out.println("设备注册失败");
return;
}
//DVR工作状态
NET_DVR_WORKSTATE_V30 devwork = new NET_DVR_WORKSTATE_V30();
if (!sdk.NET_DVR_GetDVRWorkState_V30(cameraInfo.getUserId(), devwork)) {
// 返回Boolean值,判断是否获取设备能力
System.out.println("返回设备状态失败");
}
IntByReference ibrBytesReturned = new IntByReference(0);// 获取IP接入配置参数
NET_DVR_IPPARACFG ipcfg = new NET_DVR_IPPARACFG();//IP接入配置结构
ipcfg.write();
Pointer lpIpParaConfig = ipcfg.getPointer();
//获取相关参数配置
sdk.NET_DVR_GetDVRConfig(cameraInfo.getUserId(), HCNetSDK.NET_DVR_GET_IPPARACFG, new NativeLong(0),
lpIpParaConfig, ipcfg.size(), ibrBytesReturned);
ipcfg.read();
System.out.print("IP地址:" + cameraInfo.getCameraIp());
System.out.println("|设备状态:" + devwork.dwDeviceStatic);// 0正常,1CPU占用率过高,2硬件错误,3未知
//System.out.println("ChanNum"+devinfo.byChanNum);
// 显示模拟通道
for (int i = 0; i < devinfo.byChanNum; i++) {
System.out.print("Camera" + i + 1);// 模拟通道号名称
System.out.print("|是否录像:" + devwork.struChanStatic[i].byRecordStatic);// 0不录像,不录像
System.out.print("|信号状态:" + devwork.struChanStatic[i].bySignalStatic);// 0正常,1信号丢失
System.out.println("|硬件状态:" + devwork.struChanStatic[i].byHardwareStatic);// 0正常,1异常
}
//注销用户
sdk.NET_DVR_Logout(cameraInfo.getUserId());//释放SDK资源
sdk.NET_DVR_Cleanup();
}
/**
* 抓拍图片
* @param cameraInfo
* @throws IOException
*/
public void getDVRPic(MonitorCameraInfo cameraInfo) throws IOException {
//设置通道号,其中1正常,-1不正常
NativeLong chanLong = new NativeLong(1);
cameraInfo.setChannel(chanLong);
//System.out.println("Channel:"+chanLong);
long startTime = System.currentTimeMillis();
HCNetSDK sdk = HCNetSDK.INSTANCE;
if (!sdk.NET_DVR_Init()) {
System.out.println("SDK初始化失败");
return;
}
NET_DVR_DEVICEINFO_V30 devinfo = new NET_DVR_DEVICEINFO_V30();// 设备信息
//注册设备
NativeLong id = sdk.NET_DVR_Login_V30(cameraInfo.getCameraIp(), (short) cameraInfo.getCameraPort(),
cameraInfo.getUserName(), cameraInfo.getUserPwd(), devinfo);
cameraInfo.setUserId(id);// 返回一个用户编号,同时将设备信息写入devinfo
if (cameraInfo.getUserId().intValue() < 0) {
System.out.println("设备注册失败"+sdk.NET_DVR_GetLastError());
return;
} else {
System.out.println("id:" + cameraInfo.getUserId().intValue());
}
NET_DVR_WORKSTATE_V30 devwork = new NET_DVR_WORKSTATE_V30();
if (!sdk.NET_DVR_GetDVRWorkState_V30(cameraInfo.getUserId(), devwork)) {
// 返回Boolean值,判断是否获取设备能力
System.out.println("返回设备状态失败");
}
//System.out.println("设备注册耗时:[" + (System.currentTimeMillis() - startTime) + "]");
startTime = System.currentTimeMillis();
//图片质量
NET_DVR_JPEGPARA jpeg = new NET_DVR_JPEGPARA();
// 设置图片的分辨率
jpeg.wPicSize = 2;
// 设置图片质量
jpeg.wPicQuality = 2;
IntByReference a = new IntByReference();
//设置图片大小
ByteBuffer jpegBuffer = ByteBuffer.allocate(1024 * 1024);
// 创建文件目录和文件
SimpleDateFormat sd = new SimpleDateFormat("yyyyMMdd");
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
Date date = new Date();
//按照每天设置一个目录
String fileName = cameraInfo.getPath() +sd.format(date) ;
File files = new File(fileName);
if(!files.exists()){
files.mkdirs();
}
//按照日期文件夹放入图片
String fileNameString = fileName+"/"+ cameraInfo.getId()+sdf.format(date)+".jpg";
System.out.println(fileNameString);
File file = new File(fileNameString);
// 抓图到内存,单帧数据捕获并保存成JPEG存放在指定的内存空间中
//需要加入通道
boolean is = sdk.NET_DVR_CaptureJPEGPicture_NEW(cameraInfo.getUserId(), cameraInfo.getChannel(), jpeg,
jpegBuffer, 1024 * 1024, a);
//System.out.println("Channel:"+cameraInfo.getChannel());
System.out.println("抓图到内存耗时:[" + (System.currentTimeMillis() - startTime) + "ms]");
// 抓图到文件
//boolean is = sdk.NET_DVR_CaptureJPEGPicture(cameraInfo.UserId,cameraInfo.Channel,jpeg, fileNameString);
if (is) {
System.out.println("抓取成功,返回长度:" + a.getValue());
} else {
System.out.println("抓取失败:"+sdk.NET_DVR_GetLastError());
}
//startTime = System.currentTimeMillis();
// 存储本地,写入内容
BufferedOutputStream outputStream = null;
try {
outputStream = new BufferedOutputStream(new FileOutputStream(file));
outputStream.write(jpegBuffer.array(), 0, a.getValue());
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("存储本地耗时:[" + (System.currentTimeMillis() - startTime) + "ms]");
sdk.NET_DVR_Logout(cameraInfo.getUserId());
sdk.NET_DVR_Cleanup();
}
}
(3)多线程通道开启,首先查询数据库获取摄像头总个数,然后根据摄像头个数创建对应的线程数,第二 遍历摄像头集合,创建对应的MultiThread对象,将摄像头作为构造参数赋值;第三开启线程,并配合sleep方法。
3、摄像头参数灵活化
(1)创建数据库,将数据存储到数据库中
(2)Java使用Mybaties作为数据库连接
具体配置等信息可以参考我的博客SSM相关配置;下面大体写一下实现过程
- 创建pojo对象(get set方法篇幅所限省略)
- 数据库连接数据 db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/object_detect?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
- 创建SqlMapConfig.xml文件 功能主要为连接数据库和mapper扫描
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"></properties>
<typeAliases>
<!-- 定义单个pojo类别名
type:类的全路劲名称
alias:别名
-->
<!-- <typeAlias type="cn.chuantao.pojo.User" alias="user"/> -->
<!-- 使用包扫描的方式批量定义别名
定以后别名等于类名,不区分大小写,但是建议按照java命名规则来,首字母小写,以后每个单词的首字母大写
-->
<package name="pojo"/>
</typeAliases>
<!-- 和spring整合后 environments配置将废除-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理-->
<transactionManager type="JDBC" />
<!-- 数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<!-- <mapper resource="mapper/FindCamera.xml"/> -->
<!--
使用class属性引入接口的全路径名称:
使用规则:
1. 接口的名称和映射文件名称除扩展名外要完全相同
2. 接口和映射文件要放在同一个目录下
-->
<!-- <mapper class="cn.chuantao.mapper.UserMapper"/> -->
<!-- 使用包扫描的方式批量引入Mapper接口
使用规则:
1. 接口的名称和映射文件名称除扩展名外要完全相同
2. 接口和映射文件要放在同一个目录下
-->
<package name="mapper"/>
</mappers>
</configuration>
- 创建mapper信息:xml和对应的接口映射
(3)程序:查询数据库得到摄像头配置信息