feat(视频):

视频初步
This commit is contained in:
Guwan 2024-12-08 18:42:46 +08:00
parent b007381a2c
commit b8e7422a60
47 changed files with 2833 additions and 93 deletions

View File

@ -1,9 +1,378 @@
# 视频模块接口文档
\# 视频模块接口文档
\## 基础信息
## 基础信息
- 基础路径: `/api/videos`
- 请求头: 需要携带 `Authorization: Bearer {token}` (除了获取视频列表和视频详情)
## 接口列表
\## 接口列表
### 1. 上传视频
\### 1. 上传视频
POST /api/videos/upload
// 请求参数 (multipart/form-data)
{
file: File, // 视频文件
title: string, // 视频标题
description: string, // 视频描述
tags?: string // 视频标签,可选,多个标签用逗号分隔
}
// 响应
{
code: number, // 200表示成功
message: string, // 响应消息
data: {
id: number, // 视频ID
title: string, // 视频标题
description: string, // 视频描述
url: string, // 视频URL
coverUrl: string, // 封面URL
duration: number, // 视频时长(秒)
size: number, // 文件大小(字节)
status: string, // 状态DRAFT-草稿PUBLISHED-已发布DELETED-已删除
userId: number, // 上传用户ID
username: string, // 上传用户名
createdTime: string, // 创建时间
updatedTime: string, // 更新时间
viewCount: number, // 观看次数
likeCount: number, // 点赞次数
tags: string, // 标签
hasLiked: boolean // 当前用户是否已点赞
}
}
\### 2. 获取视频列表
GET /api/videos?pageNum=1&pageSize=10&keyword=xxx
// 请求参数 (query)
{
pageNum?: number, // 页码默认1
pageSize?: number, // 每页条数默认10
keyword?: string // 搜索关键词,可选
}
// 响应
{
code: number,
message: string,
data: {
records: Array<{ // 视频列表
id: number,
title: string,
description: string,
url: string,
coverUrl: string,
duration: number,
size: number,
status: string,
userId: number,
username: string,
createdTime: string,
updatedTime: string,
viewCount: number,
likeCount: number,
tags: string,
hasLiked: boolean
}>,
total: number, // 总记录数
size: number, // 每页条数
current: number, // 当前页码
pages: number // 总页数
}
}
\### 3. 获取视频详情
GET /api/videos/{id}
// 响应
{
code: number,
message: string,
data: {
id: number,
title: string,
description: string,
url: string,
coverUrl: string,
duration: number,
size: number,
status: string,
userId: number,
username: string,
createdTime: string,
updatedTime: string,
viewCount: number,
likeCount: number,
tags: string,
hasLiked: boolean
}
}
\### 4. 更新视频信息
PUT /api/videos/{id}
// 请求体
{
title: string,
description: string,
tags?: string
}
// 响应
{
code: number,
message: string,
data: VideoDTO // 同上面的视频详情
}
\### 5. 删除视频
DELETE /api/videos/{id}
// 响应
{
code: number,
message: string,
data: null
}
\### 6. 视频点赞/取消点赞
POST /api/videos/{id}/like
// 响应
{
code: number,
message: string,
data: null
}
\### 7. 增加观看次数
POST /api/videos/{id}/view
// 响应
{
code: number,
message: string,
data: null
}
\### 8. 获取推荐视频
GET /api/videos/recommend?limit=10
// 请求参数 (query)
{
limit?: number // 返回数量默认10
}
// 响应
{
code: number,
message: string,
data: Array<VideoDTO> // 视频列表
}
\### 9. 获取相似视频
GET /api/videos/{id}/similar?limit=10
// 请求参数 (query)
{
limit?: number // 返回数量默认10
}
// 响应
{
code: number,
message: string,
data: Array<VideoDTO> // 视频列表
}
\## 错误码说明
{
200: "操作成功",
400: "请求参数错误",
401: "未登录或token已过期",
403: "无权限执行此操作",
404: "资源不存在",
500: "服务器内部错误"
}
\## 数据结构
\### VideoDTO
{
id: number; // 视频ID
title: string; // 视频标题
description: string; // 视频描述
url: string; // 视频URL
coverUrl: string; // 封面URL
duration: number; // 视频时长(秒)
size: number; // 文件大小(字节)
status: string; // 状态DRAFT-草稿PUBLISHED-已发布DELETED-已删除
userId: number; // 上传用户ID
username: string; // 上传用户名
createdTime: string; // 创建时间
updatedTime: string; // 更新时间
viewCount: number; // 观看次数
likeCount: number; // 点赞次数
tags: string; // 标签,多个用逗号分隔
hasLiked: boolean; // 当前用户是否已点赞
}

View File

@ -2,7 +2,7 @@
## 基础信息
- 基础路径: `/api/videos`
- 请求头: 需要携带 `Authorization: Bearer {token}` (除了获取视频列表和视频详情)
- 请求头: 需要携带 `Authorization: Bearer {token}` (除了获取视频列表和视频详情)
## 接口列表

View File

@ -214,6 +214,15 @@
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.arcsoft.face</groupId>
<artifactId>arcsoft-sdk-face</artifactId>
<version>4.1.1.0</version>
<!-- <scope>system</scope>-->
<!-- <systemPath>${project.basedir}/libs/arcsoft-sdk-face-3.0.0.0.jar</systemPath>-->
</dependency>
</dependencies>
<build>

View File

@ -1,44 +0,0 @@
import torch
import torch.nn as nn
import torch.optim as optim
import pandas as pd
from sklearn.preprocessing import StandardScaler
class RecommendModel(nn.Module):
def __init__(self, input_size, embedding_size):
super().__init__()
self.encoder = nn.Sequential(
nn.Linear(input_size, 256),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(256, 128),
nn.ReLU(),
nn.Linear(128, embedding_size)
)
def forward(self, x):
return self.encoder(x)
def train_model():
# 加载数据
videos_df = pd.read_csv('videos.csv')
behaviors_df = pd.read_csv('behaviors.csv')
# 数据预处理
scaler = StandardScaler()
features = scaler.fit_transform(videos_df[['duration', 'view_count', 'like_count']])
# 创建模型
model = RecommendModel(input_size=features.shape[1], embedding_size=128)
criterion = nn.CosineEmbeddingLoss()
optimizer = optim.Adam(model.parameters())
# 训练模型
for epoch in range(100):
# ... 训练代码
# 保存模型
torch.save(model.state_dict(), '../src/main/resources/models/recommend_model.pt')
if __name__ == '__main__':
train_model()

View File

@ -2,15 +2,15 @@ package com.guwan.backend.Handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Date;
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createdTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updatedTime", LocalDateTime.class, LocalDateTime.now());
}
@Override

View File

@ -1,24 +0,0 @@
package com.guwan.backend;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
public class JwtExample {
public static void main(String[] args) {
// 选择您的 HMAC-SHA 算法
SignatureAlgorithm algorithm = SignatureAlgorithm.HS256;
// 使用推荐的密钥生成方法生成密钥
SecretKey key = Keys.secretKeyFor(algorithm);
System.out.println("key = " + key);
SecretKey key2 = Keys.secretKeyFor(SignatureAlgorithm.HS512);
System.out.println("key2 = " + key2);
// 现在 key 是一个足够安全的密钥可以用于 JWT 签名
// 您可以使用这个密钥来创建和验证 JWT
}
}

View File

@ -1,15 +1,15 @@
package com.guwan.backend.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.security.SecurityScheme;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Configuration

View File

@ -31,7 +31,7 @@ public class SecurityConstants {
public static final List<String> STATIC_RESOURCES = List.of(
"/static/**", // 静态资源目录
"/public/**", // 公共资源目录
"/error", // 错误页面
"/swagger-ui.html"
"/error" // 错误页面
);
}

View File

@ -1,18 +1,33 @@
package com.guwan.backend.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.arcsoft.face.ActiveDeviceInfo;
import com.arcsoft.face.ActiveFileInfo;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.enums.ExtractType;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import com.guwan.backend.common.Result;
import com.guwan.backend.service.EmailService;
import com.guwan.backend.util.RedisUtils;
import com.guwan.backend.util.SmsUtils;
import com.guwan.backend.face.dto.FaceRecognitionResDTO;
import com.guwan.backend.face.service.FaceEngineService;
import com.guwan.backend.util.MinioUtil;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.thymeleaf.context.Context;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.List;
import java.util.regex.Pattern;
@Slf4j
@RestController
@ -21,6 +36,160 @@ import org.thymeleaf.context.Context;
@Validated
public class DemoController {
@Autowired
private FaceEngineService faceEngineService;
@Autowired
private MinioClient minioClient;
@Autowired
private MinioUtil minioUtil;
@PostMapping("/uploadFile")
public Result<String> uploadFile(String bucketName, MultipartFile file){
return Result.success(minioUtil.getUrl(minioUtil.getFileUrl
(bucketName, minioUtil.uploadFile(bucketName, file))));
}
public int saveTenPerson() {
try {
try {
GetObjectArgs args = GetObjectArgs.builder()
.bucket("images")
.object("test/webwxgetmsgimg.jpg")
.build();
//判断人脸照片是否合格
//1.保存到本地
InputStream tempInputStream = minioClient.getObject(args);
//----------------算法检测----------------------------------------------
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = tempInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
byte[] bytes = outputStream.toByteArray();
outputStream.close();
tempInputStream.close();
ImageInfo rgbData = ImageFactory.getRGBData(bytes);
List<FaceInfo> faceInfoList = faceEngineService.detectFaces(rgbData);
if (CollectionUtil.isNotEmpty(faceInfoList)) {
FaceInfo faceInfo = faceInfoList.get(0);
FaceRecognitionResDTO faceRecognitionResDTO = new FaceRecognitionResDTO();
faceRecognitionResDTO.setRect(faceInfo.getRect());
byte[] featureBytes = faceEngineService.extractFaceFeature(rgbData, faceInfo, ExtractType.REGISTER);
if (featureBytes != null) {
System.out.println("featureBytes = " + featureBytes);
}else{
log.error("图片不合格,未检测到人脸");
return 2;
}
}else{
log.error("图片不合格,未检测到人脸");
return 2;
}
} catch (Exception e) {
e.printStackTrace();
return 10;
}
} catch (Exception e) {
e.printStackTrace();
}
return 1;
}
@GetMapping("/offline")
public Result activeOffline() {
FaceEngine faceEngine;
String USER_HOME = System.getProperty("user.home");
System.out.println(USER_HOME);
String jvmName = System.getProperty("java.vm.name", "").toLowerCase();
String osName = System.getProperty("os.name", "").toLowerCase();
String osArch = System.getProperty("os.arch", "").toLowerCase();
String abiType = System.getProperty("sun.arch.abi", "").toLowerCase();
String libPath = System.getProperty("sun.boot.library.path", "").toLowerCase();
USER_HOME = System.getProperty("user.home");
if (jvmName.startsWith("dalvik") && osName.startsWith("linux")) {
osName = "android";
} else if (jvmName.startsWith("robovm") && osName.startsWith("darwin")) {
osName = "ios";
osArch = "arm";
} else if (osName.startsWith("mac os x") || osName.startsWith("darwin")) {
osName = "macosx";
} else {
int spaceIndex = osName.indexOf(' ');
if (spaceIndex > 0) {
osName = osName.substring(0, spaceIndex);
}
}
if (osArch.equals("i386") || osArch.equals("i486") || osArch.equals("i586") || osArch.equals("i686")) {
osArch = "x86";
} else if (osArch.equals("amd64") || osArch.equals("x86-64") || osArch.equals("x64")) {
osArch = "x86_64";
} else if (osArch.startsWith("aarch64") || osArch.startsWith("armv8") || osArch.startsWith("arm64")) {
osArch = "arm64";
} else if ((osArch.startsWith("arm")) && ((abiType.equals("gnueabihf")) || (libPath.contains("openjdk-armhf")))) {
osArch = "armhf";
} else if (osArch.startsWith("arm")) {
osArch = "arm";
}
String PLATFORM = osName + "-" + osArch;
System.out.println("PLATFORM = " + PLATFORM);
String CACHE_LIB_FOLDER = USER_HOME + "/.arcface/cache/" + "4.1" + "/" + PLATFORM + "/";
System.out.println("CACHE_LIB_FOLDER = " + CACHE_LIB_FOLDER);
if (Pattern.matches("windows.*", osName)) {
System.out.println("osName = " + osName);
faceEngine = new FaceEngine(CACHE_LIB_FOLDER);
}else{
// faceEngine = new FaceEngine("D:\\00_桌面\\ArcSoft_ArcFacePro_windows_x64_java_V4.1\\libs\\WIN64");
faceEngine = new FaceEngine(CACHE_LIB_FOLDER);
}
ActiveDeviceInfo activeDeviceInfo = new ActiveDeviceInfo();
int code = faceEngine.getActiveDeviceInfo(activeDeviceInfo);
// System.out.println("\n设备信息" + activeDeviceInfo.toString() + "\n"); // 这个com.arcsoft.face.model.ActiveDeviceInfo@4ad9b7b并不是设备信息
System.out.println("设备信息:" + activeDeviceInfo.getDeviceInfo() + "\n"); // 这个的结果才是设备信息
ActiveFileInfo activeFileInfo = new ActiveFileInfo();
faceEngine.getActiveFileInfo(activeFileInfo);
System.out.println("激活文件信息:" + activeFileInfo.toString());
System.out.println("activeFileInfo = " + activeFileInfo);
return Result.success(activeDeviceInfo.getDeviceInfo());
}
}

