前言
項目采購的海康威視的抓拍機,需要首先獲取抓拍圖片,之后基於抓拍圖片進行圖像處理。本文基於海康SDK開發文檔,按照開發步驟,咨詢海康技術人員,現將開發過程記錄如下。
主要過程的c++代碼:

/************************************************************************ * Copyright(c) 2016 ZRJ * All rights reserved. * * File: snap.h * Brief: 抓拍機配置頭文件 * Version: 1.0 * Author: ZRJ * Email: happyamyhope@163.com * Date: 2016/12/08 * History: * 20161202: 添加布防級別與退出撤防功能; * 20161207: 設置抓拍等待時間等;添加系統時間; * 20161208: 添加復制文件夾操作; * 20161208: 添加啟動程序初始化圖片不保存;刪除網絡觸發抓拍部分; * 20161209: 刪除網絡觸發抓拍部分; * 20161220: 現場調試增加的功能; * 20170109: 刪除FIFO隊列數據; * 20170123: 添加創建文件夾功能,刪除預覽取流部分; ************************************************************************/ //------------------------------------------------------------------------- //頭文件 #include <stdio.h> #include <iostream> #include "Windows.h" #include "HCNetSDK.h" //獲取系統時間 #include <time.h> //OPENCV庫文件 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2\opencv.hpp> #include <opencv2\imgproc\imgproc.hpp> using namespace cv; using namespace std; typedef HWND(WINAPI *PROCGETCONSOLEWINDOW)(); PROCGETCONSOLEWINDOW Getconsolewindow; //----------------------------------------------------------------------- //調參 int iNum = 1;//抓拍圖片的數目初始化值 int pic_number = 1001;//抓拍文件夾存圖數目 //int j = 1;//顯示抓拍圖片的數目 //注冊設備 LONG IUserID;//攝像機設備 char *sDVRIP = "10.0.10.3";//抓拍攝像機設備IP地址 short wDVRPort = 8000;//設備端口號 char* sUserName = "admin";//登錄的用戶名 char* sPassword = "admin123";//用戶密碼 // time_t t; char now[64]; // int fifo_flag = 0;//隊列標志; double interval_time;//當前抓拍圖片與前一幀抓拍時間之差;單位是ms; double interval_time_thres = 40000;//前后兩幀抓拍時間間隔閾值40s;單位是ms; double pre_time;//系統開始時間;前一次抓拍圖片的時間點; double cur_time; // LONG IHandle = -1;//報警布防; bool snap_flag = false;//是否抓拍的標志位 NET_DVR_DEVICEINFO_V30 struDeviceInfo;//設備信息 //--------------------------------------------------------------------------------- //函數聲明 BOOL SHDeleteFolder(LPCTSTR pstrFolder);//刪除指定目錄 void CopyFolder(SHFILEOPSTRUCT fop);//抓拍圖片文件夾操作 void CreateFolder(char* filename);//不存在則創建文件夾; void Init();//初始化 void Connect();//設置連接事件與重連時間 bool Login(char *sDVRIP, short wDVRPort, char *sUserName, char *sPassword);//注冊攝像機設備 void CALLBACK MSesGCallback(LONG ICommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void *pUser);//報警回調函數 void SetMessageCallBack();//設置報警回調函數 void SetupAlarm();//報警布防 void CloseAlarm();//報警撤防 void OnExit(void);//退出 extern int snap();//抓拍 //----------------------------------------------------------------------------------------- //函數定義 //初始化 void Init() { NET_DVR_Init(); } //設置連接事件與重連時間 void Connect() { NET_DVR_SetConnectTime(2000, 1); NET_DVR_SetReconnect(10000, true); } //注冊攝像機設備 bool Login(char *sDVRIP, short wDVRPort, char *sUserName, char *sPassword) { IUserID = NET_DVR_Login_V30(sDVRIP, wDVRPort, sUserName, sPassword, &struDeviceInfo); if (IUserID < 0) { std::cout << "Login Failed! Error number:" << NET_DVR_GetLastError() << std::endl; NET_DVR_Cleanup(); return false; } else { std::cout << "Login Successfully!" << std::endl; return true; } } //報警回調函數 void CALLBACK MSesGCallback(LONG ICommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void *pUser) { //unsigned int i = 0; char filename[100]; FILE *fSnapPic = NULL; //std::cout << "Callback MSesGCallback one time..." << std::endl; //std::cout << "ICommand:" << ICommand << std::endl; //以下代碼僅供參考,實際應用中不建議在該回調函數中直接處理數據保存文件, //例如可以使用消息的方式(PostMessage)在消息響應函數里進行處理 switch (ICommand) { case COMM_UPLOAD_PLATE_RESULT://交通抓拍結果(老報警消息) { NET_DVR_PLATE_RESULT struPlateResult = { 0 }; memcpy(&struPlateResult, pAlarmInfo, sizeof(struPlateResult)); //獲取場景圖 if (struPlateResult.dwPicLen != 0 && struPlateResult.byResultType == 1) { snap_flag = true; //計時 cur_time = (double)getTickCount();//當前抓拍圖片的時間點; t = time(NULL);//系統當前時間 strftime(now, sizeof(now), "%Y%m%dT%H%M%S", localtime(&t)); sprintf(filename, ".\\pic\\testpic%d_%s.jpg", iNum, now); fSnapPic = fopen(filename, "wb"); fwrite(struPlateResult.pBuffer1, struPlateResult.dwPicLen, 1, fSnapPic); //前后兩次抓拍圖片的時間差 if (fifo_flag == 0)//系統開始 { interval_time = 100; fifo_flag = 1; } else { interval_time = (cur_time - pre_time) * 1000 / (getTickFrequency());//單位是ms; } //fifo標志位轉換 if (interval_time > interval_time_thres) { if (fifo_flag == 1) { fifo_flag = 2; } else if (fifo_flag == 2) { fifo_flag = 1; } } //std::cout << "fifo_flag: " << fifo_flag << std::endl; //獲取抓拍圖片隊列 if (fifo_flag == 1) { std::cout << "fifo1 push back one time..." << std::endl; } else if (fifo_flag == 2) { std::cout << "fifo2 push back one time..." << std::endl; } iNum++; if (pic_number == iNum) { iNum = 1; CopyFolder(fop);//抓拍圖片文件夾操作 } else { } pre_time = cur_time; fclose(fSnapPic); } else { snap_flag = false; } //其他信息處理 break; } case COMM_ITS_PLATE_RESULT://交通抓拍結果(新報警信息) { NET_ITS_PLATE_RESULT struITSPlateResult = { 0 };//識別結果結構體;dwPicNum memcpy(&struITSPlateResult, pAlarmInfo, sizeof(struITSPlateResult)); struITSPlateResult.dwPicNum = 1;//圖片數量(與byGroupNum不同,代表本條信息附帶的圖片數量) //保存場景圖 if ((struITSPlateResult.struPicInfo[0].dwDataLen != 0) && (struITSPlateResult.struPicInfo[0].byType == 1)) { snap_flag = true; //計時 cur_time = (double)getTickCount(); //系統當前時間 t = time(NULL); strftime(now, sizeof(now), "%Y%m%dT%H%M%S", localtime(&t)); sprintf(filename, ".\\pic\\testITSpic%d_%s.jpg", iNum, now); fSnapPic = fopen(filename, "wb"); fwrite(struITSPlateResult.struPicInfo[0].pBuffer, struITSPlateResult.struPicInfo[0].dwDataLen, 1, fSnapPic); //前后兩次抓拍圖片的時間差 if (fifo_flag == 0)//系統開始 { interval_time = 100; fifo_flag = 1; } else { interval_time = (cur_time - pre_time) * 1000 / (getTickFrequency());//單位是ms; } //std::cout << "interval_time: " << interval_time << std::endl; //fifo標志位轉換 if (interval_time > interval_time_thres) { if (fifo_flag == 1) { fifo_flag = 2; } else if (fifo_flag == 2) { fifo_flag = 1; } } //std::cout << "fifo_flag: " << fifo_flag << std::endl; //獲取抓拍圖片隊列 if (fifo_flag == 1) { std::cout << "fifo1 push back one time..." << std::endl; } else if (fifo_flag == 2) { std::cout << "fifo2 push back one time..." << std::endl; } iNum++; if (pic_number == iNum) { iNum = 1; CopyFolder(fop);//抓拍圖片文件夾操作 } else { } pre_time = cur_time; //std::cout << "pre_time: " << pre_time << std::endl; fclose(fSnapPic); } else { snap_flag = false; } //其他處理信息...... break; } default: break; } return; } //設置報警回調函數 void SetMessageCallBack() { NET_DVR_SetDVRMessageCallBack_V30(MSesGCallback, NULL); } //報警布防 void SetupAlarm() { //啟動布防 NET_DVR_SETUPALARM_PARAM struSetupParam = { 0 }; struSetupParam.dwSize = sizeof(NET_DVR_SETUPALARM_PARAM); //上傳報警信息類型:0-老報警信息(NET_DVR_PLATE_RESULT), 1-新報警信息(NET_ITS_PLATE_RESULT) struSetupParam.byAlarmInfoType = 1; struSetupParam.byLevel = 1;//布防優先級:0- 一等級(高),1- 二等級(中),2- 三等級(低) //bySupport 按位表示,值:0 - 上傳,1 - 不上傳; bit0 - 表示二級布防是否上傳圖片; IHandle = NET_DVR_SetupAlarmChan_V41(IUserID, &struSetupParam);//建立報警上傳通道,獲取報警等信息。 if (IHandle < 0) { std::cout << "NET_DVR_SetupAlarmChan_V41 Failed! Error number:" << NET_DVR_GetLastError() << std::endl; NET_DVR_Logout(IUserID); NET_DVR_Cleanup(); return; } std::cout << "\n" << endl; } //報警撤防 void CloseAlarm() { //撤銷布防上傳通道 if (!NET_DVR_CloseAlarmChan_V30(IHandle))//布防句柄IHandle { std::cout << "NET_DVR_CloseAlarmChan_V30 Failed! Error number:" << NET_DVR_GetLastError() << std::endl; NET_DVR_Logout(IUserID); NET_DVR_Cleanup(); return; } IHandle = -1;//布防句柄; } void OnExit(void) { std::cout << "Begin exit..." << std::endl; //報警撤防 CloseAlarm(); //釋放相機 NET_DVR_Logout(IUserID);//注銷用戶 NET_DVR_Cleanup();//釋放SDK資源 } //抓拍 extern int snap() { atexit(OnExit); int CycleCount = 10; bool LoginSuccess = false; //獲取控制台窗口句柄 HMODULE hKernel32 = GetModuleHandle("kernel32"); Getconsolewindow = (PROCGETCONSOLEWINDOW)GetProcAddress(hKernel32, "GetConsoleWindow"); CreateFolder(picfile);//不存在則創建文件夾; CreateFolder(savepicfile);//不存在則創建文件夾; Init();//初始化 Connect();//設置連接事件與重連時間 //注冊設備 if (Login(sDVRIP, wDVRPort, sUserName, sPassword)) { LoginSuccess = true; SetMessageCallBack();//設置報警回調函數 SetupAlarm();//報警布防 return 0; } else { for (int i = 0; i < CycleCount; i++) { std::cout << "Begin to Login again......" << std::endl; if (Login(sDVRIP, wDVRPort, sUserName, sPassword)) { LoginSuccess = true; SetMessageCallBack();//設置報警回調函數 SetupAlarm();//報警布防 break; } } if (!LoginSuccess) { std::cout << "Failed! The application has tried 10 times to login. But still can not login! Press any key to exit......" << std::endl; } } return -1; } int main() { atexit(OnExit); snap(); getchar(); }
注意:
1.第一步需要配置SDK和opencv相關文件;
2.熟悉抓拍機SDk的開發流程,可參見開發文檔;
3.布防成功之后,需要添加退出撤防功能;
4.保存圖片可能需要用到相關文件夾操作;
5.預覽取流部分可以根據需要選擇,為了實時性本文沒有使用;
6.為了之后的圖像處理過程,將圖片數據存在FIFO隊列中,但一定要記得及時刪除相應的隊列,否則會造成內存泄漏,從而導致系統崩潰(寶寶就不小心犯了這樣的低級錯誤,以此為戒!!!);
7.布防級別的設置,這個一定要問清楚技術支持,因為你根本就查不到,反正寶寶沒有查到!!!寶寶委屈但寶寶不說~~~
體會
第一次接觸SDk開發,期間真是各種心酸,說多了都是淚~~~不過,整個過程首先你需要知道大致的框架和流程,認真研讀需要部分的開發文檔,一般也會有代碼示例可以參考,按照文檔的思路走你就一定會成功的,相信自己!!!中間過程如果遇到任何問題可以及時咨詢技術支持,最好郵件聯系,那段時間海康的技術支持可能都記着寶寶啦~~另外,第一次做某件事情擔心害怕也是正常的,但是你一定要相信自己,靜下心去做,只有真正去實踐了,才會遇到問題,遇到問題也不要怕更不要急躁,可以查資料,可以問大神,可以問技術支持,總會解決的!!!無論做什么事情都要一步步去做,在實踐的過程中所有問題和答案就會越來越清晰了~~
完