上传人脸图片到人脸库
controller:
@RestController
@RequestMapping("/facePicture")
public class FacePictureHandleController {
@Autowired
private FacePictureHandleService facePictureHandleService;
@RequestMapping(value = "/handle/uploadFile")
public String padUploadFile(@RequestParam("file") MultipartFile file,HttpServletRequest request) {
try {
FacePicture facePicture=new FacePicture();
HttpSession session = request.getSession(true);
Map<String, String> userInfo = (Map<String, String>) session
.getAttribute(SessionConstants.SESSION_KEY_USER_INFO);
facePicture.setCardId(userInfo.get("loginUser"));
File f=null;
String fileName = file.getOriginalFilename();
try {
//获取文件字节数组
byte [] bytes = file.getBytes();
File pfile = new File("/fileupload/");
//判断文件夹是否存在
if(!pfile.exists()){
//不存在时,创建文件夹
pfile.mkdirs();
}
//创建文件
f = new File(pfile, fileName);
//写入指定文件夹
OutputStream out = new FileOutputStream(f);
out.write(bytes);
} catch (IOException e) {
e.printStackTrace();
return "上传失败";
}
Integer facePictureRes = facePictureHandleService.saveFacePictureHandle(facePicture,f.getPath());
if(facePictureRes==0){
return "上传失败";
}
return "上传成功";
} catch (Exception e) {
log.error("上传文件异常", e);
}
return "上传失败";
}
}
service:
@Service
public class FacePictureHandleService {
/**
*配置文件
*/
@Value("${groupId}")
private String groupId;
@Value("${uploadWaitTime}")
private int uploadWaitTime;
@Value("${groupName}")
private String groupName;
@Autowired
private FacePictureHandleDao facePictureHandleDao;
@Autowired
private FaceDetectService faceDetectService;
@Autowired
private UserInfoDAO userInfoDao;
/**
* 保存人脸图片到人脸库
* @return
*/
public Integer saveFacePictureHandle(FacePicture facePicture, String filePath) throws UnsupportedEncodingException {
Memory fileMemory=new Memory(2000000);
try {
ToolKits.ReadAllFileToMemory(filePath,fileMemory);
} catch (IOException e) {
e.printStackTrace();
}
List<UserInfo> byUserNameIn = userInfoDao.findByUserNameIn(Collections.singletonList(facePicture.getCardId()));
facePicture.setName(byUserNameIn.get(0).getName());
facePicture.setFaceDatabaseName(groupName);
String gender = byUserNameIn.get(0).getGender();
int sex=0;
if("F".equals(gender)){
sex=2;
}else if("M".equals(gender)){
sex=1;
}
Long fId= facePictureHandleDao.queryFaceDatabaseId(facePicture.getCardId());
if(fId==null){ //未上传图片
//上传图片到人脸库
boolean updalodRes = addPerson(groupId, fileMemory, facePicture.getName(), sex, false, "", 0, "",facePicture);
if(updalodRes){
facePictureHandleDao.save(facePicture);
return 1;
}
}else{ //已上传过图片
String uId = facePictureHandleDao.queryFaceDatabaseUid(fId);
//修改人脸库图片
boolean updalodRes = modifyPerson(groupId, uId,fileMemory, facePicture.getName(), sex, false, "", 0, "");
if(updalodRes){
facePicture.setId(fId);
int i = facePictureHandleDao.updatePictureData(fId);
return i;
}
}
File file=new File(filePath);
if(file!=null){
file.deleteOnExit();
}
return 0;
}
public File transferToFile(MultipartFile multipartFile) {
File file = null;
try {
String originalFilename = multipartFile.getOriginalFilename();
String[] filename = originalFilename.split("\\.");
file=File.createTempFile(filename[0]+"temp", filename[1]);
multipartFile.transferTo(file);
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
/**
* 添加人员
* @param groupId 人脸库ID
* @param memory 图片数据
* @param personName 姓名
* @param sex 性别
* @param isBirthday 是否下发生日
* @param birthday 生日
* @param byIdType 证件类型
* @param idNo 证件号
* @return
*/
public boolean addPerson(String groupId,
Memory memory,
String personName,
int sex,
boolean isBirthday,
String birthday,
int byIdType,
String idNo,
FacePicture facePicture) throws UnsupportedEncodingException {
/*
* 入参
*/
NetSDKLib.NET_IN_OPERATE_FACERECONGNITIONDB stuIn = new NetSDKLib.NET_IN_OPERATE_FACERECONGNITIONDB();
stuIn.emOperateType = NetSDKLib.EM_OPERATE_FACERECONGNITIONDB_TYPE.NET_FACERECONGNITIONDB_ADD;
/ 使用人员扩展信息 //
stuIn.bUsePersonInfoEx = 1;
// 人脸库ID
System.arraycopy(groupId.getBytes(), 0, stuIn.stPersonInfoEx.szGroupID, 0, groupId.getBytes().length);
// 生日设置
if(isBirthday) {
String[] birthdays = birthday.split("-");
stuIn.stPersonInfoEx.wYear = (short)Integer.parseInt(birthdays[0]);
stuIn.stPersonInfoEx.byMonth = (byte)Integer.parseInt(birthdays[1]);
stuIn.stPersonInfoEx.byDay = (byte)Integer.parseInt(birthdays[2]);
}
// 性别,1-男,2-女,作为查询条件时,此参数填0,则表示此参数无效
stuIn.stPersonInfoEx.bySex = (byte)sex;
// 人员名字
try {
System.arraycopy(personName.getBytes("GBK"), 0, stuIn.stPersonInfoEx.szPersonName, 0, personName.getBytes("GBK").length);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 证件类型
stuIn.stPersonInfoEx.byIDType = (byte)byIdType;
// 证件号
System.arraycopy(idNo.getBytes(), 0, stuIn.stPersonInfoEx.szID, 0, idNo.getBytes().length);
// 图片张数、大小、缓存设置
if(memory != null) {
stuIn.stPersonInfoEx.wFacePicNum = 1; // 图片张数
stuIn.stPersonInfoEx.szFacePicInfo[0].dwFileLenth = (int)memory.size(); // 图片大小
stuIn.stPersonInfoEx.szFacePicInfo[0].dwOffSet = 0;
stuIn.nBufferLen = (int)memory.size();
stuIn.pBuffer = memory;
}
/*
* 出参
*/
NetSDKLib.NET_OUT_OPERATE_FACERECONGNITIONDB stuOut = new NetSDKLib.NET_OUT_OPERATE_FACERECONGNITIONDB() ;
stuIn.write();
boolean bRet = faceDetectService.getNetsdk().CLIENT_OperateFaceRecognitionDB(faceDetectService.getLoginHandle(), stuIn, stuOut, uploadWaitTime);
stuIn.read();
if(bRet) {
System.out.println("szUID : " + new String(stuOut.szUID).trim());
} else {
System.err.println(faceDetectService.netsdk.CLIENT_GetLastError());
}
facePicture.setFaceDatabaseUid(new String(stuOut.szUID, "GBK").trim());
return bRet;
}
/**
* 修改人员信息
* @param groupId 人脸库ID
* @param uid 人员唯一标识符
* @param memory 图片数据
* @param personName 姓名
* @param sex 性别
* @param isBirthday 是否下发生日
* @param birthday 生日
* @param byIdType 证件类型
* @param idNo 证件号
* @return true:成功 , false:失败
*/
public boolean modifyPerson(String groupId,
String uid,
Memory memory,
String personName,
int sex,
boolean isBirthday,
String birthday,
int byIdType,
String idNo) {
// 入参
NetSDKLib.NET_IN_OPERATE_FACERECONGNITIONDB stuIn = new NetSDKLib.NET_IN_OPERATE_FACERECONGNITIONDB();
stuIn.emOperateType = NetSDKLib.EM_OPERATE_FACERECONGNITIONDB_TYPE.NET_FACERECONGNITIONDB_MODIFY;
/ 使用人员扩展信息
stuIn.bUsePersonInfoEx = 1;
// 人脸库ID
System.arraycopy(groupId.getBytes(), 0, stuIn.stPersonInfoEx.szGroupID, 0, groupId.getBytes().length);
// 人员唯一标识符
System.arraycopy(uid.getBytes(), 0, stuIn.stPersonInfoEx.szUID, 0, uid.getBytes().length);
// 生日设置
if(isBirthday) {
String[] birthdays = birthday.split("-");
stuIn.stPersonInfoEx.wYear = (short)Integer.parseInt(birthdays[0]);
stuIn.stPersonInfoEx.byMonth = (byte)Integer.parseInt(birthdays[1]);
stuIn.stPersonInfoEx.byDay = (byte)Integer.parseInt(birthdays[2]);
}
// 性别,1-男,2-女,作为查询条件时,此参数填0,则表示此参数无效
stuIn.stPersonInfoEx.bySex = (byte)sex;
// 人员名字
try {
System.arraycopy(personName.getBytes("GBK"), 0, stuIn.stPersonInfoEx.szPersonName, 0, personName.getBytes("GBK").length);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 证件类型
stuIn.stPersonInfoEx.byIDType = (byte)byIdType;
// 证件号
System.arraycopy(idNo.getBytes(), 0, stuIn.stPersonInfoEx.szID, 0, idNo.getBytes().length);
// 图片张数、大小、缓存设置
if(memory != null) {
stuIn.stPersonInfoEx.wFacePicNum = 1; // 图片张数
stuIn.stPersonInfoEx.szFacePicInfo[0].dwFileLenth = (int)memory.size(); // 图片大小
stuIn.stPersonInfoEx.szFacePicInfo[0].dwOffSet = 0;
stuIn.nBufferLen = (int)memory.size();
stuIn.pBuffer = memory;
}
// 出参
NetSDKLib.NET_OUT_OPERATE_FACERECONGNITIONDB stuOut = new NetSDKLib.NET_OUT_OPERATE_FACERECONGNITIONDB() ;
stuIn.write();
boolean b = faceDetectService.getNetsdk().CLIENT_OperateFaceRecognitionDB(faceDetectService.getLoginHandle(), stuIn, stuOut, uploadWaitTime);
if(!b) {
System.err.println("修改人员失败" + ToolKits.getErrorCodePrint());
return false;
}
stuIn.read();
return true;
}
/**
* 查询人脸库
* @param groupId 需要查找的人脸库ID; 为空表示查找所有的人脸库
*/
public NetSDKLib.NET_FACERECONGNITION_GROUP_INFO[] findGroupInfo(String groupId) {
NetSDKLib.NET_FACERECONGNITION_GROUP_INFO[] groupInfoRet = null;
/*
* 入参
*/
NetSDKLib.NET_IN_FIND_GROUP_INFO stuIn = new NetSDKLib.NET_IN_FIND_GROUP_INFO();
System.arraycopy(groupId.getBytes(), 0, stuIn.szGroupId, 0, groupId.getBytes().length);
/*
* 出参
*/
int max = 20;
NetSDKLib.NET_FACERECONGNITION_GROUP_INFO[] groupInfo = new NetSDKLib.NET_FACERECONGNITION_GROUP_INFO[max];
for(int i = 0; i < max; i++) {
groupInfo[i] = new NetSDKLib.NET_FACERECONGNITION_GROUP_INFO();
}
NetSDKLib.NET_OUT_FIND_GROUP_INFO stuOut = new NetSDKLib.NET_OUT_FIND_GROUP_INFO();
stuOut.pGroupInfos = new Memory(groupInfo[0].size() * groupInfo.length); // Pointer初始化
stuOut.pGroupInfos.clear(groupInfo[0].size() * groupInfo.length);
stuOut.nMaxGroupNum = groupInfo.length;
ToolKits.SetStructArrToPointerData(groupInfo, stuOut.pGroupInfos); // 将数组内存拷贝给Pointer
if(faceDetectService.netsdk.CLIENT_FindGroupInfo(faceDetectService.getLoginHandle(), stuIn, stuOut, 4000)) {
// 将Pointer的值输出到 数组 NET_FACERECONGNITION_GROUP_INFO
ToolKits.GetPointerDataToStructArr(stuOut.pGroupInfos, groupInfo);
if(stuOut.nRetGroupNum > 0) {
// 根据设备返回的,将有效的人脸库信息返回
groupInfoRet = new NetSDKLib.NET_FACERECONGNITION_GROUP_INFO[stuOut.nRetGroupNum];
for(int i = 0; i < stuOut.nRetGroupNum; i++) {
groupInfoRet[i] = groupInfo[i];
}
}
} else {
System.err.println("查询人员信息失败" + ToolKits.getErrorCodePrint());
return null;
}
return groupInfoRet;
}
}
Dao(JPA):
@Repository
public interface FaceDatabasePictureDao extends CrudRepository<FacePicture, String> {
//查询是否已上传过图片
@Query("select id from FacePicture where cardId=?")
public Long queryFaceDatabaseId(String cardId);
//查询是否已上传过图片
@Query("select faceDatabaseUid from FacePicture where id=?")
public String queryFaceDatabaseUid(Long id);
@Modifying
@Query(value="update FacePicture set updatedDate=sysdate() where id=?")
@Transactional
public int updatePictureData(Long id);
}
人脸识别对比
由于项目启动时就要启动,使用@PostConstruct注解。
service:
@Service
public class FaceDetectService{
@Value("${mstrIp}")
private String m_strIp;
@Value("${mnPort}")
private int m_nPort;
@Value("${mstrUser}")
private String m_strUser;
@Value("${mstrPassword}")
private String m_strPassword;
private static boolean bInit = false;
private static boolean bLogopen = false;
@Autowired
private FaceDetectDao faceDetectDaoA;
private static FaceDetectDao faceDetectDao;
@PostConstruct
public void init() {
faceDetectDao=this.faceDetectDaoA;
//初始化
InitTest(disConnect, haveReConnect);
//登录
Login();
Run();
}
public NetSDKLib getNetsdk(){
return netsdk;
}
public NetSDKLib.LLong getLoginHandle(){
return loginHandle;
}
public static final NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;
// 登陆句柄
public static NetSDKLib.LLong loginHandle = new NetSDKLib.LLong(0);
// 订阅句柄
private static NetSDKLib.LLong r_loginHandle = new NetSDKLib.LLong(0);
// 设备信息扩展
private NetSDKLib.NET_DEVICEINFO_Ex deviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
// 全景图
private static BufferedImage globalBufferedImage = null;
// 人脸图
private static BufferedImage personBufferedImage = null;
// 候选人图
private static BufferedImage candidateBufferedImage = null;
// 用于人脸检测
@Value("${face.groupId}")
private int groupId;
@Value("${face.channel}")
private int channel;
@Value("${face.bNeedPicture}")
private int bNeedPicture;
private static int index = -1;
// 设备断线通知回调
private static DisConnect disConnect = new DisConnect();
// 网络连接恢复
private static HaveReConnect haveReConnect = new HaveReConnect();
public void Run() {
//订阅
RealLoadPicture(channel, bNeedPicture, AnalyzerDataCB.getInstance());
}
// 设备断线回调: 通过 CLIENT_Init 设置该回调函数,当设备出现断线时,SDK会调用该函数
private static class DisConnect implements NetSDKLib.fDisConnect {
public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
System.out.printf("Device[%s] Port[%d] DisConnect!\n", pchDVRIP, nDVRPort);
}
}
// 网络连接恢复,设备重连成功回调
// 通过 CLIENT_SetAutoReconnect 设置该回调函数,当已断线的设备重连成功时,SDK会调用该函数
private static class HaveReConnect implements NetSDKLib.fHaveReConnect {
public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
System.out.printf("ReConnect Device[%s] Port[%d]\n", pchDVRIP, nDVRPort);
}
}
/**
* 写成静态主要是防止被回收
*/
private static class AnalyzerDataCB implements NetSDKLib.fAnalyzerDataCallBack {
private AnalyzerDataCB() {
}
private static class AnalyzerDataCBHolder {
private static final FaceDetectService.AnalyzerDataCB instance = new FaceDetectService.AnalyzerDataCB();
}
public static FaceDetectService.AnalyzerDataCB getInstance() {
return FaceDetectService.AnalyzerDataCB.AnalyzerDataCBHolder.instance;
}
public int invoke(
NetSDKLib.LLong lAnalyzerHandle,
int dwAlarmType,
Pointer pAlarmInfo,
Pointer pBuffer,
int dwBufSize,
Pointer dwUser,
int nSequence,
Pointer reserved) {
if (lAnalyzerHandle.longValue() == 0 || pAlarmInfo == null) {
return -1;
}
switch (dwAlarmType) {
case NetSDKLib.EVENT_IVS_FACERECOGNITION: // /< 人脸识别事件
{
// DEV_EVENT_FACERECOGNITION_INFO 结构体比较大,new对象会比较耗时, ToolKits.GetPointerData内容拷贝是不耗时的。
// 如果多台设备或者事件处理比较频繁,可以考虑将 static DEV_EVENT_FACERECOGNITION_INFO msg = new
// DEV_EVENT_FACERECOGNITION_INFO(); 改为全局。
// 写成全局,是因为每次new花费时间较多, 如果改为全局,此case下的处理需要加锁
// 加锁,是因为共用一个对象,防止数据出错
// 耗时800ms左右
NetSDKLib.DEV_EVENT_FACERECOGNITION_INFO msg = new NetSDKLib.DEV_EVENT_FACERECOGNITION_INFO();
// 耗时20ms左右
ToolKits.GetPointerData(pAlarmInfo, msg);
//保存人脸对比信息
try {
saveFaceDetectInfo(msg);
} catch (IOException e) {
e.printStackTrace();
}
// 释放内存
msg = null;
System.gc();
break;
}
default:
break;
}
return 0;
}
/**
* 保存人脸对比信息
*/
public void saveFaceDetectInfo(NetSDKLib.DEV_EVENT_FACERECOGNITION_INFO faceMsg) throws IOException {
//人脸信息
if (faceMsg.stuFaceData != null) {
Res res=new Res();
FaceDetect faceDetect =new FaceDetect();
faceDetect.setName(new String(faceMsg.stuCandidatesEx[0].stPersonInfo.szPersonName, "GBK")
.trim());
faceDetect.setSex(res.getSex(faceMsg.stuFaceData.emSex));
faceDetect.setDetectTime(Timestamp.valueOf(faceMsg.UTC.toString()));
faceDetect.setFaceDatabaseName(new String(faceMsg.stuCandidatesEx[0].stPersonInfo.szGroupName, "GBK")
.trim());
faceDetect.setSimilarity(String.valueOf(faceMsg.stuCandidatesEx[0].bySimilarity));
faceDetect.setFaceDatabaseUid(new String(faceMsg.stuCandidatesEx[0].stPersonInfo.szUID).trim());
faceDetectDao.save(faceDetect);
}
};
}
// 订阅
public void RealLoadPicture(int channel, int bNeedPicture, NetSDKLib.fAnalyzerDataCallBack callback) {
r_loginHandle = netsdk.CLIENT_RealLoadPictureEx(loginHandle, channel,
NetSDKLib.EVENT_IVS_ALL, bNeedPicture, callback, null, null);
if (loginHandle.longValue() == 0) {
System.err.println("CLIENT_RealLoadPictureEx Failed, Error:" + ToolKits.getErrorCodePrint());
} else {
System.out.println("通道[" + channel + "]订阅成功!");
}
}
//初始化
public static boolean InitTest(NetSDKLib.fDisConnect disConnect, NetSDKLib.fHaveReConnect haveReConnect) {
bInit = netsdk.CLIENT_Init(disConnect, null);
if (!bInit) {
System.out.println("Initialize SDK failed");
return false;
}
//打开日志,可选
NetSDKLib.LOG_SET_PRINT_INFO setLog = new NetSDKLib.LOG_SET_PRINT_INFO();
File path = new File("./sdklog/");
if (!path.exists()) {
path.mkdir();
}
String logPath = path.getAbsoluteFile().getParent() + "\\sdklog\\" + ToolKits.getDate() + ".log";
setLog.nPrintStrategy = 0;
setLog.bSetFilePath = 1;
System.arraycopy(logPath.getBytes(), 0, setLog.szLogFilePath, 0, logPath.getBytes().length);
System.out.println(logPath);
setLog.bSetPrintStrategy = 1;
bLogopen = netsdk.CLIENT_LogOpen(setLog);
if (!bLogopen) {
System.err.println("Failed to open NetSDK log");
}
// 设置断线重连回调接口,设置过断线重连成功回调函数后,当设备出现断线情况,SDK内部会自动进行重连操作
// 此操作为可选操作,但建议用户进行设置
netsdk.CLIENT_SetAutoReconnect(haveReConnect, null);
//设置登录超时时间和尝试次数,可选
int waitTime = 5000; //登录请求响应超时时间设置为5S
int tryTimes = 1; //登录时尝试建立链接1次
netsdk.CLIENT_SetConnectTime(waitTime, tryTimes);
// 设置更多网络参数,NET_PARAM的nWaittime,nConnectTryNum成员与CLIENT_SetConnectTime
// 接口设置的登录设备超时时间和尝试次数意义相同,可选
NetSDKLib.NET_PARAM netParam = new NetSDKLib.NET_PARAM();
netParam.nConnectTime = 10000; // 登录时尝试建立链接的超时时间
netParam.nGetConnInfoTime = 3000; // 设置子连接的超时时间
netParam.nGetDevInfoTime = 3000;//获取设备信息超时时间,为0默认1000ms
netsdk.CLIENT_SetNetworkParam(netParam);
return true;
}
//登陆
public void Login() {
// 登陆设备
int nSpecCap = NetSDKLib.EM_LOGIN_SPAC_CAP_TYPE.EM_LOGIN_SPEC_CAP_TCP; // TCP登入
IntByReference nError = new IntByReference(0);
loginHandle = netsdk.CLIENT_LoginEx2(m_strIp, m_nPort, m_strUser,
m_strPassword, nSpecCap, null, deviceInfo, nError);
if (loginHandle.longValue() != 0) {
System.out.printf("Login Device[%s] Success!\n", m_strIp);
} else {
System.err.printf("Login Device[%s] Fail.Error[0x%x]\n", m_strIp, netsdk.CLIENT_GetLastError());
LoginOut();
}
}
//登出
public void LoginOut() {
if (loginHandle.longValue() != 0) {
netsdk.CLIENT_Logout(loginHandle);
}
System.out.println("END...");
netsdk.CLIENT_Cleanup();
}
/**
* 设备断线回调
*/
private static class DisConnectCallBack implements NetSDKLib.fDisConnect {
private DisConnectCallBack() {
}
private static class CallBackHolder {
private static FaceDetectService.DisConnectCallBack instance = new FaceDetectService.DisConnectCallBack();
}
public static FaceDetectService.DisConnectCallBack getInstance() {
return FaceDetectService.DisConnectCallBack.CallBackHolder.instance;
}
public void invoke(NetSDKLib.LLong lLoginID, String pchDVRIP, int nDVRPort, Pointer dwUser) {
System.out.printf("Device[%s] Port[%d] DisConnect!\n", pchDVRIP, nDVRPort);
}
}
/**
* 设备重连回调
*/
private static class HaveReConnectCallBack implements NetSDKLib.fHaveReConnect {
private HaveReConnectCallBack() {
}
private static class CallBackHolder {
private static FaceDetectService.HaveReConnectCallBack instance = new FaceDetectService.HaveReConnectCallBack();
}
public static FaceDetectService.HaveReConnectCallBack getInstance() {
return FaceDetectService.HaveReConnectCallBack.CallBackHolder.instance;
}
public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
System.out.printf("ReConnect Device[%s] Port[%d]\n", pchDVRIP, nDVRPort);
}
}
}
Dao(JPA):
@Repository
public interface FaceDetectDao extends CrudRepository<FaceDetect, String> {
}
注意:
1、上传图片到人脸库时有一个问题,修改人员信息时,修改的图片与上一个图片是同一张时时会导致图片是空的(不知道什么原因)。
2、new Memory设置太大会导致超时。