View File

@ -24,7 +24,7 @@ public class VideoController {
@Operation(summary = "上传视频", description = "上传视频文件并返回视频信息")
@SecurityRequirement(name = "bearer-jwt")
// @SecurityRequirement(name = "bearer-jwt")
@PostMapping("/upload")
public Result<VideoDTO> uploadVideo(
@Parameter(description = "视频文件") @RequestParam("file") MultipartFile file,

View File

@ -0,0 +1,120 @@
//package com.guwan.backend.face;
//
//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
//import lombok.extern.slf4j.Slf4j;
//import net.shapelight.common.config.GlobalValue;
//import net.shapelight.modules.face.entity.UserCompareInfo;
//import net.shapelight.modules.face.service.FaceEngineService;
//import net.shapelight.modules.face.util.Base64Util;
//import net.shapelight.modules.face.util.UserInfo;
//import net.shapelight.modules.face.util.UserRamGroup;
//import net.shapelight.modules.ten.entity.TenCellEntity;
//import net.shapelight.modules.ten.entity.TenPersonEntity;
//import net.shapelight.modules.ten.service.TenCellService;
//import net.shapelight.modules.ten.service.TenPersonService;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.boot.ApplicationArguments;
//import org.springframework.boot.ApplicationRunner;
//import org.springframework.core.annotation.Order;
//import org.springframework.stereotype.Component;
//
//import java.util.List;
//
//@Component
//@Order(1)
//@Slf4j
//public class FaceEngineAutoRun implements ApplicationRunner {
// @Autowired
// private FaceEngineService faceEngineService;
// @Autowired
// private TenPersonService tenPersonService;
// @Autowired
// private TenCellService tenCellService;
// @Autowired
// private GlobalValue globalValue;
//
// @Override
// public void run(ApplicationArguments args) throws Exception {
// // 任务初始化
// log.debug("服务启动。。。。。初始化人脸库");
//// Map<String, String> fileMap = Maps.newHashMap();
//// fileMap.put("zhao1", "赵丽颖");
//// fileMap.put("yang1", "杨紫");
//// fileMap.put("baixue", "白雪");
//// fileMap.put("chenchuang", "陈创");
//// for (String f : fileMap.keySet()) {
//// ClassPathResource resource = new ClassPathResource("static/images/" + f + ".jpg");
//// InputStream inputStream = resource.getInputStream();
//// ImageInfo rgbData = ImageFactory.getRGBData(inputStream);
//// List<FaceInfo> faceInfoList = faceEngineService.detectFaces(rgbData);
//// if (CollectionUtil.isNotEmpty(faceInfoList)) {
//// byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfoList.get(0), ExtractType.REGISTER);
//// UserRamCache.UserInfo userInfo = new UserCompareInfo();
//// userInfo.setFaceId(f);
//// userInfo.setName(fileMap.get(f));
//// userInfo.setFaceFeature(feature);
//// //这边注册到内存缓存中也可以根据业务注册到数据库中
//// UserRamCache.addUser(userInfo);
//// }
//// }
//
//// int count = tenPersonService.findCount();
//// int pageSize = 1000;
//// int page = count/pageSize;
//// if(count%1000!=0){
//// page = page+1;
//// }
//// int faceCount = 0;
//// for (int i = 0; i < page; i++) {
//// int start = i*1000;
//// List<TenPersonEntity> listPage = tenPersonService.listPage(start,1000);
//// for(TenPersonEntity personEntity: listPage){
//// if(personEntity.getFeature()!=null && personEntity.getFeature().length()>0){
//// UserRamCache.UserInfo userInfo = new UserCompareInfo();
//// userInfo.setFaceId(personEntity.getPersonId()+"");
//// userInfo.setName(personEntity.getName());
//// userInfo.setFaceFeature(Base64Util.base64ToBytes(personEntity.getFeature()));
//// //这边注册到内存缓存中
//// UserRamCache.addUser(userInfo);
//// faceCount++;
//// }
//// }
//// }
//
// System.out.println(globalValue.getTenantId());
//
// List<TenCellEntity> cellList = tenCellService.list(new QueryWrapper<TenCellEntity>()
// .eq("tenant_id",globalValue.getTenantId())
// .eq("delete_flag",0));
// for(TenCellEntity cellEntity: cellList){
// String cellId = cellEntity.getCellId()+"";
// System.out.println("cellId = " + cellId);
// System.out.println("UserRamGroup = " + UserRamGroup.class);
// UserRamGroup.addCell(cellId);
// UserRamGroup.addOrgId(cellEntity.getOrgId(),cellId);
// int count = tenPersonService.findCount(cellId);
// int pageSize = 1000;
// int page = count/pageSize;
// if(count%1000!=0){
// page = page+1;
// }
// int faceCount = 0;
// for (int i = 0; i < page; i++) {
// int start = i*1000;
// List<TenPersonEntity> listPage = tenPersonService.listPage(start,1000, cellId);
// for(TenPersonEntity personEntity: listPage){
// if(personEntity.getFeature()!=null && personEntity.getFeature().length()>0){
// UserInfo userInfo = new UserCompareInfo();
// userInfo.setFaceId(personEntity.getPersonId()+"");
// userInfo.setName(personEntity.getName());
// userInfo.setFaceFeature(Base64Util.base64ToBytes(personEntity.getFeature()));
// //这边注册到内存缓存中
// UserRamGroup.addUser(userInfo,cellId);
// faceCount++;
// }
// }
// }
// log.debug(cellEntity.getName()+":初始化人脸库完成,共 "+faceCount+"");
// }
// }
//}

View File

@ -0,0 +1,133 @@
package com.guwan.backend.face.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
@Slf4j
@Configuration
public class ArcFaceAutoConfiguration implements InitializingBean, DisposableBean {
private static final String PLATFORM;
private static final String USER_HOME;
public static String CACHE_LIB_FOLDER;
@Value("${config.arcface-sdk.version}")
public String ARC_FACE_VERSION;
static {
String jvmName = System.getProperty("java.vm.name", "").toLowerCase();
String osName = System.getProperty("os.name", "").toLowerCase();
String osArch = System.getProperty("os.arch", "").toLowerCase();
String abiType = System.getProperty("sun.arch.abi", "").toLowerCase();
String libPath = System.getProperty("sun.boot.library.path", "").toLowerCase();
USER_HOME = System.getProperty("user.home");
if (jvmName.startsWith("dalvik") && osName.startsWith("linux")) {
osName = "android";
} else if (jvmName.startsWith("robovm") && osName.startsWith("darwin")) {
osName = "ios";
osArch = "arm";
} else if (osName.startsWith("mac os x") || osName.startsWith("darwin")) {
osName = "macosx";
} else {
int spaceIndex = osName.indexOf(' ');
if (spaceIndex > 0) {
osName = osName.substring(0, spaceIndex);
}
}
if (osArch.equals("i386") || osArch.equals("i486") || osArch.equals("i586") || osArch.equals("i686")) {
osArch = "x86";
} else if (osArch.equals("amd64") || osArch.equals("x86-64") || osArch.equals("x64")) {
osArch = "x86_64";
} else if (osArch.startsWith("aarch64") || osArch.startsWith("armv8") || osArch.startsWith("arm64")) {
osArch = "arm64";
} else if ((osArch.startsWith("arm")) && ((abiType.equals("gnueabihf")) || (libPath.contains("openjdk-armhf")))) {
osArch = "armhf";
} else if (osArch.startsWith("arm")) {
osArch = "arm";
}
PLATFORM = osName + "-" + osArch;
}
@Override
public void afterPropertiesSet() throws IOException {
CACHE_LIB_FOLDER = USER_HOME + "/.arcface/cache/" + ARC_FACE_VERSION + "/" + PLATFORM + "/";
loadLibrary();
}
public void loadLibrary() throws IOException {
String baseFolder = "";
String suffix = ".dll";
if ("windows-x86_64".equals(PLATFORM)) {
baseFolder = "WIN64";
} else if ("windows-x86".equals(PLATFORM)) {
baseFolder = "WIN32";
} else if ("linux-x86_64".equals(PLATFORM)) {
baseFolder = "LINUX64";
suffix = ".so";
}
if ("".equals(baseFolder)) {
throw new RuntimeException("ArcFace不支持该操作系统");
}
File file = new File(CACHE_LIB_FOLDER);
if (!file.exists()) {
file.mkdirs();
}
List<String> libList = new LinkedList<>();
libList.add("libarcsoft_face");
libList.add("libarcsoft_face_engine");
libList.add("libarcsoft_face_engine_jni");
for (String lib : libList) {
ClassPathResource resource = new ClassPathResource("libs/" + ARC_FACE_VERSION + "/" + baseFolder + "/" + lib + suffix);
InputStream inputStream = resource.getInputStream();
int faceLength = inputStream.available();
File facePath = new File(CACHE_LIB_FOLDER + lib + suffix);
if (facePath.exists()) {
if (facePath.length() == faceLength) {
continue;
}
facePath.delete();
}
writeToLocal(CACHE_LIB_FOLDER + lib + suffix, inputStream);
}
}
private void writeToLocal(String destination, InputStream input)
throws IOException {
int index;
byte[] bytes = new byte[1024 * 100];
FileOutputStream fileOutputStream = new FileOutputStream(destination);
while ((index = input.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, index);
}
fileOutputStream.flush();
fileOutputStream.close();
input.close();
}
@Override
public void destroy() throws Exception {
}
}

View File

@ -0,0 +1,14 @@
package com.guwan.backend.face.dto;
import lombok.Data;
/**
* @author shentao
* @desc
* @date 2022/3/30
*/
@Data
public class CompareFacesReqDTO {
private String image1;
private String image2;
}

View File

@ -0,0 +1,12 @@
package com.guwan.backend.face.dto;
import lombok.Data;
@Data
public class FaceAddReqDTO {
private String name;
private String image;
}

View File

@ -0,0 +1,10 @@
package com.guwan.backend.face.dto;
import lombok.Data;
@Data
public class FaceDetectReqDTO {
private String image;
}

View File

@ -0,0 +1,14 @@
package com.guwan.backend.face.dto;
import com.arcsoft.face.Rect;
import lombok.Data;
@Data
public class FaceDetectResDTO {
private Rect rect;
private int orient;
private int faceId = -1;
private int age = -1;
private int gender = -1;
private int liveness = -1;
}

View File

@ -0,0 +1,10 @@
package com.guwan.backend.face.dto;
import lombok.Data;
@Data
public class FaceRecognitionReqDTO {
private String image;
}

View File

@ -0,0 +1,15 @@
package com.guwan.backend.face.dto;
import com.arcsoft.face.Rect;
import lombok.Data;
@Data
public class FaceRecognitionResDTO {
private Rect rect;
private String personId;
private String name;
private float similar;
}

View File

@ -0,0 +1,14 @@
package com.guwan.backend.face.dto;
import lombok.Data;
@Data
public class GetFaceListResDTO {
private String id;
private String name;
private String url;
}

View File

@ -0,0 +1,12 @@
package com.guwan.backend.face.entity;
import lombok.Data;
@Data
public class ProcessInfo {
private int age;
private int gender;
private int liveness;
}

View File

@ -0,0 +1,12 @@
package com.guwan.backend.face.entity;
import com.guwan.backend.face.util.UserInfo;
import lombok.Data;
@Data
public class UserCompareInfo extends UserInfo {
private Float similar;
private Integer isHeadOnView; //0否1是
}

View File

@ -0,0 +1,50 @@
package com.guwan.backend.face.enums;
import com.guwan.backend.face.rpc.ErrorCode;
import lombok.Getter;
@Getter
public enum ErrorCodeEnum implements ErrorCode {
/**
* 成功
*/
SUCCESS(0, "success", "成功"),
FAIL(1, "fail", "失败"),
PARAM_ERROR(2, "param error", "参数错误"),
SYSTEM_ERROR(999, "system error", "系统错误"),
;
private Integer code;
private String desc;
private String descCN;
ErrorCodeEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
ErrorCodeEnum(Integer code, String desc, String descCN) {
this.code = code;
this.desc = desc;
this.descCN = descCN;
}
@Override
public Integer getCode() {
return code;
}
@Override
public String getDesc() {
return desc;
}
@Override
public String getDescCN() {
return descCN;
}
}

View File

@ -0,0 +1,46 @@
package com.guwan.backend.face.face;
import com.arcsoft.face.FaceInfo;
public class FacePreviewInfo {
private FaceInfo faceInfo;
private int trackId;
private int gender = -1;
public FacePreviewInfo(FaceInfo faceInfo, int trackId) {
this.faceInfo = faceInfo;
this.trackId = trackId;
}
public FacePreviewInfo(FaceInfo faceInfo, int trackId, int gender) {
this.faceInfo = faceInfo;
this.trackId = trackId;
this.gender = gender;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public FaceInfo getFaceInfo() {
return faceInfo;
}
public void setFaceInfo(FaceInfo faceInfo) {
this.faceInfo = faceInfo;
}
public int getTrackId() {
return trackId;
}
public void setTrackId(int trackId) {
this.trackId = trackId;
}
}

View File

@ -0,0 +1,292 @@
package com.guwan.backend.face.face;
import com.arcsoft.face.*;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.ErrorInfo;
import com.arcsoft.face.enums.ExtractType;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import com.guwan.backend.face.config.ArcFaceAutoConfiguration;
import com.guwan.backend.face.factory.FaceEngineFactory;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public final class FaceRecognize {
/**
* VIDEO模式人脸检测引擎用于预览帧人脸追踪
*/
private static FaceEngine ftEngine;
/**
* 人脸注册引擎
*/
private static FaceEngine regEngine;
/**
* 用于人脸识别的引擎池
*/
private static GenericObjectPool<FaceEngine> frEnginePool;
private static volatile ConcurrentHashMap<Integer, FaceResult> faceResultRegistry = new ConcurrentHashMap<>();
private static ExecutorService frService = Executors.newFixedThreadPool(20);
public static ConcurrentHashMap<String, byte[]> faceFeatureRegistry = new ConcurrentHashMap<>();
/**
* 初始化引擎
*/
public void initEngine(String appId, String sdkKey, String activeKey, String activeFile) {
//引擎配置
ftEngine = new FaceEngine(ArcFaceAutoConfiguration.CACHE_LIB_FOLDER);
int activeCode;
if (StringUtils.isNotEmpty(activeFile)) {
activeCode = ftEngine.activeOffline(activeFile);
} else {
activeCode = ftEngine.activeOnline(appId, sdkKey, activeKey);
}
EngineConfiguration ftEngineCfg = new EngineConfiguration();
ftEngineCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_VIDEO);
ftEngineCfg.setFunctionConfiguration(FunctionConfiguration.builder().supportFaceDetect(true).build());
int ftInitCode = ftEngine.init(ftEngineCfg);
//引擎配置
regEngine = new FaceEngine(ArcFaceAutoConfiguration.CACHE_LIB_FOLDER);
EngineConfiguration regEngineCfg = new EngineConfiguration();
regEngineCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
regEngineCfg.setFunctionConfiguration(FunctionConfiguration.builder().supportFaceDetect(true).supportFaceRecognition(true).build());
int regInitCode = regEngine.init(regEngineCfg);
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxIdle(5);
poolConfig.setMaxTotal(5);
poolConfig.setMinIdle(5);
poolConfig.setLifo(false);
EngineConfiguration frEngineCfg = new EngineConfiguration();
frEngineCfg.setFunctionConfiguration(FunctionConfiguration.builder().supportFaceRecognition(true).build());
frEnginePool = new GenericObjectPool(new FaceEngineFactory(appId, sdkKey, activeKey,activeFile, frEngineCfg), poolConfig);//底层库算法对象池
if (!(activeCode == ErrorInfo.MOK.getValue() || activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue())) {
log.error("activeCode: " + activeCode);
throw new RuntimeException("activeCode: " + activeCode);
}
if (ftInitCode != ErrorInfo.MOK.getValue()) {
log.error("ftInitEngine: " + ftInitCode);
throw new RuntimeException("ftInitEngine: " + ftInitCode);
}
if (regInitCode != ErrorInfo.MOK.getValue()) {
log.error("regInitEngine: " + regInitCode);
throw new RuntimeException("regInitEngine: " + regInitCode);
}
}
public static void registerFace(String imagePath) {
log.info("正在注册人脸");
int count = 0;
if (regEngine != null) {
File file = new File(imagePath);
File[] files = file.listFiles();
for (File file1 : files) {
ImageInfo imageInfo = ImageFactory.getRGBData(file1);
if (imageInfo != null) {
List<FaceInfo> faceInfoList = new ArrayList<>();
int code = regEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(),
imageInfo.getImageFormat(), faceInfoList);
if (code == 0 && faceInfoList.size() > 0) {
FaceFeature faceFeature = new FaceFeature();
int resCode = regEngine.extractFaceFeature(imageInfo, faceInfoList.get(0), ExtractType.REGISTER, 0, faceFeature);
if (resCode == 0) {
int lastIndexOf = file1.getName().lastIndexOf(".");
String name = file1.getName().substring(0, file1.getName().length() - lastIndexOf - 1);
faceFeatureRegistry.put(name, faceFeature.getFeatureData());
log.info("成功注册人脸:" + name);
count++;
}
}
}
}
log.info("人脸注册完成,共注册:" + count + "张人脸");
} else {
throw new RuntimeException("注册失败,引擎未初始化或初始化失败");
}
}
public static void registerFace(Map<String, byte[]> face) {
face.forEach((k, v) -> {
faceFeatureRegistry.put(k, v.clone());
});
}
public static void removeFace(String name) {
faceFeatureRegistry.remove(name);
}
public static void clearFace() {
faceFeatureRegistry.clear();
}
public static FaceResult getFaceResult(FaceInfo faceInfo, ImageInfo imageInfo) {
FaceResult faceResult = faceResultRegistry.get(faceInfo.getFaceId());
if (faceResult == null) {
faceResult = new FaceResult();
faceResultRegistry.put(faceInfo.getFaceId(), faceResult);
frService.submit(new FaceInfoRunnable(faceInfo, imageInfo, faceResult));
} else if (faceResult.isFlag()) {
return faceResult;
}
return null;
}
public List<FacePreviewInfo> detectFaces(ImageInfo imageInfo) {
if (ftEngine != null) {
List<FaceInfo> faceInfoList = new ArrayList<>();
int code = ftEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(),
imageInfo.getImageFormat(), faceInfoList);
List<FacePreviewInfo> previewInfoList = new LinkedList<>();
for (FaceInfo faceInfo : faceInfoList) {
FacePreviewInfo facePreviewInfo = new FacePreviewInfo();
facePreviewInfo.setFaceInfo(faceInfo);
previewInfoList.add(facePreviewInfo);
}
clearFaceResultRegistry(faceInfoList);
return previewInfoList;
}
return null;
}
private long lastClearTime = System.currentTimeMillis();
//清理过时的人脸
private void clearFaceResultRegistry(List<FaceInfo> faceInfoList) {
if (System.currentTimeMillis() - lastClearTime > 5000) {
Iterator<Integer> iterator = faceResultRegistry.keySet().iterator();
for (; iterator.hasNext(); ) {
Integer next = iterator.next();
boolean flag = false;
for (FaceInfo faceInfo : faceInfoList) {
if (next.equals(faceInfo.getFaceId())) {
flag = true;
}
}
if (!flag) {
iterator.remove();
}
}
lastClearTime = System.currentTimeMillis();
}
}
@Data
public static class FaceResult {
private boolean flag = false;
private String name;
private float score;
}
@Data
public class FacePreviewInfo {
private FaceInfo faceInfo;
private int age;
private boolean liveness;
}
private static class FaceInfoRunnable implements Runnable {
private FaceInfo faceInfo;
private ImageInfo imageInfo;
private FaceResult faceResult;
public FaceInfoRunnable(FaceInfo faceInfo, ImageInfo imageInfo, FaceResult faceResult) {
this.faceInfo = faceInfo;
this.imageInfo = imageInfo;
this.faceResult = faceResult;
}
@Override
public void run() {
FaceEngine frEngine = null;
try {
frEngine = frEnginePool.borrowObject();
if (frEngine != null) {
FaceFeature faceFeature = new FaceFeature();
int resCode = frEngine.extractFaceFeature(imageInfo, faceInfo, ExtractType.RECOGNIZE, 0, faceFeature);
if (resCode == 0) {
float score = 0.0F;
Iterator<Map.Entry<String, byte[]>> iterator = faceFeatureRegistry.entrySet().iterator();
for (; iterator.hasNext(); ) {
Map.Entry<String, byte[]> next = iterator.next();
FaceFeature faceFeatureTarget = new FaceFeature();
faceFeatureTarget.setFeatureData(next.getValue());
FaceSimilar faceSimilar = new FaceSimilar();
frEngine.compareFaceFeature(faceFeatureTarget, faceFeature, faceSimilar);
if (faceSimilar.getScore() > score) {
score = faceSimilar.getScore();
faceResult.setName(next.getKey());
}
}
log.info("相似度:" + score);
if (score >= 0.8f) {
faceResult.setScore(score);
faceResult.setFlag(true);
faceResultRegistry.put(faceInfo.getFaceId(), faceResult);
} else {
faceResultRegistry.remove(faceInfo.getFaceId());
}
}
}
} catch (Exception e) {
} finally {
if (frEngine != null) {
frEnginePool.returnObject(frEngine);
}
}
}
}
}

