如何通過(guò)??低曉O(shè)備網(wǎng)絡(luò)SDK進(jìn)行Java二次開(kāi)發(fā)攝像頭車牌識(shí)別詳解
前言
作為實(shí)習(xí)生接的第一個(gè)需求,雖然很簡(jiǎn)單,但是還是要記錄一下在這個(gè)過(guò)程中遇到的問(wèn)題和解決辦法。
開(kāi)發(fā)流程
- 打開(kāi)海康威視官網(wǎng)找到開(kāi)發(fā)文檔,并下載對(duì)應(yīng)的設(shè)備網(wǎng)絡(luò)SDK ,在設(shè)備網(wǎng)絡(luò)SDK的壓縮包里面也有對(duì)應(yīng)的開(kāi)發(fā)文檔的chm版,查看比較方便,同樣在其中我們可以找到Java的demo,注意將dll支持庫(kù)復(fù)制到demo的lib目錄下之后就可以嘗試運(yùn)行了。
設(shè)備檢測(cè)車輛時(shí)進(jìn)行車牌識(shí)別、圖片抓拍,并且上傳識(shí)別抓拍結(jié)果。識(shí)別和抓拍是設(shè)備實(shí)現(xiàn)的,由設(shè)備主動(dòng)上傳,SDK被動(dòng)接收。這也就是說(shuō),我們只需要在后端new一個(gè)SDK對(duì)象,與設(shè)備建立連接之后選擇好布防方式,設(shè)備那一端就會(huì)把信息上傳給SDK,由SDK來(lái)處理信息。布防通俗理解就是打開(kāi)識(shí)別功能的意思,這里布防有兩種方式,
1)報(bào)警布防方式,是指SDK主動(dòng)連接設(shè)備,建立報(bào)警上傳通道,設(shè)備發(fā)生報(bào)警之后發(fā)送給SDK。需要先注冊(cè)登錄設(shè)備。
2)報(bào)警監(jiān)聽(tīng)方式,是指觸發(fā)事件時(shí)設(shè)備主動(dòng)連接SDK并且上傳報(bào)警信息,SDK在設(shè)定的端口上監(jiān)聽(tīng)和接收。需要先在設(shè)備端配置報(bào)警主機(jī)的IP和端口,和SDK監(jiān)聽(tīng)的IP、端口需要一致。我這里選擇報(bào)警布防方式,由于海康威視的SDK是根據(jù)C++編寫(xiě)的,里面的JavaSDK也是根據(jù)C++翻譯來(lái)的,所有比較反直覺(jué),不過(guò)沒(méi)事,我會(huì)講清楚遇到的問(wèn)題。demo的代碼我就不貼出來(lái)了,文檔都有,我重點(diǎn)講一下遇到的問(wèn)題。
問(wèn)題和解決方案
dll庫(kù)加載不到的問(wèn)題
如果直接運(yùn)行demo的話,不出意外應(yīng)該沒(méi)什么問(wèn)題,除非你沒(méi)用自己的設(shè)備和ip然后登錄失敗,并且?guī)煲彩悄芗虞d到的。
但是我們做二次開(kāi)發(fā)總會(huì)需要把這些東西整合到項(xiàng)目中,可能是單體項(xiàng)目,可能是微服務(wù)項(xiàng)目,大部分時(shí)候我們需要把lib放到項(xiàng)目一個(gè)固定的文件夾,比如WEB-INF下的lib目錄,比如我的項(xiàng)目就是,這個(gè)目錄下本來(lái)就放了很多依賴,已經(jīng)add as library了。
private static boolean createSDKInstance() { if (hCNetSDK == null) { synchronized (HCNetSDK.class) { String strDllPath = ""; try { if (osSelect.isWindows()) //win系統(tǒng)加載庫(kù)路徑 strDllPath = System.getProperty("user.dir") + "\\lib\\HCNetSDK.dll"; else if (osSelect.isLinux()) //Linux系統(tǒng)加載庫(kù)路徑 strDllPath = System.getProperty("user.dir") + "/lib/libhcnetsdk.so"; hCNetSDK = (HCNetSDK) Native.loadLibrary(strDllPath, HCNetSDK.class); System.out.println(strDllPath); } catch (Exception ex) { System.out.println("loadLibrary: " + strDllPath + " Error: " + ex.getMessage()); return false; } } } return true; }
Native.loadLibrary()這個(gè)方法,前一個(gè)參數(shù)是要加載的dll的位置,后一個(gè)是要加載的類,在demo里面第一個(gè)參數(shù)填的是絕對(duì)路徑,我在整合到項(xiàng)目里面的時(shí)候發(fā)現(xiàn)這里總是報(bào)錯(cuò),后來(lái)調(diào)試發(fā)現(xiàn)獲取到的路徑是tomcat部署目錄的路徑,而在項(xiàng)目根目錄新建lib文件夾他沒(méi)有識(shí)別到。后來(lái)嘗試寫(xiě)成絕對(duì)路徑,寫(xiě)成絕對(duì)路徑還是不行,報(bào)錯(cuò)變成了很奇怪的一條信息,顯示找不到xxx(dll的絕對(duì)路徑)在xxx(WEB-INF下的lib的目錄),但是實(shí)際上我已經(jīng)將dll放到了WEB-INF下的lib目錄,為什么找不到呢?
后來(lái)搜索發(fā)現(xiàn)Native.loadLibrary()這個(gè)方法第一個(gè)參數(shù)只需要填dll支持的名稱就可以了,于是改為
if (hcNetSDK == null) { synchronized (HCNetSDK.class) { String strDllPath = ""; try { // 加載庫(kù) hcNetSDK = (HCNetSDK) Native.loadLibrary("HCNetSDK", HCNetSDK.class); } catch (Exception ex) { logger.info("loadLibrary: " + strDllPath + " Error: " + ex.getMessage()); return false; } }
現(xiàn)在就可以加載到了,分析原因可能是Native.loadLibrary()這個(gè)方法會(huì)到項(xiàng)目指定的lib目錄下去找對(duì)應(yīng)的dll,如果項(xiàng)目沒(méi)有指定lib目錄,那就需要指定絕對(duì)路徑,如果指定了,那么就只需要名稱就可以了??赡芪业睦斫庥袉?wèn)題,大家有更好的理解歡迎指出。
老舊版本sdk不兼容的問(wèn)題
如果你使用的是新下載的sdk,那么下邊這個(gè)問(wèn)題應(yīng)該不會(huì)遇到
在demo給出的HCNetSdk.java中,可以看到所有的類都繼承了一個(gè)Structure,這是因?yàn)镴ava中沒(méi)有結(jié)構(gòu)體,而我們前面提到他這個(gè)sdk是由c++翻譯來(lái)的,所以他自定義了一個(gè)結(jié)構(gòu)體,但是有一個(gè)問(wèn)題,老版本的sdk中,這個(gè)Structure里面有一個(gè)getFiledName()方法需要自己實(shí)現(xiàn),我們得自定義一個(gè)BaseStructure,寫(xiě)好這個(gè)方法之后再把sdk中所有集成Structure的地方換成我們這個(gè)自定義的BaseStructure,代碼如下
public class BaseStructure extends Structure { @Override protected List<String> getFieldOrder() { return getFiledName(this); } public static List<String> getFiledName(Object o) { Field[] fields = o.getClass().getDeclaredFields(); String[] fieldNames = new String[fields.length]; for (int i = 0; i < fields.length; i++) { fieldNames[i] = fields[i].getName(); } return Arrays.asList(fieldNames); } }
但是在新版的sdk中,這個(gè)方法已經(jīng)不需要我們重寫(xiě)了。
我的項(xiàng)目中之前使用過(guò)老版的sdk編寫(xiě)過(guò)一些老模塊,所以我碰到了上邊這個(gè)問(wèn)題。另外就是我在更換老版sdk時(shí)發(fā)現(xiàn),在老版sdk中很多方法使用了nativelong這個(gè)類型作為返回值或者變量類型,而在新版sdk中,這些都變成了int,所以如果你使用的sdk有新老版本沖突的問(wèn)題,可以嘗試把nativelong類型都換成int。
關(guān)鍵實(shí)現(xiàn)流程
創(chuàng)建sdk實(shí)例;
if (hCNetSDK == null) { if (!createSDKInstance()) { System.out.println("Load SDK fail"); return; } }
初始化并加載日志
/**初始化*/ hCNetSDK.NET_DVR_Init(); /**加載日志*/ hCNetSDK.NET_DVR_SetLogToFile(3, "./sdklog1", false);
編寫(xiě)并設(shè)置回調(diào)函數(shù)(回調(diào)函數(shù)就是處理車牌信息的函數(shù),COMM_ITS_PLATE_RESULT這個(gè)類型的lCommand就是我們需要的車牌信息的情況,可以在里面編寫(xiě)自己的邏輯),車牌照片有不同的類型,可以根據(jù)需要自己保存照片,文檔里面有寫(xiě)不同編號(hào)對(duì)應(yīng)不同場(chǎng)景圖
public class FMSGCallBack implements HCNetSDK.FMSGCallBack_V31 { //報(bào)警信息回調(diào)函數(shù) public boolean invoke(int lCommand, HCNetSDK.NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) { LOGGER.info("報(bào)警事件類型: lCommand:" + Integer.toHexString(lCommand)); String MonitoringSiteID; switch (lCommand) { case 0x3058: LOGGER.info("報(bào)警事件類型: 0x3058 車輛黑白名單數(shù)據(jù)需要同步報(bào)警上傳"); break; case HCNetSDK.COMM_UPLOAD_PLATE_RESULT: LOGGER.info("報(bào)警事件類型: COMM_UPLOAD_PLATE_RESULT"); break; case HCNetSDK.COMM_ITS_PLATE_RESULT: // 交通抓拍結(jié)果(新報(bào)警信息) HCNetSDK.NET_ITS_PLATE_RESULT strItsPlateResult = new HCNetSDK.NET_ITS_PLATE_RESULT(); //可以在這里處理自己的邏輯 break; default: LOGGER.info("報(bào)警類型:" + Integer.toHexString(lCommand)); break; } return true; } }
if (fMSFCallBack_V31 == null) { fMSFCallBack_V31 = new FMSGCallBack_V31(); Pointer pUser = null; if (!hCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(fMSFCallBack_V31, pUser)) { System.out.println("設(shè)置回調(diào)函數(shù)失敗!"); return; } else { System.out.println("設(shè)置回調(diào)函數(shù)成功!"); } }
設(shè)置結(jié)果分離參數(shù)
/** 設(shè)備上傳的報(bào)警信息是COMM_VCA_ALARM(0x4993)類型, 在SDK初始化之后增加調(diào)用NET_DVR_SetSDKLocalCfg(enumType為NET_DVR_LOCAL_CFG_TYPE_GENERAL)設(shè)置通用參數(shù)NET_DVR_LOCAL_GENERAL_CFG的byAlarmJsonPictureSeparate為1, 將Json數(shù)據(jù)和圖片數(shù)據(jù)分離上傳,這樣設(shè)置之后,報(bào)警布防回調(diào)函數(shù)里面接收到的報(bào)警信息類型為COMM_ISAPI_ALARM(0x6009), 報(bào)警信息結(jié)構(gòu)體為NET_DVR_ALARM_ISAPI_INFO(與設(shè)備無(wú)關(guān),SDK封裝的數(shù)據(jù)結(jié)構(gòu)),更便于解析。*/ HCNetSDK.NET_DVR_LOCAL_GENERAL_CFG struNET_DVR_LOCAL_GENERAL_CFG = new HCNetSDK.NET_DVR_LOCAL_GENERAL_CFG(); struNET_DVR_LOCAL_GENERAL_CFG.byAlarmJsonPictureSeparate = 1; //設(shè)置JSON透?jìng)鲌?bào)警數(shù)據(jù)和圖片分離 struNET_DVR_LOCAL_GENERAL_CFG.write(); Pointer pStrNET_DVR_LOCAL_GENERAL_CFG = struNET_DVR_LOCAL_GENERAL_CFG.getPointer(); hCNetSDK.NET_DVR_SetSDKLocalCfg(17, pStrNET_DVR_LOCAL_GENERAL_CFG);
接著登錄設(shè)備并布防即可
lUserID=Alarm.loginDevice( "10.9.137.17", (short) 8000, "admin", "hik12345"); //登錄設(shè)備 lAlarmHandle=Alarm.setAlarmChan(lUserID);//報(bào)警布防,和報(bào)警監(jiān)聽(tīng)二選一即可
有一點(diǎn)需要注意,他這個(gè)車牌識(shí)別無(wú)法直接識(shí)別靜止的圖片,比如說(shuō)我們當(dāng)時(shí)采用放個(gè)平板(平板上放個(gè)車牌照片)的方式測(cè)試,攝像頭就沒(méi)有抓拍,移動(dòng)了好幾次才識(shí)別出來(lái),大家測(cè)試的時(shí)候可以注意。雖然它此時(shí)沒(méi)有抓拍車牌,但還是有一些報(bào)警信息,
報(bào)警事件類型: 0x3058 車輛黑白名單數(shù)據(jù)需要同步報(bào)警上傳
這個(gè)信息隔一段時(shí)間就會(huì)出現(xiàn),問(wèn)了客服說(shuō)這個(gè)信息不用解析,大家也可以將這個(gè)信息作為自己攝像頭是否正常布防的調(diào)試信息,如果有這個(gè)類型的報(bào)警說(shuō)明已經(jīng)布防成功了。
總結(jié)
到此這篇關(guān)于如何通過(guò)??低曉O(shè)備網(wǎng)絡(luò)SDK進(jìn)行Java二次開(kāi)發(fā)攝像頭車牌識(shí)別的文章就介紹到這了,更多相關(guān)??低曉O(shè)備網(wǎng)絡(luò)SDK攝像頭車牌識(shí)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java8函數(shù)式編程應(yīng)用小結(jié)
Java8非常重要的就是引入了函數(shù)式編程的思想,使得這門經(jīng)典的面向?qū)ο笳Z(yǔ)言有了函數(shù)式的編程方式,彌補(bǔ)了很大程度上的不足,函數(shù)式思想在處理復(fù)雜問(wèn)題上有著更為令人稱贊的特性,本文給大家介紹Java8函數(shù)式編程應(yīng)用小結(jié),感興趣的朋友一起看看吧2023-12-12解決@Scheduled定時(shí)器使用@Thransactional事物問(wèn)題
這篇文章主要介紹了解決@Scheduled定時(shí)器使用@Thransactional事物問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08Java項(xiàng)目實(shí)現(xiàn)模擬ATM機(jī)
這篇文章主要為大家詳細(xì)介紹了Java項(xiàng)目實(shí)現(xiàn)模擬ATM機(jī),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05約定優(yōu)于配置_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
以前做項(xiàng)目,總是寫(xiě)Ant配置文件,滿足于自己更靈活的配置,而沒(méi)有去思考,這么做到底值不值得2017-08-08SpringMVC中的請(qǐng)求參數(shù)接收方式
這篇文章主要介紹了SpringMVC中的請(qǐng)求參數(shù)接收方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04Java用?Gradle配置compile及implementation和api的區(qū)別
這篇文章主要介紹了Java用Gradle配置compile及implementation和api的區(qū)別,文章圍繞主題的相關(guān)資料展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-06-06詳解spring boot starter redis配置文件
spring-boot-starter-Redis主要是通過(guò)配置RedisConnectionFactory中的相關(guān)參數(shù)去實(shí)現(xiàn)連接redis service。下面通過(guò)本文給大家介紹在spring boot的配置文件中redis的基本配置,需要的的朋友參考下2017-07-07Java實(shí)現(xiàn)批量修改txt文件名稱的方法示例
這篇文章主要介紹了Java實(shí)現(xiàn)批量修改txt文件名稱的方法,結(jié)合實(shí)例形式分析了Java針對(duì)目錄文件遍歷及文件讀寫(xiě)、屬性操作等相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-03-03微信小程序獲取手機(jī)號(hào)的完整實(shí)例(Java后臺(tái)實(shí)現(xiàn))
我們?cè)谧鲂〕绦蜷_(kāi)發(fā)的過(guò)程中,經(jīng)常會(huì)涉及到用戶身份的問(wèn)題,最普遍的就是我們要獲取用戶的手機(jī)號(hào)碼,下面這篇文章主要給大家介紹了關(guān)于微信小程序獲取手機(jī)號(hào)的完整實(shí)例,后臺(tái)由Java實(shí)現(xiàn),需要的朋友可以參考下2022-06-06