1、前言
最近有个项目要用到人脸识别,由于很多原因最后决定采购海康的人脸识别摄像头(支持人脸抓拍+人脸比对等功能),项目中要集成的功能包括:人脸识别结果的返回和人员信息的下发配置。
2、开发环境
- ubuntu18.04系统
- CH-HCNetSDKV6.1.4.42_build20200527_linux64
- C++11
吐槽一下:海康摄像头目前的SDK只提供了linux x86版本的32位和64位的版本,并没有提供linux arm的sdk,这个真是让人摸不着头脑
3、功能开发
-
海康提供的example
参考这个例子基本上就可以把登录问题搞定了
-
参考开发文档
#include <stdio.h>
#include <iostream>
#include "Windows.h"
#include "HCNetSDK.h"
using namespace std;
//时间解析宏定义
#define GET_YEAR(_time_) (((_time_)>>26) + 2000)
#define GET_MONTH(_time_) (((_time_)>>22) & 15)
#define GET_DAY(_time_) (((_time_)>>17) & 31)
#define GET_HOUR(_time_) (((_time_)>>12) & 31)
#define GET_MINUTE(_time_) (((_time_)>>6) & 63)
#define GET_SECOND(_time_) (((_time_)>>0) & 63)
BOOL CALLBACK MessageCallback(LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void* pUser)
{
switch(lCommand)
{
case COMM_SNAP_MATCH_ALARM: //人脸比对结果信息
{
NET_VCA_FACESNAP_MATCH_ALARM struFaceMatchAlarm = {0};
memcpy(&struFaceMatchAlarm, pAlarmInfo, sizeof(NET_VCA_FACESNAP_MATCH_ALARM));
NET_DVR_TIME struAbsTime = {0};
struAbsTime.dwYear = GET_YEAR(struFaceMatchAlarm.struSnapInfo.dwAbsTime);
struAbsTime.dwMonth = GET_MONTH(struFaceMatchAlarm.struSnapInfo.dwAbsTime);
struAbsTime.dwDay = GET_DAY(struFaceMatchAlarm.struSnapInfo.dwAbsTime);
struAbsTime.dwHour = GET_HOUR(struFaceMatchAlarm.struSnapInfo.dwAbsTime);
struAbsTime.dwMinute = GET_MINUTE(struFaceMatchAlarm.struSnapInfo.dwAbsTime);
struAbsTime.dwSecond = GET_SECOND(struFaceMatchAlarm.struSnapInfo.dwAbsTime);
printf("人脸比对结果上传[0x%x]:[%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d]fSimilarity[%f]FaceID[%d]BlackListID[%d]"
"Sex[%d]Glasses[%d]Age[%d]MatchPicNum[%d]PicTransType[%d]\n", lCommand,
struAbsTime.dwYear, struAbsTime.dwMonth, struAbsTime.dwDay, struAbsTime.dwHour, struAbsTime.dwMinute,
struAbsTime.dwSecond, struFaceMatchAlarm.fSimilarity, struFaceMatchAlarm.struSnapInfo.dwSnapFacePicID,
struFaceMatchAlarm.struBlackListInfo.struBlackListInfo.dwRegisterID, struFaceMatchAlarm.struSnapInfo.bySex,
struFaceMatchAlarm.struSnapInfo.byGlasses, struFaceMatchAlarm.struSnapInfo.byAge,
struFaceMatchAlarm.byMatchPicNum, struFaceMatchAlarm.byPicTransType);
//保存抓拍图片
if (struFaceMatchAlarm.dwSnapPicLen > 0 && struFaceMatchAlarm.pSnapPicBuffer != NULL && struFaceMatchAlarm.byPicTransType == 0)
{
char cFilename[256] = {0};
HANDLE hFile;
DWORD dwReturn;
char chTime[128];
sprintf(chTime,"%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",struAbsTime.dwYear,struAbsTime.dwMonth,struAbsTime.dwDay,struAbsTime.dwHour,struAbsTime.dwMinute,struAbsTime.dwSecond);
sprintf(cFilename, "FaceSnapPic[%s][%s].jpg",struFaceMatchAlarm.struSnapInfo.struDevInfo.struDevIP.sIpV4, chTime);
hFile = CreateFile(cFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
break;
}
WriteFile(hFile, struFaceMatchAlarm.pSnapPicBuffer, struFaceMatchAlarm.dwSnapPicLen, &dwReturn, NULL);
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
//保存黑名单人脸图片
if (struFaceMatchAlarm.struBlackListInfo.dwBlackListPicLen > 0 && struFaceMatchAlarm.struBlackListInfo.pBuffer1 != NULL)
{
char cFilename[256] = {0};
HANDLE hFile;
DWORD dwReturn;
char chTime[128];
sprintf(chTime,"%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",struAbsTime.dwYear,struAbsTime.dwMonth,struAbsTime.dwDay,struAbsTime.dwHour,struAbsTime.dwMinute,struAbsTime.dwSecond);
sprintf(cFilename, "FaceBlackListPic[%s].jpg", chTime);
hFile = CreateFile(cFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
break;
}
WriteFile(hFile, struFaceMatchAlarm.struBlackListInfo.pBuffer1, struFaceMatchAlarm.struBlackListInfo.dwBlackListPicLen, &dwReturn, NULL);
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
}
break;
default:
printf("其他报警,报警信息类型: 0x%x\n", lCommand);
break;
}
return TRUE;
}
void main() {
//---------------------------------------
// 初始化
NET_DVR_Init();
//设置连接时间与重连时间
NET_DVR_SetConnectTime(2000, 1);
NET_DVR_SetReconnect(10000, true);
//---------------------------------------
// 注册设备
LONG lUserID;
//登录参数,包括设备地址、登录用户、密码等
NET_DVR_USER_LOGIN_INFO struLoginInfo = {0};
struLoginInfo.bUseAsynLogin = 0; //同步登录方式
strcpy(struLoginInfo.sDeviceAddress, "192.0.0.64"); //设备IP地址
struLoginInfo.wPort = 8000; //设备服务端口
strcpy(struLoginInfo.sUserName, "admin"); //设备登录用户名
strcpy(struLoginInfo.sPassword, "abcd1234"); //设备登录密码
//设备信息, 输出参数
NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = {0};
lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40);
if (lUserID < 0)
{
printf("Login failed, error code: %d\n", NET_DVR_GetLastError());
NET_DVR_Cleanup();
return;
}
//设置报警回调函数
NET_DVR_SetDVRMessageCallBack_V31(MessageCallback, NULL);
//启用布防
LONG lHandle;
NET_DVR_SETUPALARM_PARAM struAlarmParam={0};
struAlarmParam.dwSize=sizeof(struAlarmParam);
//其他报警布防参数不需要设置
lHandle = NET_DVR_SetupAlarmChan_V41(lUserID, & struAlarmParam);
if (lHandle < 0)
{
printf("NET_DVR_SetupAlarmChan_V41 error, %d\n", NET_DVR_GetLastError());
NET_DVR_Logout(lUserID);
NET_DVR_Cleanup();
return;
}
Sleep(50000); //等待过程中,如果设备上传报警信息,在报警回调函数里面接收和处理报警信息
//撤销布防上传通道
if (!NET_DVR_CloseAlarmChan_V30(lHandle))
{
printf("NET_DVR_CloseAlarmChan_V30 error, %d\n", NET_DVR_GetLastError());
NET_DVR_Logout(lUserID);
NET_DVR_Cleanup();
return;
}
//注销用户
NET_DVR_Logout(lUserID);
//释放SDK资源
NET_DVR_Cleanup();
return;
}
参考这段代码就可以把获取人脸对比信息的搞定了。
- 参考流程写一段上传人脸图片入库的代码,这里默认库已经建好了,且只有唯一一个库的ID=1,现在就是往里面写入发送图片就可以了。
NET_DVR_Init();
NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, (void *)this);
NET_DVR_SetConnectTime(3000, 2);
NET_DVR_SetReconnect(60000, false);
hikSnapInfo *data_info = new hikSnapInfo;
NET_DVR_DEVICEINFO_V40 strutDeviceInfo{0};
NET_DVR_USER_LOGIN_INFO structLoginInfo{0};
structLoginInfo.bUseAsynLogin = false;
structLoginInfo.wPort = port;
memcpy(structLoginInfo.sDeviceAddress, camera_ip.c_str(), NET_DVR_DEV_ADDRESS_MAX_LEN);
memcpy(structLoginInfo.sUserName, camera_username.c_str(), NAME_LEN);
memcpy(structLoginInfo.sPassword, camera_password.c_str(), NAME_LEN);
data_info->iUserID = NET_DVR_Login_V40(&structLoginInfo, &strutDeviceInfo);
string set_res = "";
if (data_info->iUserID < 0)
{
LOG(ERROR) << "Login failed, error code:" << NET_DVR_GetLastError() << ";ip:" << camera_ip << ";user_name:" << camera_username << ";password:" << camera_password;
NET_DVR_Cleanup();
delete data_info;
data_info = nullptr;
return JsonReturn(1001, "Login failed");
}
else
{
LOG(INFO) << "Login " << camera_ip << ":" << port << " success";
}
NET_DVR_XML_CONFIG_INPUT input_param;
string search_library_cmd = "GET /ISAPI/Intelligent/FDLib/1";
input_param.lpRequestUrl = const_cast<char *>(search_library_cmd.c_str());
input_param.dwSize = sizeof(input_param);
input_param.dwRequestUrlLen = search_library_cmd.size();
input_param.lpInBuffer = NULL;
input_param.dwInBufferSize = 0;
NET_DVR_XML_CONFIG_OUTPUT output_param;
int out_size = 512;
char *save_output = new char[out_size];
output_param.dwSize = sizeof(output_param);
output_param.lpOutBuffer = save_output;
output_param.dwOutBufferSize = sizeof(char) * out_size;
output_param.lpStatusBuffer = NULL;
output_param.dwStatusSize = 0;
//first search default face library
bool find_re = NET_DVR_STDXMLConfig(data_info->iUserID, &input_param, &output_param);
if (find_re)
{
printf("%s\n", save_output);
LOG(INFO) << "search face library success then do upload face";
NET_DVR_FACELIB_COND facelib_cond;
facelib_cond.dwSize = sizeof(facelib_cond);
string face_id = "1";
memcpy(facelib_cond.szFDID, face_id.c_str(), face_id.size());
facelib_cond.byConcurrent = 0;
facelib_cond.byCover = 0;
facelib_cond.byCustomFaceLibID = 0;
//init upload face
int upload_handle = NET_DVR_UploadFile_V40(data_info->iUserID, IMPORT_DATA_TO_FACELIB, &facelib_cond, sizeof(facelib_cond), NULL, NULL, 0);
if (upload_handle == -1)
{
LOG(ERROR) << "init upload failed code:" << NET_DVR_GetLastError();
return JsonReturn(1001, "init upload failed");
}
else
{
LOG(INFO) << "upload file beign ....";
vector<PersonInfo> persons_upload_info;
cJSON *root = cJSON_Parse(person_info.c_str());
cJSON *json_person_info = cJSON_GetObjectItem(root, "persons");
if (json_person_info == nullptr)
{
LOG(ERROR) << "person info has problem";
return JsonReturn(1001, "person info has problem");
}
else
{
cJSON *child_person = json_person_info->child;
while (child_person != nullptr)
{
PersonInfo person_obj;
cJSON *json_id = cJSON_GetObjectItem(child_person, "id");
if (json_id == nullptr)
{
LOG(ERROR) << "ID json not exists please check config json file";
return JsonReturn(1001, "person info has problem");
}
person_obj.id = json_id->valuestring;
cJSON *json_image = cJSON_GetObjectItem(child_person, "image");
if (json_image == nullptr)
{
LOG(ERROR) << "image json not exists please check config json file";
return JsonReturn(1001, "person info has problem");
}
string image_path = json_image->valuestring;
cJSON *json_name = cJSON_GetObjectItem(child_person, "name");
if (json_name == nullptr)
{
LOG(ERROR) << "name json not exists please check config json file";
return JsonReturn(1001, "person info has problem");
}
string person_name = json_name->valuestring;
LOG(INFO) << "person id is : " << person_obj.id << " :" << image_path << " : " << person_name;
child_person = child_person->next;
stringstream buffer;
ifstream in(image_path);
buffer.clear();
buffer.str("");
buffer << in.rdbuf();
string contents(buffer.str());
string image_name = BaseName(image_path);
if (contents.size() > 1000)
{
//todo this only consider the jpg format
NET_DVR_SEND_PARAM_IN param_in;
param_in.pSendData = reinterpret_cast<unsigned char *>(const_cast<char *>(contents.c_str()));
param_in.dwSendDataLen = contents.size();
LOG(INFO) << "image size is :" << param_in.dwSendDataLen;
param_in.byPicType = 1;
param_in.byPicURL = 0;
param_in.byUploadModeling = 0;
param_in.dwPicMangeNo = 0;
param_in.dwPicDisplayTime = 0;
memcpy(param_in.sPicName, image_name.c_str(), NAME_LEN);
string xml_header = "<FaceAppendData>";
string name_data = "<name>" + person_name + "</name>";
string certificate_number = "<certificateNumber>" + person_obj.id + "</certificateNumber>";
//todo may be need to save
//string person_info_extend = "<PersonInfoExtendList><PersonInfoExtend><id>" + person_obj.id + "</id></PersonInfoExtend></PersonInfoExtendList>";
string xml_end = "</FaceAppendData>";
string append_data = xml_header + name_data + certificate_number + xml_end;
param_in.pSendAppendData = reinterpret_cast<unsigned char *>(const_cast<char *>(append_data.c_str()));
param_in.dwSendAppendDataLen = append_data.size();
NET_DVR_TIME_V30 net_time;
struct tm *t;
time_t tt;
time(&tt);
t = localtime(&tt);
net_time.wYear = t->tm_year + 1900;
net_time.byMonth = t->tm_mon;
net_time.byDay = t->tm_mday;
net_time.byHour = t->tm_hour;
net_time.byMinute = t->tm_min;
net_time.bySecond = t->tm_sec;
param_in.struTime = net_time;
int re = NET_DVR_UploadSend(upload_handle, ¶m_in, NULL);
if (re == -1)
{
LOG(ERROR) << "send data occurs problem " << NET_DVR_GetLastError();
}
else
{
LOG(INFO) << " do face uplaod .... ";
unsigned int progress = 0;
while (true)
{
int state = NET_DVR_GetUploadState(upload_handle, &progress);
if (state == -1)
{
LOG(ERROR) << "get upload state occurs problem " << NET_DVR_GetLastError();
break;
}
if (state != 2)
{
if (state == 1)
{
person_obj.status = 1;
NET_DVR_UPLOAD_FILE_RET upload_ret;
NET_DVR_GetUploadResult(upload_handle, &upload_ret, sizeof(upload_ret));
person_obj.pic_id = reinterpret_cast<char *>(upload_ret.sUrl);
LOG(INFO) << "upload one face image success " << person_obj.id << "and pic id is :" << person_obj.pic_id;
}
else
{
person_obj.status = 0;
if (state == 39 || state == 40 || state == 41)
{
LOG(ERROR) << "extract face feature occurs problem";
}
}
break;
}
}
}
}
else
{
LOG(INFO) << "imagae size is too small" << contents.size();
person_obj.status = 0;
}
persons_upload_info.push_back(person_obj);
}
}
set_res = GenerateResult(1000, persons_upload_info);
}
NET_DVR_UploadClose(upload_handle);
}
else
{
LOG(ERROR) << "search error code:" << NET_DVR_GetLastError();
return JsonReturn(1001, "Please first create library");
}
delete[] save_output;
return set_res;