View File

@ -0,0 +1,71 @@
package com.guwan.backend.face.factory;
import com.arcsoft.face.EngineConfiguration;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.enums.ErrorInfo;
import com.guwan.backend.face.config.ArcFaceAutoConfiguration;
import com.guwan.backend.face.enums.ErrorCodeEnum;
import com.guwan.backend.face.rpc.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
@Slf4j
public class FaceEngineFactory extends BasePooledObjectFactory<FaceEngine> {
private String appId;
private String sdkKey;
private String activeKey;
private String activeFile;
private EngineConfiguration engineConfiguration;
public FaceEngineFactory(String appId, String sdkKey, String activeKey, String activeFile, EngineConfiguration engineConfiguration) {
this.appId = appId;
this.sdkKey = sdkKey;
this.activeKey = activeKey;
this.activeFile=activeFile;
this.engineConfiguration = engineConfiguration;
}
@Override
public FaceEngine create() {
FaceEngine faceEngine = new FaceEngine(ArcFaceAutoConfiguration.CACHE_LIB_FOLDER);
/* FaceEngine faceEngine = new FaceEngine("/softTemp/jar/82K111TPM121XK4H.dat");*/
// FaceEngine faceEngine = new FaceEngine("/home/huangyifang/gb/咸阳师范/ArcSoft_ArcFacePro_linux_java_V4.1/libs/LINUX64/");
int activeCode;
if (StringUtils.isNotEmpty(activeFile)) {
activeCode = faceEngine.activeOffline(activeFile);
} else {
activeCode = faceEngine.activeOnline(appId, sdkKey, activeKey);
}
log.debug("引擎激活errorCode:" + activeCode);
if (activeCode != ErrorInfo.MOK.getValue() && activeCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
log.error("引擎激活失败" + activeCode);
throw new BusinessException(ErrorCodeEnum.FAIL, "引擎激活失败" + activeCode);
}
int initCode = faceEngine.init(engineConfiguration);
if (initCode != ErrorInfo.MOK.getValue()) {
log.error("引擎初始化失败" + initCode);
throw new BusinessException(ErrorCodeEnum.FAIL, "引擎初始化失败" + initCode);
}
log.debug("初始化引擎errorCode:" + initCode);
return faceEngine;
}
@Override
public PooledObject<FaceEngine> wrap(FaceEngine faceEngine) {
return new DefaultPooledObject<>(faceEngine);
}
@Override
public void destroyObject(PooledObject<FaceEngine> p) throws Exception {
FaceEngine faceEngine = p.getObject();
int result = faceEngine.unInit();
super.destroyObject(p);
}
}

