海康威视摄像头人脸识别开发流程及踩过的坑

海康威视摄像头人脸识别开发流程及踩过的坑

前段时间,接手了公司的海康摄像头的二次开发的工作,在网上查了很多资料,但基本上只能解决其中一部分问题,还有很多问题,在不停纠缠海康技术支持后,才勉强搞定,在这里把自己的开发心得及踩过的坑写下来,希望对大家有所帮助。

我的开发环境使用的是 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 = RWProperties.getCameraInfo();
		String port2 = map.get("port");
		Short s = new Short(port2);
		short myPort = s.shortValue();
		
		userID = hCNetSDK.NET_DVR_Login_V30(map.get("ip"), myPort, map.get("username"), map.get("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.arraycopy(str.getBytes(), 0, ptrUrl.byValue, 0, str.length());
        ptrUrl.write();
        struInput.lpRequestUrl = ptrUrl.getPointer();
        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 = strInBuffer.getBytes();
        ptrByte.write();
        struInput.lpInBuffer = ptrByte.getPointer();
        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 = ptrOutByte.getPointer();
        struOutput.dwOutBufferSize = HCNetSDK.ISAPI_DATA_LEN;
        HCNetSDK.BYTE_ARRAY ptrStatusByte = new HCNetSDK.BYTE_ARRAY(HCNetSDK.ISAPI_STATUS_LEN);
        struOutput.lpStatusBuffer = ptrStatusByte.getPointer();
        struOutput.dwStatusSize = HCNetSDK.ISAPI_STATUS_LEN;
        struOutput.write();
        if (hCNetSDK.NET_DVR_STDXMLConfig(userID, struInput, struOutput)) {
            String xmlStr = struOutput.lpOutBuffer.getString(0);
            // dom4j解析xml
            try {
                Document document;
                document = DocumentHelper.parseText(xmlStr);
                Element FDLibInfoList = document.getRootElement();
                // 同时迭代当前节点下面的所有子节点
                Iterator<Element> iterator = FDLibInfoList.elementIterator();
                Element FDLibInfo = iterator.next();
                Iterator<Element> iterator2 = FDLibInfo.elementIterator();
                while (iterator2.hasNext()) {
                    Element e = iterator2.next();
                    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 = struInput.getPointer();

		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 = RWProperties.getURL();
        String imgFilePath = null;
        String resJson = sendPost(map.get("getFaceURL"), "db="+map.get("db"));
        try {
            JSONArray ja = new JSONArray(resJson);
            for ( int i = ja.length() - 1; i > 0; i-- ) {
                //String remark = "http://192.168.0.210:8080/FaceServer";
                JSONObject object = ja.getJSONObject(i);
                String xh = object.getString("xh");
                String dw = object.getString("dw");
                String remark = map.get("projectURL") + object.getString("remark");
                String xm = object.getString("xm");
                String updateDate = object.getString("update_date");
                // 将图片转为 base 64
                String base64 = NetImageToBase64(remark);
                // base64 转为图片
                imgFilePath = GenerateImage(base64);
                // **********************
                Map<String,String> rMap = new HashMap<String,String>();
                rMap.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 += rMap.get(item);
                    xml += "</" + item + ">";
                }
                xml += "</FaceAppendData>";
                // 将 xml 写入文件
                String filePathtoXml = filePath + xmlName + ".xml";
                File myFile = new File(filePathtoXml);
                if (!myFile.exists()) {
                    myFile.createNewFile();
                }
                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 FileInputStreamnew File( imgFilePath ) );
                xmlfile = new FileInputStreamnew 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 );
                picfile.read( ptrpicByte.byValue );
                xmlfile.read( ptrxmlByte.byValue );
                ptrpicByte.write();
                ptrxmlByte.write();
                Thread.sleep( 5000 );
                HCNetSDK.NET_DVR_SEND_PARAM_IN struSendParam = new HCNetSDK.NET_DVR_SEND_PARAM_IN();
                struSendParam.pSendData = ptrpicByte.getPointer();
                struSendParam.dwSendDataLen = picdataLength;
                struSendParam.pSendAppendData = ptrxmlByte.getPointer();
                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, struSendParam.getPointer(), null);
                while (true) {
                    NativeLong uploadState = getUploadState();
                    if (uploadState.toString().equals("1")) {
                        System.out.println("上传成功");
                        // NET_DVR_GetUploadResult(m_lUploadHandle, HCNetSDK.IMPORT_DATA_TO_FACELIB, 12);
                        break;
                    } else if (uploadState.toString().equals("2")) {
                        System.out.println("正在上传");
                    } else if (uploadState.toString().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= struPicRet.getPointer();
                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.arraycopy(str.getBytes(), 0, ptrUrl.byValue, 0, str.length());
        ptrUrl.write();
        struInput.lpRequestUrl = ptrUrl.getPointer();
        struInput.dwRequestUrlLen = str.length();
        String strInBuffer = new String("<FaceContrastList xmlns=\"http://www.hikvision.com/ver20/XMLSchema\" 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 = strInBuffer.getBytes();
        ptrByte.write();
        struInput.lpInBuffer = ptrByte.getPointer();
        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 = ptrOutByte.getPointer();
        struOutput.dwOutBufferSize = HCNetSDK.ISAPI_DATA_LEN;
        HCNetSDK.BYTE_ARRAY ptrStatusByte = new HCNetSDK.BYTE_ARRAY(HCNetSDK.ISAPI_STATUS_LEN);
        struOutput.lpStatusBuffer = ptrStatusByte.getPointer();
        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();
        String str = "DELETE /ISAPI/Intelligent/FDLib/" + FDID + "\r\n";
        HCNetSDK.BYTE_ARRAY ptrUrl = new HCNetSDK.BYTE_ARRAY(HCNetSDK.BYTE_ARRAY_LEN);
        System.arraycopy(str.getBytes(), 0, ptrUrl.byValue, 0, str.length());
        ptrUrl.write();
        struInput.lpRequestUrl = ptrUrl.getPointer();
        struInput.dwRequestUrlLen = str.length();
        String strInBuffer = new String("<CreateFDLibList><CreateFDLib><id>1</id><name></name><thresholdValue>1</thresholdValue><customInfo /></CreateFDLib></CreateFDLibList>");
        HCNetSDK.BYTE_ARRAY ptrByte = new HCNetSDK.BYTE_ARRAY(10 * HCNetSDK.BYTE_ARRAY_LEN);
        ptrByte.byValue = strInBuffer.getBytes();
        ptrByte.write();
        struInput.lpInBuffer = ptrByte.getPointer();
        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 = ptrOutByte.getPointer();
        struOutput.dwOutBufferSize = HCNetSDK.ISAPI_DATA_LEN;
        HCNetSDK.BYTE_ARRAY ptrStatusByte = new HCNetSDK.BYTE_ARRAY(HCNetSDK.ISAPI_STATUS_LEN);
        struOutput.lpStatusBuffer = ptrStatusByte.getPointer();
        struOutput.dwStatusSize = HCNetSDK.ISAPI_STATUS_LEN;
        struOutput.write();
        if (hCNetSDK.NET_DVR_STDXMLConfig(userID, struInput, struOutput)) {
            System.out.println("true");
        } else {
            System.out.println("false");
        }
    }
结语

作为刚入行没多久的人,自己的代码写的还比较烂,如果对各位造成困扰,还请见谅,日后一定会努力提升自己的代码功底,让自己的代码更加规范,完整代码已经上传 Github,需要完整代码的,请移步 https://github.com/mxr1994/shexiang 进行查看。

posted @   ChrisMeng  阅读(15870)  评论(5编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示