目前对海康摄像头的使用以及java的对接还是很多的,最近做了两个项目,
一个是安全帽报警人脸抓拍(单个摄像头)。一个是获取车辆信息并抓拍车牌图片(30个摄像头)。
写一篇文章记录一下开发过程,也可供大家学习借鉴,不懂也可留言。
海康sdk官网下载 —按照提示下载解压。
将sdk整合到springboot项目中,以下是以4个摄像头抓拍为例子写的。
我的结构目录
因为要同时启动多个摄像头进行抓拍车辆,所以使用了多线程,具体效果是实现了,但是可能还有优化的余地。
如果有懂多线程的大佬还请指导一二。
这里再导入jar包的时候需要再pom文件中配置,不然可能启动会检测不到
com
jna
1.0.0
system
${project.basedir}/src/main/resources/lib/jna.jar
com
examples
1.0.0
system
${project.basedir}/src/main/resources/lib/examples.jar
package com.danger.utils;
/**
* @Author: SONGtiank
* @Description: 摄像头信息
* @Date: 2020/11/3 9:34
* @Version: 1.0
*/
public class CameraInfo {
public static final String[][] cameraInfo =new String[31][];
//在这只展示几个,数组用于存放摄像头信息:ip username password port
//因为这里所有的摄像头用户名密码一致,所以我只存了ip和端口
//如果不一致的话可以将用户名密码存入
static {
cameraInfo[0] = new String[]{
"172.172.2.9","8000"};
cameraInfo[1] = new String[]{
"172.172.2.10","8000"};
cameraInfo[2] = new String[]{
"172.172.2.11","8000"};
cameraInfo[3] = new String[]{
"172.172.2.12","8000"};
....
}
}
package com.danger.vehicle.controller;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* @Description: 配置类实现AsyncConfigurer接口,并重写getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor,
* 这样我们就获得一个基于线程池TaskExecutor
* @author SONGtiank
* @Date: 2020/11/2 16:55
* @Version: 1.0
*/
@Configuration
@ComponentScan("com.danger.vehicle")
@EnableAsync//利用@EnableAsync注解开启异步任务支持
public class CustomMultiThreadingConfig implements AsyncConfigurer{
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数15:线程池创建时候初始化的线程数
executor.setCorePoolSize(15);
//最大线程数31:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(31);
//缓冲队列500:用来缓冲执行任务的队列
executor.setQueueCapacity(500);
//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(60);
//线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("DailyAsync-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
}
}
package com.danger.hktv.basics;
import com.danger.hktv.ClientDemo.HCNetSDK;
import com.danger.utils.*;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.ByteBuffer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @Author: SONGtiank
* @Description: 海康sdk服务类
* @Date: 2020/11/3 9:50
* @Version: 1.0
*/
public class HikVisionService {
private static Logger logger = LoggerFactory.getLogger(HikVisionService.class);
static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE;
static String m_sUsername = "admin";//设备用户名
static String m_sPassword = "*******";//设备密码
static short m_sPort = 8000;//端口号,这是默认的
public NativeLong lUserID;//用户句柄
public NativeLong lAlarmHandle;//报警布防句柄
public int lListenHandle;//报警监听句柄
public NativeLong RemoteConfig;
public static int code = 5;
//撤防
public void CloseAlarmChan() {
//报警撤防
if (lAlarmHandle.intValue() > -1) {
if (hCNetSDK.NET_DVR_CloseAlarmChan_V30(lAlarmHandle)) {
System.out.println("撤防成功");
lAlarmHandle = new NativeLong(-1);
}
}
}
public void initMemberFlowUpload(String m_sDeviceIP, int remainMinuteTime) throws InterruptedException {
// 初始化
Boolean flag = hCNetSDK.NET_DVR_Init();
if (flag){
System.out.println("初始化成功");
}else{
System.out.println("初始化失败");
}
//设置连接时间与重连时间
hCNetSDK.NET_DVR_SetConnectTime(2000, 1);
hCNetSDK.NET_DVR_SetReconnect(100000, true);
//设备信息, 输出参数
HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();
HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();
// 注册设备-登录参数,包括设备地址、登录用户、密码等
m_strLoginInfo.sDeviceAddress = new byte[hCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
System.arraycopy(m_sDeviceIP.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, m_sDeviceIP.length());
m_strLoginInfo.sUserName = new byte[hCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
System.arraycopy(m_sUsername.getBytes(), 0, m_strLoginInfo.sUserName, 0, m_sUsername.length());
m_strLoginInfo.sPassword = new byte[hCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
System.arraycopy(m_sPassword.getBytes(), 0, m_strLoginInfo.sPassword, 0, m_sPassword.length());
m_strLoginInfo.wPort = m_sPort;
m_strLoginInfo.bUseAsynLogin = false; //是否异步登录:0- 否,1- 是
m_strLoginInfo.write();
//设备信息, 输出参数
int lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo,m_strDeviceInfo);
if(lUserID< 0){
System.out.println("hCNetSDK.NET_DVR_Login_V30()"+"\n" +hCNetSDK.NET_DVR_GetErrorMsg(null));
hCNetSDK.NET_DVR_Cleanup();
return;
}
//设置报警回调函数
if (!hCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(this::MsesGCallBack,null )){
System.out.println("设置回调函数失败"+hCNetSDK.NET_DVR_GetLastError());
return;
}else {
System.out.println("设置回调函数成功");
}
//启用布防
HCNetSDK.NET_DVR_SETUPALARM_PARAM lpSetupParam = new HCNetSDK.NET_DVR_SETUPALARM_PARAM();
lpSetupParam.dwSize = 0;
lpSetupParam.byLevel = 1;//布防优先级:0- 一等级(高),1- 二等级(中)
lpSetupParam.byAlarmInfoType = 1;//上传报警信息类型: 0- 老报警信息(NET_DVR_PLATE_RESULT), 1- 新报警信息(NET_ITS_PLATE_RESULT)
int lAlarmHandle = hCNetSDK.NET_DVR_SetupAlarmChan_V41(lUserID,lpSetupParam);
if (lAlarmHandle< 0)
{
System.out.println("NET_DVR_SetupAlarmChan_V41 error, %d\n"+hCNetSDK.NET_DVR_GetLastError());
hCNetSDK.NET_DVR_Logout(lUserID);
hCNetSDK.NET_DVR_Cleanup();
return;
}
System.out.println("布防成功,开始监测车辆");
//启动监听----------------------------------------------
int iListenPort = 8000;
String m_sListenIP = "127.0.0.1";
lListenHandle = hCNetSDK.NET_DVR_StartListen_V30(m_sListenIP, (short) iListenPort, this::MsesGCallBack, null);
if(lListenHandle < 0) {
// JOptionPane.showMessageDialog(null, "启动监听失败,错误号:" + hCNetSDK.NET_DVR_GetLastError());
}
else {
System.out.println("启动监听成功");
}
}
public boolean MsesGCallBack(int lCommand, HCNetSDK.NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {
System.out.println("〈--进入回调,开始识别车牌--〉");
try {
String sAlarmType = new String();
String[] newRow = new String[3];
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");
String imgName =sf.format(new Date())+".jpg";
String clImgName ="cl"+sf.format(new Date())+".jpg";
String[] sIP = new String[2];
switch (lCommand) {
case 0x2800: //交通抓拍结果上传
HCNetSDK.NET_DVR_PLATE_RESULT strPlateResult = new HCNetSDK.NET_DVR_PLATE_RESULT();
strPlateResult.write();
Pointer pPlateInfo = strPlateResult.getPointer();
pPlateInfo.write(0, pAlarmInfo.getByteArray(0, strPlateResult.size()), 0, strPlateResult.size());
strPlateResult.read();
try {
String srt3=new String(strPlateResult.struPlateInfo.sLicense,"GBK");
sAlarmType = sAlarmType + ":抓拍上传,车牌:"+ srt3;
}
catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
newRow[0] = dateFormat.format(new Date());
//报警类型
newRow[1] = sAlarmType;
//报警设备IP地址
sIP = new String(pAlarmer.sDeviceIP).split("\0", 2);
newRow[2] = sIP[0];
break;
case 0x3050: //交通抓拍的终端图片上传
HCNetSDK.NET_ITS_PLATE_RESULT strItsPlateResult = new HCNetSDK.NET_ITS_PLATE_RESULT();
strItsPlateResult.write();
Pointer pItsPlateInfo = strItsPlateResult.getPointer();
pItsPlateInfo.write(0, pAlarmInfo.getByteArray(0, strItsPlateResult.size()), 0, strItsPlateResult.size());
strItsPlateResult.read();
try {
byte byDangerousVehicles = strItsPlateResult.byDangerousVehicles;
String srt3=new String(strItsPlateResult.struPlateInfo.sLicense,"GBK");
sAlarmType ="是否危化品:"+byDangerousVehicles+"-->"+ sAlarmType + ",车辆类型:"+ CarType.getCarType(strItsPlateResult.byVehicleType+"".trim()) + ",交通抓拍上传,车牌:"+ srt3;
Map<String,String> paramMap = new HashMap<String,String>();
paramMap.put("type", CarType.getCarType(strItsPlateResult.byVehicleType+"".trim()));车辆类型
String filename = "D:\\imgUpload\\"+new String(pAlarmer.sDeviceIP).trim()+"\\";
String carFileName = "D:\\carImg\\"+new String(pAlarmer.sDeviceIP).trim()+"\\";
if("黄".equals(srt3.substring(0, 1).trim())){
paramMap.put("plateNumber", srt3.substring(1, srt3.length()).trim());//车牌号
paramMap.put("byCountry",srt3.substring(1, 2).trim());//省份
paramMap.put("byColor",srt3.substring(0, 1).trim());//车牌颜色
paramMap.put("cameraIp",new String(pAlarmer.sDeviceIP).trim());//ip地址
paramMap.put("picTime",dateFormat.format(new Date()));//当前时间
paramMap.put("wSpeed",String.valueOf(new Random().nextInt(55-5)+5));//速度 这是假的
paramMap.put("byIllegalType", BreakRulesType.getBreakRulesType(strItsPlateResult.wIllegalType));
for(int i=0;i<strItsPlateResult.dwPicNum;i++) {
if(strItsPlateResult.struPicInfo[i].dwDataLen>0) {
FileOutputStream fout;
if(strItsPlateResult.struPicInfo[i].byType==0){
//车牌图片
File file = new File(filename+imgName);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
fout = new FileOutputStream(filename+imgName);
logger.info("文件路径"+filename+imgName);
//将字节写入文件
long offset = 0;
ByteBuffer buffers = strItsPlateResult.struPicInfo[i].pBuffer.getByteBuffer(offset, strItsPlateResult.struPicInfo[i].dwDataLen);
byte [] bytes = new byte[strItsPlateResult.struPicInfo[i].dwDataLen];
buffers.rewind();
buffers.get(bytes);
fout.write(bytes);
fout.close();
}
if(strItsPlateResult.struPicInfo[i].byType==1){
//车辆场景图片
File file = new File(carFileName+clImgName);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
fout = new FileOutputStream(carFileName+clImgName);
logger.info("文件路径"+carFileName+clImgName);
//将字节写入文件
long offset = 0;
ByteBuffer buffers = strItsPlateResult.struPicInfo[i].pBuffer.getByteBuffer(offset, strItsPlateResult.struPicInfo[i].dwDataLen);
byte [] bytes = new byte[strItsPlateResult.struPicInfo[i].dwDataLen];
buffers.rewind();
buffers.get(bytes);
fout.write(bytes);
fout.close();
}
}
}
}
}
catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
logger.info("识别信息:---》" +sAlarmType +" ip:"+new String(pAlarmer.sDeviceIP).trim()+"时间:"+dateFormat.format(new Date()));
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
}
package com.danger.vehicle.service;
import com.danger.hktv.basics.HikVisionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* @Description: 创建线程任务服务
* @Author SONGtiank
* @Date: 2020/11/3 16:55
* @Version: 1.0
*/
@Service
public class CustomMultiThreadingService {
private Logger logger = LoggerFactory.getLogger(CustomMultiThreadingService.class);
/**
* @Description:通过@Async注解表明该方法是一个异步方法,
* 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
* @Author SONGtiank
* @Date: 2020/11/2 16:55
* @Version: 1.0
* @Throws
* @param m_ip
*/
@Async
public void executeAysncTask1(String m_ip) throws InterruptedException{
HikVisionService hikVisionService = new HikVisionService();
hikVisionService.initMemberFlowUpload(m_ip, 100);
}
@Async
public void executeAsyncTask2(String m_ip)throws InterruptedException{
HikVisionService hikVisionService = new HikVisionService();
hikVisionService.initMemberFlowUpload(m_ip, 100);
}
@Async
public void executeAsyncTask3(String m_ip)throws InterruptedException{
HikVisionService hikVisionService = new HikVisionService();
hikVisionService.initMemberFlowUpload(m_ip, 100);
}
@Async
public void executeAsyncTask4(String m_ip)throws InterruptedException{
HikVisionService hikVisionService = new HikVisionService();
hikVisionService.initMemberFlowUpload(m_ip, 100);
}
}
package com.danger.vehicle.controller;
import com.danger.utils.CameraInfo;
import com.danger.vehicle.service.CustomMultiThreadingService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Timer;
import java.util.TimerTask;
/**
* @description 摄像头控制类
* @author SONGtiank
* @Date: 2020/11/4 9:11
* @Version: 1.0
*/
@Controller
public class CameraController {
private org.slf4j.Logger logger = LoggerFactory.getLogger(CameraController.class);
@Autowired
private CustomMultiThreadingService customMultiThreadingService;
@RequestMapping("/healthy")
@ResponseBody
public String healthy() throws InterruptedException {
return "启动成功";
}
@ResponseBody
@RequestMapping(value="/dotask")
public String doTask() throws InterruptedException{
Timer timer = new Timer();// 实例化Timer类
timer.schedule(new TimerTask() {
public void run() {
//撤销布防上传通道
try {
customMultiThreadingService.executeAysncTask1(CameraInfo.cameraInfo[0][0]);
customMultiThreadingService.executeAsyncTask2(CameraInfo.cameraInfo[1][0]);
customMultiThreadingService.executeAsyncTask3(CameraInfo.cameraInfo[2][0]);
customMultiThreadingService.executeAsyncTask4(CameraInfo.cameraInfo[3][0]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 1000*60*5,1000*60*5);// 这里毫秒
customMultiThreadingService.executeAysncTask1(CameraInfo.cameraInfo[0][0]);
customMultiThreadingService.executeAsyncTask2(CameraInfo.cameraInfo[1][0]);
customMultiThreadingService.executeAsyncTask3(CameraInfo.cameraInfo[2][0]);
customMultiThreadingService.executeAsyncTask4(CameraInfo.cameraInfo[3][0]);
return "success";
}
}
package com.danger.utils;
/**
* @Author: SONGtiank
* @Description: 违章类型
* @Date: 2020/11/5 8:50
* @Version: 1.0
*/
public class BreakRulesType {
public static String getBreakRulesType( short rulesType ) {
if(1349 <= rulesType && rulesType <=1728 && rulesType !=1357){
return "超速行驶";
}else
if(1018<= rulesType && rulesType <=1625){
switch (rulesType){
case 1018:
return "占用非机动车道";
case 1019:
return "占用专用车道";
case 1039:
return "城市违停";
case 1208:
return "不按导向车道行驶";
case 1211:
return "路口停车";
case 1228:
return "路口滞留";
case 1240:
return "未系安全带";
case 1301:
return "逆行";
case 1344:
return "违反禁令标志";
case 1357:
return "未礼让行人";
case 1625:
return "违反信号灯";
}
}else {
switch (rulesType){
case 4613:
return "高速路违停";
case 4615:
return "不按规定行驶";
case 13451:
return "压线";
case 13453:
return "违法变道";
case 16251:
return "绿灯停车";
case 4016:
return "低速行驶";
case 4306:
return "低速行驶";
case 6046:
return "超速行驶";
case 6047:
return "超速行驶";
case 6048:
return "超速行驶";
case 6050:
return "超速行驶";
}
}
return "未检测到违章";
}
}
package com.danger.utils;
/**
* @Description:车辆类型
* @Date: 2020/11/4 15:29
* @Version: 1.0
*/
public class CarType {
public static String getCarType( String carType ) {
switch (carType) {
case "0":
return "未知";
case "1":
return "客车";
case "2":
return "货车";
case "3":
return "轿车";
case "4":
return "面包车";
case "5":
return "小货车";
case "6":
return "行人";
case "7":
return "二轮车";
case "8":
return "三轮车";
case "9":
return "SUV";
case "10":
return "中型课车";
case "11":
return "机动车";
case "12":
return "非机动车";
case "13":
return "小型轿车";
case "14":
return "微型轿车";
case "15":
return "皮卡车";
case "16":
return "集装箱卡车";
case "17":
return "微卡";
case "18":
return "渣土车";
case "19":
return "吊车,工程车";
case "20":
return "油罐车";
case "21":
return "混凝土搅拌车";
case "22":
return "平板拖车";
case "23":
return "两厢轿车";
case "24":
return "三厢轿车";
case "25":
return "轿跑";
case "26":
return "小型客车";
default:
return null;
}
}
}