View File

@ -0,0 +1,64 @@
package com.guwan.backend.face.rpc;
import lombok.Data;
/**
* @Author: st7251
* @Date: 2018/11/23 14:18
*/
@Data
public class BusinessException extends RuntimeException {
private ErrorCode errorCode;
private String msg;
private String msgCN;
public BusinessException(Response response) {
this.errorCode = new ErrorCode() {
@Override
public Integer getCode() {
return response.getCode();
}
@Override
public String getDesc() {
return response.getMsg();
}
@Override
public String getDescCN() {
return response.getMsg();
}
};
this.msg=response.getMsg();
this.msgCN=response.getMsg();
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getDesc());
this.errorCode = errorCode;
this.msg= errorCode.getDesc();
this.msgCN=errorCode.getDescCN();
}
public BusinessException(ErrorCode errorCode, String msg) {
super(errorCode.getDesc());
this.errorCode = errorCode;
this.msg = msg;
this.msgCN=msg;
}
public BusinessException(Throwable cause, ErrorCode errorCode) {
super(cause);
this.errorCode = errorCode;
this.msg= errorCode.getDesc();
this.msgCN=errorCode.getDescCN();
}
public BusinessException(Throwable cause, ErrorCode errorCode, String msg) {
super(cause);
this.errorCode = errorCode;
this.msg = msg;
this.msgCN=msg;
}
}

View File

@ -0,0 +1,12 @@
package com.guwan.backend.face.rpc;
public interface ErrorCode {
Integer getCode();
String getDesc();
String getDescCN();
}

View File

@ -0,0 +1,55 @@
//package com.guwan.backend.face.rpc;
//
//
//import com.guwan.backend.face.enums.ErrorCodeEnum;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.web.bind.annotation.ExceptionHandler;
//import org.springframework.web.bind.annotation.RestControllerAdvice;
//
//@RestControllerAdvice
//@Slf4j
//public class GlobalExceptionHandler{
//
//
// /**
// * 自定义异常
// */
// @ExceptionHandler(BusinessException.class)
// public Response businessException(BusinessException e) {
// log.error(e.getMessage(), e);
// Response response = new Response();
// response.setCode(e.getErrorCode().getCode());
// response.setMsg(e.getMsgCN());
// return response;
// }
//
// @ExceptionHandler(IllegalArgumentException.class)
// public Response handleIllegalArgumentException(IllegalArgumentException e) {
// log.error(e.getMessage(), e);
// Response response = new Response();
// response.setCode(ErrorCodeEnum.PARAM_ERROR.getCode());
// response.setMsg(e.getMessage());
// return response;
// }
//
// @ExceptionHandler(Exception.class)
// public Response handleException(Exception e) {
// log.error(e.getMessage(), e);
// Response response = new Response();
// response.setCode(ErrorCodeEnum.SYSTEM_ERROR.getCode());
// response.setMsg(ErrorCodeEnum.SYSTEM_ERROR.getDescCN());
// return response;
// }
//
//
//
//// @ExceptionHandler(InternalServerErrorException.class)
//// @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
//// public ResponseEntity<String> handleInternalServerErrorException(InternalServerErrorException ex) {
//// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());
//// }
//
//
//
//
//}

View File

@ -0,0 +1,39 @@
package com.guwan.backend.face.rpc;
import lombok.Data;
@Data
public class Response<T> {
private int code = -1;
private String msg = "success";
private T data;
public static <T> Response<T> newSuccessResponse(T data) {
return newResponse(data, 0, "success");
}
public static <T> Response<T> newFailedResponse(Integer code, String message) {
return newResponse(null, code, message);
}
public static <T> Response<T> newFailedResponse(ErrorCode ErrorCode) {
return newResponse(null, ErrorCode.getCode(), ErrorCode.getDesc());
}
public static <T> Response<T> newFailedResponse(ErrorCode ErrorCode, String message) {
return newResponse(null, ErrorCode.getCode(), message);
}
public static <T> Response<T> newResponse(T data, Integer code, String message) {
Response<T> response = new Response<T>();
response.setCode(code);
response.setMsg(message);
if (data != null && data instanceof String && "".equals(data)) {
response.setData(null);
} else {
response.setData(data);
}
return response;
}
}

View File

