海康威视摄像头⼈脸识别开发流程及踩过的坑
海康威视摄像头⼈脸识别开发流程及踩过的坑
前段时间,接⼿了公司的海康摄像头的⼆次开发的⼯作,在⽹上查了很多资料,但基本上只能解决其中⼀部分问题,还有很多问题,在不停纠缠海康技术⽀持后,才勉强搞定,在这⾥把⾃⼰的开发⼼得及踩过的坑写下来,希望对⼤家有所帮助。
我的开发环境使⽤的是 Java1.8 + Tomcat 8.5
所贴的代码仅为⼀部分代码,仅供参考,后⾯会放出完整代码,⽅便⼤家查看
PS:不得不说,海康的 SDK 真的⾮常不友好,好多⽂档中写的接⼝在我们使⽤的 HCNetSDK 中都没有声明,需要我们⾃⼰来进⾏声明,也因为这个问题,折腾了我好久。
开发流程及坑
下载海康SDK
这⼀步我就不过多介绍了,⽹上有很多说明,总之出现问题了,尝试各种解决⽅案,总会解决的,接着导⼊相关的⽂件。
初始化 SDK
在初始化 SDK 这⼀块,还好,基本上不会有什么问题,即便有问题,根据报错信息也能定位问题,这⾥贴上我的初始化代码。
public void init() {
lHandle = -1;
lListenHandle = -1;
Boolean login = this.login();
if (login) {
// 注册成功,进⾏布防
this.SetupAlarmChan();
//FaceSpot.set
}
}
/**
* ⽤户注册
*/
public Boolean login() {
// 初始化
boolean initSuc = hCNetSDK.NET_DVR_Init();
if (!initSuc) {
System.out.println("初始化失败, 错误代码:" + hCNetSDK.NET_DVR_GetLastError());
log.info("初始化失败, 错误代码:" + hCNetSDK.NET_DVR_GetLastError());
} else {
System.out.println("接⼝初始化成功");
log.info("初始化接⼝成功");
// 开启⽇志
boolean file = hCNetSDK.NET_DVR_SetLogToFile(3, "D:\\SdkLog\\", true);
System.out.println("开启⽇志:" + file);
hCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(fMSFCallBack, null);
}
ip = HCNetDeviceConUtil.ip;
NET_DVR_DEVICEINFO_V30 s30 = new HCNetSDK.NET_DVR_DEVICEINFO_V30();
Map<String, String> map = CameraInfo();
String port2 = ("port");
Short s = new Short(port2);
short myPort = s.shortValue();
userID = hCNetSDK.NET_DVR_Login_("ip"), myPort, ("username"), ("password"), s30);
// 设置回调
if (userID.longValue() == -1) {
System.out.println("注册失败,失败原因为:" + hCNetSDK.NET_DVR_GetLastError());
log.info("注册失败,失败原因为:" + hCNetSDK.NET_DVR_GetLastError());
return false;
} else {
System.out.println("注册成功");
log.info("注册成功:" + userID);
// 你也可以在这⾥做⼀些你想做的操作
return true;
}
}
报警布防
报警布防后摄像头开始正式⼯作,在这⾥有⼀个坑就是,我们的⼈脸识别的结果都是通过回调函数返回给我们的,在这⾥,我们的回调函数⼀定要设置成静态全局的,不然很容易被回收掉,我当时没注意这个问题,⼀直纳闷,为什么部署到服务器上,没过多长时间,就⽆法进⼊回调函数了,后才发现是这个问题
/**
* 报警布防
*/
public void SetupAlarmChan() {
if (fMSFCallBack == null) {
// 这⾥是以前写的,后来没有删除,但是 fMSFCallBack ⼀定要声明成全局的
// 这⾥的 fMSFCallBack 已经在全局进⾏声明了,所以不会进⼊这个判断
fMSFCallBack = new FMSGCallBackController();
FMSGCallBack_V31 fMessageCallBack = new FMSGCallBackController();
Pointer pUser = null;
if (!hCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(fMessageCallBack, pUser)) {
System.out.println("设置回调函数失败:" + hCNetSDK.NET_DVR_GetLastError());
log.info("设置回调函数失败:" + hCNetSDK.NET_DVR_GetLastError());
}
}
HCNetSDK.NET_DVR_SETUPALARM_PARAM m_strAlarmInfo = new HCNetSDK.NET_DVR_SETUPALARM_PARAM();
m_strAlarmInfo.dwSize = m_strAlarmInfo.size();
m_strAlarmInfo.byLevel = 1;
m_strAlarmInfo.byAlarmInfoType = 1;
m_strAlarmInfo.write();
// m_strAlarmInfo.byFaceAlarmDetection = 1;
// NativeLong test = new NativeLong(1);
// userID
NativeLong lHandle = hCNetSDK.NET_DVR_SetupAlarmChan_V41(userID, m_strAlarmInfo);
if (lHandle.longValue() == -1) {
System.out.println("布防失败,失败原因:" + hCNetSDK.NET_DVR_GetLastError());
log.info("布防失败,失败原因:" + hCNetSDK.NET_DVR_GetLastError());
} else {
System.out.println("布防成功");
log.info("布防成功");
}
// 要保证程序不会停⽌,想了⼀个笨办法,进⾏死循环,当然如果你有更好的办法欢迎指正
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
回调函数
回调函数不知道怎么说,对着 SDK 开发⽂档,⼀步⼀步来就好了,回调函数代码就放在后⾯的代码⾥⾯了
FMSGCallBackController.java
创建⼈脸库
以上,基本上,你的功能就完成了,当然光这样我们的摄像头是⽆法⼯作的,我们还需要创建⼈脸库,上传图⽚,⼈脸建模等⼀系列操作,这⾥我们⼀步步的来。
/**
* 创建⼈脸库
* @param FDLibName
* @return
*/
@SuppressWarnings("unchecked")
public boolean CreateFDLib(String FDLibName) {
HCNetSDK.NET_DVR_XML_CONFIG_INPUT struInput = new HCNetSDK.NET_DVR_XML_CONFIG_INPUT();
struInput.dwSize = struInput.size();
String str = "POST /ISAPI/Intelligent/FDLib\r\n";
HCNetSDK.BYTE_ARRAY ptrUrl = new HCNetSDK.BYTE_ARRAY(HCNetSDK.BYTE_ARRAY_LEN);
System.Bytes(), 0, ptrUrl.byValue, 0, str.length());
ptrUrl.write();
struInput.lpRequestUrl = Pointer();
struInput.dwRequestUrlLen = str.length();
String strInBuffer = new String("<CreateFDLibList><CreateFDLib><id>1</id><name>" + FDLibName
+ "</name><thresholdValue>1</thresholdValue><customInfo /></CreateFDLib></CreateFDLibList>");
HCNetSDK.BYTE_ARRAY ptrByte = new HCNetSDK.BYTE_ARRAY(10 * HCNetSDK.BYTE_ARRAY_LEN);
ptrByte.byValue = Bytes();
ptrByte.write();
struInput.lpInBuffer = Pointer();
struInput.dwInBufferSize = strInBuffer.length();
struInput.write();
HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT struOutput = new HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT();
struOutput.dwSize = struOutput.size();
HCNetSDK.BYTE_ARRAY ptrOutByte = new HCNetSDK.BYTE_ARRAY(HCNetSDK.ISAPI_DATA_LEN);
struOutput.lpOutBuffer = Pointer();
struOutput.dwOutBufferSize = HCNetSDK.ISAPI_DATA_LEN;
HCNetSDK.BYTE_ARRAY ptrStatusByte = new HCNetSDK.BYTE_ARRAY(HCNetSDK.ISAPI_STATUS_LEN);
struOutput.lpStatusBuffer = Pointer();
struOutput.dwStatusSize = HCNetSDK.ISAPI_STATUS_LEN;
struOutput.write();
if (hCNetSDK.NET_DVR_STDXMLConfig(userID, struInput, struOutput)) {
String xmlStr = String(0);
// dom4j解析xml
try {
Document document;
document = DocumentHelper.parseText(xmlStr);
Element FDLibInfoList = RootElement();
// 同时迭代当前节点下⾯的所有⼦节点
Iterator<Element> iterator = FDLibInfoList.elementIterator();
Element FDLibInfo = ();
Iterator<Element> iterator2 = FDLibInfo.elementIterator();
while (iterator2.hasNext()) {
Element e = ();
if (e.getName().equals("FDID")) {
String id = e.getText();
m_FDID = Integer.parseInt(id);
}
}
} catch (DocumentException e1) {
e1.printStackTrace();
return false;
}
return true;
// 获取根节点元素对象
} else {
int code = hCNetSDK.NET_DVR_GetLastError();
JOptionPane.showMessageDialog(null, "创建⼈脸库失败: " + code);
return false;
}
}
在创建⼈脸库的时候,接⼝说明看了很久⼀直没看懂,后来也是参考⽹上的Demo ,后来我个⼈的理
解是,通过某⼀种请求,去请求⼀个路径,然后传⼊他所需要的 XML 格式的信息,来对摄像头进⾏操作。
建⽴长连接
创建完⼈脸库后,就需要进⾏上传图⽚的操作了,但是上传图⽚之前,我们需要建⽴长连接,这样我们才能进⾏图⽚的上传
/**
* 建⽴长连接
*
* @param index
* @return
*/
public boolean UploadFile(int index) {
// 返回true,说明⽀持⼈脸
HCNetSDK.NET_DVR_FACELIB_COND struInput = new HCNetSDK.NET_DVR_FACELIB_COND();
struInput.dwSize = struInput.size();
struInput.szFDID = String.valueOf(index).getBytes();
struInput.byConcurrent = 0;
struInput.byCover = 1;
struInput.byCustomFaceLibID = 0;
struInput.write();
Pointer lpInput = Pointer();
NativeLong ret = hCNetSDK.NET_DVR_UploadFile_V40(userID, HCNetSDK.IMPORT_DATA_TO_FACELIB, lpInput,
struInput.size(), null, null, 0);
if (ret.longValue() == -1) {
// JOptionPane.showMessageDialog(null, "上传图⽚⽂件失败: " + code);
return false;
} else {
m_lUploadHandle = ret;
return true;
}
}
上传⼈脸数据
现在开始正式进⾏⼈脸图⽚的上传,因为海康摄像头对⼈脸图⽚的要求较为苛刻,所以我们在上传的
时候,需要对图⽚进⾏⼀些处理,我这⾥是通过另⼀个接⼝获取到⼈脸信息,然后下载到本地,在上传⾄摄像头,届时,你可以通过⾃⼰的实际情况来进⾏修改
/**
* 上传⼈脸数据
*/
public void UploadSend() {
Map<String, String> map = URL();
String imgFilePath = null;
String resJson = ("getFaceURL"), "db="+("db"));
try {
JSONArray ja = new JSONArray(resJson);
for ( int i = ja.length() - 1; i > 0; i-- ) {
//String remark = "192.168.0.210:8080/FaceServer";
JSONObject object = ja.getJSONObject(i);
String xh = String("xh");
String dw = String("dw");
String remark = ("projectURL") + String("remark");
String xm = String("xm");
String updateDate = String("update_date");
// 将图⽚转为 base 64
String base64 = NetImageToBase64(remark);
// base64 转为图⽚
imgFilePath = GenerateImage(base64);
/
/ **********************
Map<String,String> rMap = new HashMap<String,String>();
dom4j读取xmlrMap.put("name", xm + "-" + xh + "-" + dw);
rMap.put("bornTime", updateDate);
// rMap.put("sex", "⼥");
// rMap.put("province", "123");
// rMap.put("city", "福州市");
String xml = "<FaceAppendData>";
for (String item : rMap.keySet())
xml += "<" + item + ">";
xml += (item);
xml += "</" + item + ">";
}
xml += "</FaceAppendData>";
// 将 xml 写⼊⽂件
String filePathtoXml = filePath + xmlName + ".xml";
File myFile = new File(filePathtoXml);
if (!ists()) {
}
FileWriter resultFile = new FileWriter( filePathtoXml );
PrintWriter myFile1 = new PrintWriter( resultFile );
myFile1.println( xml );
resultFile.close();
// **********************
// parames.add(new BasicNameValuePair("name", "测试"));
// ******************************
Thread.sleep( 5000 );
FileInputStream picfile = null;
FileInputStream xmlfile = null;
int picdataLength = 0;
int xmldataLength = 0;
picfile = new FileInputStream( new File( imgFilePath ) );
xmlfile = new FileInputStream( new File( filePathtoXml ) );
picdataLength = picfile.available();
xmldataLength = xmlfile.available();
if ( picdataLength < 0 || xmldataLength < 0 ) {
return;
}
HCNetSDK.BYTE_ARRAY ptrpicByte = new HCNetSDK.BYTE_ARRAY( picdataLength );
HCNetSDK.BYTE_ARRAY ptrxmlByte = new HCNetSDK.BYTE_ARRAY( xmldataLength );
ptrpicByte.write();
ptrxmlByte.write();
Thread.sleep( 5000 );
HCNetSDK.NET_DVR_SEND_PARAM_IN struSendParam = new HCNetSDK.NET_DVR_SEND_PARAM_IN(); struSendParam.pSendData = Pointer();
struSendParam.dwSendDataLen = picdataLength;
struSendParam.pSendAppendData = Pointer();
struSendParam.dwSendAppendDataLen = xmldataLength;
if ( struSendParam.pSendData == null || struSendParam.pSendAppendData == null ||
struSendParam.dwSendDataLen == 0 || struSendParam.dwSendAppendDataLen == 0 ) {
return;
}
struSendParam.byPicType = 1;
struSendParam.dwPicMangeNo = 0;
struSendParam.write();
//Thread.sleep(1000);
NativeLong iRet = hCNetSDK.NET_DVR_UploadSend(m_lUploadHandle, Pointer(), null);
while (true) {
NativeLong uploadState = getUploadState();
if (String().equals("1")) {
System.out.println("上传成功");
// NET_DVR_GetUploadResult(m_lUploadHandle, HCNetSDK.IMPORT_DATA_TO_FACELIB, 12);
break;
} else if (String().equals("2")) {
System.out.println("正在上传");
} else if (String().equals("29")) {
System.out.println("图⽚未识别到⽬标");
break;
} else {
System.out.println("其他错误:" + uploadState);
hCNetSDK.NET_DVR_UploadClose(uploadState);
UploadFile(m_FDID);
break;
}
}
//Thread.sleep(1000);
HCNetSDK.NET_DVR_UPLOAD_FILE_RET struPicRet = new HCNetSDK.NET_DVR_UPLOAD_FILE_RET(); Pointer lpPic= Pointer();
struPicRet.write();
boolean bRet = hCNetSDK.NET_DVR_GetUploadResult(m_lUploadHandle, lpPic, struPicRet.size());
if (bRet) {
System.out.println("继续下⼀次上传");
}
if (iRet.longValue() < 0) {
System.out.println("NET_DVR_UploadSend fail,error=" + hCNetSDK.NET_DVR_GetLastError());
} else {
System.out.println("NET_DVR_UploadSend success");
System.out.println("dwSendDataLen =" + struSendParam.dwSendDataLen);
System.out.println("dwSendAppendDataLen =" + struSendParam.dwSendAppendDataLen);
}
//Thread.sleep(5000);
try {
picfile.close();
xmlfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (JSONException e3) {
e3.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
开启⼈脸⽐对功能
当⼀切准备就绪的时候,我们就可以开启⼈脸⽐对的功能了,因为如果不开启⼈脸⽐对的功能话,我们可能⽆法进⾏⼈脸⽐对。
这⾥我不得不说海康的坑,开启⼈脸⽐对功能,⽂档中居然没有详细说明,后来联系了技术⽀持才知道,需要⾃⼰进⼊摄像头的管理页⾯,⼿动开启或关闭⼀次,然后⾃⼰抓包,去寻参数。
这⾥我就不讲抓包的做法了,毕竟我还是⼀个菜鸟,怕误⼈⼦弟,如果不会的话,可以去看看相关的博客,不过我还是贴上,我抓包的过程。
在抓包⼯具中了很久,到类似和⼈脸⽐对开关有关的东西注意:这⾥是 put 请求
双击查看详情,到⼀个可疑的连接,点击它
发现了开关⼈脸⽐对的 XML
当然我为了保险起见,全部都复制下来了。
/**
* 开启⼈脸库的⽐对信息
* @param FDID
*/
public void getFDLib(String FDID) {
HCNetSDK.NET_DVR_XML_CONFIG_INPUT struInput = new HCNetSDK.NET_DVR_XML_CONFIG_INPUT();
struInput.dwSize = struInput.size();
String str = "PUT /ISAPI/Intelligent/channels/" + 1 + "/faceContrast";
HCNetSDK.BYTE_ARRAY ptrUrl = new HCNetSDK.BYTE_ARRAY(HCNetSDK.BYTE_ARRAY_LEN);
System.Bytes(), 0, ptrUrl.byValue, 0, str.length());
ptrUrl.write();
struInput.lpRequestUrl = Pointer();
struInput.dwRequestUrlLen = str.length();
String strInBuffer = new String("<FaceContrastList xmlns=\"www.hikvision/ver20/XMLSche
ma\" version=\"2.0\">" +
"<FaceContrast>" +
"<id>1</id>" +
"<enable>true</enable>" +
"<AttendanceSaveEnable>false</AttendanceSaveEnable>" +
"<faceContrastType>faceContrast</faceContrastType>" +
"<contrastFailureAlarmUpload>false</contrastFailureAlarmUpload>" +
"<QuickContrast>" +
"<enabled>false</enabled>" +
"<snapTime>5.000</snapTime>" +
"<threshold>70</threshold>" +
"<quickConfigMode>custom</quickConfigMode>" +
"<Custom>" +
"<timeOutMode>infinite</timeOutMode>" +
"<duplicateContrastMode>success</duplicateContrastMode>" +
"</Custom>" +
"</QuickContrast>" +
"<alarmStorageEnable>false</alarmStorageEnable>" +
"<mixedTargetDetectionWithFaceContrast>false</mixedTargetDetectionWithFaceContrast>" +
"</FaceContrast>" +
"</FaceContrastList>");
HCNetSDK.BYTE_ARRAY ptrByte = new HCNetSDK.BYTE_ARRAY(10 * HCNetSDK.BYTE_ARRAY_LEN);
ptrByte.byValue = Bytes();
ptrByte.write();
struInput.lpInBuffer = Pointer();
struInput.dwInBufferSize = strInBuffer.length();
struInput.write();
HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT struOutput = new HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT();
struOutput.dwSize = struOutput.size();
HCNetSDK.BYTE_ARRAY ptrOutByte = new HCNetSDK.BYTE_ARRAY(HCNetSDK.ISAPI_DATA_LEN);
struOutput.lpOutBuffer = Pointer();
struOutput.dwOutBufferSize = HCNetSDK.ISAPI_DATA_LEN;
HCNetSDK.BYTE_ARRAY ptrStatusByte = new HCNetSDK.BYTE_ARRAY(HCNetSDK.ISAPI_STATUS_LEN);
struOutput.lpStatusBuffer = Pointer();
struOutput.dwStatusSize = HCNetSDK.ISAPI_STATUS_LEN;
struOutput.write();
if (hCNetSDK.NET_DVR_STDXMLConfig(userID, struInput, struOutput)) {
System.out.println("true111");
} else {
System.out.println("false2222");
}
}
删除⼈脸库
没过⼀段时间,都需要对⼈脸库进⾏⼀次同步,尝试了很多办法,最后发现,还是这样最简单直接,索性就删除以前的⼈脸库。
注意:在你删除⼈脸库后,摄像头会⾃动关闭⼈脸⽐对的功能,此时你要在进⾏上⾯开启⼈脸⽐对的操作,这样摄像头才能正常⼯作,当然我觉得更好的⽅法是,在让摄像头重启⼀次。
/**
* 删除⼈脸库
* @param FDID
*/
public void delFDLib (String FDID) {
HCNetSDK.NET_DVR_XML_CONFIG_INPUT struInput = new HCNetSDK.NET_DVR_XML_CONFIG_INPUT();
struInput.dwSize = struInput.size();
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论