@ -0,0 +1,483 @@
//package com.guwan.backend.face.rtsp;
//
//import cn.hutool.core.collection.CollectionUtil;
//import cn.hutool.json.JSONObject;
//import com.arcsoft.face.FaceInfo;
//import com.arcsoft.face.enums.ExtractType;
//import com.arcsoft.face.toolkit.ImageFactory;
//import com.arcsoft.face.toolkit.ImageInfo;
//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
//import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
//import io.minio.MinioClient;
//import io.minio.PutObjectOptions;
//import lombok.extern.slf4j.Slf4j;
//import net.shapelight.common.config.GlobalValue;
//import net.shapelight.common.config.MinioConfig;
//import net.shapelight.common.utils.Constant;
//import net.shapelight.common.utils.UUIDUtil;
//import net.shapelight.modules.face.entity.UserCompareInfo;
//import net.shapelight.modules.face.service.FaceEngineService;
//import net.shapelight.modules.face.util.UserInfo;
//import net.shapelight.modules.face.util.UserRamGroup;
//import net.shapelight.modules.feignClient.CxFeignClient;
//import net.shapelight.modules.iCq.controller.video.vo.FaceVideoVo;
//import net.shapelight.modules.ten.dao.TenCellDao;
//import net.shapelight.modules.ten.entity.TenCellEntity;
//import net.shapelight.modules.ten.entity.TenPersonEntity;
//import net.shapelight.modules.ten.service.TenPersonService;
//import net.shapelight.modules.ten.service.impl.TenPersonServiceImpl;
//import org.bytedeco.javacpp.avutil;
//import org.bytedeco.javacv.FFmpegFrameGrabber;
//import org.bytedeco.javacv.Frame;
//import org.bytedeco.javacv.FrameGrabber;
//import org.bytedeco.javacv.Java2DFrameConverter;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.beans.factory.annotation.Value;
//import org.springframework.stereotype.Component;
//import org.springframework.web.multipart.MultipartFile;
//
//import javax.imageio.ImageIO;
//import java.awt.image.BufferedImage;
//import java.io.ByteArrayInputStream;
//import java.io.ByteArrayOutputStream;
//import java.io.IOException;
//import java.io.InputStream;
//import java.text.SimpleDateFormat;
//import java.util.ArrayList;
//import java.util.HashMap;
//import java.util.List;
//import java.util.Map;
//import java.util.concurrent.ConcurrentHashMap;
//
//import static org.bytedeco.javacpp.avutil.AV_LOG_ERROR;
//
//@Component
//@Slf4j
//public class RtspFrameGrabber {
//
// @Autowired
// private FaceEngineService faceEngineService;
//
// @Autowired
// private TenPersonService personService;
//
// @Autowired
// private GlobalValue globalValue;
//
// @Autowired
// private CxFeignClient feignClient;
// /**
// * 用于记录人脸识别相关状态
// */
// public ConcurrentHashMap<Integer, Integer> requestFeatureStatusMap = new ConcurrentHashMap<>();
// /**
// * rtsp视频流url
// */
// @Value("${global.Url.rtspUrl}")
// private String rtspUrl;
// /**
// * 帧抓取器
// */
// private FFmpegFrameGrabber grabber;
// /**
// * 视频帧率
// */
// private int frameRate = 25;
// /**
// * 视频码率
// */
//// private int bitRate = 2000000;
// private int bitRate = 128000;
// /**
// * 视频宽度
// */
// private int frameWidth = 480;
// /**
// * 视频高度
// */
// private int frameHeight = 270;
// @Autowired
// private TenPersonServiceImpl tenPersonService;
//
// @Autowired
// private TenCellDao tenCellDao;
//
// @Autowired
// private MinioConfig minioConfig;
// @Autowired
// private MinioClient minioClient;
//
//
// private void createGrabber() {
// try {
//
// grabber = FFmpegFrameGrabber.createDefault(rtspUrl);
// grabber.setOption("rtsp_transport", "tcp");
//
//// grabber.setOption("reconnect", "1");
//// grabber.setOption("reconnect_at_eof", "1");
//// grabber.setOption("reconnect_streamed", "1");
//// grabber.setOption("reconnect_delay_max", "2");
//// grabber.setOption("preset", "veryfast");
//// grabber.setOption("probesize", "192");
//// grabber.setOption("tune", "zerolatency");
//// grabber.setFrameRate(30.0);
//// grabber.setOption("buffer_size", "" + this.bufferSize);
//// grabber.setOption("max_delay", "500000");
//// grabber.setOption("stimeout", String.valueOf(20000));
//// grabber.setOption("loglevel", "quiet");
//
//
//// grabber.setOption("appkey", "****");//海康视频 appkey
//// grabber.setOption("secret", byte2Base64);//海康视频 secret
//// grabber.setOption("port", "446");//默认443
//// grabber.setOption("enableHTTPS", "1"); //是否启用HTTPS协议这里总是填1
//// grabber.setOption("rtsp_flags", "prefer_tcp");
// grabber.setOption("stimeout", "5000000");//5秒
//
//
// //设置帧率
// grabber.setFrameRate(frameRate);
// //设置获取的视频宽度
// grabber.setImageWidth(frameWidth);
// //设置获取的视频高度
// grabber.setImageHeight(frameHeight);
// //设置视频bit率
// grabber.setVideoBitrate(bitRate);
// grabber.start();
// } catch (Exception e) {
// log.error(e.getMessage());
// }
// }
// public List<FaceVideoVo> startGrabber(String cellId) {
//
// List<FaceVideoVo> faceVideoVos = new ArrayList<>();
//
// avutil.av_log_set_level(AV_LOG_ERROR);
// Java2DFrameConverter java2DFrameConverter = new Java2DFrameConverter();
// if (grabber == null) {
// //log.info("连接rtsp" + rstp + "开始创建grabber");
// createGrabber();
// }
// try {
// Frame frame = grabber.grabImage();
// if (frame != null) {
//// logger.info("处理帧.............................................");
// BufferedImage bi = java2DFrameConverter.getBufferedImage(frame);
//
// byte[] bytes = imageToBytes(bi, "jpg");
//
// /* InputStream frameInputStream = new ByteArrayInputStream(bytes);
// String frameFileName = "temp/" + "frame_" + UUIDUtil.uuid()
// + ".jpg";
//
// PutObjectOptions framePutObjectOptions = new PutObjectOptions(bytes.length, -1);
// framePutObjectOptions.setContentType("image/jpeg");
//
// minioClient.putObject(
// minioConfig.getBucketName(), frameFileName, frameInputStream, framePutObjectOptions);
//
// System.out.println("文件名 = " + globalValue.getMinioEndpoint() + "/" +
// globalValue.getMinioBucketName() + "/" + frameFileName);*/
// /* // 读取图片
// BufferedImage image = ImageIO.read(new File("C:\\Users\\zhangbo\\OneDrive\\图片\\Camera Roll\\1寸相片.jpg"));
//
// // 创建字节输出流
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
//
// // 使用ImageIO将BufferedImage编码为byte[]
// ImageIO.write(image, "jpg", baos);
//
// // 转换为byte数组
// byte[] imageBytes = baos.toByteArray();*/
// faceVideoVos = imageRecognition(bytes, cellId);
// System.out.println("faceVideoVos = " + faceVideoVos);
//
// } else {
// log.error("解码失败");
// if (grabber != null) {
// try {
// grabber.stop();
// } catch (FrameGrabber.Exception ex) {
// log.error("grabber stop exception: " + ex.getMessage());
// } finally {
// grabber = null;
// }
// }
// }
// } catch (Exception e) {
// log.error(e.getMessage());
//
// if (grabber != null) {
// try {
// grabber.stop();
// } catch (FrameGrabber.Exception ex) {
// log.error("grabber stop exception: " + ex.getMessage());
// } finally {
// grabber = null;
// }
// }
//
// } finally {
// grabber = null;
// }
//// try {
//// Thread.sleep(100);
//// } catch (InterruptedException e) {
//// logger.error(e.getMessage());
//// }
//
// return faceVideoVos;
// }
//
// public List<FaceVideoVo> recognition(MultipartFile file,String cellId) {
// try {
// byte[] bytes = file.getBytes();
// return imageRecognitionNew(bytes, cellId);
//
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// }
//
// private List<FaceVideoVo> imageRecognition(byte[] bytes,String cellId) {
// List<FaceVideoVo> temp = new ArrayList<>();
// if (bytes != null && bytes.length > 0) {
// ImageInfo imageInfo = ImageFactory.getRGBData(bytes);
// List<FaceInfo> faceInfoList = faceEngineService.detectFaces(imageInfo);
//
//
//
//
// if (CollectionUtil.isNotEmpty(faceInfoList)) {
// faceInfoList.forEach(faceInfo -> {
// /* FaceRecognitionResDTO faceRecognitionResDTO = new FaceRecognitionResDTO();
// faceRecognitionResDTO.setRect(faceInfo.getRect());*/
//
// byte[] featureBytes = faceEngineService.extractFaceFeature(imageInfo,
// faceInfo, ExtractType.REGISTER);
// if (featureBytes != null) {
// //底库
// List<UserInfo> userInfoList = UserRamGroup.getUserList(cellId);
// //人脸对比 这里长度永远为1
// List<UserCompareInfo> userCompareInfoList = faceEngineService
// .faceRecognition(featureBytes, userInfoList, Float.parseFloat(globalValue.getRecFaceThd()));
//
//
//
//
// FaceVideoVo faceVideoVo = new FaceVideoVo();
//
// //System.out.println("faceInfo = " + faceInfo.getRect());
//
// faceVideoVo.setRect(faceInfo.getRect());
//
// if (faceInfo.getFace3DAngle().getYaw() > 20 || faceInfo.getFace3DAngle().getYaw() < -20) {
// faceVideoVo.setIsHeadOnView(0);
// }else if (faceInfo.getFace3DAngle().getPitch() > 20 || faceInfo.getFace3DAngle().getPitch() < -20) {
// faceVideoVo.setIsHeadOnView(0);
// }else {
// faceVideoVo.setIsHeadOnView(1);
// }
//
// faceVideoVo.setPersonId(userCompareInfoList.get(0).getFaceId()).setName(userCompareInfoList.get(0).getName());
//
//
//
// InputStream frameInputStream = new ByteArrayInputStream(bytes);
// String frameFileName = "temp/" + "frame_" + UUIDUtil.uuid()
// + ".jpg";
//
// PutObjectOptions framePutObjectOptions = new PutObjectOptions(bytes.length, -1);
// framePutObjectOptions.setContentType("image/jpeg");
//
// try {
// frameInputStream.close();
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
//
// try {
// minioClient.putObject(
// minioConfig.getBucketName(), frameFileName, frameInputStream, framePutObjectOptions);
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
//
// System.out.println("文件名 = " + globalValue.getMinioEndpoint() + "/" +
// globalValue.getMinioBucketName() + "/" + frameFileName);
//
//
//
// faceVideoVo.setImageUrl(frameFileName);
//
// temp.add(faceVideoVo);
//
//
//
// TenPersonEntity tenPerson = personService.getOne(new LambdaQueryWrapper<TenPersonEntity>()
// .eq(TenPersonEntity::getPersonId, userCompareInfoList.get(0).getFaceId()));
// Map<Integer, String> personnelTypeMap = new HashMap<>();
// personnelTypeMap.put(Constant.PERSON_TYPE_OWNER, "2");//内部人员
// personnelTypeMap.put(Constant.PERSON_TYPE_MEMBER, "1");//承包商
// personnelTypeMap.put(Constant.PERSON_TYPE_TENANT, "3");//长期供应商
// personnelTypeMap.put(Constant.PERSON_TYPE_GUEST, "4");//访客
//
// //
// Map<String, Object> params = new HashMap<>();
// params.put("pmWatchVideoRecordId", "");
// params.put("orgId", tenPerson.getOrgId());
// params.put("orgName", tenCellDao.selectOne(new QueryWrapper<TenCellEntity>().eq("cell_id", tenPerson.getCellId())).getName());
// params.put("personnelName", tenPerson.getName());
// params.put("personnelId", tenPerson.getOpenId());
// params.put("personnelCardId", tenPerson.getIdCard());
// params.put("personnelType", personnelTypeMap.get(tenPerson.getPersonType()));
// params.put("dictSex", tenPerson.getGender() == 0 ? "" : "");
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// params.put("watchVideoTime", sdf.format(System.currentTimeMillis()));
//
// if(tenPerson.getIsWatchSafeVideo() != 1){
// tenPerson.setIsWatchSafeVideo(faceVideoVo.getIsHeadOnView() == 0 ? 2 : 1);
// }
//
//
// //tenPerson.setIsWatchSafeVideo();
//
// tenPersonService.updateById(tenPerson);
//
// System.out.println("params = " + params);
//
// if(faceVideoVo.getIsHeadOnView() == 1){
// JSONObject jsonObject = feignClient.savePmWatchVideoRecord(params);
// if (jsonObject.getBool("success") != null && jsonObject.getBool("success")) {
// personService.update(new LambdaUpdateWrapper<TenPersonEntity>()
// .set(TenPersonEntity::getIsWatchSafeVideo, 1)
// .eq(TenPersonEntity::getPersonId, userCompareInfoList.get(0).getFaceId()));
// }
// }
//
//
// } else {
// log.error("图片不合格,未检测到人脸");
// }
// });
//
// } else {
// log.error("图片不合格,未检测到人脸");
// }
//
// } else {
// }
// return temp;
// }
//
//
// private List<FaceVideoVo> imageRecognitionNew(byte[] bytes,String cellId) {
// List<FaceVideoVo> temp = new ArrayList<>();
// if (bytes != null && bytes.length > 0) {
// ImageInfo imageInfo = ImageFactory.getRGBData(bytes);
// List<FaceInfo> faceInfoList = faceEngineService.detectFaces(imageInfo);
//
//
//
//
// if (CollectionUtil.isNotEmpty(faceInfoList)) {
// faceInfoList.forEach(faceInfo -> {
// /* FaceRecognitionResDTO faceRecognitionResDTO = new FaceRecognitionResDTO();
// faceRecognitionResDTO.setRect(faceInfo.getRect());*/
//
// byte[] featureBytes = faceEngineService.extractFaceFeature(imageInfo,
// faceInfo, ExtractType.REGISTER);
// if (featureBytes != null) {
// //底库
// List<UserInfo> userInfoList = UserRamGroup.getUserList(cellId);
// //人脸对比 这里长度永远为1
// List<UserCompareInfo> userCompareInfoList = faceEngineService
// .faceRecognition(featureBytes, userInfoList, Float.parseFloat(globalValue.getRecFaceThd()));
//
//
//
//
// FaceVideoVo faceVideoVo = new FaceVideoVo();
//
// //System.out.println("faceInfo = " + faceInfo.getRect());
//
// faceVideoVo.setRect(faceInfo.getRect());
//
// if (faceInfo.getFace3DAngle().getYaw() > 40 || faceInfo.getFace3DAngle().getYaw() < -40) {
// faceVideoVo.setIsHeadOnView(0);
// }else if (faceInfo.getFace3DAngle().getPitch() > 40 || faceInfo.getFace3DAngle().getPitch() < -40) {
// faceVideoVo.setIsHeadOnView(0);
// }else {
// faceVideoVo.setIsHeadOnView(1);
// }
//
// faceVideoVo.setPersonId(userCompareInfoList.get(0).getFaceId()).setName(userCompareInfoList.get(0).getName());
//
//
//
// InputStream frameInputStream = new ByteArrayInputStream(bytes);
// String frameFileName = "temp/" + "frame_" + UUIDUtil.uuid()
// + ".jpg";
//
// PutObjectOptions framePutObjectOptions = new PutObjectOptions(bytes.length, -1);
// framePutObjectOptions.setContentType("image/jpeg");
//
// try {
// frameInputStream.close();
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
//
// try {
// minioClient.putObject(
// minioConfig.getBucketName(), frameFileName, frameInputStream, framePutObjectOptions);
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
//
// System.out.println("文件名 = " + globalValue.getMinioEndpoint() + "/" +
// globalValue.getMinioBucketName() + "/" + frameFileName);
//
//
//
// faceVideoVo.setImageUrl(frameFileName);
//
// temp.add(faceVideoVo);
//
//
//
//
//
// } else {
// log.error("图片不合格,未检测到人脸");
// }
// });
//
// } else {
// log.error("图片不合格,未检测到人脸");
// }
//
// }
// return temp;
// }
//
// /**
// * 图片转字节数组
// *
// * @param _bi 图片数据
// * @return 图片字节码
// */
// private byte[] imageToBytes(BufferedImage _bi, String _format) {
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// try {
// ImageIO.write(_bi, _format, baos);
// } catch (IOException e) {
// log.error(e.getMessage());
// return null;
// }
// return baos.toByteArray();
// }
//
//}

View File

@ -0,0 +1,28 @@
package com.guwan.backend.face.service;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.enums.ExtractType;
import com.arcsoft.face.toolkit.ImageInfo;
import com.guwan.backend.face.entity.ProcessInfo;
import com.guwan.backend.face.entity.UserCompareInfo;
import com.guwan.backend.face.util.UserInfo;
import java.util.List;
public interface FaceEngineService {
List<FaceInfo> detectFaces(ImageInfo imageInfo);
Float compareFace(ImageInfo imageInfo1, ImageInfo imageInfo2) ;
byte[] extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo, ExtractType extractType);
List<UserCompareInfo> faceRecognition(byte[] faceFeature, List<UserInfo> userInfoList, float passRate) ;
List<ProcessInfo> process(ImageInfo imageInfo, List<FaceInfo> faceInfoList);
}

View File

@ -0,0 +1,354 @@
package com.guwan.backend.face.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.arcsoft.face.*;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.enums.ExtractType;
import com.arcsoft.face.toolkit.ImageInfo;
import com.google.common.collect.Lists;
import com.guwan.backend.face.entity.ProcessInfo;
import com.guwan.backend.face.entity.UserCompareInfo;
import com.guwan.backend.face.enums.ErrorCodeEnum;
import com.guwan.backend.face.factory.FaceEngineFactory;
import com.guwan.backend.face.rpc.BusinessException;
import com.guwan.backend.face.service.FaceEngineService;
import com.guwan.backend.face.util.UserInfo;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
@Service("faceEngineService")
@Slf4j
public class FaceEngineServiceImpl implements FaceEngineService {
public final static Logger logger = LoggerFactory.getLogger(FaceEngineServiceImpl.class);
@Value("${config.arcface-sdk.app-id}")
public String appId;
@Value("${config.arcface-sdk.sdk-key}")
public String sdkKey;
@Value("${config.arcface-sdk.active-key}")
public String activeKey;
@Value("${config.arcface-sdk.active-file}")
public String activeFile;
@Value("${config.arcface-sdk.detect-pool-size}")
public Integer detectPooSize;
@Value("${config.arcface-sdk.compare-pool-size}")
public Integer comparePooSize;
private ExecutorService compareExecutorService;
//通用人脸识别引擎池
private GenericObjectPool<FaceEngine> faceEngineGeneralPool;
//人脸比对引擎池
private GenericObjectPool<FaceEngine> faceEngineComparePool;
@PostConstruct
public void init() {
GenericObjectPoolConfig detectPoolConfig = new GenericObjectPoolConfig();
detectPoolConfig.setMaxIdle(detectPooSize);
detectPoolConfig.setMaxTotal(detectPooSize);
detectPoolConfig.setMinIdle(detectPooSize);
detectPoolConfig.setLifo(false);
EngineConfiguration detectCfg = new EngineConfiguration();
FunctionConfiguration detectFunctionCfg = new FunctionConfiguration();
detectFunctionCfg.setSupportFaceDetect(true);//开启人脸检测功能
detectFunctionCfg.setSupportFaceRecognition(true);//开启人脸识别功能
detectFunctionCfg.setSupportAge(true);//开启年龄检测功能
detectFunctionCfg.setSupportGender(true);//开启性别检测功能
detectFunctionCfg.setSupportLiveness(true);//开启活体检测功能
detectCfg.setFunctionConfiguration(detectFunctionCfg);
detectCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);//图片检测模式如果是连续帧的视频流图片那么改成VIDEO模式
detectCfg.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);//人脸旋转角度
faceEngineGeneralPool = new GenericObjectPool(new FaceEngineFactory(appId, sdkKey, activeKey, activeFile, detectCfg), detectPoolConfig);//底层库算法对象池
//初始化特征比较线程池
GenericObjectPoolConfig comparePoolConfig = new GenericObjectPoolConfig();
comparePoolConfig.setMaxIdle(comparePooSize);
comparePoolConfig.setMaxTotal(comparePooSize);
comparePoolConfig.setMinIdle(comparePooSize);
comparePoolConfig.setLifo(false);
EngineConfiguration compareCfg = new EngineConfiguration();
FunctionConfiguration compareFunctionCfg = new FunctionConfiguration();
compareFunctionCfg.setSupportFaceRecognition(true);//开启人脸识别功能
compareCfg.setFunctionConfiguration(compareFunctionCfg);
compareCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);//图片检测模式如果是连续帧的视频流图片那么改成VIDEO模式
compareCfg.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);//人脸旋转角度
faceEngineComparePool = new GenericObjectPool(new FaceEngineFactory(appId, sdkKey, activeKey, activeFile, compareCfg), comparePoolConfig);//底层库算法对象池
compareExecutorService = Executors.newFixedThreadPool(comparePooSize);
}
@Override
public List<FaceInfo> detectFaces(ImageInfo imageInfo) {
FaceEngine faceEngine = null;
try {
faceEngine = faceEngineGeneralPool.borrowObject();
if (faceEngine == null) {
throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败");
}
//人脸检测得到人脸列表
List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
//人脸检测
int errorCode = faceEngine.detectFaces(imageInfo.getImageData(),
imageInfo.getWidth(), imageInfo.getHeight(),
imageInfo.getImageFormat(), faceInfoList);
if (errorCode == 0) {
return faceInfoList;
} else {
log.error("人脸检测失败errorCode" + errorCode);
}
} catch (Exception e) {
log.error("", e);
} finally {
if (faceEngine != null) {
//释放引擎对象
faceEngineGeneralPool.returnObject(faceEngine);
}
}
return null;
}
@Override
public Float compareFace(ImageInfo imageInfo1, ImageInfo imageInfo2) {
List<FaceInfo> faceInfoList1 = detectFaces(imageInfo1);
List<FaceInfo> faceInfoList2 = detectFaces(imageInfo2);
if (CollectionUtil.isEmpty(faceInfoList1)) {
throw new BusinessException(ErrorCodeEnum.FAIL, "照片1未检测到人脸");
}
if (CollectionUtil.isEmpty(faceInfoList2)) {
throw new BusinessException(ErrorCodeEnum.FAIL, "照片2未检测到人脸");
}
byte[] feature1 = extractFaceFeature(imageInfo1, faceInfoList1.get(0), ExtractType.REGISTER);
byte[] feature2 = extractFaceFeature(imageInfo2, faceInfoList2.get(0), ExtractType.RECOGNIZE);
FaceEngine faceEngine = null;
try {
faceEngine = faceEngineGeneralPool.borrowObject();
if (faceEngine == null) {
throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败");
}
FaceFeature faceFeature1 = new FaceFeature();
faceFeature1.setFeatureData(feature1);
FaceFeature faceFeature2 = new FaceFeature();
faceFeature2.setFeatureData(feature2);
//提取人脸特征
FaceSimilar faceSimilar = new FaceSimilar();
int errorCode = faceEngine.compareFaceFeature(faceFeature1, faceFeature2, faceSimilar);
if (errorCode == 0) {
return faceSimilar.getScore();
} else {
log.error("特征提取失败errorCode" + errorCode);
}
} catch (Exception e) {
log.error("", e);
} finally {
if (faceEngine != null) {
//释放引擎对象
faceEngineGeneralPool.returnObject(faceEngine);
}
}
return null;
}
/**
* 人脸特征
*
* @param imageInfo
* @return
*/
@Override
public byte[] extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo, ExtractType extractType) {
FaceEngine faceEngine = null;
try {
faceEngine = faceEngineGeneralPool.borrowObject();
if (faceEngine == null) {
throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败");
}
FaceFeature faceFeature = new FaceFeature();
//提取人脸特征
int errorCode = faceEngine.extractFaceFeature(imageInfo, faceInfo, extractType, 0, faceFeature);
if (errorCode == 0) {
return faceFeature.getFeatureData();
} else {
log.error("特征提取失败errorCode" + errorCode);
}
} catch (Exception e) {
log.error("", e);
} finally {
if (faceEngine != null) {
//释放引擎对象
faceEngineGeneralPool.returnObject(faceEngine);
}
}
return null;
}
@Override
public List<UserCompareInfo> faceRecognition(byte[] faceFeature, List<UserInfo> userInfoList, float passRate) {
List<UserCompareInfo> resultUserInfoList = Lists.newLinkedList();//识别到的人脸列表
FaceFeature targetFaceFeature = new FaceFeature();
targetFaceFeature.setFeatureData(faceFeature);
List<List<UserInfo>> faceUserInfoPartList = Lists.partition(userInfoList, 1000);//分成1000一组多线程处理
CompletionService<List<UserCompareInfo>> completionService = new ExecutorCompletionService(compareExecutorService);
for (List<UserInfo> part : faceUserInfoPartList) {
completionService.submit(new CompareFaceTask(part, targetFaceFeature, passRate));
}
for (int i = 0; i < faceUserInfoPartList.size(); i++) {
List<UserCompareInfo> faceUserInfoList = null;
try {
faceUserInfoList = completionService.take().get();
} catch (InterruptedException | ExecutionException e) {
}
if (CollectionUtil.isNotEmpty(userInfoList)) {
resultUserInfoList.addAll(faceUserInfoList);
}
}
resultUserInfoList.sort((h1, h2) -> h2.getSimilar().compareTo(h1.getSimilar()));//从大到小排序
return resultUserInfoList;
}
@Override
public List<ProcessInfo> process(ImageInfo imageInfo, List<FaceInfo> faceInfoList) {
FaceEngine faceEngine = null;
try {
//获取引擎对象
faceEngine = faceEngineGeneralPool.borrowObject();
if (faceEngine == null) {
throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败");
}
int errorCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList, FunctionConfiguration.builder().supportAge(true).supportGender(true).supportLiveness(true).build());
if (errorCode == 0) {
List<ProcessInfo> processInfoList = Lists.newLinkedList();
//性别列表
List<GenderInfo> genderInfoList = new ArrayList<GenderInfo>();
faceEngine.getGender(genderInfoList);
//年龄列表
List<AgeInfo> ageInfoList = new ArrayList<AgeInfo>();
faceEngine.getAge(ageInfoList);
//活体结果列表
List<LivenessInfo> livenessInfoList = new ArrayList<LivenessInfo>();
faceEngine.getLiveness(livenessInfoList);
for (int i = 0; i < genderInfoList.size(); i++) {
ProcessInfo processInfo = new ProcessInfo();
processInfo.setGender(genderInfoList.get(i).getGender());
processInfo.setAge(ageInfoList.get(i).getAge());
processInfo.setLiveness(livenessInfoList.get(i).getLiveness());
processInfoList.add(processInfo);
}
return processInfoList;
}
} catch (Exception e) {
logger.error("", e);
} finally {
if (faceEngine != null) {
//释放引擎对象
faceEngineGeneralPool.returnObject(faceEngine);
}
}
return null;
}
private class CompareFaceTask implements Callable<List<UserCompareInfo>> {
private List<UserInfo> userInfoList;
private FaceFeature targetFaceFeature;
private float passRate;
public CompareFaceTask(List<UserInfo> userInfoList, FaceFeature targetFaceFeature, float passRate) {
this.userInfoList = userInfoList;
this.targetFaceFeature = targetFaceFeature;
this.passRate = passRate;
}
@Override
public List<UserCompareInfo> call() throws Exception {
FaceEngine faceEngine = null;
List<UserCompareInfo> resultUserInfoList = Lists.newLinkedList();//识别到的人脸列表
try {
faceEngine = faceEngineComparePool.borrowObject();
for (UserInfo userInfo : userInfoList) {
FaceFeature sourceFaceFeature = new FaceFeature();
sourceFaceFeature.setFeatureData(userInfo.getFaceFeature());
FaceSimilar faceSimilar = new FaceSimilar();
faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
System.out.println("faceSimilar.getScore() = " + faceSimilar.getScore());
if (faceSimilar.getScore() > passRate) {//相似值大于配置预期加入到识别到人脸的列表
UserCompareInfo info = new UserCompareInfo();
info.setName(userInfo.getName());
info.setFaceId(userInfo.getFaceId());
info.setSimilar(faceSimilar.getScore());
resultUserInfoList.add(info);
}
}
} catch (Exception e) {
logger.error("", e);
} finally {
if (faceEngine != null) {
faceEngineComparePool.returnObject(faceEngine);
}
}
return resultUserInfoList;
}
}
}

View File

@ -0,0 +1,33 @@
package com.guwan.backend.face.util;
import org.springframework.util.ObjectUtils;
import java.util.Base64;
public class Base64Util {
public static String base64Process(String base64Str) {
if (!ObjectUtils.isEmpty(base64Str)) {
String photoBase64 = base64Str.substring(0, 30).toLowerCase();
int indexOf = photoBase64.indexOf("base64,");
if (indexOf > 0) {
base64Str = base64Str.substring(indexOf + 7);
}
base64Str = base64Str.replaceAll(" ", "+");
base64Str = base64Str.replaceAll("\r|\n", "");
return base64Str;
}
return "";
}
public static byte[] base64ToBytes(String base64) {
if (ObjectUtils.isEmpty(base64)) {
return null;
}
String base64Process = base64Process(base64);
byte[] decode = Base64.getDecoder().decode(base64Process);
return decode;
}
}

View File

@ -0,0 +1,225 @@
package com.guwan.backend.face.util;
import com.arcsoft.face.*;
import com.arcsoft.face.enums.*;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import com.arcsoft.face.toolkit.ImageInfoEx;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class FaceEngineTest {
public static void main(String[] args) {
//激活码从官网获取
String appId = "DEnAZa1bWXcaAxyWUg33QZaKCmMkNmrQxuKZJQGmZsHJ";
String sdkKey = "vWbvUyStZeartSaM6QoTzPYWFpSaj4uhfDmRifSzCd6";
String activeKey = "82G1-11QA-713Y-8NB4";
System.err.println("注意如果返回的errorCode不为0可查看com.arcsoft.face.enums.ErrorInfo类获取相应的错误信息");
//人脸识别引擎库存放路径
FaceEngine faceEngine = new FaceEngine("/home/huangyifang/gb/咸阳师范/ArcSoft_ArcFacePro_linux_java_V4.1/libs/LINUX64");
//激活引擎
int errorCode = faceEngine.activeOnline(appId, sdkKey, activeKey);
System.out.println("引擎激活errorCode:" + errorCode);
ActiveDeviceInfo activeDeviceInfo = new ActiveDeviceInfo();
//采集设备信息可离线
errorCode = faceEngine.getActiveDeviceInfo(activeDeviceInfo);
System.out.println("采集设备信息errorCode:" + errorCode);
System.out.println("设备信息:" + activeDeviceInfo.getDeviceInfo());
// faceEngine.activeOffline("d:\\ArcFacePro64.dat.offline");
ActiveFileInfo activeFileInfo = new ActiveFileInfo();
errorCode = faceEngine.getActiveFileInfo(activeFileInfo);
System.out.println("获取激活文件errorCode:" + errorCode);
System.out.println("激活文件信息:" + activeFileInfo.toString());
//引擎配置
EngineConfiguration engineConfiguration = new EngineConfiguration();
engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
engineConfiguration.setDetectFaceMaxNum(10);
//功能配置
FunctionConfiguration functionConfiguration = new FunctionConfiguration();
functionConfiguration.setSupportAge(true);
functionConfiguration.setSupportFaceDetect(true);
functionConfiguration.setSupportFaceRecognition(true);
functionConfiguration.setSupportGender(true);
functionConfiguration.setSupportLiveness(true);
functionConfiguration.setSupportIRLiveness(true);
functionConfiguration.setSupportImageQuality(true);
functionConfiguration.setSupportMaskDetect(true);
functionConfiguration.setSupportUpdateFaceData(true);
engineConfiguration.setFunctionConfiguration(functionConfiguration);
//初始化引擎
errorCode = faceEngine.init(engineConfiguration);
System.out.println("初始化引擎errorCode:" + errorCode);
VersionInfo version = faceEngine.getVersion();
System.out.println(version);
//人脸检测
ImageInfo imageInfo = ImageFactory.getRGBData(new File("/home/huangyifang/gb/咸阳师范/10.jpg"));
List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
errorCode = faceEngine.detectFaces(imageInfo, faceInfoList);
System.out.println("人脸检测errorCode:" + errorCode);
System.out.println("检测到人脸数:" + faceInfoList.size());
ImageQuality imageQuality = new ImageQuality();
errorCode = faceEngine.imageQualityDetect(imageInfo, faceInfoList.get(0), 0, imageQuality);
System.out.println("图像质量检测errorCode:" + errorCode);
System.out.println("图像质量分数:" + imageQuality.getFaceQuality());
//特征提取
FaceFeature faceFeature = new FaceFeature();
errorCode = faceEngine.extractFaceFeature(imageInfo, faceInfoList.get(0), ExtractType.REGISTER, 0, faceFeature);
System.out.println("特征提取errorCode:" + errorCode);
//人脸检测2
ImageInfo imageInfo2 = ImageFactory.getRGBData(new File("/home/huangyifang/gb/咸阳师范/10.jpg"));
List<FaceInfo> faceInfoList2 = new ArrayList<FaceInfo>();
errorCode = faceEngine.detectFaces(imageInfo2, faceInfoList2);
System.out.println("人脸检测errorCode:" + errorCode);
System.out.println("检测到人脸数:" + faceInfoList.size());
//特征提取2
FaceFeature faceFeature2 = new FaceFeature();
errorCode = faceEngine.extractFaceFeature(imageInfo2, faceInfoList2.get(0), ExtractType.RECOGNIZE, 0, faceFeature2);
System.out.println("特征提取errorCode:" + errorCode);
//特征比对
FaceFeature targetFaceFeature = new FaceFeature();
targetFaceFeature.setFeatureData(faceFeature.getFeatureData());
FaceFeature sourceFaceFeature = new FaceFeature();
sourceFaceFeature.setFeatureData(faceFeature2.getFeatureData());
FaceSimilar faceSimilar = new FaceSimilar();
errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
System.out.println("特征比对errorCode:" + errorCode);
System.out.println("人脸相似度:" + faceSimilar.getScore());
//人脸属性检测
FunctionConfiguration configuration = new FunctionConfiguration();
configuration.setSupportAge(true);
configuration.setSupportGender(true);
configuration.setSupportLiveness(true);
configuration.setSupportMaskDetect(true);
errorCode = faceEngine.process(imageInfo, faceInfoList, configuration);
System.out.println("图像属性处理errorCode:" + errorCode);
//性别检测
List<GenderInfo> genderInfoList = new ArrayList<GenderInfo>();
errorCode = faceEngine.getGender(genderInfoList);
System.out.println("性别:" + genderInfoList.get(0).getGender());
//年龄检测
List<AgeInfo> ageInfoList = new ArrayList<AgeInfo>();
errorCode = faceEngine.getAge(ageInfoList);
System.out.println("年龄:" + ageInfoList.get(0).getAge());
//活体检测
List<LivenessInfo> livenessInfoList = new ArrayList<LivenessInfo>();
errorCode = faceEngine.getLiveness(livenessInfoList);
System.out.println("活体:" + livenessInfoList.get(0).getLiveness());
//口罩检测
List<MaskInfo> maskInfoList = new ArrayList<MaskInfo>();
errorCode = faceEngine.getMask(maskInfoList);
System.out.println("口罩:" + maskInfoList.get(0).getMask());
//IR属性处理
ImageInfo imageInfoGray = ImageFactory.getGrayData(new File("/home/huangyifang/gb/咸阳师范/10.jpg"));
List<FaceInfo> faceInfoListGray = new ArrayList<FaceInfo>();
errorCode = faceEngine.detectFaces(imageInfoGray, faceInfoListGray);
FunctionConfiguration configuration2 = new FunctionConfiguration();
configuration2.setSupportIRLiveness(true);
errorCode = faceEngine.processIr(imageInfoGray, faceInfoListGray, configuration2);
//IR活体检测
List<IrLivenessInfo> irLivenessInfo = new ArrayList<>();
errorCode = faceEngine.getLivenessIr(irLivenessInfo);
System.out.println("IR活体" + irLivenessInfo.get(0).getLiveness());
//获取激活文件信息
ActiveFileInfo activeFileInfo2 = new ActiveFileInfo();
errorCode = faceEngine.getActiveFileInfo(activeFileInfo2);
//更新人脸数据
errorCode = faceEngine.updateFaceData(imageInfo, faceInfoList);
//高级人脸图像处理接口
ImageInfoEx imageInfoEx = new ImageInfoEx();
imageInfoEx.setHeight(imageInfo.getHeight());
imageInfoEx.setWidth(imageInfo.getWidth());
imageInfoEx.setImageFormat(imageInfo.getImageFormat());
imageInfoEx.setImageDataPlanes(new byte[][]{imageInfo.getImageData()});
imageInfoEx.setImageStrides(new int[]{imageInfo.getWidth() * 3});
List<FaceInfo> faceInfoList1 = new ArrayList<>();
errorCode = faceEngine.detectFaces(imageInfoEx, DetectModel.ASF_DETECT_MODEL_RGB, faceInfoList1);
ImageQuality imageQuality1 = new ImageQuality();
errorCode = faceEngine.imageQualityDetect(imageInfoEx, faceInfoList1.get(0), 0, imageQuality1);
FunctionConfiguration fun = new FunctionConfiguration();
fun.setSupportAge(true);
errorCode = faceEngine.process(imageInfoEx, faceInfoList1, fun);
List<AgeInfo> ageInfoList1 = new ArrayList<>();
int age = faceEngine.getAge(ageInfoList1);
FaceFeature feature = new FaceFeature();
errorCode = faceEngine.extractFaceFeature(imageInfoEx, faceInfoList1.get(0), ExtractType.REGISTER, 0, feature);
errorCode = faceEngine.updateFaceData(imageInfoEx, faceInfoList1);
//设置活体测试
errorCode = faceEngine.setLivenessParam(0.5f, 0.7f, 0.3f);
System.out.println("设置活体活体阈值errorCode:" + errorCode);
LivenessParam livenessParam=new LivenessParam();
errorCode = faceEngine.getLivenessParam(livenessParam);
//注册人脸信息1
FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo();
faceFeatureInfo.setSearchId(5);
faceFeatureInfo.setFaceTag("FeatureData1");
faceFeatureInfo.setFeatureData(faceFeature.getFeatureData());
errorCode = faceEngine.registerFaceFeature(faceFeatureInfo);
//注册人脸信息2
FaceFeatureInfo faceFeatureInfo2 = new FaceFeatureInfo();
faceFeatureInfo2.setSearchId(6);
faceFeatureInfo2.setFaceTag("FeatureData2");
faceFeatureInfo2.setFeatureData(faceFeature2.getFeatureData());
errorCode = faceEngine.registerFaceFeature(faceFeatureInfo2);
//获取注册人脸个数
FaceSearchCount faceSearchCount = new FaceSearchCount();
errorCode = faceEngine.getFaceCount(faceSearchCount);
System.out.println("注册人脸个数:" + faceSearchCount.getCount());
//搜索最相似人脸
SearchResult searchResult = new SearchResult();
errorCode = faceEngine.searchFaceFeature(faceFeature, CompareModel.LIFE_PHOTO, searchResult);
System.out.println("最相似人脸Id:" + searchResult.getFaceFeatureInfo().getSearchId());
//更新人脸信息
FaceFeatureInfo faceFeatureInfo3 = new FaceFeatureInfo();
faceFeatureInfo3.setSearchId(6);
faceFeatureInfo3.setFaceTag("FeatureData2Update");
faceFeatureInfo3.setFeatureData(faceFeature2.getFeatureData());
errorCode = faceEngine.updateFaceFeature(faceFeatureInfo3);
//移除人脸信息
errorCode = faceEngine.removeFaceFeature(6);
//引擎卸载
errorCode = faceEngine.unInit();
}
}

View File

@ -0,0 +1,11 @@
package com.guwan.backend.face.util;
import lombok.Data;
@Data
public class UserInfo {
private String faceId;
private String name;
private byte[] faceFeature;
}

View File

@ -0,0 +1,66 @@
package com.guwan.backend.face.util;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
public class UserRamCache {
private final ConcurrentHashMap<String, UserInfo> USER_INFO_MAP = new ConcurrentHashMap<>();
private final Set<Listener> REGISTER = new CopyOnWriteArraySet<>();
public void addUser(UserInfo userInfo) {
USER_INFO_MAP.put(userInfo.getFaceId(), userInfo);
for (Listener listener : REGISTER) {
listener.onAdd(userInfo);
}
}
public void removeUser(String faceId) {
UserInfo userInfo = USER_INFO_MAP.remove(faceId);
for (Listener listener : REGISTER) {
listener.onRemove(userInfo);
}
}
public List<UserInfo> getUserList() {
List<UserInfo> userInfoList = Lists.newLinkedList();
userInfoList.addAll(USER_INFO_MAP.values());
return userInfoList;
}
public void clear(){
USER_INFO_MAP.clear();
REGISTER.clear();
}
public void addListener(Listener listener) {
REGISTER.add(listener);
}
public void removeListener(Listener listener) {
REGISTER.remove(listener);
}
// @Data
// public class UserInfo {
//
// private String faceId;
// private String name;
// private byte[] faceFeature;
//
// }
public interface Listener {
default void onAdd(UserInfo userInfo) {
}
default void onRemove(UserInfo userInfo) {
}
}
}

View File

@ -0,0 +1,42 @@
package com.guwan.backend.face.util;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class UserRamGroup {
private static final ConcurrentHashMap<String, UserRamCache> USER_RAM_GROUP_MAP = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, String> ORG_CELL_MAP = new ConcurrentHashMap<>();
public static void addCell(String cellId){
UserRamCache cell = new UserRamCache();
USER_RAM_GROUP_MAP.put(cellId,cell);
}
public static void removeCell(String cellId){
USER_RAM_GROUP_MAP.remove(cellId);
}
public static void addUser(UserInfo userInfo, String cellId) {
USER_RAM_GROUP_MAP.get(cellId).addUser(userInfo);
}
public static void removeUser(String faceId, String cellId) {
USER_RAM_GROUP_MAP.get(cellId).removeUser(faceId);
}
public static List<UserInfo> getUserList(String cellId) {
return USER_RAM_GROUP_MAP.get(cellId).getUserList();
}
public static void addOrgId(String orgId,String cellId){
ORG_CELL_MAP.put(orgId,cellId);
}
public static String getOrgCellMap(String orgId) {
return ORG_CELL_MAP.get(orgId);
}
public static void clear(){
USER_RAM_GROUP_MAP.clear();
}
}

View File

@ -42,7 +42,7 @@ public class VideoServiceImpl implements VideoService {
try {
// 上传视频文件到MinIO
String fileName = minioUtil.uploadFile("videos", file);
String url = minioUtil.getFileUrl("videos", fileName);
String url = minioUtil.getUrl(minioUtil.getFileUrl("videos", fileName));
// 创建视频记录
Video video = new Video();
@ -209,7 +209,7 @@ public class VideoServiceImpl implements VideoService {
BeanUtils.copyProperties(video, dto);
// 设置是否已点赞
dto.setHasLiked(hasLiked(video.getId()));
// dto.setHasLiked(hasLiked(video.getId()));
return dto;
}

View File

@ -31,6 +31,7 @@ public class MinioUtil {
* 创建存储桶
*/
public void createBucket(String bucketName) {
System.out.println("bucketName = " + bucketName);
try {
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!found) {
@ -46,6 +47,11 @@ public class MinioUtil {
* 上传文件
*/
public String uploadFile(String bucketName, MultipartFile file) {
createBucket(bucketName);
try {
String fileName = generateFileName(file.getOriginalFilename());
InputStream inputStream = file.getInputStream();
@ -153,6 +159,11 @@ public class MinioUtil {
}
}
public String getUrl(String url) {
return url.substring(0, url.indexOf("?"));
}
/**
* 生成文件名
*/

View File

@ -8,8 +8,8 @@ spring:
# 视频上传配置
servlet:
multipart:
max-file-size: 500MB
max-request-size: 500MB
max-file-size: 500MB
max-request-size: 500MB
# 数据库配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
@ -111,4 +111,17 @@ springdoc:
group-configs:
- group: '默认'
paths-to-match: '/**'
packages-to-scan: com.guwan.backend.controller
packages-to-scan: com.guwan.backend.controller
config:
arcface-sdk:
version: 4.1
app-id: BYYPcPa3qQaAA88HZw7awne1BqZiePgT6axtALtK2qun
sdk-key: 3eFAwSCMQRhoUoAaZ2Li9XUVDUubAWYksQtNPWD32UrX
active-key: 82K1-11TQ-W11B-H7MU
active-file: ArcFacePro64.dat
detect-pool-size: 16
compare-pool-size: 16
rec-face-thd: 0.8
rec-id-thd: 0.5

Binary file not shown.

Binary file not shown.